ADR-0042: Distributed Locking
Статус
Принято.
Контекст
В условиях multi-replica k8s deployment несколько pod-ов могут одновременно попытаться выполнить одну и ту же операцию: активировать бонус для игрока, запустить scheduled job, обработать payment callback. Оптимистичный concurrency через EF xmin достаточен для коротких DB write операций, но не защищает от race conditions при длинных операциях с external calls или при необходимости fail-fast без retry.
Решение
Использовать madelson/DistributedLock как единую библиотеку distributed locking с двумя backend-ами:
- Redis — операционные locks (incentive claim, job deduplication, payment callback). Быстрый, TTL-based.
- PostgreSQL advisory lock — migration locks. Надёжнее, survives Redis restart.
IPinLockProvider абстрагирует backend. Сервисы не зависят напрямую от DistributedLock.*.
DistributedJobRunner обеспечивает паттерн leader election для scheduled jobs: только один pod выполняет job.
Lock keys всегда включают entity type и entity id — для изоляции на уровне объекта, а не типа.
Последствия
- Incentive claim race condition устранён — повторная активация одного бонуса невозможна.
- Scheduled jobs выполняются ровно один раз при любом количестве replicas.
- Distributed lock не заменяет DB transaction — оба используются совместно.
- Операция при занятом lock возвращает
Result.Failс typed error, не retry (кроме migration lock).
Ссылки
Обратные ссылки
Автоссылки: где используется этот документ.
- Фоновые задачи (1): Background Job: CommentCountReconcileJob
- 11-distributed-locking.md (1): Distributed Locking — Архитектура и Практическое Руководство