Exactly once, CAP и эффект домино. Принципы отказоустойчивых финтех-систем

Почему отказоустойчивость в финтехе так важна
Отказоустойчивость в финтехе критически важна, потому что она
обеспечивает непрерывность работы сервисов, защищает чувствительные данные клиентов и гарантирует соблюдение регуляторных требований. Любой сбой может привести к невозможности совершить транзакцию, что вызовет финансовые и репутационные потери компании.
В финансовых операциях важна семантика exactly once. Это значит, что действие должно быть обязательно выполнено, но только один раз — даже если в системе произошли сбои. Например, нельзя допустить, чтобы платеж был обработан несколько раз или не прошел вовсе.
Другая важная проблема — гонки данных (race conditions). Это ситуация, когда несколько процессов одновременно меняют одни и те же данные — итоговый результат зависит от того, какой процесс система обработала первым. В финансовых операциях порядок действий критичен. Например, на счете есть 10 у.е., и пользователь совершает два списания: на 10 у.е. и на 5 у.е. Если система перепутает порядок, то сначала может списаться 5 у.е., и на балансе останется еще 5 у.е. — из-за этого второе списание (которое должно было пройти первым) уже не выполнится.
Поэтому при разработке финтех-решений приходится принимать архитектурные компромиссы в соответствии с теоремой CAP. Она гласит, что в распределенной системе нельзя одновременно обеспечить следующие свойства:
C — Consistency. Все узлы системы видят одни и те же данные в одно и то же время. Каждый запрос получает последнее записанное значение.
A — Availability. Каждый запрос к системе получает корректный ответ (успех или ошибка), без гарантии, что данные актуальны. Система всегда доступна для ответа.
P — Partition tolerance. Система продолжает работать и обрабатывать запросы, даже если связь между узлами сети нарушена.
Таким образом, масштабируя сервисы для повышения их устойчивости и доступности, мы начинаем испытывать сложности с согласованностью. И это одна из главных трудностей в построении крупных финтех-систем.
Что приводит высоконагруженные системы к сбоям
Разберем наиболее типичные проблемы, которые встречаются при отказах масштабных распределенных систем:
Stuck pending transaction. Во многих системах транзакция выполняется не сразу: сначала создается промежуточное состояние (pending transaction), затем система ждет ответа от RMS (Risk Management System), и только после этого операция завершается. Если ответ не приходит, такая транзакция «зависает», продолжая занимать память и блокируя потоки. Это приводит к деградации системы.
Общие точки отказа. При согласовании работы нескольких сервисов часто используется общая база данных. Таким образом, при ее отказе перестают работать все сервисы, которые от нее зависят.
Потеря сообщений. Иногда сообщения, отправленные через Kafka, могут потеряться или обработаться некорректно. В результате часть событий не доходит до нужных сервисов, и состояние системы становится некорректным — данные перестают совпадать с реальностью.
Перегрузка сервиса. Запросы могут распределиться неравномерно и перегрузить один из сервисов — в итоге несколько подов завершат работу. Это вызовет деградацию зависимых сервисов и последующий эффект домино.
Какие практики помогут обеспечить отказоустойчивость системы
Всем сервисам необходим мониторинг для выявления слабых мест, деградации производительности и утечек памяти. Также необходимо регулярно проводить chaos testing и fault injection — практики искусственного создания сбоев и внедрения ошибок для анализа поведения системы. Это позволит предупредить опасные ситуации до того, как они произойдут.
Для баз данных следует выбирать минимально необходимый уровень изоляции. Повышение уровня изоляции приводит к большей безопасности данных, но система начинает работать медленнее. Также стоит использовать горизонтальное масштабирование (шардирование). Это означает разделение данных между несколькими базами. Такой подход снижает нагрузку на каждую базу и, тем самым, повышает отказоустойчивость.
В распределенных логах рекомендую устанавливать параметр unclean.leader.election.enable=false — он означает, что при падении главного сервера нельзя назначить главным сервер, содержащий неполные данные. Такой подход немного замедлит систему, но поможет избежать потери данных — что важнее при финансовых операциях.
Чтобы предотвратить эффект домино при падении сервиса, советую использовать три приема:
Circuit Breaker — это паттерн проектирования, который временно прекращает запросы к серверу, отвечающему слишком медленно или с ошибками. Такой подход защищает систему от перегрузки и дает «проблемному» сервису время на восстановление.
Retry-политики — они определяют количество повторных запросов и добавляют между ними задержку, чтобы не перегрузить систему. Retry-политики позволяют автоматизировать повторные запросы, но при этом следят чтобы они не привели к сбою.
Rate limiter — это механизм, который ограничивает количество запросов, которые клиент может выполнить к серверу за определенный промежуток времени. Это также защищает систему от перегрузки и обеспечивает ее стабильность.
И наконец, важно изолировать критичные компоненты, которые обеспечивают стабильность платежных операций. Например, финансовые события не должны стоять в одной очереди с логами, аналитикой или фоновыми задачами. Также платежная логика должна иметь собственные ресурсы, а данные — храниться в отдельных базах. Для платежных операций задаются свои лимиты по нагрузке и скорости обработки — это защищает их от всплесков трафика, вызванных некритичными запросами.
Что делать в случае отказа системы
В первую очередь, важно понять, с чем связан сбой. Почти всегда упавшему поду требуется redeploy — в 90% случаев это помогает устранить проблему.
После стабилизации системы нужно перейти к восстановлению сервисов, которые хранят состояние: баз данных, очередей сообщений и т.д. Важно восстановить все состояния — например, при расхождении балансов необходимо вручную подготовить отсутствующие транзакции, которые можно найти по логам.
После восстановления следует провести рефлексию инцидента — понять, почему произошел сбой, и какие меры нужно принять. Например, снизить rate limits, увеличить количество реплик сервисов, пересмотреть настройки масштабирования и т.д.
Тенденции в отказоустойчивости
Главная тенденция — это автоматизация. Появляется всё больше инструментов, способных автоматически выявить деградацию и выполнить действия по восстановлению без участия инженера. Например, уже существуют системы, которые при превышении latency «умеют» самостоятельно совершать rollback или redeploy конкретного пода. В будущем это приведет к появлению полноценных self-healing инфраструктур.
Опубликовано 23.09.2024



