ИТ развој

Debug Your Architecture, Not Just Your Code

Дебагирајте ја вашата архитектура, не само вашиот код

Тврдоглав продукциски баг може да биде подарок: можеби ви кажува дека кодот го прави токму она што архитектурата го направила веројатно.

Искусните инженери учат да разликуваат дефект во функција од дефект во обликот на системот. Првиот прашува: „Која линија е погрешна?“ Вториот прашува: „Зошто ова барање може да стигне до три сервиси, да запише во две бази на податоци и сè уште да нема јасен сопственик на исходот?“ И двете се важни, но само едното спречува истата категорија инциденти да се врати под различен тикет.

Дебагирањето на архитектурата значи следење на однесувањето преку граници: HTTP барања, редици, шеми, контејнери, кешови, конфигурација за распоредување и оперативни претпоставки. Тоа е помалку уредно од поправање услов, но токму таму обично се добиваат или губат доверливоста и одржливоста.

Симптомите често упатуваат подалеку од вистинскиот дефект

Разгледајте PHP крајна точка што повремено истекува додека креира нарачка. Профилирањето може да открие бавно барање кон базата на податоци, па непосредниот одговор е да се додаде индекс. Тоа може да е правилно. Но поширока трага може да покаже дека крајната точка исто така повикува давател на услуги за плаќање, резервира залиха, испраќа е-пошта и синхроно ја ажурира аналитиката пред да врати одговор.

Тогаш бавното барање е придонесувач, но не нужно и архитектонскиот проблем. Крајната точка станала слој за оркестрација на работа со многу различни карактеристики на латентност, неуспех и повторување.

Корисно прво прашање е: што мора да успее пред корисникот да може да добие корисен одговор? Авторизацијата на плаќањето може да е суштинска. Испораката на е-пошта веројатно не е. Аналитиката речиси сигурно не е. Раздвојувањето на овие одговорности го намалува времето на одговор и ги прави неуспесите полесни за објаснување.

Следете го целото барање, не само стек-трагата

Стек-трагата ви кажува каде се појавил исклучокот. Архитектонската трага ви кажува што се случило пред и по него. За API барање што не успева, мапирајте ја патеката од влезната точка до трајната состојба и надворешните несакани ефекти.

  • Кој сервис го прифаќа барањето и го валидира?
  • Кои записи во базата на податоци се авторитативни?
  • Кои надворешни повици се случуваат синхроно?
  • Што автоматски се повторува и од кого?
  • Што се случува ако процесот прекине откако ќе успее еден несакан ефект?
  • Која компонента може безбедно да ја повтори операцијата?

Последното прашање открива честа слабост. Истекувањето на време кај клиентот не значи дека серверот не направил ништо. Ако клиент повтори POST /orders по мрежен неуспех, првото барање можеби ја создало нарачката, но не успеало да го испрати одговорот. Без стратегија за идемпотентност, повторувањето може да создаде втора нарачка.

Идемпотентноста не е само API детаљ. Таа е договор меѓу клиентот, апликацијата, базата на податоци и зависните сервиси. Практичен дизајн чува клуч за идемпотентност доставен од клиентот заедно со резултирачкиот ресурс или одговор. Повторените барања со истиот клуч го враќаат претходниот резултат наместо повторно да ја извршат деловната операција.

Направете ги транзициите на состојби експлицитни

Многу неуспеси во бекендот стануваат појасни кога се претставени како транзиции наместо како булови знаменца. Нарачката не е едноставно „платена“ или „неплатена“. Таа може да биде во чекање, авторизирана, потврдена, откажана или рефундирана. Секоја транзиција треба да има сопственик, дозволени претходници и јасен третман на повторените пораки.

Ова не бара сложена рамка. Бара да се одолее на искушението неповрзани контролери, задачи и администраторски скрипти да ги ажурираат истите полиња со малку различни правила.

Трансакциите штитат помалку отколку што луѓето претпоставуваат

Трансакциите во базата на податоци се суштински, но покриваат само ресурси што учествуваат во таа трансакција. Тие не повлекуваат е-пошта, не поништуваат HTTP повик и не отповикуваат порака што веќе е објавена до брокер.

Кревок шаблон изгледа вака:

$order = $orders->create($payload);
$paymentGateway->charge($order);
$eventBus->publish(new OrderCreated($order));

Ако наплатата успее, а објавувањето не успее, системот има платена нарачка без настан. Повторувањето на целото барање може повторно да му наплати на клиентот. Обвиткувањето на записот во базата на податоци во трансакција не ги решава надворешните несакани ефекти.

Поотпорен пристап е деловната состојба и записот за појдовен настан да се потврдат заедно. Работник подоцна може да го објави тој запис, да ги повтори неуспесите и да го означи како испорачан. Овој шаблон често се нарекува outbox. Неговата вредност не е во името; вредноста е во тоа што трајната состојба и намерата за известување на други системи се запишуваат атомски.

