2015-06-12 2 views
2

Я не уверен в правильном дизайне подхода.Оптимистическая блокировка и повторная попытка

Мы используем оптимистичную блокировку с использованием long инкрементной версии, размещенной на каждом объекте. Каждое обновление такого объекта выполняется с помощью алгоритма сравнения и свопинга, который просто преуспевает или выходит из строя в зависимости от того, является ли какое-то другое средство обновления клиента тем временем или нет. Классическая оптимистическая блокировка, например. спящий режим.

Нам также необходимо принять повторный подход. Мы используем хранилище на основе http (etcd), и может случиться, что некоторый запрос на обновление просто отключен.

И вот в чем проблема. Как сочетать оптимистичную блокировку и повторную попытку. Вот конкретная проблема, с которой я сталкиваюсь.

Позвольте сказать, что у меня есть объект, имеющий version=1, и я пытаюсь его обновить. Следующая версия, очевидно, 2. Мой клиент, чем выполняет условное обновление. Он успешно выполняется только в том случае, если версия с сохранением - 1, и она автоматически обновляется до version=2. Все идет нормально.

Теперь скажем, что ответ на запрос обновления не поступает. Невозможно сказать, удалось ли это или нет в данный момент. Единственное, что я могу сделать сейчас, это: повторите попытку обновления. В объекте памяти все еще содержится version=1, намеревающийся обновить значение до 2.

Настоящая проблема возникает сейчас. Что делать, если второе обновление не выполняется, потому что версия с сохранением - 2, а не 1?

Существует две возможные причины:

  1. первый запрос действительно вызвало обновление - операция прошла успешно, но ответ был потерян или мой клиент тайм-аут, что угодно. Он просто не пришел, но он прошел
  2. другой клиент выполнил обновление одновременно на фоне

Сейчас я не могу сказать, что это правда. Мой клиент обновил объект или какой-либо другой клиент? Произошла или не прошла операция?

Текущий подход, который мы используем только , сравнивает постоянный объект и объект в основной памяти. Либо как java равно, либо json-контент. Если они равны, методы обновления объявляются успешными. Я не доволен алгоритмом, поскольку он не дешев и не разумен для меня.

Другой возможный подход заключается в том, чтобы не использовать long, но вместо этого timestamp. Каждый клиент генерирует собственную метку времени в рамках операции обновления в смысле, что потенциальный одновременный клиент будет генерировать другие с высокой вероятностью. Проблема для меня - это вероятность, особенно когда два параллельных обновления будут поступать с одной машины.

Есть ли другое решение?

ответ

1

IMHO, as etcd построен на основе HTTP, который по своей сути является небезопасным протоколом, будет очень сложно иметь пуленепробиваемое решение.

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

Таким образом, если 2 операции зависят друг от друга (перевод денег с одного банковского счета на другой), вы можете убедиться, что оба они в порядке или нет, и вы можете просто реализовать в базе данных журнал «операций» с их статус, чтобы иметь возможность позже увидеть, прошел ли какой-либо особый вопрос, консультируясь с журналом, даже если вы были отключены в середине фиксации.

Но я просто не могу представить такое решение для etcd. Так что, если кто-то не найдет лучший способ, вы остались с двумя вариантами

  • Используйте классической базы данных SQL в интерфейсе, используя etcd (или эквивалент) в качестве простого кэша
  • принимают слабые стороны протокол

BTW, я не думаю, что временная метка взамен длинного номера версии укрепит систему, потому что при высокой нагрузке вероятность того, что две транзакции клиента будут использовать одну и ту же метку времени, увеличивается. Может быть, вы можете попробовать добавить уникальный идентификатор (идентификатор клиента или только технический uuid) в свои поля, а когда версия равна n + 1, просто сравните UUID, который его увеличил: если он принадлежит вам, транзакция прошла, если не был id не.

Но действительно худшая проблема возникла бы, если на данный момент вы можете прочитать версию, это не при n + 1, а уже при n + 2. Если UUID принадлежит вам, вы уверены, что ваша транзакция прошла, но если его никто не может сказать.

1

Вы можете поддельные транзакции в etcd с использованием двухэтапного протокола.

Алгоритм обновления:

Первый этап: записать обновление etcd

  • добавить «обновление закрепки» узел с довольно небольшой TTL. Если он существует, подождите, пока он не исчезнет, ​​и повторите попытку.
  • Добавить сторожевой кодекс. Вы ДОЛЖНЫ прерываться, если выполнение следующих шагов занимает больше времени, чем TTL блокировки (или если вы не обновите его).
  • добавить узел «Обновить план» с [старыми, новыми] значениями. Его структура зависит от вас, но вам нужно убедиться, что старые значения скопированы, когда вы удерживаете блокировку.
  • добавить узел с исправленным обновлением. На этом этапе вы «атомарно» обновили данные.

Вторая фаза: выполнить фактическое обновление

  • читать «планово-обновления» узел и применить изменения, которые она содержит.
    • Если сбой не выполнен, убедитесь, что новое значение присутствует.
    • Если это не так, у вас есть серьезная проблема. Выйдите.
  • удалить узел привержен-обновления
  • удалить узел обновления плана
  • удалить обновление блокировки узла

Если вы хотите прочитать непротиворечивые данные:

  • Пока нет узла фиксированного обновления, ваши данные в порядке.
  • В противном случае дождитесь его удаления.

    • Всякий раз, когда фиксированное обновление присутствует, но update-lock нет, инициируйте восстановление.

восстановление транзакций, если вы нашли узел обновление плана без замка:

  • Получить обновления блокировку.
  • если нет объекта исправленного обновления, удалите план и отпустите блокировку.
  • В противном случае перейдите к разделу «Вторая фаза», выше.