Микросервис баланса пользователей.
Приложение хранит в себе идентификаторы пользователей и их баланс. Взаимодействие с ним осуществляется исключительно с помощью брокера очередей.
По требованию внешней системы, микросервис может выполнить одну из следующих операций со счетом пользователя:
- Списание
- Зачисление
- Перевод от пользователя к пользователю (будет плюсом, но не обязательно)
Блокирование с последующим списанием или разблокированием. Заблокированные средства недоступны для использования. Блокировка означает что некая операция находится на авторизации и ждет какого-то внешнего подтверждения, ее можно впоследствии подтвердить или отклонить
После проведения любой из этих операций генерируется событие-ответ в одну из очередей.
Основные требования к воркерам:
- Код воркеров должен безопасно выполняться параллельно в разных процессах
- Воркеры могут запускаться одновременно в любом числе экземпляров и выполняться произвольное время
- Все операции должны обрабатываться корректно, без двойных списаний, отрицательный баланс не допускается
В пояснительной записке к выполненному заданию необходимо указать перечень используемых инструментов и технологий, способ развертки приложения, общий механизм работы (интерфейсы ввода/вывода)
Будет плюсом покрытие кода юнит-тестами.
Требования к окружению: Язык программирования: PHP 7 (PSR-2) либо Go 1.10+ Можно использовать: любые фреймворки, реляционные БД для хранения баланса, брокеры очередей, key-value хранилища.
- Сервис поддерживает обработку паники.
- Сервис поддерживает создание и хранение журнала выполненных операций.
- Сервис поддерживает полную консистентность данных
- Сервис поддерживает graceful shutdown.
- Сервис НЕ поддерживает Heartbeat для базы данных.
- На момент старта сервиса, должен быть уже запущен NATS.
- MySQL - для хранения данных
- NATS - брокер сообщений
- github.com/adverax/echo - легковесный фреймворк. Как таковой он здесь не используется. Нужен просто его пакет database/sql для работы с базой данных.
- github.com/nats-io/go-nats - клиентский пакет подключения брокера сообщений NATS.
- github.com/BurntSushi/toml - пакет для загрузки файла конфигурации.
Ответ на каждый запрос включает статус его выполнения: 0. Операция прошла успешно
- Неизвестная ошибка
- Операция устарела
- Для выполнения операции недостаточно средств
- Аккаунт не найден
Списание средств со счета.
- Subject/Queue - bank.credit
- Request: {"uid":1,"account":1,"amount":10}
- Response: {"status":1}
Зачисление средств на счет.
- Subject/Queue - bank.debit
- Request: {"uid":1,"account":1,"amount":10}
- Response: {"status":1}
Перевод средст с одного счета на другой.
- Subject/Queue - bank.transfer
- Request: {"uid":1,"src":1,"dst":2,"amount":10}
- Response: {"status":1}
Блокировка средств.
- Subject/Queue - bank.acquire
- Request: {"uid":1,"account":1,"amount":10}
- Response: {"status":1}
Подтверждение блокированных средств.
- Subject/Queue - bank.commit
- Request: {"uid":1,"account":1}
- Response: {"status":1}
Возврат блокированных средств.
- Subject/Queue - bank.rollback
- Request: {"uid":1,"account":1}
- Response: {"status":1}
Для достижения идемпотентности, в каждой операции должен присутствовать ее уникальный номер uid. Каждая операция, при записи в базу данных, регистрирует действие в таблице истории. При существовании одинакового ключа (work_index) происходит ошибка базы данных, которую мы трактуем, как устаревание операции (идемпотентный случай). Аналогчно работает и таблица активов.
В сервисе используется пакет доступа к базе данных github.com/adverax/echo/database/sql, который позволяет эмулировать вложенные транзакции, а также хранить область видимости в контексте. Это позволяет нам осуществлять декомпозицию функционала работы с базой на отдельные функции, не заботясь о контексте выполнения запросов.
Для каждой таблицы используется собственный менеджер.
База содержит следующие таблицы:
- account - текущее состояние счета пользователя
- asset - зарезервированные средства. Для удовлетворения требования идемпотентности, таблица содержит уникальный индекс work_index (account, uid).
- history - журнал выполненных операций. Для удовлетворения требования идемпотентности, таблица содержит уникальный индекс work_index (account, uid, op).
В качестве брокера сообщений используется NATS (без гарантированной доставки сообщений). Для упрощения реализации каждый тип операции имеет собственный Subject и Queue. Множество воркеров подключаются к одной и той же очереди, что позволяет нам организовать конкурентный захват сообщения. Полученное сообщение брокер делегирует банку для дальнейшей обработки, после чего формирует ответ, который возвращается брокеру. Таким образом, каждый endpoint брокера по существу является простым адаптером со следующей логикой работы:
- Декодировать данные
- Вызвать метод банка
- Кодировать данные и вернуть их брокеру.
Для достижения максимальной производительности можно было перенести логику операций в хранимые процедуры.
- Установить требуемые библиотеки
- Скомпилировать сервис
- Создать базу данных (перейти в каталог database и выполнить команду: mysql -uMyName -pMyPassword < create.sql).
- Настроить файл конфигурации
- Запустить NATS.
- Запустить сервис на выполнение Развертывания как такового не требуется - достаточно просто использовать выполнимый файл.
Для основных методов менеджеров написаны модульные тесты. Эти тесты были написаны на скорую руку, поэтому качество их кода оставляет желать лучшего. Однако, они позволяют проверить работоспособность sql кода.
Для запуска тестирования необходимо сделать клон базы данных под именем billing_test.