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

Блог ·

Зелений, бо не міг туди дістатися

Наш контрактний тест відповідав на найзвичайніший наказ кодом «не підтримується». Кожен тест, що залежав від нього, був зеленим не з тієї причини.

Автор: Provibr

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

До цього тижня та заглушка відповідала на найзвичайніший наказ, який лише буває, вимкни ту машину, кодом COMMAND_STATUS_UNSUPPORTED.

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

Що робить відсутня гілка з тестами навколо неї

У рушія потоків є вузол дії: виконати наказ проти сервера, а тоді піти дротом ok або дротом error залежно від того, як усе минуло. Той вузол був у продукті від раунду, що його запровадив, і мав тести.

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

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

Заглушка мусила тримати стан, а не лише відповідати

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

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

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

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

Дві речі, яких заглушка навмисно не робить

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

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

Другий будильник робить вимірювання випадковим

Друге виправлення того самого раунду стосується вимірювання, а не продукту.

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

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

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

Це є малою кількістю коду заради великої кількості довіри. Припущення, записане в коментарі, є припущенням; те саме припущення, записане як перевірка, що йде першою, є передумовою.

Повторам потрібні стеля і зростання паузи

Поки добрі шляхи нарешті стали досяжними, шляхи невдач теж як слід зміряли.

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

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

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

Дрібниці, які можна знайти лише запуском

Затримка на три секунди не робить недійсним вузол. Вона робить недійсним увесь граф, бо мінімумом є пʼять, і виконавець не повертає геть нічого замість виконання зі зламаним вузлом. Це є правильним і водночас збиває з пантелику першого разу, бо ви очікували помилки про вузол, а дістали мовчання про граф.

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

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

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

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

  1. Тест, що не може дістатися доброго шляху, є зеленим не з тієї причини. Спитайте, що робив би цей тест, якби можливість вилучили.
  2. Заглушка, що відхиляє все, є не заглушкою, а стіною. Кожен тест за нею міряє відмову.
  3. Підробка мусить тримати стан, а не лише відповідати. Якщо платформа вирішує за стовпцем, щось мусить той стовпець рухати.
  4. Не робіть заглушку суворішою за справжню річ. Вигаданий код помилки випробовує контракт, якого ніхто не втілює.
  5. Протягуйте параметр навіть тоді, коли не можете на нього зважити, щоб тест міг довести, що він надійшов.
  6. Два будильники на одній базі даних роблять вимірювання випадковим. Один із них перемагає, і ви не можете сказати який.
  7. Перетворіть передумову з коментаря на перевірку, що йде першою. Тоді вона є фактом, а не сподіванням.
  8. Доведіть сторожу, вимкнувши її. Стеля повторів, якої ніхто не сягав, є твердженням.
  9. Заявлений рядок є невидимим для запиту, що шукає роботу, яка настала. Щось мусить повертати назад застарілі заявки.
  10. Невідвідані вузли належать до журналу як пропущені, інакше ранній вихід не відрізнити від іншого маршруту.

Для ясності про те, чим це є: сім раніше не доведених гілок пройшли наскрізно, кожну зі зустрічним червоним доказом; перевірка злиття пройшла 16 із 16 кроків без жодного рядка невдачі, набір вебу був зеленим на 4583, пакунки, шлюз і інструменти разом на 7287, власний набір агента-заглушки на 481, наскрізний скрипт на 89, гілка будильника на 13, гілка листа про погодження на 22, а цикл зі справжніми діями на 18. Не доведено: нічого не запускали проти справжнього Proxmox, нечитний граф на самому рядку очікування, партія, більша за розмір партії, і справжня доставка пошти через SMTP.

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

Читати далі

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

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