Шардирование сервиса объявлений Авито Доставки. Часть II. avitotech.. avitotech. cpu.. avitotech. cpu. авито.. avitotech. cpu. авито. Блог компании AvitoTech.. avitotech. cpu. авито. Блог компании AvitoTech. Управление проектами.. avitotech. cpu. авито. Блог компании AvitoTech. Управление проектами. Управление разработкой.. avitotech. cpu. авито. Блог компании AvitoTech. Управление проектами. Управление разработкой. шардинг.. avitotech. cpu. авито. Блог компании AvitoTech. Управление проектами. Управление разработкой. шардинг. шардирование.

Привет, меня зовут Артем, и я работаю в Авито с 2016 года. Начинал как тестировщик, затем вырос в backend-инженера, с 2019 года пишу на golang, а сейчас руковожу командой разработки в Авито Доставке в роли техлида. Это вторая часть истории про шардирование сервиса объявлений Авито Доставки, где мы расскажем о реализации шардирования и полученном результате.

В первой части мы обрисовали проблему масштабирования сервиса, рассказали о том какие у нас были варианты решения, почему выбрали шардирование, поговорили про нюансы шардирования и составили план реализации.

Первую часть статьи можно прочитать здесь.

Шардирование сервиса объявлений Авито Доставки. Часть II - 1

Содержание:

Реализация: как мы это сделали — на дворе 2023Q2

1. Создание шардированного хранилища

Само создание хранилища — дело довольно стандартное в компании, но подключение новой базы к старому сервису оказалось не так просто. У нас в инфраструктуре зашит принцип один сервис — одна база, и это хороший подход в целом, так как обеспечивает отсутствие «коммунальных баз», которыми пользуются несколько сервисов, что весьма полезно. Но есть нюансы: по такому принципу подключение второй базы к одному сервису также проблематично. Процесс занял несколько итераций общения с DBA, которые не давали большого результата.

В итоге помог бывший DBA, перешедший в бэкенд. Ниже — то, как он описал порядок необходимых действий:

Проделал как раз вчера с сервисом mod-duplicates-calculator, ему надо было доступ в базу сервиса mod-duplicates-search-ng (хранилище mod-duplicates-search-ng2).

Я добавил доступ:

access:
 services:
 - name: service-mod-duplicates-search-ng
 - name: service-mod-duplicates-calculator

Добавил пул:

pgbouncer:
  pools:
    - name: master
      dbname: master
      pool_size: 75
      pool_mode: transaction
    - name: mod_duplicates_calculator
      dbname: master
      pool_size: 10
      pool_mode: transaction

Он буквально спас проект на этом этапе, расписав шаги подключения для коллег из DBA, так как он сам делал подобное только что. Тут важны такие горизонтальные связи и взгляд с другой стороны.

2. Реализация запросов с учетом шардирования

Дальше реализация запросов с учётом шардирования. Сначала подключаемся к кластеру, кластер — это по факту просто слайс подключений к базам, собственно нашим шардам.

type Cluster struct { _shards []*DB
    metrics *metrics.Metrics
}

Далее определяем интерфейс кластера, он состоит из 6-и методов.

