Подтвердить что ты не робот

Неужели транзакции neo4j cypher нарушены?

TL; DR: Я либо теряю рассудок, либо транзакции neo4j немного сломаны. Похоже, что незафиксированные узлы доступны за пределами совершенных транзакций с отсутствующими свойствами - или что-то столь же странное.

В нашем приложении node.js используется neo4j. Часть его должна генерировать уникальные идентификаторы. У нас есть следующий запрос cypher, предназначенный для поиска последнего :Id -type node и попытка совершить новый :Id node с last_uuid+1.

MATCH (i:Id) WITH i ORDER BY i.uuid DESC LIMIT 1  #with it like a sub-return, will "run" the rest with the last i at read-time
CREATE (n:Id {label:"Test"}) 
SET n.uuid = i.uuid + 1
RETURN n

Существует также ограничение:

neo4j-sh (?)$ schema
Indexes
  ON :Id(uuid) ONLINE (for uniqueness constraint) 

Constraints
  ON (id:Id) ASSERT id.uuid IS UNIQUE

И DB инициализируется с помощью (:Id{uuid:1}), чтобы начать эту радость.

Код приложения в основном повторяет указанный выше запрос до тех пор, пока он не будет успешным. Если два или более запроса на создание идентификатора одновременно попадают, только один из них пройдет, остальные будут терпеть неудачу и будут повторно проверены кодом приложения.

Это сработало, пока мы не попробовали его параллельно.

Код начал возвращать данные без uuid. После большого исследования выясняется, что часть записи запроса (CREATE...) каким-то образом получает: Id из MATCH, который не имеет .uuid(или других) свойств. Это не должно быть возможным. Это единственный код, который работает на этих узлах.

Самое странное (возможно) дело в том, что если я сохраню i nodeid, чтобы найти этот node в БД, он действительно существует и имеет свойство .uuid.

Чтобы изолировать это поведение, я написал PoC: neo4j-transaction-test Это должно быть очень просто запустить с nodejs.

В основном это немного больше, чем приведенный выше код - пытается создать Id, установив prev_label, prev_nodeid и prev_uuid в предыдущие значения node (i). Он запускает запрос для каждого запроса GET, который он получает на localhost: 9339 и выдает:

 > node server.js 
 * 1412125626667 Listening on 9339
 Req Id | Datetime | -> $uuid $nodeid
 1 1412125631677 'GET'       # When it first receives the GET request
 1 1412125631710 '->' 9 60   # When neo4j returns; numbers are $uuid $node_id)

когда все начинает становиться параллельным, запросы могут завершиться неудачно:

3 1412125777096 '(retry) (0)' 'Node 64 already exists with label Id and property "uuid"=[13]'
4 1412125777098 '(retry) (0)' 'Node 64 already exists with label Id and property "uuid"=[13]'
de[]

что и следовало ожидать, и они повторены. Если мы "захлопываем" сервер с помощью пары reqs в секунду (ab -n 1000 -c 10 http://localhost:9339/), мы, в конце концов, увидим:

...
59 1412127103011 'GET'
23 1412127103024 'ERROR - EMPTY UUID' '{"this_nodeid":22,"prev_nodeid":20,"label":"Test"}'

Error: Empty UUID received

(и, в конце концов, я имею в виду почти мгновенно) A node возвращается без uuid, prev_uuid или prev_label. this_nodeid и prev_nodeid относятся к внутреннему id neo4j. Если мы посмотрим на них, начиная с предыдущего (i) Id node (by nodeid - 20):

neo4j-sh (?)$ match (i) where id(i)=20 return i;
+--------------------------------------------------------------------------------------------+
| i                                                                                          |
+--------------------------------------------------------------------------------------------+
| Node[20]{uuid:10,label:"Test",prev_label:"Test",prev_uuid:9,prev_nodeid:17,this_nodeid:20} |
+--------------------------------------------------------------------------------------------+
1 row
19 ms

Это точно так, как должно быть. .uuid и все. Новый действительно создается так же, как он возвращается:

neo4j-sh (?)$ match (i) where id(i)=22 return i;
+------------------------------------------------------+
| i                                                    |
+------------------------------------------------------+
| Node[22]{label:"Test",prev_nodeid:20,this_nodeid:22} |
+------------------------------------------------------+
1 row
17 ms

Нет prev_label или prev_uuid. Как это возможно? Что мне не хватает? Является неполным: Id node просачивается в мой запрос?

Я попытался перезапустить, уничтожить данные dir, перезапустить после очистки данных dir, очистить журналы (ничего интересного или даже скучного, но в нужное время - когда это происходит). Теперь я нахожусь в точке, где я задаю себе вопрос о том, как это должно работать.

Это 12.04 с neo4j 2.1.1. Дополнительная информация о версии и журналы запуска/завершения Neo4j.

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

4b9b3361

Ответ 1

Мы заметили подобную проблему в Neo4J во время одновременной транзакционной записи, и было исправлено, введенное в Neo4J 2.2.5 для решения этой проблемы (у нас есть поддержка Enterprise и поднят билет, чтобы получить это исправление). У вас может не быть точно такой же проблемы, но, возможно, стоит попробовать еще раз использовать 2.2.5, чтобы увидеть, все еще проблема.

Как вы сказали, есть лучшие способы генерации идентификаторов. Кроме того, вы должны использовать MAX, чтобы получить последние, а не LIMIT и ORDER BY, но помимо точки также: -).

Удачи.