Не нарушены ли зашифрованные транзакции neo4j?
TL;DR: Я либо схожу с ума, либо транзакции neo4j слегка нарушены. Похоже, что незафиксированные узлы доступны за пределами зафиксированных транзакций, с отсутствующими свойствами - или чем-то столь же странным.
Наше приложение node.js использует neo4j. Часть этого должна генерировать уникальные идентификаторы. У нас есть следующий запрос шифра, который предназначен для поиска последнего :Id
тип узла и попытка совершить новый :Id
узел с 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
И БД инициализируется с (:Id{uuid:1})
чтобы начать эту радость.
Код приложения в основном повторяет вышеуказанный запрос, пока он не будет успешным. Если два или более запроса на создание идентификатора будут выполнены одновременно, будет выполнен только один из них, остальные не пройдут и будут повторены кодом приложения.
Это работало, пока мы не попробовали это параллельно.
Код начал возвращать данные без uuid. После многих исследований выясняется, что часть запроса на запись (CREATE...) каким-то образом получает:Id из MATCH, который не имеет свойств.uuid (или других). Это не должно быть возможно. Это единственный код, который работает на этих узлах.
Самое странное (возможно) то, что если я спасу i
"s nodeid
чтобы найти этот узел в БД, он действительно существует и имеет свойство.uuid.
Чтобы изолировать это поведение, я написал PoC: neo4j -action-test. Это должно быть действительно просто для запуска с nodejs.
Это в основном немного больше, чем приведенный выше код - пытается создать Id, настройка prev_label
, prev_nodeid
, а также prev_uuid
к предыдущим значениям узла (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[]
чего и следовало ожидать, и они повторяются. Если мы "хлопаем" сервер с парой запросов в секунду (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
(и, в конце концов, я имею в виду почти мгновенно) Узел возвращается без uuid, prev_uuid или prev_label. this_nodeid и prev_nodeid ссылаются на внутренний идентификатор neo4j. Если мы посмотрим на это, начиная с предыдущего (i
) Идентификационный узел (по идентификатору узла - 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 просачивается в мой запрос?
Я попытался перезапустить, стереть каталог данных, перезапустить после стирания каталога данных, очистить журналы (ничего интересного или даже скучного, но в нужное время - когда вышеописанное происходит). Сейчас я нахожусь в точке, где я подвергаю сомнению свое понимание того, как это должно работать.
Это 12.04 с neo4j 2.1.1. Дополнительная информация о версии и журналы запуска / выключения Neo4j.
Я знаю, что это не оптимальный способ создания UUID. Этот вопрос касается понимания того, как эти результаты возможны, если транзакции neo4j работают так, как ожидалось.
1 ответ
Мы заметили аналогичную проблему в Neo4J во время одновременной записи транзакций, и в Neo4J 2.2.5 было исправлено, чтобы решить эту проблему (у нас есть поддержка Enterprise и мы подняли заявку, чтобы исправить это). У вас может не быть точно такой же проблемы, но, возможно, стоит попробовать еще раз, используя 2.2.5, чтобы убедиться, что это все еще проблема.
Как вы сказали, есть лучшие способы для создания идентификаторов. Кроме того, вы должны использовать MAX
чтобы получить последнюю версию вместо LIMIT
а также ORDER BY
, но это к тому же еще и суть;-).
Удачи.