Блог ·
Тайм-аут не є кнопкою зупинки
Дедлайн завершує ваше очікування, а не роботу. Що межа в одну годину робить із довгим резервним копіюванням і як записати результат, якого ніхто не знає.
Автор: Provibr
Кожна віддалена команда потребує дедлайну. Ця частина безсумнівна: процес, який може чекати вічно, врешті чекатиме вічно, а один застряглий запит триматиме воркер, зʼєднання і рядок у вашій черзі, доки хтось щось не перезапустить.
Значно менш очевидно те, що дедлайн насправді робить. Він завершує очікування на вашому боці. Він не завершує, у загальному випадку, роботу на іншому боці. Усе складне в довгих операціях живе саме в цьому проміжку.
Ми запускаємо агента на гіпервізорах клієнтів і надсилаємо йому команди. Резервне копіювання віртуальної машини є тією командою, яка змусила нас поставитися до цього проміжку серйозно, бо копіювання цілком чесно може тривати годину, а година це рівно те місце, де стоять наші власні межі.
Межа не є вподобанням
Наш агент обмежує дедлайн кожної команди однією годиною. Платформа може попросити більше; вона цього не отримає. Обмеження це один min() на значенні, що приходить у запиті, де нуль читається як «не задано», а запасним варіантом є дві хвилини, коли поля немає.
Це обмеження має наслідок, який люди пропускають, коли виставляють тайм-аути у файлі конфігурації. Якщо платформа вірить у дедлайн, у який агент не вірить, ви маєте дві системи, що розходяться в питанні, коли команда мертва, і це розходження проявляється найгіршим можливим симптомом: агент здається, звітує і йде далі, а платформа досі тримає рядок відкритим і чекає на відповідь, яка ніколи не прийде в очікуваній нею формі.
Тож правило, до якого ми дійшли, нудне і все одно варте того, щоб його записати. Кожен тайм-аут на боці платформи обирається з огляду на стелю агента. Наші дедлайни для резервного копіювання і відновлення становлять одну годину саме тому, що це стеля, і з кількох десятків дедлайнів команд вони єдині два, які її торкаються.
Дедлайни не є питанням стилю
Ось як насправді виглядають наші дедлайни команд і чому кожен з них саме такий.
| Команда | Дедлайн | Чому саме це число |
|---|---|---|
| Читання апаратної конфігурації, читання правил брандмауера | 30 с | Одне читання. Довше означає, що зламано щось інше. |
| Запис правил брандмауера | 120 с | Один виклик API на правило, в обидва боки, на межі кількості правил. |
| Перелік точок відновлення | 60 с | Один виклик на кожне сховище резервних копій на вузлі, а каталогове сховище з тисячами архівів читає власний індекс з диска. |
| Самооновлення агента | 300 с | Завантажити, порахувати хеш, записати. Щедро для повільного каналу в клієнта. |
| Створення резервної копії | 3600 с | Стеля агента. |
| Відновлення з резервної копії | 3600 с | Та сама стеля, досягається швидше: відновлення переміщує приблизно вдвічі більше байтів, ніж перемістило копіювання. |
| Підготовка нової хмарної машини | 1200 с | Оновлення пакетів на образі, якому може бути кілька тижнів, а потім установлення. |
| Виконання міграції панелі | 1800 с | Дві панелі повністю прочитано, записано і перераховано. |
Два з цих рядків є радше історіями, ніж числами.
Рядок про хмарну машину колись провалювався до типових двох хвилин. Двох хвилин вистачає для образу, який випадково виявився свіжим, і не вистачає ні для чого іншого, тож платформа звітувала про невдачу, поки встановлення всередині машини тривало і завершувалося кількома хвилинами пізніше. Замовлення було позначено як невдале. Машина працювала. Це найгірший різновид неправди: не збій, а суперечність, і розвʼязувати її доводиться клієнтові.
Рядок про міграцію є найдовшим терміном, який не є стелею агента, і міркування за ним є підрахунком, а не відчуттям. Мігратор робить один виклик API на кожного користувача і один на кожен сервер до панелі призначення. На тисячі серверів це тисяча вихідних викликів і тисяча відповідей назад, з повним читанням обох боків перед цим і перерахунком після.
Ціна помилки не є симетричною
Це принцип, що лежить під обома історіями. Надто короткий дедлайн і надто довгий дедлайн відмовляють цілком по-різному, і ставлення до них як до двох боків того самого регулятора щоразу дає вам неправильне число.
Задовгий: операція, яка справді померла, тримає рядок відкритим ще якийсь час. Хтось чекає. Нічого не пошкоджено, нічого не продубльовано, і остаточне прибирання минає без пригод.
Закороткий: ви записуєте невдачу для роботи, яка вдалася. Тепер ваші записи й дійсність розходяться, а кожен ваш шлях відновлення (повтор, повторне створення, повернення коштів, сповіщення) націлений на проблему, якої немає, і створить другу.
Ці випадки не однаково погані, тож і запаси в них не мають бути однакового розміру. Для всього, що створює, переміщує або видаляє справжні ресурси, ми свідомо беремо щедрий бік і записуємо чому.
Два годинники, що стартують у різні моменти
У будь-якій системі команд із чергою є тонкість, яку легко проґавити і легко виміряти. Наша платформа обчислює дедлайн як абсолютний момент, у мить, коли записується рядок команди. Агент обчислює свій дедлайн як тривалість, що починається, коли він береться до виконання.
Це різні годинники. Якщо агент офлайн, або зайнятий, або команда лежить у черзі тридцять секунд, платформа вже витратила тридцять секунд свого бюджету, а агент ні. Платформа здається першою.
Це правильний порядок, і варто прямо сказати чому. Дедлайн платформи відповідає на питання, яке клієнт насправді ставить: скільки минуло відтоді, як я натиснув? Дедлайн агента відповідає на інше: скільки цій частині роботи дозволено тривати? Час за настінним годинником від натискання є тим числом, про яке йдеться в розмові з підтримкою. Зробити їх одним числом було б охайніше і відповідало б на неправильне питання.
Там ховається і третій годинник. У нашому агенті очікування на дозвіл паралельності відбувається поза дедлайном виконання. Команда, поставлена в чергу за іншою командою до тієї самої інтеграції, не палить власний бюджет, поки чекає. Якщо ваші дедлайни щільні, перевірте, який з ваших годинників включає чергу; відповідь рідко збігається з тією, що в документації.
Що дедлайн насправді скасовує
Коли дедлайн агента спливає, він сигналізує про скасування і перестає чекати. Те, чого це скасування досягає, є вашим власним кодом: цикл опитування, який питав гіпервізор, як просувається завдання.
Воно не досягає гіпервізора. Резервне копіювання триває далі. Воно завершиться або впаде, цілком без нас. Нічого зі скасування не дійшло до дискового масиву.
Це не обмеження, яке слід обійти інженерією. Це чесна форма задачі, і корисний хід полягає в тому, щоб записати результат правдиво, а не вдавати. Класифікація, що пережила зіткнення з продакшном, має три результати, а не два.
- Невдача. Ми знаємо, що нічого не сталося. Клієнт може повторити, і прибирати нема чого.
- Невідомо. Ми перестали слухати, перш ніж зʼявилася відповідь. Щось може існувати.
- Потребує звірки. Ми маємо певні підстави вважати, що існує щось, чого ми не можемо пояснити.
Відновлення, що вичерпало час, є найгострішим випадком. На гіпервізорі тепер може працювати машина, для якої в нашій базі даних немає рядка. Позначити це невдачею було б брехнею, що ховає машину; позначити успіхом було б брехнею, що машину вигадує. Це позначається як таке, що потребує перевірки, і панель просить клієнта подивитися, а не пропонує йому кнопку повтору, яка збудувала б другу машину поруч із першою.
Ми записали це правило в код як перелік певних випадків, а не непевних. Чотири конкретні коди помилок означають, що робота ніколи не починалася, бо всі чотири походять з перевірок, які агент виконує до першого виклику. Усе інше, включно з будь-яким кодом помилки, який ми додамо пізніше й забудемо класифікувати, за замовчуванням є непевним. Напрямок має значення: забутий код, що тихо записується як невдача, змушує наявну машину зникнути із записів, а забутий код, що записується як непевний, коштує комусь лише одного погляду.
Непевний результат потребує способу стати певним
Статус, що означає «хтось має перевірити», є чесним лише тоді, коли хтось або щось справді перевіряє. У нас це воркер, який періодично просить кожну машину з історією резервних копій перелічити свої точки відновлення знову, тож архів, що тихо зник у провайдера, стає сповіщенням, а не списком, який став коротшим того дня, коли клієнт випадково зазирнув.
На цей цикл накладено свідоме обмеження дешевизни, бо кожне перелічення є командою, що виконується на чужому хості. Він відвідує лише машини, які мають політику або принаймні одну відому точку відновлення, і бере обмежену порцію за раунд. Коли цього перестане вистачати, правильним виправленням є курсор, а не більше число.
Цикл також має пережити ситуацію, коли два ваші власні сервери вирішать запустити його тієї самої секунди. Ми робимо це стійким ключем ідемпотентності, складеним з машини і номера вікна, а не з позначки часу: два сервери ніколи не цокають на тій самій мілісекунді, але вони напевно перебувають усередині того самого шестигодинного вікна, тож позначка часу не дедуплікувала б нічого.
Що ми порадили б вам записати
Якщо ви обираєте дедлайни для віддаленої роботи, чотири питання проведуть вас більшу частину шляху.
- Яка тверда стеля і хто її застосовує? Якщо два компоненти розходяться, на практиці перемагає менший, а більший є вигадкою.
- Коли стартує кожен годинник? Час у черзі або входить у бюджет, або ні, і обидві відповіді можна захистити. Незнання захистити не можна.
- Що скасовує спливання часу? Якщо відповідь це «наше власне очікування», скажіть це вголос, бо решта задуму випливає саме з цього.
- На який бік падає некласифікований результат? Зробіть непевність станом за замовчуванням, а певність нехай буде тим, що доводиться записувати.
Ніщо з цього не робить довге резервне копіювання коротшим. Воно робить правдивим запис про нього, а саме цей запис ви читатимете о другій ночі.