Neo4j: слияние создает дубликаты узлов

моя модель базы данных пользователей и MAC-адреса. Пользователь может иметь несколько MAC-адресов, но MAC может принадлежать только одному пользователю. Если какой-то пользователь устанавливает свой MAC и этот MAC уже связан с другим пользователем, существующая связь удаляется и создается новая связь между новым владельцем и этим MAC. Другими словами, MAC перемещается между пользователями.

это конкретный экземпляр запроса шифрования, который я использую для назначения MAC-адресов:

MATCH (new:User { Id: 2 })
MERGE (mac:MacAddress { Value: "D857EFEF1CF6" })
WITH new, mac
OPTIONAL MATCH ()-[oldr:MAC_ADDRESS]->(mac)
DELETE oldr
MERGE (new)-[:MAC_ADDRESS]->(mac)

в запрос отлично работает в моих тестах, но в производстве, по какой-то странной причине это иногда создает дубликат MacAddress узлы (и новые отношения между Пользователем и каждым из этих узлов). То есть у конкретного пользователя может быть несколько MacAddress узлы с тем же Value.

Я могу сказать, что это разные узлы, потому что у них разные идентификаторы узлов. Я также уверен, что Values точно такие же, потому что я могу сделать collect(distinct mac.Value) на них и результатом является коллекция с одним элементом. Приведенный выше запрос является единственным в коде, который создает MacAddress узлы.

Я использую Neo4j 2.1.2. Что здесь происходит?

спасибо, Ян!--8-->

2 ответов


вы уверены, что это все запросы, которые вы запускаете? MERGE имеет эту действительно общую ловушку, где она объединяет все, что вы даете ему. Итак, вот что люди ожидают:

neo4j-sh (?)$ MERGE (mac:MacAddress { Value: "D857EFEF1CF6" });
+-------------------+
| No data returned. |
+-------------------+
Nodes created: 1
Properties set: 1
Labels added: 1
1650 ms
neo4j-sh (?)$ MERGE (mac:MacAddress { Value: "D857EFEF1CF6" });
+--------------------------------------------+
| No data returned, and nothing was changed. |
+--------------------------------------------+
17 ms
neo4j-sh (?)$ match (mac:MacAddress { Value: "D857EFEF1CF6" }) return count(mac);
+------------+
| count(mac) |
+------------+
| 1          |
+------------+
1 row
200 ms

пока все хорошо. Это то, чего мы ожидаем. Теперь смотрите:

neo4j-sh (?)$ MERGE (mac:MacAddress { Value: "D857EFEF1CF6" })-[r:foo]->(b:SomeNode {label: "Foo!"});
+-------------------+
| No data returned. |
+-------------------+
Nodes created: 2
Relationships created: 1
Properties set: 2
Labels added: 2
178 ms
neo4j-sh (?)$ match (mac:MacAddress { Value: "D857EFEF1CF6" }) return count(mac);                    
+------------+
| count(mac) |
+------------+
| 2          |
+------------+
1 row
2 ms

подождите, WTF произошло здесь? Мы снова указали только один и тот же MAC-адрес, почему создается дубликат?

на документация по слиянию указывает, что "слияние будет не частично использовать существующие шаблоны - это все или ничего. Если требуется частичное совпадение, это может быть достигнуто путем разделения шаблона на несколько предложений слияния". Поэтому, когда мы запускаем этот путь MERGE весь путь еще не существует, он создает все в нем, включая дубликат узла mac-адреса.

часто возникают вопросы о дублированных узлах, созданных MERGE, и 99 раз из 100, это то, что происходит.


Это ответ, который я получил от поддержки Neo4j (акцент мой):

Я уже получил некоторые отзывы от нашей команды, и в настоящее время известно, что это может произойти при отсутствии ограничений. Слияние эффективно соответствует или создает - и эти два шага выполняются независимо в транзакции. Учитывая одновременное выполнение и уровень изоляции" Read committed", между ними существует условие гонки.

команда сделал некоторое обсуждение о том, как обеспечить более высокую гарантию перед лицом параллелизма, и сделать это отмечено в качестве запроса функции для рассмотрения.

между тем, они заверили меня, что использование ограничения обеспечит уникальность вы ищете.