Skip to content

Roda API - testing Roda, Sequel, rack-test, example of microservice

License

Notifications You must be signed in to change notification settings

RobBikmansurov/roda-api

Repository files navigation

Ruby

Roda Json Api

rake db:migrate rake db:seed # initial population with data for performance testing

rackup # start app

rake # start tests

Техническое задание

Требуется создать JSON API сервис на Ruby. В качестве веб-фреймворка, можете использовать sinatra, hanami, roda или что-нибудь другое, но не Ruby on Rails. Доспут к БД можете осуществлять с помощью ORM (active_record, sequel, rom), можете и без ORM, как посчитаете нужным.

1. Сущности:

  1. Юзер. Имеет только логин.
  2. Пост. Принадлежит юзеру. Имеет заголовок, содержание, айпи автора (сохраняется отдельно для каждого поста).
  3. Оценка. Принадлежит посту. Принимает значение от 1 до 5.

2. Экшены:

  1. Создать пост. Принимает заголовок и содержание поста (не могут быть пустыми), а также логин и айпи автора. Если автора с таким логином еще нет, необходимо его создать. Возвращает либо атрибуты поста со статусом 200, либо ошибки валидации со статусом 422.
  2. Поставить оценку посту. Принимает айди поста и значение, возвращает новый средний рейтинг поста. Важно: экшен должен корректно отрабатывать при любом количестве конкурентных запросов на оценку одного и того же поста.
  3. Получить топ N постов по среднему рейтингу. Просто массив объектов с заголовками и содержанием.
  4. Получить список айпи, с которых постило несколько разных авторов. Массив объектов с полями: айпи и массив логинов авторов.

3. База данных

Базу данных используем PostgreSQL. Для девелопмента написать скрипт в db/seeds.rb, который генерирует тестовые данные. Часть постов должна получить оценки. Скрипт должен использовать созданный JSON API сервер (можно посылать запросы курлом или еще чем-нибудь).

Постов в базе должно быть хотя бы 200к, авторов лучше сделать в районе 100 штук, айпишников использовать штук 50 разных.

Экшены должны на стандартном железе работать достаточно быстро как для указанного объема данных (быстрее 100 мс, если будет работать медленне, то ничего страшного, все равно присылайте решение), так и для намного большего, то есть нужен хороший запас в плане оптимизации запросов. Для этого можно использовать денормализацию данных и любые другие средства БД. Можно использовать любые нужные гемы, обязательно наличие спеков, хорошо покрывающих разные кейсы. Архитектуру сервиса организуйте на "свой вкус". Желательно не использовать генераторов и вообще обойтись без лишних мусорных файлов в репозитории.

Принятые решения

Данные

  1. Users: id, login
  2. Posts: id, title, content, ip, user_id, ratings_sum, ratings_count
  3. Ratings: id, rating, created_at, post_id

Хранение и вычисление рейтинга поста

Для поста будем хранить сумму и количество его рейтингов (оценок). Это позволит иметь данные для быстрого расчета рейтинга при в экшене оценки поста. Достаточно будет сохранить новый рейтинг, рассчитать новые сумму оценок ratings_summa += rating и количество оценок ratings_count += 1. И сразу можно вернуть новое значение среденего рейтинга: ratings_summa / ratings_count.

В случае сбоев рейтинг поста можно всегда пересчитать по значениям из таблицы Ratins

Roda + Sequel

Для реализации выбран фреймворк Roda (1. самый призводительный после rake, 2. интересно было с ним поработать, т.к. ранее много о нем слышал, но не тестировал) Для работы с БД выбрана библиотека Sequel того же автора. Она немного облегчит работу с моделями и позвоит сократить код.

Подготовка и запуск

База данных

Параметры доступа к БД хранятся в .env-файле:

$ cat .env
PGDATABASE=api
PGUSER=api
PGPASSWORD=api_pwd

Для работы нужно создать пользователя БД, задать ему пароль, создать БД.

$ sudo -u postgres psql
# create user api with password 'api_pwd';
# create database api owner api;

Затем выполнить миграции rake db:migrate и запустить сервер rackup после чего можно заселить первоначальные данные rake db:seed (при заполнении даными идут обращения к серверу с помощью вызовов Net::HTTP).

С помощью генератора данных Faker будет сделано (Внимание! Может работать долго - 25 минут):

  • 100 пользователей (авторов);
  • 50 ip-адроесов IP4;
  • 200к постов, примерно 10% будут иметь оценки;

