Для выполнения задачи выбрал веб-фреймворк tornado, так как он:
- асинхронный, неблокирующий
- весьма производительный даже в синхронном режиме
- имеет большое количество асинхронных драйверов для всех популярныъ диалектов SQL, а также NoSQL-хранилищ
- достаточно популярный и хорошо документированный для поиска информации и устранения проблем
- написанное приложение можно быстро запустить без конфигурирования каких-либо дополнительных прослоек между python-кодом и ОС (например, WSGI-контейнера)
Поскольку в качестве хранилища можно было выбрать исключительно реляционную СУБД, я остановился на PostgreSQL, так как у неё:
- есть асинхронный драйвер для tornado (и даже не один; я остановился на самом популярном – momoko)
- хороший функционал (аналитические функции, наследование, партиционирование итп)
- более очевидный и удобный синтаксис, чем, например, у MS SQL / MySQL
Для нагрузочного тестирования выбрал утилиту для unix-систем Siege, т.к. она достаточно проста, удобна, есть много настраиваемых параметров для тестирования, позволяет оценить возможность конкурентного доступа к ресурсу со стороны клиентов.
- tornado имеет более сложные конструкции, чем, например, Flask
- если мы будем обязаны использовать MS SQL Server в качестве хранилища, для него нет асинхронного драйвера для tornado и создание высокопроизводительного микросервиса будет невозможным
- конструкции по валидации параметров входящих HTTP-запросов от клиентов выглядят на мой взгляд несколько неэлегантно. Собственный валидатор в
decorators.py
написал умышленно (для tornado есть готовые решения, например, WTForms/Tornado tools, в рабочих проектах обычно использую JSONschema), т.к. поскольку это тестовое задание, хотел попробовать написать сам. - на текущий момент баланс можно посчитать не в любой заданной валюте, а лишь в рублёвом эквиваленте
- dockerfile
- веб-клиент
- больше тестов, более сложные проверки
Тестировал на рабочей машине, 1 процесс, без балансировщика. Результаты представлены ниже:
Запрос баланса по партнёру с id=1 (500 000 транзакций)
$ siege http://localhost:3008/balance?partner_id=1
** SIEGE 3.0.5
** Preparing 20 concurrent users for battle.
The server is now under siege...
Lifting the server siege... done.
Transactions: 233 hits
Availability: 100.00 %
Elapsed time: 9.46 secs
Data transferred: 0.00 MB
Response time: 0.31 secs
Transaction rate: 24.63 trans/sec
Throughput: 0.00 MB/sec
Concurrency: 7.71
Successful transactions: 233
Failed transactions: 0
Longest transaction: 0.56
Shortest transaction: 0.14
Как следует из отчёта выше, максимальное время запроса баланса при конкурентном доступе составило 560 мс, что удовлетворяет требованиям задачи.
Произвольные запросы по конфигу:
$ siege -f siege-urls.txt -t10s -c 20
** SIEGE 3.0.5
** Preparing 20 concurrent users for battle.
The server is now under siege...
Lifting the server siege... done.
Transactions: 380 hits
Availability: 100.00 %
Elapsed time: 9.94 secs
Data transferred: 0.53 MB
Response time: 0.08 secs
Transaction rate: 38.23 trans/sec
Throughput: 0.05 MB/sec
Concurrency: 3.22
Successful transactions: 380
Failed transactions: 0
Longest transaction: 0.59
Shortest transaction: 0.00
templates
web-client.html
веб-клиент (HTML+JS)
static
style.css
стили для HMTL
loader.py
скрипт-генератор тестовых данныхrequirements.txt
файл зависимостей для pipsiege-urls.txt
конфиг-файл для Siegedockerfile
докерфайл (не готов)decorators.py
валидатор входных данныхweb.py
основной файл веб-приложенияtests.py
unit testsddl.sql
создание структуры БД, ограничений, партиций, тестовых партнёров
- Ubuntu 14.04+
- Python 3.4.3+
- PostgreSQL DB 9.6+
sudo pip3 install -r requirements.txt
- Создаём структуру:
psql ivan -h 127.0.0.1 -d ivan -a -f ddl.sql
- Наполняем тестовыми данными (500 000 транзакций):
python3 loader.py
python3 web.py
python3 tests.py
- Устанавливаем unix-утилиту Siege:
sudo apt-get install siege --upgrade
- Запускаем тестирование на 10с с 20 пользователями:
siege -f siege-urls.txt -t10s -c 20