Сохраните результат по idempotency_key и защищайте проверку вместе с созданием одной критической секцией. В production идемпотентность должна опираться на постоянное хранилище.
class PayoutService:
Сохраните результат по idempotency_key и защищайте проверку вместе с созданием одной критической секцией. В production идемпотентность должна опираться на постоянное хранилище.
Короткий ответ
Что ответить на собеседовании
Подробный разбор
Ответ с пояснениями
Условие
class PayoutService: def init(self): # ... pass
def create_payout(self, idempotency_key: str, terminal_id: int, amount: float) -> dict:
"""
Должен вернуть словарь вида:
{"payout_id": <int>, "terminal_id": <int>, "amount": <float>, "status": "success"}
Гарантии:
- при первом вызове с новым idempotency_key — создаётся новая выплата
- при повторном вызове с уже использованным idempotency_key — возвращается
результат первого вызова, новая выплата НЕ создаётся
- amount должен быть > 0, иначе — бросить ValueError
"""
raise NotImplementedError
Ответ
Сохраните результат по idempotency_key и защищайте проверку вместе с созданием одной критической секцией. В production идемпотентность должна опираться на постоянное хранилище.
Ниже учебная реализация в памяти одного экземпляра. Копия ответа не позволяет вызывающему испортить сохранённый результат. Повтор с тем же ключом возвращает первый результат, как требует условие.
import math
from threading import Lock
class PayoutService:
def __init__(self):
self._results = {}
self._lock = Lock()
self._next_id = 1
def create_payout(self, idempotency_key: str,
terminal_id: int, amount: float) -> dict:
if not math.isfinite(amount) or amount <= 0:
raise ValueError("amount must be finite and positive")
with self._lock:
if idempotency_key not in self._results:
self._results[idempotency_key] = {
"payout_id": self._next_id,
"terminal_id": terminal_id,
"amount": amount,
"status": "success",
}
self._next_id += 1
return self._results[idempotency_key].copy()
Здесь нет реального перевода денег. Для настоящих выплат нужны уникальный ключ в БД, атомарное сохранение состояния и идемпотентный вызов провайдера; float заменяют денежным представлением без двоичного округления.