Перейти до вмісту

Блог ·

Дочірнє виконання будить батьківське

Потік, що викликає інший потік, міг би опитувати або бути сповіщеним. Ми зробили обидва, а запасний годинник є добовим, а не пʼятихвилинним, і на це є причина.

Автор: Provibr

Потік тепер може викликати інший потік. Виконання, що викликає, спиняється, викликане виконання розпочинається, а коли викликане виконання завершується, те, що викликало, триває дротом ok або дротом error.

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

Батьківське не чекає, йому кажуть

Природним способом це написати є батьківське виконання, що чекає. Батьківське знає, на яке виконання воно чекає, тож може спитати. Опитувати раз на десять секунд, дивитися, чи дочірнє завершилося, і тривати, коли завершилося.

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

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

Те сповіщення є одним твердженням: оновити рядок очікування, де збігається ідентифікатор дочірнього виконання і статус усе ще є очікуванням.

Чому той ідентифікатор є стовпцем, а не частиною згустка стану

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

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

Зовнішній ключ стоїть на SET NULL, а не на CASCADE. Якщо дочірнє виконання приберуть, очікування батьківського не має зникнути разом із ним, бо тоді батьківське є призупиненим без нічого, що коли-небудь його розбудить. Утрату посилання можна пережити; утрату рядка ні.

Запасний годинник є добовим, і саме число є аргументом

Сповіщення є механізмом. Годинник є страхувальною сіткою на випадок, коли сповіщення не надходить ніколи: контейнер, що впав на півдорозі завершення дочірнього виконання, рядок, якого торкнулися вручну.

Очевидним значенням для страхувальної сітки є щось мале. Пʼять хвилин видаються відповідальними.

Це є хибним, а причиною є те, що дочірньому виконанню дозволено робити. Дочірнє виконання може містити людське погодження, а погодження може мати крайній строк за тридцять днів. З пʼятихвилинним годинником батьківське виконання того дочірнього прокидається тисячі разів за ті тридцять днів, щоб дізнатися, що нічого не змінилося, і кожне з тих пробуджень є запитом HTTP від шлюзу до контейнера вебу.

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

Загальна форма: проміжок повтору не є «настільки коротким, наскільки ми можемо собі дозволити». Він є функцією від найдовшого правомірного очікування, і якщо ви не спитали, яким воно є, то проміжок є здогадом.

Годинник, якого не можна вкоротити, є годинником, якого не можна довести

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

Спокусою є довести її вручну: залізти в базу даних, посунути час пробудження в минуле, подивитися, як виконання поновлюється. Це доводить поновлення. Годинника воно не доводить, бо годинник є саме тією річчю, яку ви підмінили.

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

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

Друге з цього не є оборонним кодуванням заради самого себе. Копіювання нашого прикладового файлу середовища дає вам змінну наявною і порожньою, а порожній рядок не є невизначеним. Перетворення його на число дає нуль. Без цього сита скопійований файл налаштувань поставив би проміжок на нуль і вкинув би кожне батьківське виконання з незавершеним дочірнім у цикл пробуджень проти контейнера вебу. Відмінність між порожнім рядком і невизначеним уже кусала нас, у місці, де вона коштувала маршрутові звантаження помилки 404.

Дві межі, кожну забезпечено двічі

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

Статично, коли потік зберігають: граф обходять, потоки, які він викликає, розвʼязують, а цикл або надто глибокий ланцюг відхиляють, назвавши ланцюг у помилці. Динамічно, під час виконання: ті самі дві межі знову, з власними кодами відмови.

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

Глибина є стовпцем на виконанні, а не чимось обчисленим обходом батьківських. Це суперечить лінії, якої ми зазвичай тримаємося, а саме що все виводжуване має виводитися. Тут вона не справджується, бо посилання на батьківське стоїть на SET NULL: щойно батьківське виконання приберуть, ланцюг не можна пройти назад, і глибина дочірнього виконання, що триває, стала б непізнаваною саме тієї миті, коли вона має значення.

Що зміряв наступний раунд

Гілки, які цей дизайн лишив відкритими, пройшли в пізнішому раунді, і дві з них є цікавими.

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

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

Що ми б записали

  1. Сторона, що знає, має казати, а не бути спитаною. Опитування перетворює вашу затримку на значення в налаштуваннях.
  2. Використайте наново шлях пробудження, який ви вже маєте. Рішення і завершене дочірнє виконання є для черги тією самою подією.
  3. Значення, яке читають ззовні згустка стану, належить до стовпця. Це вирішує напрямок, а не послідовність.
  4. Ставте на SET NULL, а не на CASCADE, коли рядок, який було б вилучено, є чиїмось єдиним способом бути розбудженим.
  5. Проміжок страхувальної сітки є функцією від найдовшого правомірного очікування, а не від того, наскільки чуйними ви хотіли б видаватися.
  6. Висловлюйте розмін у запитах і годинах. Тридцять змарнованих тактів проти доби невидимості є рішенням; «пʼять хвилин» є рефлексом.
  7. Стала, якої не можна змінити, є гілкою, якої не можна довести. Гачок належить поруч зі сталою.
  8. Хай гачок робить лише безпечніше, і затискайте його проти виробничого значення.
  9. Порожня змінна середовища не є невизначеною, а перетворення її на число дає вам нуль.
  10. Перевіряйте межу там, де її можна виправити, і там, де вона мусить триматися. Незалежно редагованим графам потрібні обидві.

Для ясності про те, чим це є: перевірка злиття пройшла 16 із 16 кроків без жодного рядка невдачі, набір вебу був зеленим на 4583, пакунки, шлюз і інструменти разом на 7286, обхід на 75, статична перевірка підпотоків на 13, наскрізний запуск проти бази даних розробки на 58, вимірювання одержувачів на 4, браузерний тест зі справжнім робітником шлюзу на 31, у якому девʼяностосекундна пауза поновилася через девʼяносто пʼять секунд, тест редактора на 9, а сканування доступності не знайшло нічого на шести сторінках. Не доведено на той час: запасний годинник, підпотік усередині циклу, зникле дочірнє виконання і гілка вичерпання повторів. Ті чотири пройшли в наступному раунді.

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

Читати далі

Почніть з одного хоста й одного агента

Створіть обліковий запис, встановіть агента однією командою та підключіть свій перший хост Proxmox. Пробний період триває 14 днів і не потребує платіжних даних.