Страницы

Поиск по вопросам

понедельник, 6 января 2020 г.

Стратегия удаления просроченных записей

#архитектура


Есть некоторое хранилище кодов доступа, каждый из которых обладает двумя свойствами
- временем истечения и количеством попыток подтверждения этого кода. После того, как
любой из этих параметров так или иначе истек, код уже нельзя использовать, и его необходимо
удалить из БД. Тут я вижу две стратегии, как с ним поступить:


Удалять код при запросе этого кода из БД. Плюсы: нет фоновых задач, минусы: race
conditions (кто-то может получить запись с retries = 1 в тот момент, когда кто-то в
параллель пытается подтвердить тот же код) и объемный код простого получения записи
Написать background-worker, который будет постоянно обходить базу на предмет нахождения
устаревших записей. Плюсы: получение записей происходит как обычно (остаются проверки
на валидность записи, но не нужно вызывать ее удаление), минусы: некоторый лаг и существование
фоновых воркеров, все те же race conditions.


Обе стратегии мне кажутся дурацкими, и я не могу придумать, как правильно решить
задачу (хотя мне кажется, что есть более очевидное решение, способное свести лаг к
минимуму). Само приложение распределенное, но использование сервиса распределенных
блокировок вполне допустимо (однако само по себе его использование спасет только от
race conditions).

Какая стратегия будет наиболее правильной для удаления подобных записей?
    


Ответы

Ответ 1



Проверка кода должна выполняться транзакцией с одновременным декрементов попыток. Это поможет избежать race condition. Начать транзакцию Получить ключ Нет ключа? – вернуть FALSE Счётчик оставшихся попыток больше 1? Нет – удалить запись и вернуть TRUE Уменьшить счётчик Вернуть TRUE Конец транзакции Если БД не поддерживает Lock'и можно имитировать их механизм, записывая в доп. поле случайную величину как ключ эксклюзивной сессии клиента. Вариант, который не подошёл Держать коды в Redis, с указанием времени истечения хранения. В значении хранить кол-во использований и время истечения. Просроченные ключи удалит сам Redis или скрипт при просроченном запросе (если Redis протормозит почему-то, и значение как-то выживет). Так же удалять ключи при последней, по счётчику, допустимой попытке использования. UPd. Масштабируемость возможна – посмотрите предложенное решение для распределённых lock'ов: distributed locks with Redis.

Ответ 2



В результате длинной головоболи, которую я придумал себе сам, было решено делать очищающие бекграунд-воркеры плюс удаление на лету там, где это возможно - это решение возникло просто из-за того, что в проекте подразумевается постраничный вывод, а фильтрация страниц на лету превращается в совсем уж дикий ад и (теоретически) может препятствовать прогрессу выполнения. Поэтому получилось комбинированное (и, не скрою, болезненное) решение, которое безусловно, не является ожидаемо волшебным. Что до идеальной инфраструктуры, то тут на самом деле прав Sergiks - вся боль исчезает, если лишние записи фильтруются на уровне самого хранилища, неважно, redis это, couchbase или aerospike. Теоретически, этот уровень можно воспроизвести дополнительным слоем менеджеров внутри приложения, но это настолько же болезненная процедура, и остается только надеяться, что практика создания записей с ttl получит более широкое применение в базах данных.

Комментариев нет:

Отправить комментарий