У меня есть кусок кода, который удаляет вершину и фиксирует транзакцию.Следующая операция все еще видит вершину по какой-то причине.Также странно, что он видит только то, что иногда может быть основан на времени и т. Д. Например, графовая служба - содержит -> маршрут
операция 1: удаление содержит ребро и удаление вершины и операция фиксации
2: get содержит ребро из сервисного узла, и он по-прежнему получает узел маршрута, который был удален в операции 1
2 операции выполняются одна за другой и не выполняются параллельно, поэтому не возникает проблем с чтением до 1-гоcommit.
Также, если 1-й коммит завершен успешно, то я понимаю, что все остальные потоки должны немедленно увидеть обновления.
с использованием janusgraph api для java с cassandra db
sampleпсевдокод:
synchronized methodA:
do some operations
figure out route X need to be deleted from graph
get all routes using contains edge from service node
// service---contains--> route
get route X from all routes
singlethreadExecutor.submitTask(DeleteRoute X)
update some other DB with service without route X
Task DeleteRoute (route x)
get route X from graph DB
delete route X vertex
commit
Operation1 calls into methodA:
service with 4 routes R1,R2, R3, R4
Expected to delete R3
Works as expected
R3 is deleted from graph as well as other DB
Operation2 calls into methodA:
service expected routes in graph with R1, R2, R4
however, method A still gets all 4 routes including R3 which is deleted in operation 1
Обратите внимание, что метод А синхронизирован, поэтому операции 1 и 2 не конфликтуют друг с другом.операция 1 завершена, а затем начинается операция 2
Меня это озадачивает, особенно когда мои журналы указывают, что фиксация завершена для операции 1, а операция 2 все еще получает узел маршрута R3 из графа с использованием janusgraph api.
Мы не используем потоковую транзакцию. Мы не используем новую транзакцию. Мы полагаемся на то, что tinkerpop открывает новую транзакцию с первой операцией для потока.
фрагменты журнала:
Операция 1:
2019-06-17 14: 58: 25,213 |deleteNode: маршрут: 1560307936368: 1683669533 2019-06-17 14: 58: 25 216 |совершить 2019-06-17 14: 58: 25,350 |Время, затраченное на коммит = 133
Операция 2:
2019-06-17 14: 58: 25,738 |updateNode 2019-06-17 14: 58: 25,739 |updateNode Узел, подлежащий обновлению: маршрут: 1560307936368: 1683669533 2019-06-17 14: 58: 25,740 |updateVertex: вершина обновлена для ключа: маршрут: 1560307936368: 1683669533 2019-06-17 14: 58: 25,741 |Время обновления updateNode, занятое в updateNode = 3
Как вы можете видеть, Операция 1 удаляет узел маршрута и фиксирует, а операция 2, когда она читает из графика, все еще получает тот же узел маршрута и смогла его обновить.Наш обновляющий API проверяет, присутствует ли вершина перед обновлением, и выдает ошибку, если ее нет.
Таким образом, очевидно, что вершина все еще возвращается из графа с использованием janusgraph getVertex api на основе ключа идентификатора узла, даже если удаление было успешными фиксация была завершена как раз перед этим.
Тот же самый код работает, как и ожидалось, если разница во времени между двумя операциями манипулируется, чтобы быть больше пары минут.
Мы также настроили использоватьjanushgraph cache.
Учитывая все это, я действительно озадачен, как это вообще происходит.
Я могу понять, работают ли две операции как-то параллельно и наступают друг на друга и участвуют в гонке.условия могут дать мне устаревшие данные, но операции синхронизируются и происходят одна за другой.
ожидается, что не вернет вершину во 2-й операции после ее удаления и фиксации в 1-й операции, особенно когда обе операции синхронизированы и происходятодин за другим без каких-либо фаяприманки / исключения.
Вариант использования 1:
Поток-1 ---- вызовы ---> синхронизированный метод-1 ---> получитьребро / вершина, обновить вершину, зафиксировать ---- отправляет ---> singleThreadedExecutorTask ---> удалить ребро / вершину, зафиксировать ----> вызовы -> синхронизированный метод-1 (для операции 2) ----> здесь get edge / vertex по-прежнему получает старое ребро / vertex
Я могу понять вариант использования 2, где область транзакции предназначена для потока с первой операцией, а все, что зафиксировано в других потоках, не являетсявидимый в этой области транзакции, поэтому я должен в идеале зафиксировать транзакцию перед запуском операции 2, чтобы увидеть изменения.
Я пробовал это для варианта использования 2, и он работает как ожидалось !!
Вариант использования 2:
Thread-1 ---- вызовы ---> синхронизированный метод-1 ---> получить ребро / вершину, обновить вершину, зафиксировать ---- отправляет ---> singleThreadedExecutorTask ---> удалить ребро / вершину , совершить ----> Завершение темы-1.
Примерно через минуту:
Thread-2 ---- вызовы ---> синхронизированный метод-1 ---> получить ребро / вершину, обновить вершину, зафиксировать ---- отправляет ---> singleThreadedExecutorTask ---> удалить ребро / вершину ,
commit ----> Thread-2 Завершает.
Проблема Вызов Thread-2 в синхронизированный метод-1 по-прежнему получает старый ребро / вершину, которая удалена как часть процесса Thread-1.
Теперь в этом случае.
Транзакция с областью потока-1 открывается с первой операцией графа, и эта транзакция закрывается сразу после обновления.
После того, как задача singleThreadedExecutor запускается в отдельном потоке, она открывает собственную новую транзакцию для 1-й операции и закрывает транзакцию с фиксацией, когда задача завершена.
Поток-2, когда он запускается через минуту, открывает собственную транзакцию с областью потока с 1-й операцией графа - эта операция get в этой новой области транзакции потока должна быть в состоянии получить правильные данные без удаленного ребра / вершины из потока 1, особенно с учетом ти начинается почти через 1 минуту.
Это даже не кластерная установка.
И даже с кластерной настройкой - я думал, что кворум должен быть удовлетворен, прежде чем вызовы коммитов могут вернуться, а остальная часть репликации может произойти независимо (с задержкой)
Это часть, которую я не могу понять, конечно, если я добавлю ручное вмешательство с двумя потоками, такими как запуск потока 1, может быть через 2 минуты, это работает по какой-то причине.
2 минуты, кажется, очень долго для возможной последовательности в этом случае.
Так каков вариант для приложения, чтобы справиться с этим?
Есть ли способ заставить графическую операцию ждать возможной согласованности?
Как и thread-2, я могу указать, что первая операция get должна ждать, пока она не вернет согласованные данные, разрешив все конфликты и т. Д.
Я не думаю, что открытие новой транзакции в потоке 2 или попытка выполнить какое-либо глобальное принятие для закрытия предыдущей устаревшей транзакции, если таковой существует, является правильным способом сделать это, поскольку это только начало нового потока.