Блог ·
Обʼєднання замість звіряння
Крок автоматизації застиг на стані, який побачив. Полагодженням був не фоновий процес: той рядок пояснює рішення, а виправлення знищує це пояснення.
Автор: Provibr
Потік автоматизації може надіслати команду машині й чекати на неї. Якщо очікування спливає, крок хибить, а виконання бере свій вихід помилки. Це правильно: потік просив чекати, доки роботу буде зроблено, а її не зроблено, отже ніхто не знає, чи наступний вузол зараз діятиме на машині, що досі вимикається.
Крок записує те, що побачив тієї миті. Виміряно в попередньому раунді проти справжнього агента, він записав sent, а за шістдесят мілісекунд команда перейшла у збіглий час. Крок і далі казав sent, наступного дня, назавжди.
Очевидне прочитання полягає в тому, що рядок застарів і потребує полагодження. Це прочитання хибне, і зʼясування причини забрало більшу частину роздумів цього раунду.
Рядок є поясненням, а не полем стану
Застигле значення не є копією стану команди, яка відхилилася. Це відповідь на інше питання: що знав потік, коли вирішив узяти вихід помилки.
Напишіть поверх timeout, і рядок тепер каже, що потік відреагував на збіглий час. Він цього не робив. Він відреагував на очікування, що спливало, поки команда була ще в дорозі, а це інша подія з іншою причиною та іншим полагодженням. За місяць той, хто читав би це виконання, зробив би хибний висновок із рядка, виправленого з добрими намірами.
Застигле значення отже лишається застиглим. По-справжньому хибним був коментар над ним, який обіцяв, що з цього рядка можна дізнатися, чи команда врешті вдалася. Не можна було, а обіцянка в коментарі має придатність, що триває до дня, коли хтось на неї покладеться.
Справжнє питання, чи все врешті склалося, вирішують читанням поточного стану команди тієї миті, коли хтось дивиться. Сторінка виконання показує обидва поруч: що потік бачив тоді і що є правдою тепер.
| Звідки походить | На що відповідає | |
|---|---|---|
| Тоді | Рядок аудиту, застиглий у мить рішення | Чому виконання взяло той вихід, який узяло |
| Тепер | Таблиця команд, прочитана під час відкриття сторінки | Чи робота врешті завершилася |
| Рядка немає | Жодне з двох; показано як власний стан | Команди більше немає в обліку |
Цей останній рядок важить. Якщо рядок команди прибрано, показ не відкочується до повторення застиглого значення. «Більше немає в обліку» і «досі каже sent» є двома різними фактами, а сторінка, що подає друге, коли правдиве перше, бреше з незворушним обличчям.
Три причини, чому фоновий процес програє обʼєднанню
Іншим варіантом був фоновий процес, що переписував би остаточний стан у рядок аудиту, щойно команду вирішено. В інших місцях ми маємо процеси саме такої форми, тож це був справжній вибір, а не опудало.
Фоновий процес перезаписує пояснення. Як вище. Виходом є написати друге поле поруч, і це чесно, але це другий різновид правди, який читали б рівно в одному місці: у тому, що тепер робить обʼєднання.
Фоновий процес лагодить лише те, що стається після нього. Рядок, з якого почалося все це дослідження, уже існує. Розгорнутий сьогодні процес ніколи його не торкнувся б, як і жодного виконання з двох попередніх раундів, бо їхні команди вирішено давно, і жодна майбутня подія туди вже не завітає. Обʼєднання показує їх усі від дня запуску, зокрема й той, який ми шукали.
Фоновий процес приносить цілий апарат. Оновлення рядків у тлі потребує ритму, заявки, щоб два примірники не зіштовхнулися, очікування проти розбіжності, відповіді про зберігання та власного шляху збою, і кожне з цього може стояти хибно о третій ночі. Пошук жмені ідентифікаторів за первинним ключем, коли хтось відкриває сторінку, не потребує нічого з цього.
Загальне правило під цим: краще читати джерело, ніж копіювати його, хіба що копія відповідає на питання, на яке джерело відповісти не може. Тут копія відповідає на інше питання і потрібні обидві, а це саме той випадок, коли їх тримають нарізно.
Ідентифікатор вийшов зі стовпця JSON, отже запит обмежено
Одна дрібниця в тому обʼєднанні не є ані оптимізацією, ані ввічливістю.
Ідентифікатор команди лежить усередині блоку JSON на рядку аудиту. Стовпці JSON не мають зовнішніх ключів, отже ніщо не запевняє, що ідентифікатор усередині належить цьому обліковому запису. Сьогодні він належить, бо його туди записав наш власний код. Це не межа, це звичка.
Тому пошук обмежено не лише ідентифікатором, а й організацією. Без цієї межі виправлене вручну значення в тому блоці читало б стан чужої команди. Малий витік, легко пропустити, і саме з тих, які знаходить лише той, хто їх шукав.
Один потік на момент, з моментом у ключі
Тариф на розміщення міг привʼязати один потік автоматизації, в одному стовпці. Той стовпець означав «виконай це, коли машину буде підготовлено», і це не було записано ніде, окрім поведінки функції, яка його читала.
Клієнти хотіли також потік під час перевстановлення і потік під час видалення. Очевидним полагодженням є три стовпці. Воно хибне з причини, варта названня: у кожному з тих трьох стовпців міг би стояти потік, чий власний тригер каже інше. Потік, що працює під час видалення, посаджений у стовпець, який спрацьовує під час підготовки. Незмінне правило «тригер каже, коли він працює» стає порушним у трьох місцях замість жодного, а схема ніде не заперечує.
Привʼязка отже стала окремою таблицею, з тарифом і моментом разом як унікальним ключем. Один потік на момент і на тариф, накинутий покажчиком, а не дбайливістю.
Дві дрібниці тієї міграції варті збереження.
Момент зберігають як текст із обмеженням перевірки, що перелічує три значення, а не як наявний перелік тригерів. Той перелік має пʼять значень, і два з них є тригерами, яких цей механізм не обирає ніколи: ручне виконання починає людина, а виконання через вебхук починає вхідний запит. Стовпець із переліком радо прийняв би будь-який із них, і тоді існував би рядок привʼязки, з яким ніколи нічого не стається. Налаштування, яке мовчки нічого не робить, гірше за налаштування, якого бракує.
А старий стовпець прибрано в тій самій міграції, що перенесла його вміст. Лишити його як зручне похідне читання означає саме той другий різновид правди, який ця таблиця й приходить усунути. Компілятор знайшов кожного читача, щойно стовпець вийшов зі схеми; їх було сім. Перенесення відбувається перед прибиранням, в одній транзакції, отже немає миті, коли налаштування клієнта не лежить ніде.
Перенесення до того ж бере власний тригер кожного потоку, а не припускає значення старого стовпця. Рядки, чий потік ніс інший момент, за старим кодом не працювали б ніколи; тепер привʼязка, яку зробила людина, справді робить те, що вона гадала.
Пошта за згодою, у графі, а не в налаштуваннях
Потік може написати сповіщення, і в цьому раунді таке сповіщення може вийти й поштою. Це виявилося питанням типових значень, а не надсилання.
Поштові категорії платформи є переліком заборон: те, що не вимкнено окремо, увімкнено. Для наявних категорій це правильно, усі вони є речами, про які клієнт мусить дізнатися. Для сповіщення, чий текст хтось написав у редакторі автоматизації, це неправильно, бо потік, що працює на кожному замовленні, писав би цілій команді щоразу, доки хтось не знайшов би налаштування.
Замість перевертати перелік типове значення живе у графі: позначка на вузлі вимкнена, а ввімкнути її є свідомою дією того, хто написав текст. Категорія й далі існує, щоб пошту можна було вимкнути у звичному місці, точнісінько як інші. Два механізми, кожен для того, у чому він добрий.
Пробне виконання ніколи не надсилає пошти. Воно пише сповіщення в панелі, яке є внутрішнім і закривається одним клацанням, і не надсилає нічого назовні, з тієї самої причини, з якої пробне виконання ніколи не торкається машини: пошта потрапляє до скриньок справжніх людей, а кнопка випробування, що когось дивує, не є кнопкою випробування.
Ще одна дрібниця з тієї самої роботи. Вважалося, що бік надсилання недосяжний звідти, де працюють потоки, і саме тому це відкладали. Виміряно, було навпаки: функція, яку ми хотіли обійти, існує саме тому, що інший процес не вміє складати пошту, і вона просить процес, у якому потоки й так працюють, скласти її. Обхід був довший за пряму дорогу, а пряма дорога має на один стрибок HTTP, один підпис і один шлях збою менше.
Що ми записали б
- Рядок аудиту пояснює рішення. Не лагодьте його. Питання про те, що є правдою тепер, має іншу відповідь і заслуговує на власне місце.
- Полагодження в тлі лагодить лише майбутнє. Якщо рядок, що спричинив роботу, лишився б неторканим, це проєктна відповідь, а не дрібниця.
- Читайте джерело, хіба що копія відповідає на те, чого джерело не може.
- Ідентифікатор зі стовпця JSON не має зовнішнього ключа. Обмежте запит обліковим записом; те, що значення записав наш код, межею не є.
- Кладіть розрізнення в ключ, а не в назву стовпця. Три стовпці є трьома місцями, де можна зберегти хибне; один ключ не є жодним.
- Прибирайте старий стовпець у тій самій міграції. Лишене похідне читання є тим другим різновидом правди, який ви усували.
- Перелік заборон є хибним типовим значенням для тексту, який написав клієнт. Згода лежить там, де пишуть текст.
Для ясності: усе це побудовано і працює. Саме надсилання доведено драйвером, що лише записує в журнал, а не проти справжнього поштового сервера, а привʼязку тарифу ще не випробувано справжнім замовленням.
Із платформної роботи над Provibr. Як поводяться очікування та вихід помилки від початку, описано в дріт, який ви тягнете, і рівно один тригер, а чому збіглий час не є кнопкою зупинення, описано в час очікування не є кнопкою зупинення.