Фото EJ Yao на Unsplash
В Gusto ми активно використовуємо Sidekiq для більшості завдань в фоновому режимі. Ми є постійними користувачами Sidekiq та його підприємницьких рішень Sidekiq Enterprise і Sidekiq Pro більше 10 років. На сьогодні ми обробляємо в середньому 150 мільйонів завдань щодня, щоб підтримувати різноманітні робочі навантаження. Серед них:
- Дії, ініційовані користувачем, та дії, що блокують користувача
- Рутинна, запланована робота
- Відновлення даних
- Критичні завдання, що залежать від термінів банківських або зарплатних виплат
- TODOs та нагадування
Ця стаття описує чотири внутрішні інструменти, які ми використовуємо в Gusto для керування продуктивністю та надійністю наших завдань Sidekiq, щоб допомогти нам забезпечити стабільний та швидкий досвід для наших клієнтів.
Рекомендоване читання: Перш ніж зануритись, рекомендуємо прочитати Масштабування Sidekiq в Gusto для розуміння концепцій і контексту, що буде згадано в цій статті.
Фон
Більшість коду Gusto існує в величному моноліті. Для простоти припустимо, що весь код працює в одному середовищі і спільно використовує одну базу даних, черги, екземпляр Redis і інфраструктуру розгортання.
Ми маємо ~50 черг. Це поєднання спеціалізованих черг латентності, які обробляють 95% нашого обсягу завдань, та командних черг для певних критичних завдань (наприклад, банківські платежі та заробітна плата). Кожен Pod Sidekiq працює в Multiprocess Mode і налаштований на обробку завдань з однієї черги.
SIDEKIQ_COUNT=5 bin/sidekiqswarm - config config/sidekiq.yml - environment $RAILS_ENV - concurrency 1 - queue {{ $myQueue }}
Щоб підтримати різноманітні робочі навантаження та потрібну терміновість і критичність, ми використовуємо черги латентності, як описано в попередній статті: Масштабування Sidekiq в Gusto. Сьогодні більше 95% наших завдань виконуються на спільних чергах латентності.
Тепер можуть виникнути деякі питання, які ми не обговорювали.
- Як ми підтримуємо латентність для цих черг?
- Як ми забезпечуємо продуктивність завдань і попереджаємо або зупиняємо їх погіршення?
- Як ми зменшуємо вплив проблемних завдань із загальними ресурсами і монолітною архітектурою, яку використовуємо в Gusto?
Існуючі проблеми
Ось деякі проблеми, які нам потрібно вирішити, щоб підтримувати продуктивність наших черг:
Шумні сусіди в межах черги
Шумні сусіди — це завдання, що перевищують «контракт» для заданої черги латентності. Наприклад, коли завдання в черзі Within5Minutes займає більше 5 хвилин для виконання.
Наші черги — це спільні ресурси (тобто, кілька завдань виконуються в одній черзі), тому шумні сусіди змушують інші завдання в черзі виконуватися довше. Це призводить до затримок у обробці.
Шумні сусіди часто є результатом:
- Погіршення продуктивності класу завдання
- Надмірно великого обсягу завдань
Зомбі-завдання
Це довготривалі завдання, які ніколи не завершуються в межах циклу розгортання. У Gusto, коли ми розгортаємо нову версію Sidekiq workers, ми QUIET старі workers на 30 секунд перед їх завершенням. Коли новий процес Sidekiq починається, наприклад, під час розгортання, Superfetch забирає завершені, але неповні завдання і ставить їх на початок черги. Ці завдання, що не завершуються, потім заважають процесу Sidekiq виконувати нову роботу. Коли їх багато на одній черзі, вони можуть фактично заблокувати чергу для нових завдань.
Проблеми, пов'язані з навантаженням
Це системні проблеми (наприклад...)
db cpu load, черга запитів, контейнер OOM) що призводять до погіршення продуктивності виконання завдань Sidekiq
Рішення
Ми створили кілька рішень всередині компанії, щоб пом'якшити та запобігти проблемам, описаним вище.
Kill Switching
Kill Switch був одним з перших вдосконалень, які ми створили, з метою зміни поведінки завдань у розгорнутих системах. Незважаючи на наші найкращі зусилля, іноді в нас виникають проблемні завдання, які можуть стати шумними сусідами. Kill Switch надає нам три варіанти, які ми можемо використовувати для зміни поведінки завдання:
- Зупинити завдання і відправити його в морг
- Зупинити завдання без відправки в морг
- Перенаправити завдання на іншу чергу
Ми реалізували Kill Switch як Server Middleware та Client Middleware. Це дозволяє нам перехоплювати завдання як до того, як вони потраплять у чергу, так і після того, як вони вийдуть. Ми зберігаємо змінену поведінку або перемикач у Redis для швидкого доступу.
Автоматичне перенаправлення неефективних завдань
Як описано вище, Kill Switch служив нам багато років. Однак, коли ми почали об'єднувати завдання в спільні черги латентності, ми помітили, що використовуємо цей інструмент частіше для перенаправлення або зупинки проблемних завдань. Більше того, цей процес був надзвичайно ручним:
- Отримуємо сповіщення про перевищення латентності черги.
- Ручне визначення проблемного завдання через панелі моніторингу та метрики.
- Спілкування з відповідною командою.
- Визначення, що робити з проблемним завданням. Ми майже завжди переміщали його в чергу з вищою латентністю.
Цей процес мав кілька проблем. Він був надзвичайно ручним, що вимагало залучення кількох команд для майже однакових дій. Також це був реактивний підхід, що означає, що допустима латентність вже була перевищена, перш ніж ми діяли. Тому ми спроектували проактивну, автоматичну систему для перенаправлення завдань, які, ймовірно, перевищать латентність. Система працювала таким чином:
- Збирала метрики про продуктивність завдань Sidekiq, зокрема кількість завдань одного класу, що потрапили в чергу за певний період часу, та середній час виконання завдань цього класу.
- Обчислювала оцінене навантаження завдань, що знаходяться в черзі, протягом цього певного періоду часу.
- Перенаправляла завдання в іншу чергу, якщо навантаження перевищує доступну пропускну здатність оригінальної черги латентності.
- Сповіщала команду власника про те, що їх завдання було перенаправлене.
Ця система зменшила кількість випадків перевищення латентності наших черг на понад 70%, навіть коли ми збільшили обсяг завдань на ~30% і залишили інфраструктуру без змін.
SidekiqWatchdog для зомбі-завдань
Ми створили SidekiqWatchdog (не плутати з watchdog liveliness check), який моніторить всі працюючі процеси і завершує завдання та процеси, що виконуються довше ніж налаштований час. Цей SidekiqWatchdog працює на лідері Sidekiq Senate, щоб забезпечити наявність лише одного запущеного екземпляра в будь-який момент часу. При старті лідера він створює новий потік, щоб періодично сканувати Sidekiq::Workset, оцінювати виконувані завдання та надсилати TERM сигнали процесам з завданнями, які перевищили попередньо заданий час виконання. Ми спроектували його так, щоб він був налаштовуваний як для кожної черги, так і для кожного завдання, щоб критичні черги можна було підтримувати на більш суворих стандартах. Ця система можлива, оскільки ми використовуємо однопотоковий режим Sidekiq Multiprocess.
SidekiqWatchdog забезпечує блокування критичних черг, ідентифікує проблемні довготривалі завдання, впроваджує критерії продуктивності для завдань Sidekiq і допомогла нам з розслідуванням завислих потоків у процесах Sidekiq.
Автоматичне призупинення черг при системних збої
Незважаючи на наші зусилля зменшити вплив завдань одне на одного, вони все ще взаємодіють.
Однією з ситуацій, яку ми спостерігали, є висока паралельність завдань, які виконують особливо повільні запити. Це призводить до значного погіршення продуктивності бази даних, що, в свою чергу, уповільнює виконання всіх інших завдань, що взаємодіють з базою даних. Історично це вважалося принаймні SEV-2, коли учасники інциденту метушилися, намагаючись ідентифікувати та вбивати проблемне завдання. Після кількох інцидентів ми усвідомили, що завдяки нашій дефініції черг латентності ми маємо доступ до важливого елементу інформації — скільки часу завдання може перебувати в черзі латентності, перш ніж його потрібно виконати. Це означає, що під час інцидентів є підмножина черг латентності, які можна безпечно призупинити без впливу на бізнес-процеси.
У періоди високого навантаження на базу даних або під час інцидентів, ми тепер автоматично призупиняємо черги з латентністю більше 1 години. Це досягає наступного:
- Зменшує додаткові ручні обов'язки для учасників інциденту
- Зменшує навантаження на базу даних і таким чином знижує серйозність інциденту
- Допомагає нам ідентифікувати проблемні завдання
- Якщо ми помічаємо різке зниження використання процесора бази даних, це свідчить, що проблемне завдання працює в черзі з високою латентністю
- Якщо різкого зниження не відбувається, то ймовірно, що проблемне завдання знаходиться в іншій черзі
Це працює в поєднанні з іншими інструментами та моніторами, щоб швидко ідентифікувати та вирішити проблемні завдання. Як тільки ці завдання виявляються, вони можуть бути керовані за допомогою Kill Switch, описаного вище.
Висновки
Sidekiq — це потужний інструмент для фонової обробки, і як будь-який інструмент, він може бути налаштований для кращого підходу до специфічних потреб і проблем. В Gusto ми розробили набір рішень, таких як Kill Switch, автоматичне перенаправлення, SidekiqWatchdog і автоматичне призупинення черг, щоб вирішити проблеми шумних сусідів, зомбі-завдань і системних збоїв. Ці вдосконалення не тільки покращили продуктивність, пропускну здатність і надійність обробки завдань Sidekiq, але й зменшили ручне втручання та покращили нашу реакцію на інциденти. Сьогодні наші черги латентності, що обробляють 95% нашого обсягу завдань, стали стабільнішими завдяки проактивному вирішенню проблемних завдань і зменшенню їхнього впливу.
Як ми продовжуємо масштабуватися, ми залишаємося відданими вдосконаленню цих інструментів і дослідженню нових рішень для задоволення змінюваних вимог. Поділившись нашим досвідом, ми сподіваємося надати корисні ідеї для тих, хто стикається з подібними проблемами. Якщо цей пост надихнув вас на нові ідеї або у вас є вдосконалення, якими ви хочете поділитися, ми з радістю почуємо від вас!
Gusto шукає нових співробітників! Дізнайтесь більше на gusto.com/careers.
Перекладено з: Scaling Sidekiq at Gusto Vol 2