type clusterInterface interface { 
    SharedByItemID(itemID int64) (*database.DB, error) 
    QueryxContext( 
      ctx context.Context,
      sharedMapping map[int64][]int64,
      buildQueryFunc func([]int64) (string, []interface{}),
      scanFunc func(rows *sqlx.Rows) error,
    ) []error
    ShardsMappingByItemIds(itemIDs []int64) map[int64][]int64 
    MainShard() *database.DB 
    Shards() []*database.DB 
    Close() []error 
  1. ShardByItemId — по item_id (ключ шардирования) получаем подключение к шарду, где этот айтем лежит.

  2. QueryContext — это самый интересный метод интерфейса, он гибкий и позволяет выполнять несколько запросов по ключам, мы используем его и для запросов по item_id и для запросов по user_id, для его использования нужно заполнить довольно сложные входящие параметры:

    • shardsMapping — мапа где ключ это номер шарда, а значение это слайс ключей, которые в нём лежат;

    • buildQueryFunc — функция, принимающая слайс ключей для запроса и возвращающая строку запроса и слайс аргументов для него;

    • scanFunc — функция, сканирующая и сохраняющая результат выполнения запроса, также возвращается слайс ошибок от шардов, если они есть.

  3. ShardMappingByItemId — маппит айтемы на шарды, используется для создания мапы — shardsMapping.

  4. MainShard — отдаёт подключение к главному шарду, на нём мы храним все остальные не шардируемые таблицы и используем для второстепенных/служебных запросов.

  5. Shards — отдаёт подключение сразу ко всем шардам и используется, когда мы не знаем, на каком точно шарде данные, у нас это запросы по user_id. Важно, чтобы их не было много!

  6. Close — закрывает коннект к базе.

Далее приведу пример реализации запроса всех айтемов пользователя по user_id:

У нас есть текущая функция getAllByUserID. Для того чтобы научить её работать с шардированным хранилищем, нам нужно внутри неё определить функцию работы с шардами getAllByUserIDCluster:

Шардирование сервиса объявлений Авито Доставки. Часть II - 2
  1. shardsMapping — в данном случае мы каждому шарду присваиваем один и тот же user_id, так как не знаем, в каких шардах лежат айтемы пользователя.

  2. buildQueryFunc — аргумент в запросе только один — это user_id.

  3. scanFunc — каждый айтем добавляем в общий слайс — items, обязательно используем механизм синхронизации, так как запрос к каждому шарду выполняется в отдельной горутине.

Супер, мы определили основные параметры и готовы выполнить queryContext, посмотрим, что внутри:

func (cl *Cluster) QueryxContext(
    ctx context.Context,
    shardsMapping map[int64][]int64,
    buildQueryFunc func([]int64) (string, []interface{}),
    scanFunc func(rows *sqlx.Rows) error,
    ) []error {
for shardId, ids := range shardsMapping {
    wg.Add(1)

    go func(shardId int64, ids []int64) {
    defer wg.Done()

    db := cl.shards[shardId]
query, args := buildQueryFunc(ids)
rows, err := db.QueryxContext(ctx, query, args...)
for rows.Next() {
if err := scanFunc(rows); err

В коде, если упростить queryContext : сначала строится маппинг по shardsMapping, потом для каждого шарда запускается горутина с запросом, строящимся по buildQueryFunc, после чего собираются результаты по scanFunc. Получается довольно просто и гибко, один и тот же подход работает для запросов и по item_id и по user_id.

Таким образом мы подготавливаем все наши запросы к работе с шардированным хранилищем.

Тут еще больше контента

3. Тестирование шардированного хранилища 

Дальше идет этап тестирования шардированного хранилища. Рассмотрим, как все происходило — на дворе Q3 2023 года. 

Мы подключили нашу конфигурацию в 32 шарда, для наполнения шардов данными мы решили пустить туда асинхронно пишущую нагрузку, таким образом мы провели первый тест и поняли, что с пишущей нагрузкой всё ок, конфигурация справляется и тем самым убили двух зайцев, шарды сами стали наполнятся данными.

Далее мы сделали мигратор данных, ниже приведу алгоритм его работы, базово он звучит так: возьми все данные из нешардированной базы и положи в шардированную)

Как это работает:

Заполню табличку так:

delivery_item.public> insert into items_moving
    select item_id from item where true limit 50000000
[2023-06-02 10:11:49] 50,000,000 rows affected in 24 s 442 ms

ПЕРЕД ДЕПЛОЕМ:

Складываем ИД всех айтемов которое есть в таблице item в табличку items_moving табличку хочу создать руками и после миграции удалить. Создание таблички:

create unlogged table if not exists items_moving (
    id bigint not null
);

ПОСЛЕ ДЕПЛОЯ РАБОТАЕТ ТАК:

  1. Достаем из items_moving ИДишники айтемов, которые будем перекладывать в шардированную базу.

  2. Достаем детальную информацию по каждому из этих айтемов.

  3. Раскладываем айтемы по шардам.

  4. В горугинах запускаем инсерт в каждый шард (если запись по айтему в шарде уже есть, то она попала туда «естественным путем», с ней не делаем).

  5. Удаляем из items_moving то, что удалось заинвергнуть в шардированную базу.

Таким образом мы налили данных в шардированное хранилище, так выглядит работа мигратора на графиках.

Шардирование сервиса объявлений Авито Доставки. Часть II - 4

Далее запустили в начале небольшую часть читающего трафика, примерно 30%, затем повысили до 60%, тут мы обнаружили, что нашей текущей коробки на 2 CPU уже не хватает, и повысили её до 4 CPU на шард и, наконец-то, дали все 100%.

  • Первый тест — 12 июня случился первый тест на 32 шарда

В таблице ниже можно увидеть, как росло число транзакций TPS и потребление CPU в это время, за базу для оценки эксперимента берём потребление текущего инстанса нешардированной базы.

Шардирование сервиса объявлений Авито Доставки. Часть II - 5

Видим, что при 100% трафика транзакции выросли в 10 раз, CPU — в 8.

Рост транзакций связан с батчевыми запросами, теперь для получения данных по таким запросам нам нужно идти в несколько шардов и, соответственно, делать несколько запросов, что ведёт к общему росту транзакций, а от роста транзакций закономерно растёт потребление CPU, зависимость линейная. 

Нас это не устроило, так как в таком сетапе мы потребляли значительно больше количество ресурсов, чем хотелось бы. Мы экстраполировали полученные результаты работ нешардированной базы и базы с 32 шардами и получили прогноз роста TPS и потребления CPU. 

Шардирование сервиса объявлений Авито Доставки. Часть II - 6

Исходя из расчёта видно, что уменьшив на конфигурации в 16 шардов мы получим рост общего CPU в 600%, то есть уменьшение общего CPU по сравнению с конфигурацией в 32 шарда почти в 2 раза, а нагрузка на один шард при этом вырастет незначительно, поэтому мы решили так и сделать.

При изменении конфигурации с 32 на 16 шардов мы словили первый LSR (критичный инцидент на продакшене). Дело в том, что у нас был кастомный healthcheck, который проверял доступность базы в каждом поде сервиса, и когда мы изменили конфиг базы, то половина шардов стала недоступна, а в сервисе конфиг остался прежним на 32 шарда и сервис ожидал доступности каждого из 32 шардов. Курьёзность ситуации ещё усугублялась и тем, что эта база никак не участвовала в пользовательских сценариях и её полная недоступность никак не должна была сказаться на пользователях, однако из-за этой проверки по readiness-probe сервис стал отвечать «503 Service Unavailable» на каждый запрос, что означало полный downtime.

Это также осложняло поиск корня проблемы, так как казалось, что проблема в k8s, однако мы оперативно продебажили код, нашли проблему и устранили, изменив также конфиг в сервисе, после чего всё пришло в норму. Отсюда можно сделать важный вывод — всегда надо критически оценивать, какой фукнционал точно должен работать, а каким можно пренебречь. Мы после этого, естественно, удалили данный healthcheck от греха подальше.

Итак, мы подключили конфиг из 16 шардов, очистили в них данные и налили снова. После этого мы подали на него нагрузку.

  • Второй тест — 12 июля (спустя месяц после первого) мы запустили второй тест на 16 шардов

Шардирование сервиса объявлений Авито Доставки. Часть II - 7

Результаты были похожи на наши предварительные расчёты, но всё же оказались хуже, и мы решили провести ещё один тест, снова уменьшив кол-во шардов в 2 раза, так как точно не могли сказать, что данная конфигурацию оптимальна.

Снова сменили конфиг (уже без LSR) с 16 на 8 шардов, провели манипуляции по очистке и заливки данных и подали нагрузку, а тем временем на дворе наступил Q3 2023 года.

  • Третий тест — 27 июля (спустя 2 недели после второго) мы запустили третий тест на 8 шардов

Шардирование сервиса объявлений Авито Доставки. Часть II - 8

Результаты оказались сногсшибательными! В отличие от ожидаемого роста CPU на шард у нас случилось обратное, и утилизация CPU на шард снизилась по сравнению с конфигурацией в 16 шардов и стала равной конфигурации с 32 шардами, однако за счёт уменьшения кол-ва шардов в 4 раза мы добились также 4-х кратного уменьшения общего потребления CPU! 

Но всё же оставалось непонятно, почему так вышло, и мы пошли копать глубже.

Жми сюда!

После детального изучения метрик и разговоров с dba мы выяснили, что это произошло из-за снижения конкуренции за ресурсы CPU уже внутри непосредственно серверов, на которых крутились наши шарды, так как все они были размещены на 3-х физических серверах и теперь на каждый сервер приходилось в 4 раза меньшее кол-во шардов, в результате чего ресурсов стало достаточно.

После этого мы также обратили внимание что были два шарда, на которых кол-во ошибок было заметно выше, чем на остальных шести, и решили провести эксперимент по переводу этих шардов на другой, более «хороший» сервер. В результате этот эксперимент также превзошел наши ожидания, так как после отселения одного из шардов каждый шард стал работать лучше.

Шардирование сервиса объявлений Авито Доставки. Часть II - 10

Детально покопавшись, мы выяснили, что автоматика определила два шарда на одно и тоже ядро CPU и шарды конкурировали за ресурсы. Благодаря этому эксперименту коллеги из dba нашли баг в автоматике и пошли чинить.

Отсюда можно сделать ещё один важный вывод: при работе с БД важно понимать, что происходит на самих серверах, а не только в инстансе базы данных и на сервисе.

Далее мы выделили ещё несколько действий, которые можно проделать для улучшения работы конфигурации, и реализовали их:

  • повысить коробку до 8 CPU сразу, так как текущее потребление > 50% от 4 CPU;

  • повысить память, сейчас она полностью утилизируется;

  • оптимально распределить это по нашему железу.

Дальше уменьшать кол-во шардов мы не стали, так как с уменьшением кол-ва шардов у нас уменьшался и потенциал масштабирования нагрузки без необходимости решардинга, а текущая конфигурация нас устраивала по работоспособности и потреблению железа (не шардированная база потребляла 9 CPU, а шардированная стала потреблять 18.4 CPU).

Итоговая конфигурация — мы остановились на конфигурации в 8 шардов и 8 CPU на каждый шард (а планировали изначально 32 шарда по 2 CPU), что даёт 64 CPU лимит (как и планировали) при потреблении 18,4 CPU — другими словами мы утилизировали 28% из доступного CPU, что означало возможность увеличения нагрузки на CPU x2 без риска деградаций.

Настройки коннектов

Также во время экспериментов были обнаружены проблемы с коннектами, мы вывели детальные метрики по коннектам на стороне сервиса. В пакете database/sql есть функция, позволяющая вывести детальную статистику коннектов.

Шардирование сервиса объявлений Авито Доставки. Часть II - 11

И обнаружили, что соединения долго висят в ожидании свободного коннекта, об этом свидетельствует не нулевой waitCount:

Шардирование сервиса объявлений Авито Доставки. Часть II - 12

И также плачевную картину мы видели на стороне pgbouncer с постоянным фоном cl_waiting:

Шардирование сервиса объявлений Авито Доставки. Часть II - 13

После нескольких экспериментов мы пришли к оптимальной конфигурации коннектов, полностью избавились от cl_waiting на pgbouncer и сильно уменьшили waitCount со стороны сервиса.

Распределение нагрузки по шардам получилось практически идеально равномерное:

Шардирование сервиса объявлений Авито Доставки. Часть II - 14

4. Переход от одиночной к шардированной инсталляции  

Документация на тот момент была, но оказалась неактуальной для нас: предлагалось сделать воркер по переносу данных, затем остановить запись новых данных (что равносильно даунтайму), дождаться, пока воркер перенесёт все данные, и переключить весь трафик на шардированную базу. Для критичного-сервиса — нерабочий вариант, так как мы не можем себе позволить полный даунтайм, поэтому мы пошли думать вариант без даунтайма. 

Но тогда, как говорится, все карты уже были у нас на руках, данные писались в шардированное хранилище в асинхронном режиме и у нас был worker для переноса текущих данных в новое хранилище, то есть мы могли добиться практически 100% синхронизации данных на момент окончания работы воркера.

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

Шардирование сервиса объявлений Авито Доставки. Часть II - 15

Для 1,6М RPM запросов различие было меньше 100 RPM или меньше 0,0000625% от всех запросов.

После этого мы подготовили детальный план перехода:

Шардирование сервиса объявлений Авито Доставки. Часть II - 16

И план на случай, если что-то пойдёт не так:

Шардирование сервиса объявлений Авито Доставки. Часть II - 17

DBA подтвердили план, также наш сервис отправлял данные в аналитическое хранилище и коллеги подтвердили, что с ним всё будет в порядке. Мы согласовали план со всеми заинтересованными лицами по проведению работ и стали ждать часа Х.

Итак, настал день Х, мы сделали, что могли, взяли в руки амулеты, помолились, попостились и приступили к переходу на шардированную инсталяцию.

  • Первый переход — 12 сентября. Как говорится, первый блин комом, мы словили свой второй LSR: у нас появились долгие транзакции, из-за чего база не успевала обрабатывать все запросы также быстро, как раньше:

первый переход - долгие транзакции.png

первый переход – долгие транзакции.png

 И сервис начал деградировать:

Шардирование сервиса объявлений Авито Доставки. Часть II - 19

Но мы воспользовались заранее подготовленным планом, быстро откатили изменения и всё восстановилось.

В результате расследования мы обнаружили проблему в манифестах базы данных и решили, что вся проблема была в этом. Манифесты поправили и запланировали новую дату перехода.

  • Второй переход — 14 сентября. Как же мы ошибались, всё повторилось, как и в прошлый раз, но мы среагировали ещё быстрее и обошлись без LSR. 

В результате расследования обнаружили, что проблема была в отправке данных в аналитику (ранее мы обговаривали с dba, что с этим проблем не будет), из-за особенностей реализации отправки данных при подключении к новому хранилищу все его данные пытались отправится разом, одним батчом, что вызывало долгую транзакцию, которая к тому же генерировала запредельную пишущую нагрузку по write-iops.

Так как все эти данные уже были отправлены в аналитику из основного хранилища, то по факту их уже не стоит отправлять второй раз. Исходя из этого вывода мы придумали быстрый work-around, как это обойти, просто скипнув батч, который пытается отправится, тем самым остановив деструктивные действия для базы.

Отсюда можно вывести ещё один урок — даже самые крутые профессионалы тоже люди, а людям свойственно ошибаться, не стоит полагаться только на мнение эксперта и надеяться, что всё будет хорошо.

Также благодаря этому мы обратили внимание коллег из dba на особенности работы этого механизма, и ребята позже сделали его безопасным в подобных случаях.

  • Третий переход — 18 сентября. Не зря цифра 3 считается «магическим» совершенным числом, в этот раз всё пошло так, как мы ожидали: снова начались длинные транзакции из-за отправки огромного батча в аналитику, мы применили наш work-around и скипнули батчи данных, после этого ошибки стали уходить, все запросы отрабатывали успешно, запросы с одиночной базы ушли полностью, сервис работал безупречно, а это значит, что мы, наконец, завершили переход на шардированное хранилище! И тем самым мы успели завершить цель в срок, до начала высокого сезона.

Подведем итог: мы заменили одиночную базу на 20 CPU на шардированную конфигурацию из 8 шардов каждый по 8 CPU, имели потребление CPU ~50% на одиночной базе в пике, получили от 28 до 35% потребление CPU на шардах. И в теории могли в дальнейшем применить горизонтальное масштабирование и увеличить коробку шарда с 8 CPU до 20 CPU, что давало нам большой запас в масштабировании нагрузки.

5. Завершающий этап перехода 

После успешного перехода работа над шардированием не заканчивается.

  • Нагрузочное тестирование

Для подтверждения того, что мы достигли наших целей по масштабированию, решено провести нагрузочное тестирование в продакшне. Определили план теста, собрали ленту запросов, запланировали дату и начали тест.

Шардирование сервиса объявлений Авито Доставки. Часть II - 20
Шардирование сервиса объявлений Авито Доставки. Часть II - 21

Во время теста мы довели общий трафик до 3,6M RPM (1,6М органического трафика + 2М синтетической нагрузки, дошли до этапа, когда сервис начал деградировать, и остановили тест.

Шардирование сервиса объявлений Авито Доставки. Часть II - 22

По метрикам базы видно, что мы снова уперлись в CPU:

Шардирование сервиса объявлений Авито Доставки. Часть II - 23

В итоге мы подтвердили нашу гипотезу, что можем держать нагрузку х2 от текущей — а это целых 55k RPS без деградации! — и при этом можем расшить узкое место, легко повысив лимит CPU при необходимости через горизонтальное масштабирование.

  • Обнаружили багу.

На дворе уже Q3 2023, октябрь, мы обнаружили проблему: при поиске объявлений с доставкой стали выдаваться товары выше 150к рублей, а такого быть не должно, так как у нас есть строгое ограничение, что доставку можно осуществлять только для товаров до 150к рублей. Например, айфоны с доставкой за 250к. Хотя доставки как таковой на товаре не было, это мешало поиску товаров с доставкой и вводило пользователей в заблуждение. Это наш третий LSR.

Шардирование сервиса объявлений Авито Доставки. Часть II - 24

После расследования мы обнаружили проблему: при подготовке запросов в шардированное хранилище мы посадили баг: в запросе на удаление товара не возвращался item_id 

Шардирование сервиса объявлений Авито Доставки. Часть II - 25

Из-за этого событие об удалении айтема не отправлялось, что приводило к тому, что при изменении цены товара с доставкой выше нашего лимита доставка на товаре исчезала, но другие сервисы не узнавали об изменении, а это давало такие спецэффекты, как невалидные айтемы в поиске.

После осознания проблемы мы подготовили фикс, нашли все товары, которые потенциально могла затронуть данная проблема и актуализировали данные по ним. Также мы обнаружили, что тесты не покрывали данный сценарий, и исправили это, написав unit и integration тесты (e2e мы в команде не пишем, так как накладные расходы на их поддержку сильно высоки).

Из этого можно извлечь следующий урок: не каждая проблема видна сразу. Описанный случай мы обнаружили спустя месяц после перехода на шарды и спустя три месяца после написания кода. Важно покрывать код тестами, даже не самые критичные участки.

  • Отказ от старой инсталляции

Тем временем на дворе был уже 2024Q1, январь: мы удалили старую базу и почистили свои хвосты, остававшиеся после перехода на шарды: почистили конфиги и освободили ресурсы базы с помощью dba.

итоговый тайм-лайн шардирования.png

Итоговый TimeLine

Какие уроки мы получили

  1. План может измениться самым непредсказуемым образом:

    • изначально мы хотели 2 шарда по 20 CPU;

    • dba хотели 32 шарда по 2 CPU;

    • в итоге сделали 8 шардов по 8 CPU.

  2. Возникает много нюансов, которых изначально не ждёшь:

    • батчевые запросы на шарды приводят к сильному росту TPS и CPU;

    • необходимо детально настраивать коннекты на приложении и в pgbouncer;

    • могут возникать проблемы на самих серверах, на которых живёт база;

    • старый функционал стреляет в ногу (кастомные healcheck).

  3. Шардирование помогает понять, где проблема, если сервис работает с шардами одинаково. А если шарды ведут себя по-разному, значит, проблема где-то на стороне шарда.

  4. Нужно глубоко погружаться в проблему, чтобы добиться своего от dba, так как они работают в контексте, сильно отличающемся от контекста продуктового инженера, и желательно нужно иметь заинтересованного в успехе человека, который будет общаться с dba на одном языке.

— Бу, испугался? Не бойся. DBA твой друг!

Шардирование сервиса объявлений Авито Доставки. Часть II - 27

6. Какие результаты получились спустя 2 года эксплуатации решения

На дворе Q3 2025 года. Вот наши цели и факты:

Цель

Результат

x4 по трафику

Трафик не вырос, но держим x2 

x2 по данным

Данные выросли х3

SLI > 99.9

SLI = 99.95

Latency < 80мс

latency ~ 70мс

Как видно, трафик так и не вырос, как мы ожидали, а рост кол-ва данных превысил наши ожидания, но мы с этим легко справились и имеем ещё солидный запас. Однако если такой рост продолжится и дальше на горизонте нескольких лет, возможно, понадобиться решардинг или хранилище побольше. Мы улучшили наши гарантии надёжности, а также не просели по latency времени ответа.

Кликни здесь и узнаешь

Заключение

Вот что у нас получилось:

Шардирование сервиса объявлений Авито Доставки. Часть II - 29

Плюсы:

  1. Сервис работал исправно при текущей нагрузке, но был близок к пределу в 32k RPS и не имел возможности быстрого масштабирования, мы расширили этот предел до 55k RPS и имеем возможность как горизонтального, так и вертикального масштабирования при необходимости. 

  2. Решение показало свою состоятельность, на горизонте двух лет проблем с ним не было.

  3. В дальнейшем мы также реализовали GD-сценарий, когда при деградации одного из шардов отдаём частичный ответ клиенту, что сильно увеличивает наш SLI, но об этом, возможно, расскажем в следующий раз.

Минусы:

Шардирование — это долго. У нас ушло 5 кварталов:

  1. Q1 2023 — ресерч и выбор решения.

  2. Q2 — создание хранилища, реализация обвязки.

  3. Q3 — тестирование, переход.

  4. Q4 — нагрузочное, багфикс, удаление старой базы.

  5. Q1 2024 — чистка хвостов, закрытие техдолга.

Дополнительная сложность — больше кода, сложнее поддерживать и отлаживать сервис, так как реализация шардирования полностью на нашей стороне.

Благодарности

Помимо меня над задачей также работали коллеги из Доставки: Алексей Власов, Полина Харина, Роман Хурчаков.

И огромное спасибо всем кто помогал на этом пути, особенно:

  • коллегам из DBA — за помощь и поддержку в переходе на шарды;

  • коллегам из TechPR — за помощь в подготовке статьи.

А если хотите вместе с нами помогать людям и бизнесу через технологии — присоединяйтесь к командам. Свежие вакансии есть на нашем карьерном сайте.

Автор: Fatik32

Источник

Rambler's Top100