2015-10-07 5 views
1

Я нашел параллельную проблему в своем приложении, где есть два потока, которые пытаются выполнить операцию записи в одно и то же время на одном узле или в Neo4J v2.2.5.Механизм блокировки на Neo4J

мне удалось воспроизвести проблему, используя простой способ:

  1. базы данных Загрузка и импорт пример Neo4j фильм: http://example-data.neo4j.org/files/cineasts_12k_movies_50k_actors_2.1.6.zip Поскольку база данных устарела, так что вы должны добавить allow_store_upgrade=true в conf/neo4j.properties для того, чтобы обновить авто базы данных ,

  2. Запустить Neo4j и запустить этот запрос на neo4jshell:

    match (a:Actor {name: "Claude Jade"}), (m:Movie) 
    merge (a)-[:ACTS_IN]->(m); 
    

    Это создаст ACTS_IN отношения с Актера "Claude Jade" ко всем узлам Movie. Причина этого заключается в том, чтобы сделать удаление узла «Claude Jade» актера (см. № 4 ниже) более продолжительным, поэтому вероятность одновременной проблемы больше.

  3. Скачайте программу командной строки curl, если у вас ее еще нет. Мы будем использовать curl для отправки запроса в neo4j.

  4. Создай файл Баш скрипт (имя файла до вас) с содержанием:

    #!/bin/bash 
    
    curl -XPOST http://localhost:7474/db/data/transaction/commit -H "Content-Type: application/json" -d '{"statements" : [ {"statement" : "MATCH (n:Actor {name: \"Claude Jade\"}) OPTIONAL MATCH (n)-[r]-() DELETE n, r"} ]}' & 
    curl -XPOST http://localhost:7474/db/data/transaction/commit -H "Content-Type: application/json" -d '{"statements" : [ {"statement" : "MATCH (n:Actor {name: \"Claude Jade\"}) CREATE (n)-[:ACTS_IN]->(m:Movie {title: \"Hello World\"}) RETURN m"} ]}' & 
    
    wait 
    

    Это будет работать два завиток процесса параллельно, где первый процесс попытается удалить «Claude Jade» Актер и все его отношения, а второй процесс попытается создать новое отношение ACTS_IN к актеру «Клод Джейд».

  5. Запустите файл сценария в bash, например. $ ./test.sh

Вот результат я получил:

{ 
    "results" : [{ 
      "columns" : ["m"], 
      "data" : [{ 
        "row" : [{ 
          "title" : "Hello World" 
         } 
        ] 
       } 
      ] 
     } 
    ], 
    "errors" : [] 
} { 
    "results" : [{ 
      "columns" : [], 
      "data" : [] 
     } 
    ], 
    "errors" : [{ 
      "code" : "Neo.DatabaseError.Transaction.CouldNotCommit", 
      "message" : "org.neo4j.kernel.api.exceptions.TransactionFailureException: Node record Node[3150,used=false,group=55,prop=-1,labels=Inline(0x0:[]),light] still has relationships", 
      "stackTrace" : "java.lang.RuntimeException: org.neo4j.kernel.api.exceptions.TransactionFailureException: Node record Node[3150,used=false,group=55,prop=-1,labels=Inline(0x0:[]),light] still has relationships\r\n\tat org.neo4j.server.rest.transactional.TransitionalTxManagementKernelTransaction.commit(TransitionalTxManagementKernelTransaction.java:87)\r\n\tat org.neo4j.server.rest.transactional.TransactionHandle.closeContextAndCollectErrors(TransactionHandle.java:278)\r\n\tat 
      ... 

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

Так от того, что я видел, удаление не удалось, поскольку он пытался удалить узел Актер и его всех ACTS_IN отношений, он сделал запрос MATCH первым (MATCH (n:Actor {name: \"Claude Jade\"}) OPTIONAL MATCH (n)-[r]-()), но прежде чем он выполнил DELETE n, r, 2-й процесс удалось вставить новое отношение ACTS_IN к узлу Actor, поэтому причина не удалась, потому что когда он пытался выполнить DELETE, у Актера уже было добавлено новое отношение.

Интересно, существует ли механизм блокировки Neo4J, который можно использовать для предотвращения этой проблемы?

ответ

1

Вы наблюдаете классическое состояние гонки. Чтобы предотвратить необходимость захвата блокировки на узле Claude. Cypher не имеет синтаксиса для явного захвата блокировок, поэтому мы просто устанавливаем свойство fake как первое действие и удаляем это свойство в конце - это имеет побочный эффект захвата блокировки. Так измените два заявления:

MATCH (n:Actor {name: "Claude Jade"}) 
SET n._fake = 1 // grabs the lock as first action 
WITH n 
OPTIONAL MATCH (n)-[r]-() 
DELETE n, r 

MATCH (n:Actor {name: "Claude Jade"}) 
SET n._fake = 1 // grab the lock early 
CREATE (n)-[:ACTS_IN]->(m:Movie {title: "Hello World"}) 
REMOVE n._fake // get rid of fake property 
RETURN m 
+0

Привет, спасибо за ваше решение, я попробовал, иногда это работает, но иногда я получил сообщение об ошибке для 2-го процесса завитка (создать новые отношения ACTS_IN): '{«результат» : [], "errors": [{"code": "Neo.ClientError.Statement.EntityNotFound", "message": "Невозможно загрузить NODE с идентификатором 3150."}]}' – jordom

+0

С использованием фальшивого свойства вы фактически сериализуете два оператора, так как другим придется дождаться, когда первый будет зафиксирован. Поскольку вы запускаете оба оператора одновременно, не детерминировано, какое из те, кто первым захватывает блокировку.Если оператор удаления выбирает первый, он удалит узел и rels, а совпадение второго не будет выполнено (поскольку узел ушел) –

+0

Если оператор удаления запускается первым, процесс 2-го совпадения действительно завершится неудачно , но он не должен возвращать ошибку, вместо этого он должен возвращать пустые данные. Я думаю, что произошло, когда строка 'SET n._fake = 1' была встречена во втором процессе, она остановила выполнение запроса до тех пор, пока 1-й процесс не выпустил Затем, когда он отпущен, он попытался возобновить выполнение запроса до следующей строки: 'CREATE (n) - [: ACTS_IN] -> (m: Movie {title: \" Hello World \ "}), однако узел 'n' уже удален 1-м процессом, поэтому ошибка« Невозможно загрузить NODE w с идентификатором ... ". – jordom

Смежные вопросы