Posted on 2023-12-25

Маленький Богдан дуже любив маму, тата, бабусю та обчислювати статистичні данні на домашньому сервері.

В кінці кожного дня, коли усі лягали спати, він з головою накривався ковдрою (залишаючи лише невеличкий отвір для свіжого повітря, що одночасно був і вхідним порталом для кота), відкривав свій ноутбук з Мандрейк Лінуксом і починав рахувати вхідні реквести балансувальника.

Кожен реквест здавався йому потягом, який тягне за собою грузові вагончики функцій та обчислень і пасажирські вагони мрій і сподівань.

Ось приходить перший потяг, другий, третій. Кожен з них важливий, кожен з них хтось очікує. Можливо якісь везуть подарунки, шокодадки і зустрічі з друзями, інші — оцінки до школи і татову роботу, а може один з цих безкінечних потягів везе і дідуся, який помер кілька років тому.

Усі ці металеві хробаки ідуть один за одним, утворюючи хаотичні візерунки заторів і затримань і тільки Богдан, наче супергерой Директор Вокзалу, може їх розправити і розрівняти.

Але як саме? Ця задачка не проста. Кілька тижнів потому, вони з котом вже вирішили що треба гарантувати кожному реквесту час за який він буде обрахованний. Спільними зусиллями вони вирішили назвати це SLA (ви не знаєте життя, якщо не бачили кота який намагається вимовити літеру "S")

Проблема була у тому, що гарантувати будь що дуже складно. А ще люди і машини відправляють реквести як їм заманеться — хтось одразу багато, хтось по одному.

Тому на підковдровому засіданні номер сто одинадцять (дуже важливо вести нумерацію засідань з котом) вони вирішили лімітувати кількість реквестів в секунду і в хвилину. І відповідно назвали це RPS і RPM. Було вирішено зробити RPS 10, а з RPM поки почекати.

Проблема вимірювань

Перші тестові підрахунки пройшли дуже успішно. У Богдана вже були рахівниця і таймер, тому він:

  1. Дав їх коту
  2. Наказав додавати одиницю кожен раз коли проходить реквест
  3. А сам Богдан дивився за потягами і раз у п’ятнадцять секунд знімав данні з рахівниці
  4. Після чого кіт перевертав рахівницю на правий бік, щоб обнулитись
  5. А Богдан ділив усе що прийшло на 15 щоб отримати скільки реквестів було у кожну секунду

Цей підхід вони назвали Prometheus rate()

Перші складнощі

На графіках усе було чудово. Але кіт почав жалітися: іноді усе розподілено нормально, а іноді — усі 150 реквестів приходять у останню секунду. Це була чудова знахідка яка принесла багато натхнення в роботу хлопчика і кота (вони дуже любили знаходити помилки).

Богдан пояснював це так:

– Коли ми будували цю систему, то очікували що реквести будуть приходити рівномірно.

Він намалював чорним олівцем графік з діленнями в 15 секунд і рожевим олівцем рівномірно розподілив реквести.

— А виявилось, що реквести приходять зовсім по іншому:

І це не просто помилка. Це може призвести до скупчення, до затримок і, гірше за все, до порушень домовленностей і очікувань.

Просто треба швидше працювати

І вони почали думати як це вирішити. Через деякий час кіт запропонував просто частіше перегортати рахівницю, наприклад раз на секунду. Але Богдан вже розумів що те що трапилось колись може трапитись і у майбутнєму.

І навіть якщо зменшити віконце вимірювань, то ніхто не заборонить клієнту надсилати усі десять реквестів і наприкінці першої секунди, і на початку другої.

Це також не те щоб добре, тому що майже одночасно прийде багато реквестів, які сервер-вокзал просто не зможуть опрацювати.

— Якщо робиш домовленність, то завжди треба бути готовим до найгіршого сценарію. Саме тому домовленності і існують.

— То може просто скажемо їм щоб відсилали запроси рівномірно? — запропонував кіт.

Але це був не варіант: вони не могли контролювати усіх людей і роботів які користувались сервером. А ще не в усіх годинники показують однаковий час, а навіть якщо б і показували, то сама дорога реквесту до сервера може займати різний час.

Хлопець з котом почали думати як виробити таку домовленність, яка буде:

  • прозора для усіх
  • розподілить навантаження

Пошук рішення (спосіб кота)

Тривали години і тисячоліття напружених роздумів. У сусідній кіманті папа почав хропіти. Бабуся пройшла з кухні до своєї кімнати, трішки шось пошуріла, послухала радіо і згодом стало зовсім тихо. Думки Богдана почали мандрувати.

Йому привиділась дорога до школи у світлі ще нічних ліхтарів. Очікування класу, сніданок який треба їсти швидко. Він ненавидів їсти швидко. Однокласники. Рюкзак який треба носити — навіщо, якщо є ноутбук з усіма книжками світу? Крейда на пальцях що існує в одній реальності і в одному часі що і домашній сервер, який мерехтить зеленим кожен раз коли виконує IO операції.

І ось Богдан вже стоїть на вокзалі, у формі великій для нього настільки, що рукава звисають нижче пальців, а потяги приходить, приходять, приходять. І наче вже не потяги приходять, а ложки манної каші прибувають на перон, а йому треба якось їх відтермінувати, віддалити, щоб не їсти усі відразу. Бо як можна з’їсти, ще й одночасно безкінечну кількість таких величезних ложок?

Він отямився від дрімоти. Но ноутбуці грала заставка, кіт вже спав (чи просто заплющив очі, хто зна тих котів)

«Усе залежить від того чого ми хочемо досягнути. — записав Богдан — Якщо ми хочемо рівномірно розподілити навантаження, то нам треба рахувати не кількість реквестів в секунду, а навпаки, скільки мілісекунд чи секунд має бути між реквестами.»

Він подумав і назвав це SBR — seconds between requests.

Якщо домовитись з усіма щодо цього терміну, то ми з котом зможемо гарантувати що реквестів буде не більше ніж ми можемо обробити.

А якщо хтось буде наполягати щоб був саме RPS, то ми починаємо вимірювання не на початку секунди, а коли приходить реквест і кожен раз просто відступаємо на секунду назад і рахуємо скільки реквестів там було

Наприклад, в нас є 10 реквестів від юзера:

req1:	00:00:01.000
req2:	00:00:01.030
req3:	00:00:01.142
req4:	00:00:01.145
req5:	00:00:01.250
req6:	00:00:01.353
req7:	00:00:01.380
req8:	00:00:01.450
req9:	00:00:01.456
req10:	00:00:01.580
req11:	00:00:01.900

Коли приходить десятий, то ми просто відступаємо на секунду і бачимо що їх було десять. А коли приходить одинадцятий, то ми також відступаємо на секунду (тобо до 00:00:00.900) і бачимо що було вже одинадцять.

Навіть якщо у клиєнта годинник поспішає на кілька секунд, вони можуть бути впевнені, що усе буде оброблено так як треба.

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

Такий підхід можна назвати relative RPS, тобто відносний.

Задоволений собою, Богдан закрив ноубук, переклав кота собі на груди і заснув. Усе одно завтра з ранку треба буде їсти кашу, йти до школи, щось робити і спілкуватись. Але в його снах потяги прибували рівномірно, а машиністи радісно вітались один з одним, підкидуючи картузи просто неба.