Как обеспечить высокую нагрузку на платежном сервисе
Что проверяют
Понимаете ли вы, что в платежах важен не только RPS, но и корректность под нагрузкой: ни одного двойного списания и ни одного потерянного платежа. Следом почти всегда спрашивают: провайдер ответил таймаутом, деньги списались или нет, и что делает ваш сервис.
Как отвечать
Начните с цифр: пиковый RPS, целевой p99, лимиты эквайера. Узким местом чаще оказываются внешний провайдер и база, а не Python. Дальше каркас:
- идемпотентность: клиент присылает ключ в заголовке
Idempotency-Key, в базе на него уникальный индекс, повтор возвращает сохранённый результат; - платёж как конечный автомат:
created,pending,succeeded,failed. Переход атомарный, черезSELECT ... FOR UPDATEили условныйUPDATE ... WHERE status = 'pending'; - база: PostgreSQL, пул соединений через PgBouncer, партиционирование операций по времени, реплики только для отчётов;
- асинхронность: API быстро принимает запрос, общение с провайдером и вебхуки идут через очередь (Kafka, RabbitMQ). Transactional outbox не даёт записи в базе и событию разъехаться;
- Python: FastAPI на asyncio, несколько процессов uvicorn из-за GIL,
httpxс явными таймаутами и пулом соединений; - устойчивость: ретраи с экспоненциальной задержкой только для идемпотентных вызовов, circuit breaker, лимиты на клиента;
- сверка: регулярная сверка с реестром провайдера закрывает зависшие
pending; - наблюдаемость: Prometheus и Grafana, алерты на долю отказов по провайдерам.
Где спотыкаются
Отвечают «Redis и микросервисы». Кэшировать статус платежа или баланс нельзя, источник истины одна база, кэш годится для справочников и лимитов.
Повторяют запрос к провайдеру после таймаута без ключа идемпотентности или считают таймаут отказом. Так и получаются двойные списания.
Смешивают нагрузку с безопасностью и называют двухфакторную аутентификацию мерой масштабирования. Про безопасность уместно другое: PCI DSS, данные карт не хранить у себя, работать с токенами провайдера.