Для запуска тестов можно запустить rake

Для проверки работы сервиса можно запустить его командой rackup и проверить экшены:

  1. Создать пост.
$ curl -X POST -H "Content-Type: application/json" -d '{"title":"post title 2", "content":"post content 2", "user_login":"julia", "user_ip":"192.168.1.102"}' http://localhost:9292/api/v1/posts/create

{"data": {"post":{"id":"177","title":"post title 2","content":"post content 2","rating":"0","ip":"192.168.1.102","user":"{\"id\":\"332\",\"login\":\"julia\"}"}}}rob@rbh:~/dev/roda-api$ 
  1. Поставить оценку посту.
$ curl -H "Content-Type: application/json" -X PUT -d '{"rate":"4"}' http://localhost:9292/api/v1/posts/177
{"data":{"post_id":"177","rating":"4.0"}}
  1. Получить топ N постов по среднему рейтингу.
$ curl -H "Content-Type: application/json" "http://localhost:9292/api/v1/posts?limit=10&rating=2"

{"data": {"posts": [
{"post":{"id":"155","rating":"2.00000","title":"sequi ea hic","content":"Eos eius odit. Illum perspiciatis velit. Reprehenderit voluptatibus modi."}},
{"post":{"id":"159","rating":"2.00000","title":"impedit adipisci dolor qui","content":"Omnis voluptatibus quia. Sint sed eveniet. Ad qui minus."}},
{"post":{"id":"160","rating":"2.00000","title":"qui est et fugit voluptatem quia","content":"Omnis aperiam quod. Sint excepturi consequatur."}},
{"post":{"id":"171","rating":"2.00000","title":"architecto tempore deserunt ipsum","content":"Placeat id rerum. Saepe dolorem error. Sit itaque alias. Aut voluptatibus sit. Facere et et."}} ] } }

  1. Получить список ip, с которых постило несколько разных авторов.
$ curl -H "Content-Type: application/json" "http://localhost:9292/api/v1/posts/ip_authors"

{{"data": {"ips": [
{"ip": "120.136.49.202", "authors": ["hazel.heidenreich", "brigid_brakus"]},
{"ip": "117.85.175.160", "authors": ["janise.wiza", "anja"]},
{"ip": "79.78.43.131", "authors": ["kim.prohaska", "dominica_shanahan"]},
{"ip": "208.161.246.15", "authors": ["mac", "bernice.spencer", "lizeth_lang"]},
{"ip": "220.49.104.21", "authors": ["kathleen.howell", "ja"]},
{"ip": "158.239.32.22", "authors": ["willy", "dewayne_simonis", "tierra.kirlin"]},
{"ip": "173.117.188.181", "authors": ["kurtis_stokes", "mignon_cartwright"]},
{"ip": "97.183.204.162", "authors": ["soledad", "britany.hettinger"]},
{"ip": "60.92.48.226", "authors": ["etsuko.crona", "jesenia_kohler"]},
{"ip": "168.143.34.169", "authors": ["kirk", "donn.kris"]},
{"ip": "253.40.100.93", "authors": ["javier_dooley", "jonathon"]},
{"ip": "245.203.48.200", "authors": ["mollie_kuphal", "bobby"]} ] }}

Результат

Общие затраты времени порядка 24 часов. Много времени ушло на изучение Roda, Sequel, RackTest. Тестирование на Ubuntu 20.04.1 LTS + AMD® Ryzen 5 3500u + RAM 8GB.

Загрузка данных:

$ rake db:seed
2020-10-27 13:42:04 +0500
Created 100 users by 1 sec.
Created 200000 posts 1483 sec.
2020-10-27 14:06:48 +0500

Для оценки призводительности использовал опцию -w "%{time_total}\n" в параметрах curl:

  1. Создать пост - 0,008370
  2. Поставить оценку посту - 0,009061
  3. Получить топ N постов по среднему рейтингу - 0,082882
  4. Получить список ip с разными авторами - 0,126522

Код протестирован. Есть benchmark.rb и benchmark_ips.rb

Что можно улучшить

Для выполнения требования "время ответа любого экшена < 0.1 сек." можно изменить логику получения ip-адресов, с которых было несколько авторов постов: самое простое - добавить limit + offset (paging).

About

Roda API - testing Roda, Sequel, rack-test, example of microservice

Topics

Resources

License

Stars

Watchers

Forks

Languages