Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

My project #38

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
133 changes: 133 additions & 0 deletions case-study.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
# Интро

## Что за проект

Команда `Unity` в `Rambler&Co`.

Мы занимаемся разработкой продуктов, обеспечивающих работу редакций интернет-изданий - таких как [ferra.ru](https://www.ferra.ru/), [secretmag.ru](https://secretmag.ru/) и др.

Сайты разрабатываются отдельно группой фронтенда. На бэкенде есть набор приложений и сервисов, ключевые это:

### krang

`CMS` на `postgres`, админка в которой работают редакции (фронт разрабатывается отдельной командой).

### bebop

Апишка на `mongoDB`, отдающая данные сайтам.

## Как долго уже разрабатывается

Более 5 лет.

## Как дела с перформансом

### krang

Некоторые операции, такие как создание топика и др., могут занимать существенное время.

В рамках ДЗ мы попытаемся сконцентрировться на этом приложении.

### bebop

Думаю, часть апи практически никогда не играет роль боттлнека по времени. Как обсуждалось на лекциях, сайты СМИ как правило тяжеловесны, и на их фоне выдача подготовленных данных из `mongoDB` не будет играть существенной роли.

## Есть ли мониторинг

- `New Relic` (подключен `krang`, `bebop` - планируется);
- `Kibana` с дашбордами;
- что-то еще, чего пока не видел.

## Можете ли вы навскидку предположить где в проекте есть что оптимизировать

Когда получил доступ в `NewRelic` обратил внимание на распухающее время `Redis zscan`, за последнее 3 месяца - это абсолютный лидер среди тяжеловесных операций. Задача в работе.

## Какова ваша роль в проекте, как давно работаете, чем занимаетесь

Инженер-разработчик `Ruby` группы бэкенд, в компании 1.5 года. Занимаюсь разработкой приложений/сервисов на `Ruby`, вкл. написание тестов, исправление багов и пр.

# Оптимизация Redis zscan

В последнее время стали наблюдаться проблемы с `redis` и `sidekiq`, возросло кол-во занятых воркеров / ноду.

Анализ начался с отчетов `New relic`.

Если рассматривать операции по бд, то `redis zscan` в абсолютных лидерах:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 обожаю NewRelic, там так всё сразу видно..


<img src="/screenshots/top_20_db_operations_before.png" width="200" />

Абсолютно большая часть нагрузки приходится на джобу `Sidekiq/Social::CountUpdaterJob`:

<img src="/screenshots/redis_zscan_time_consumption_by_caller.png" width="200" />

Время ответа `redis zscan` в рамках `CountUpdaterJob` за последние 3 месяца увеличилось примерно в 2 раза (1160ms → 2050ms):

<img src="/screenshots/job_breakdown.png" width="200" />

Задача данной джобы заключается в сборе данных о репостах из социальных сетей (в нашем случае `vk` и до недавнего времени - `facebook`).

Наличие `redis zscan` в джобе обусловлено тем, что при постановке джобы мы ищем дубликаты по объектам - с целью минимизации общего кол-ва запросов: требование связано с действующим на текущий момент ограничением `не более 1 запроса в 20 секунд`.

В связи с подключением новых проектов, база за последние 3 месяца выросла - пропорциональным образом (`O(N)`) возросло и время ответа `redis zscan`.

Главной целью стал поиск путей отказа от проверки дублирования и как следствие - использования `redis zscan`.

Первая мысль была в том, чтобы найти ручку `vk`, которая собирает данные по многим топикам сразу, пакетно: возможно, с момента нашего последнего обновления такая возможность появилась, но такая ручка не нашлась.

Потом обратил внимание на то, что требование в установленном на нашей стороне ограничении было актуально для `facebook`, по которому мы в настоящий момент статистику не собираем. Вариант, который я предложил, заключается в отключении `mutex` и исключении проверок на дубликаты - выполнять запросы без ограничений (т.к. со стороны апи `vk` его и нет) - таким образом, мы увеличим кол-во запросов, но это позволит нам решить найденную проблему с `redis zscan`.

До тех пор мы тестируем решение, которое связано с уменьшением таймаута `mutex`. То есть нечто среднее между полным отключением проверки и текущим вариантом.

# Результаты

Уже сейчас мы вернулись к показателям 3-месячной давности, и в джобе:

<img src="/screenshots/job_after.png" width="200" />

И по `redis zscan` в целом:

<img src="/screenshots/redis_zscan_after_2.png" width="200" />

Это сказалось на времени обработки и других важных операциях в системе - например, `topics#create`:

<img src="/screenshots/topics_create.png" width="200" />

Что важно, нагрузка на `redis` в целом также существенно спала:

<img src="/screenshots/redis.png" width="200" />

Мы планируем еще понаблюдать за ситуацией, и после финализации решения установим защитный алерт для защиты метрики.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍


# Профит

Ориентировочный срок окупаемости - 3 месяца.

```ruby
# стоимость разработчика
dev_cost_rub_month = 4_000 * 100
=> 400000 # (руб/месяц)
dev_cost_rub_day = dev_cost_rub_month / 20
=> 20000 # (руб/день)

# стоимоть требуемого увеличения мощностей (руб)
# NOTE. сейчас redis работает на двух инстансах,
# таким образом, для увеличения мощностей пропорционально возросшей нагрузки,
# нам нужно 2 дополнительных сервера
server_cost_rub_month = 3_000
=> 3000
redis_cost_rub_month = server_cost_rub_month * 2
=> 6000

# стоимость оптимизации (руб)
task_cost_rub = dev_cost_rub_day * 1
=> 20000

# срок окупаемости (месяц)
time_to_profit_month = task_cost_rub / redis_cost_rub_month
=> 3
```

Без учета того, что:
- общая нагрузка на `redis`, по сравнению с показателями 3-месячной давности, спала примерно в 2.5 раза;
- ввод дополнительного сервера не защитил бы нас от возможного возвращения проблемы в будущем;
- не только `CountUpdaterJob`, но и другие операции, использующие `redis`, стали выполняться быстрее.
Binary file added screenshots/job_after.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screenshots/job_breakdown.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screenshots/redis.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screenshots/redis_zscan_after_2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screenshots/top_20_db_operations_before.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screenshots/topics_create.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.