Блог ·
Дріт, який можна тягнути, і рівно один тригер
Тягнути дріт це легка частина. Складним було те, чого може торкатися кнопка проби, хто діє, коли ніхто не дивиться, і як довго може тривати пауза.
Автор: Provibr
Наші потоки автоматизації почалися як список кроків, стали графом, потім полотном, а цього разу нарешті отримали жест, який уявляє собі кожен, почувши «редактор потоків»: ви хапаєте крапку праворуч від вузла і тягнете дріт до крапки ліворуч від іншого.
Цей жест становить жменю рядків коду і довгий коментар, що пояснює, чому ці рядки стоять саме там. Решта раунду складалася з питань, які до перетягування не мають жодного стосунку. Скільки місць можуть запустити потік. Чого має право торкатися кнопка проби. Хто діє, коли ніхто не дивиться. І як довго крок може робити паузу.
Захоплення, що зʼїдає клік
Полотно має один шар перетворення. Вузли стоять у ньому абсолютно, а дроти становлять SVG у тому самому шарі, тож немає другої системи координат, яка могла б розійтися з першою. Ця частина навмисно нудна.
Цікавим є захоплення вказівника. Коли ви натискаєте на вихідну крапку, щось має стежити за вказівником до відпускання, зокрема й поза вікном. Очевидна реалізація захоплює на натиснутій крапці, і вона хибна в такий спосіб, що не дає жодної помилки.
Захоплення перенаправляє кожну наступну подію вказівника на елемент, який захопив, разом із pointerup, а браузер потім спрацьовує click на спільному предку натискання й відпускання. Якщо захоплює сама крапка, перетягування, що закінчується на іншій крапці, туди ніколи не доходить; воно повертається туди, де почалося. Тому захоплює поверхня, а де палець насправді відпустив, є окремим питанням, на яке відповідає document.elementFromPoint().
Ця відповідь має три щаблі. Вхідна крапка під курсором. Якщо ні, коробка вузла під курсором, що веде до його першого входу. Якщо ні, магніт: найближчий вхід у межах шістнадцяти екранних пікселів, і саме це робить жест поблажливим, не змушуючи нікого влучати в мішень завширшки десять пікселів.
Дві менші знахідки, які зʼявляються лише під час будування. React чіпляє wheel пасивно на корінь, тож заборона типової дії в його обробнику не робить нічого, і сторінка гортається, поки ви намагаєтеся збільшити; цей слухач має стояти на самому елементі, явно непасивним. А середина кривої Безьє не є центром її обмежувальної коробки: влучити в дріт означає попросити в контуру точку на заданій довжині й провести її через поточну матрицю перетворення.
Клік, потім клік, працює й далі. Це не гірший шлях: це шлях, який працює з клавіатурою, і тому крапки лишилися справжніми кнопками.
Один тригер і чотири речі, що на ньому висять
Граф має рівно один вузол тригера. Нуль є помилкою під час збереження, і два теж. Це звучить як охайність. Насправді воно несуче, і чотири можливості на ньому тримаються.
| Що це дає | Чому потрібен один тригер |
|---|---|
| Досяжність | Кожен виконуваний вузол має бути досяжним з однієї відомої точки початку |
| Виявлення циклів | Обхід у глибину з розмальовуванням потребує кореня; два корені означають два обходи і дві відповіді, які треба зливати |
| Вибір змінних | Які змінні існують, залежить від події, що спрацювала, а другий тригер означав би дві відповіді в одному редакторі |
| Обрізання контексту | Група змінних, якої ця подія дати не може, вилучається: читання її є помилкою, а не порожнім рядком |
Останній рядок окупає себе сам. Подій тригера чотири: сервер надано, перевстановлено, вилучено або запущено вручну. Вилучений сервер не має ідентифікатора операційної системи, тож уся група операційної системи зникає з контексту замість того, щоб стати порожнім обʼєктом. Зі строгими змінними читання дає тоді зрозумілу помилку «невідома змінна» саме тієї миті, коли ви зберігаєте. Спорожніть її натомість, і умова, яка її читає, стає хибною, мовчки, назавжди, на тій гілці, що мала значення.
Саме тому немає й окремого вузла введення. Ручний запуск є подією тригера, а не другим входом; додати його означало б зламати інваріант, на якому тримаються всі чотири можливості, в обмін на вузол, що малюється трохи краще.
Пробний запуск нічого не торкається і все одно проходить
Редактор має кнопку Test run, а граф тепер може містити вузол, який запускає, зупиняє, вимикає, призупиняє або відновлює сервер цього запуску. Ці два факти на одному екрані становлять пастку: хтось натисне пробу, щоб побачити, якою гілкою піде граф, і вимкне клієнтську машину.
Тому пробний запуск ніколи не надсилає команди. Вузол дії повертається негайно, позначений як пропущений, з кодом причини, поданим мовою читача. І головне: він бере гілку ok. Проба, що впала б на гілку помилки, стверджувала б, ніби граф робить те, чого не робить, а це гірше за мовчання. Справжня дія відбувається через введення, яке є живим запуском і просить підтвердження.
Хто діє, коли ніхто не дивиться
Запуск потоку не має глядача. Під час замовлення особа, яка натиснула Замовити, щонайбільше випадково є тією, кому слід приписати дію. Під час вилучення її може вже не бути. Але пошук сервера вимагає дійової особи з профілем прав, і цей профіль має звідкись узятися.
Вибір полягає в тому, що запуск діє як власник облікового запису, з трьох міркувань за вагою. Момент дозволу становить розгортання, а не запуск: збереження потоку вимагає модуля скриптів, привʼязування його до тарифу вимагає прав на тарифи, і це та сама форма, що й запланована задача чи робітник спостереження. Діяти як той, хто натиснув, зробило б граф невідтворюваним: той самий потік запускав би машину для одного покупця і не запускав для іншого через прапорець прав, який до потоку не має жодного стосунку. А справжня межа й далі лишається твердою: пошук отримує ідентифікатор організації запуску і відмовляє кожному рядку поза ним, а цей вузол за побудовою може звертатися лише до сервера цього запуску.
Перш ніж зʼявиться команда, два сита мають сказати так обидва: чи вміє цей постачальник цю команду і чи може цей рід ресурсу. На Proxmox сьогодні жодна з пʼяти дій на контейнері не відхиляється, і саме тому наскрізна проба просить ще й збільшення диска та вимагає, щоб воно було відхилене. Сито, яке ніколи не каже ні, не відрізнити від сита, яке не підключене.
Пʼять хвилин, бо пауза сидить усередині запиту
Вузол затримки дозволяє від пʼяти до трьохсот секунд. Завдання просило шістсот, і шістсот є хибними з вимірної причини: потік, спричинений замовленням, очікується всередині запиту замовлення. Десять хвилин паузи означають десять хвилин, протягом яких платний клієнт дивиться на позначку завантаження.
Тривкого стану очікування немає, і поки його немає, пауза не може пережити запит. Той шов має назву в коді, щоб «чому мій потік не може почекати годину» було питанням зі знаходною відповіддю. Він вимагає черги, здатної зберегти напівзавершений запуск і відновити його пізніше, і лише коли вона зʼявиться, можна буде взагалі говорити про цикли.
Вузол HTTP і чому це не fetch
Вузол HTTP в інструменті автоматизації становить машину для підробки запитів з боку сервера, якщо не бути уважним, а уважність тут виключає стандартний клієнт.
fetch не вміє єдиного, чого ця політика вимагає: зʼєднатися з адресою, яку вибрали ми, поки імʼя вузла лишається в Host і в SNI. Він розвʼязує сам, і саме те друге розвʼязування становить вікно, у якому живе перепривʼязування DNS. Ви просіюєте публічну адресу, а за пів секунди клієнт зʼєднується з локальною петлею, бо час життя запису дорівнював нулю.
Власний клієнт HTTP у Node приймає власне розвʼязування, і наше повертає рівно одну адресу: ту, яку схвалило сито. Host і SNI й далі виводяться з імені вузла, тож TLS перевіряється проти імені. Перевірка сертифіката ввімкнена без вимикача, а змінні середовища проксі не роблять нічого, бо той клієнт їх не читає.
Перенаправлення проходять вручну, щонайбільше три, і кожен стрибок просіюється наново від початку: схема, відомості користувача, порт, DNS, кожна адреса. Відкрите перенаправлення на точку метаданих хмари відхиляється на другому стрибку, навіть якщо перший був поважним публічним вузлом. Дозволено сім назв заголовків, і дві з них можуть нести лише посилання на збережену таємницю, яку розшифровують для запиту, вилучають з усього збереженого і записують лише за назвою.
Що б ми записали
- Захоплювати на оболонці, а не на тому, що ви схопили. Захоплення вказівника перенаправляє все подальше, а симптомом є жест, який мовчки закінчується там, де почався.
- Поблажливий жест потребує ланцюжка відступів, а не більшої мішені. Елемент під курсором, потім його батько, потім поріг відстані.
- Один вхід купує чотири можливості. Досяжність, виявлення циклів, вибір змінних і обрізання контексту потребують усі чотири одного відомого кореня. Полічити, що ви втратите, перш ніж додавати другий.
- Вилучати те, чого не може бути; не спорожнювати. Відсутня група змінних падає голосно під час збереження; порожня падає мовчки під час виконання.
- Кнопка проби, здатна торкнутися продуктиву, не є кнопкою проби. Пропустити і все одно взяти гілку успіху становить чесне поєднання.
- Паузу обмежує той, хто на неї чекає. Якщо викликач становить запит HTTP, пауза міряється секундами.
Для ясності щодо того, чим це є: потоки досяжні з екрана скриптів, а полотно тепер єдиний редактор. Три речі лишилися недоведеними. Очікування справді завершеної команди не вимірювали проти справжнього гіпервізора, перетягування вели змодельованими подіями протоколу браузера, а не рукою, і нічого з цього не вимірювали на продуктивній збірці.
Із платформної роботи Provibr. Чому граф має бути скінченним від початку, описано в потік є графом, і граф має бути скінченним, а те, як зведений екран читає чужі таблиці й ніколи в них не пише, описано в проєктувати, ніколи не заміняти.