Сравнить и поменять местами в Berkeley DB JE?

Я ищу эффективный способ реализации операции сравнения и замены в Berkeley DB. Сейчас я использую очень старую версию, однако, на первый взгляд, даже самая последняя (распространяемая с сайта Oracle) не имеет единого метода для такой операции.

Я искал какой-то метод, например

replace(Transaction, Key, ExpectedValue, NewValue) 

со следующей семантикой: БД получает значение, связанное с данным ключом, если это значение существует и равно ExpectedValue, то это значение будет изменено на NewValue, в противном случае метод возвращает неуспешный OperationStatus.

Похоже, такого метода нет, поэтому мне интересно, как это сделать наиболее эффективным способом.

Прямо сейчас я использую следующий подход: я делаю

db.get(null, key) -> {currentValue, version}
db.put(null, key, {currentValue, newRandomIdVersion}) 
db.get(null, key)

Я сравниваю значение и версию, если они совпадают, я делаю окончательное обновление, удаляя старую версию. Если какой-либо шаг терпит неудачу, весь процесс перезапускается.

Я чувствую, что это очень неоптимально - я ошибаюсь?


person Alex    schedule 11.07.2016    source источник
comment
Вы пробовали что-нибудь?   -  person Jim Garrison    schedule 11.07.2016
comment
В качестве первого шага попробуйте использовать транзакцию и посмотрите, достаточна ли производительность в масштабе.   -  person Mike Andrews    schedule 11.07.2016
comment
Вы имеете в виду просто обновить счетчик внутри транзакции? Это не поможет - параллельные транзакции просто переопределят себя.   -  person Alex    schedule 12.07.2016
comment
Ну, я не уверен, что вы подразумеваете под переопределением, но они обеспечивают атомарность. В случае конфликта одна транзакция выигрывает, а N проигрывают, и ваше приложение повторяет попытку. Если вам действительно нужны одновременные записи в одну и ту же запись, посмотрите, сможете ли вы понять, как разделить ее на несколько записей. С тем, что у вас есть, вам может быть лучше с кольцевым журналом и сортировкой правильного состояния уровня приложения при восстановлении.   -  person Mike Andrews    schedule 12.07.2016
comment
Да, вы правы, транзакции атомарны, но опять же, если они начинают одновременно обновлять одну и ту же запись, первая транзакция может не увидеть результаты обновления записи второй транзакцией и наоборот. В то же время сравнение и обмен предполагает, что никакие обновления не будут потеряны.   -  person Alex    schedule 18.07.2016


Ответы (1)


Мое решение в обновлении моего вопроса неверно, однако для его улучшения требуется лишь небольшая модификация.

Решение может быть таким: создать отдельную БД для хранения блокировок, которые будут содержать привязки ключа к какому-то счетчику. Эта БД должна допускать отсортированные дубликаты (чтобы Database.get возвращал наименьшее значение, связанное с данным ключом). Затем используйте общий монотонно увеличивающийся счетчик. Несколько потоков, пытающихся выполнить CAS, получат значения из этого счетчика и сохранят пары ключ-значение в этой базе данных блокировок. Поток, который сохранил наименьшее значение, связанное с ключом, предполагает, что у него есть разрешение на запись, и выполняет сравнение и замену нужной записи, затем удаляет свою запись из базы данных блокировок, другие потоки просто повторяют попытку.

person Alex    schedule 17.07.2016