Тестовое задание Billing на Go
@ChatGptМикросервис баланса пользователей.
Приложение хранит в себе идентификаторы пользователей и их баланс. Взаимодействие с ним осуществляется исключительно с помощью брокера очередей.
По требованию внешней системы, микросервис может выполнить одну из следующих операций со счетом пользователя:
▪Списание
▪Зачисление
▪Перевод от пользователя к пользователю (будет плюсом, но не обязательно)
Блокирование с последующим списанием или разблокированием. Заблокированные средства недоступны для использования. Блокировка означает что некая операция находится на авторизации и ждет какого-то внешнего подтверждения, ее можно впоследствии подтвердить или отклонить
После проведения любой из этих операций генерируется событие-ответ в одну из очередей.
Основные требования к воркерам:
▪Код воркеров должен безопасно выполняться параллельно в разных процессах
▪Воркеры могут запускаться одновременно в любом числе экземпляров и выполняться произвольное время
▪Все операции должны обрабатываться корректно, без двойных списаний, отрицательный баланс не допускается
В пояснительной записке к выполненному заданию необходимо указать перечень используемых инструментов и технологий, способ развертки приложения, общий механизм работы (интерфейсы ввода/вывода)
Будет плюсом покрытие кода юнит-тестами.
Требования к окружению: Язык программирования: Go Можно использовать: любые фреймворки, реляционные БД для хранения баланса, брокеры очередей, key-value хранилища.
Особенности
▪Сервис поддерживает обработку паники.
▪Сервис поддерживает создание и хранение журнала выполненных операций.
▪Сервис поддерживает полную консистентность данных
▪Сервис поддерживает graceful shutdown.
▪Сервис НЕ поддерживает Heartbeat для базы данных.
На момент старта сервиса, должен быть уже запущен NATS.
Используемые компоненты
▪MySQL - для хранения данных
▪NATS - брокер сообщений
API
Ответ на каждый запрос включает статус его выполнения: 0. Операция прошла успешно
1.Неизвестная ошибка
2.Операция устарела
3.Для выполнения операции недостаточно средств
4.Аккаунт не найден
Credit
Списание средств со счета.
Subject/Queue - bank.credit
Request: {"uid":1,"account":1,"amount":10}
Response: {"status":1}
Debit
Зачисление средств на счет.
Subject/Queue - bank.debit
Request: {"uid":1,"account":1,"amount":10}
Response: {"status":1}
Transfer
Перевод средст с одного счета на другой.
Subject/Queue - bank.transfer
Request: {"uid":1,"src":1,"dst":2,"amount":10}
Response: {"status":1}
Acquire
Блокировка средств.
Subject/Queue - bank.acquire
Request: {"uid":1,"account":1,"amount":10}
Response: {"status":1}
Commit
Подтверждение блокированных средств.
Subject/Queue - bank.commit
Request: {"uid":1,"account":1}
Response: {"status":1}
Rollback
Возврат блокированных средств.
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.