Потрошувачите сепак мора да бидат подготвени за дупликатна испорака. Во дистрибуирани системи, „точно еднаш“ обично е скапо тврдење што крие претпоставки. Дизајнирањето обработувачи да бидат идемпотентни е генерално поискрено и поотпорно.

Границите на базата на податоци се архитектонски граници

Базата на податоци може да биде брза и сепак да е погрешната точка на спрегнатост. Кога повеќе сервиси директно ги ажурираат истите табели, нивните распоредувања стануваат испреплетени. Промената на шемата повеќе не е локална миграција; таа е координирано издание меѓу непознати повикувачи.

Почнете со идентификување кој сервис го поседува секој деловен концепт. Другите сервиси можат да добиваат податоци преку API, настани или наменски модел за читање. Ова може да изгледа побавно отколку да се дозволи директен SQL пристап, но ја прави одговорноста видлива и ги намалува случајните зависности.

Во рамките на PHP апликација, истата идеја важи. Избегнувајте да им дозволувате на контролери, обработувачи на команди, работници во редици и шаблони секој да кодира сопствена логика за барања и деловни правила. Поставете ги границите на трансакциите и доменските одлуки во сервиси чии имиња ја опишуваат операцијата што се извршува, како ConfirmOrder или ReserveInventory.

Docker треба да ја открива извршната околина, не да ја прикрива

Контејнеризацијата ги прави распоредувањата повторливи само кога конфигурацијата е експлицитна. Контејнер што работи локално затоа што тивко зависи од монтиран изворен код, екстензија само за развој или претходно постоечка база на податоци не е пренослив; тој е само удобен.

Чувајте ја конфигурацијата за извршување надвор од сликата каде што е соодветно, валидирајте ги задолжителните поставки при стартување и направете здравствените проверки да ја одразуваат подготвеноста, а не само постоењето на процесот. PHP-FPM процес може да работи додека ингеренциите за базата на податоци се невалидни или не е применета задолжителна миграција.

Исто така, раздвојте ги грижите за градење од грижите за извршување. Развојните алатки, зависностите за тестирање и компајлерите не мора нужно да припаѓаат во продукциска слика. Помалите слики може да го подобрат стартувањето и да ги намалат подвижните делови, но примарната цел е јасност: распоредениот контејнер треба да содржи токму она што ѝ е потребно на апликацијата за да работи.

Работата на перформансите има потреба од системски буџет

Подесувањето на перформансите често е погрешно насочено бидејќи една метрика се третира како цел систем. Побрзо барање не помага многу ако пуловите на конекции се исцрпени, невалидирањето на кешот создава бранови на промашувања или редицата на работници расте побрзо отколку што потрошувачите можат да ја обработат.

Дефинирајте буџети за важната патека: латентност на одговорот, време во базата на податоци, време на надворешни повици, употреба на меморија и доцнење во редицата. Потоа мерете на границата каде што корисниците го чувствуваат резултатот. Ова ги претвора нејасните изјави како „апликацијата е бавна“ во проверливи прашања.

На пример, кеширањето запис за производ може да биде разумно. Кеширањето одлуки за авторизација без јасен модел за невалидирање и истекување може да создаде безбедносен проблем. На секој кеш му е потребен одговор на три прашања: што го прави валиден, кога истекува и што се случува кога е недостапен?

Дизајнирајте за следниот неуспех

Најдоброто архитектонско дебагирање остава зад себе повеќе од закрпен инцидент. Тоа ја подобрува набљудливоста, ја стеснува одговорноста и го прави следниот неуспех помалку двосмислен. Додајте идентификатори за корелација низ дневниците на барања и задачите. Запишувајте значајни промени на состојбата. Изложете ја длабочината на редицата, стапките на грешки и латентноста на зависностите. Напишете прирачници за неуспеси што се предвидливи, но ретки.

Најважно, третирајте ги повторувачките багови како повратни информации за границите. Ако истата грешка бара поправки во неколку сервиси, апстракцијата можеби е на погрешно место. Ако секоја нова функција има потреба од посебен исклучок, работниот тек можеби е недоволно специфициран. Ако распоредувањето бара координирани рачни чекори, системот можеби носи скриена спрегнатост.

Кодот е местото каде што софтверот ги изразува одлуките. Архитектурата е местото каде што тие одлуки се среќаваат со времето, неуспехот, размерот и промената. Дебагирањето на двете е начинот на кој бекендот станува не само функционален, туку и разбирлив под притисок.

Портрет на автор на блогот

Mihajlo

Јас сум Михајло - развивач поттикнат од љубопитност, дисциплина и постојаната желба да создадам нешто значајно. Споделувам увиди, упатства и бесплатни услуги за да им помогнам на другите да ја поедностават својата работа и да растат во постојано развивачкиот свет на софтверот и вештачката интелигенција.