Анаіз шляху неудачы: Next.js, Remix і застарэлыя даны автарызаціі
Аналіз прыкладу, як баг з неактуальнымі правамі вплывае на выбор между Next.js і Remix, а таксама як ацэніць фреймворкі за ўмеласцю працаваць з мутаціямі, трымкайчамі дадзеных і можлівасцю адлагоджэння.
Порэванаўленні фрэймворкаў зазвычай ацэнююць «шчаслівы» сценарый: чысты браузер, запрашаныя даны і сторнка, якая завантажваецца адразу. Системы для працы ў рэальных умовах ацэнююцца іншым спосабам — пасля змян у даных. У гэтым аналізе рассказваецца пра команду, якая стварыла аднаковы экран з автапраўяванням у Next.js і ў архітектуре на кшталт Remix, пазырэла, як Next.js выграў у всіх тестах на прыдатнасць, а потым змяніла свае рашэнне чераз адну анульваную дазволу, якая не хацела з’яўляцца. У канцы вы атрымайце практычны спосаб працэўнае ацэнкі фрэймворкаў на адпаведнасць тым неудачам, якія ваш продукт насправдзе не можа сабе прабачыць.
Ясны пераможца ў плане прыдатнасці
Порэванаўленне прыдатнасці не было блізкім. Версія Next.js адправляла менш JavaScript у браузер, шырэйшаў першы корыстны відворачанне раней, заставляла даскладную обробку даных на серверы і, завдзяк кэшаванню, робіла паўтарную навігацыю моментальной.
Потым адміністратар быў выдален зарады кантэксту кліента. Як база дадзеных, так і летак аудыту парадкавалі гэты выдаленне, а перзагрузка сторанкі паказвала правыя прывілеі. Аднак калі афектаваны корыстнік вернуўся на экран кантэксту за дапамогою навігацыі ў самай прыёмкі, адміністрацыйныя кантролы зноў з’явіліся. Кнапкі, якія малі зникнуць, зноў сталаўся виднымі.
API на бакэндзе адхіліла кожную дзейнасць з прывілеямі, таму ніякія даныя не зменіліся і не адбылося жаднага інцыдэту з безпекай. Саме гэтая пераконтролька на боку сервера ўскладніла неабходнасць завжды практыкування автарызаціі ў API, а не выводжэння яе з таго, што паказвае інтэрфейс. Але такія захаванні не спаслі рашэння па архітектуры.
У версіі з стылю Remix інжынеры за кальканое час зналі ўсі месцы, дзе моглы застацца застарэлыя данні. У версіі Next.js патрэбна была дапомога кальканых адзінакоў, двух супераслагаючыхся следоў і аднаго случайнага прымусовага перзаванчвання, каб выявіць, які слой стварыў адпаведны адпаведзень. Гэтыя дакладныя розследавання зменілі пачатковыя вынікі.
Экран, які быў створаны два разы
Этот сайт не быў простым тэставым прыкладам. Ён служыў многім корыстувачам і даваў адпаведальным за адарожненне можлівасць кераваць людзьмі, іх ролямі, ключамі API, вялічынай праваў на доступ да рэчання і інформацыяй пра тое, што адбывалася на аканті. Чытаць інфармацыю было найпашчэрэдзейшым дзеяннем, але найважлівэйшымі дзеяннямі было зберагчэнне даных: анулювання ролі, змена ключа, выдаленне члена групы, зміна праваў на перагляд рэчанняў.
Тры трэбаванні выклікалі супераслаганне падходаў:
- Шырокі час адразу ж прадстаўлення контэнту, нават для акантаў з дужа дзягучай історыяй актывацыі.
У Next.js большая частка экрана адрасавалася за дапамогою серверных компанентоў, а маленькія кліентскія компаненты викорыстоўваліся толькі для інтэрактыўных элементаў. Даныя аб рахунку, якія викорыстоўваліся калькама занурзеных лейаутоў, праходзілі через кэш, ўтаму іх не трэба было запрашваць паўтарна, а успешныя збіркі спрычынялі паўтарную пераверку.
Версія у стыле Remix была паблікавана на адміністрацыях падключэнняў па маршруту для чытання і дзеянняў для запісу, пры чым стандартныя загалоўкі кэша HTTP былі даданы толькі там, дзе вядома была безпечная трываласць іх выкарыстоўвання. Колі дзеянне завершалася, адміністрацыі падключэнняў для афектаваных маршрутаў запускаліся зноў, следуючы парадку, які быў настолькі просты, што можна было яго накрэсліць на дошцы. (На момент напісання гэтыя модель адміністрацый падключэнняў і дзеянняў продовжаюць выкарыстоўваныя ў режыме фрэймворку React Router, у які быў аднесены Remix; пераканайцеся ў актуальных дасведчэннях па канкретным называнню.)
Обе рэалізацыі былі надзейныя, і жадна з іх не была створаная так, каб праспаць. За ідеальных умоў Next.js явна выграў, што адкрыло першую памылку ў тэсте: рэальны трафік не обмежваецца ідеальнымі умовамі.
Дзе серверскія компаненты ускладнююць запит
Компаненты сервера выпалілі тое, чаму яны обяцвалі. Разыяўленневыя часткі не патрабавалі коду кліента, запошук дадзэнняў выканаваўся безпосередна ў компанентах, якія іх выкарыстоўвалі, медленныя панелі адтвараўліся без блакавання галоўнага экрана, а стабільныя даны абліков утримваліся без павторных запытоў да выхаднага сервера.
Ценай за гэта стала тое, што адна видныя стораніца тепер складаецца з кальколькі незалежных элементаў:
- Работа, абмежаная адним выкананням на сервере.
- Дадзеныя пераўтвараліся за дапамогою кэшавання на серверае.
- Стан маршрута захоўваліся ў браузеры праз усе переходы кліента.
- Кальколькі экзаментаў сервера, кожны з якіх захоўвае свой экземпляр кэшаваных значэнняў.
- Анулюванне кэша выканавалася праз запіс дадзэнняў.
- Будзь-якія проксі або CDN, якія знаходзіліся перед прыемнікам.
Тое, якій слой зберагаў застарэлую значэння, не мела значэння для пользователя, але была важкая для таго, хто працаваў у режыме нарадчыка. Адна з пазначэнняў паказвала, што база дадзеных вяртае апцэнаваную ролю; другая — што інтэрфейс праваў адображае пярэйшую. Ніхто з варыянтаў не быў некоректным: кожны фіксаваў разны запит, який праходзіў розным шляхам у системе.
Локальная розработка ніколі не відтворяла гэту проблему. Одны процес кераваў усім кэшам, інжынеры апцэнавалі данні пасля кожных змян, а тэстовыя акаунты ніколі не нараблялі той самы парадокс, як рэальны кліент, які залишаў дапынг ачытым перад змянай праваў. Адмахнувшыся ад павтарэння роботы, проект таксама пазбавіўся будзь-якага простаг спосабу визначыць, які слой падае данэе значэння.
Точна працэўнасць кожнага слоя кешу Next.js, напрыклад, хтоўкі час, пры якой кліентскі рутер зберагае павярнутыя заздалегідь фрагменты, зменялася между большымі версіямі. Гэта ўжо частка уроку: якщо ваша правільнасць залежыць ад гэтых стандартных налашчэнняў, запісайце гэтыя залежнасці і пераканаўцеся ў ўсіх апдэйтах.
Чаму саме прымусовая перзавантажэнне была справжнім ключам
Команда падтрымкі з’явіла, што прымусовая перзавантажэнне выліковала проблему. Гэта, здаецца, стосуецца кешу браузера, але браузер поясніў толькі частку прычыны. Перзавантажэнне адправляла абоўсцю новы запит да сервера, і гэты запит часта падключаліся да экземпляра без застарэлай копіі. Кліентская навігацыя замест таго перадувала шлях з іншымі данымі та іншай історыяю анулювання. Паколькі старонка выглядала правільна пасля кожнага перзавантажэння, баг продовжваў зьнікаты ўсячас, калі хтось яе пераглядаў.
Сінтэтычныя тэсты знову паказалі той самы слепы пункт. Кожны тэст запускаўся з нявядомай сесіі і паўтараў, што корыстнік з новай роллю бачыць правильную сторанку. Ніхто не пераходзіў з старых прав на новыя ў рамках існуючай сесіі. Команда стежыла за станамі, тады калі баг існаваў между станамі.
Заводзячыся гэтым адкрыццем, відтворэнне бага сталаў надзеяным:
- Ачыніце экран абліквоты, каб яго служыла адна інстанцыя прыемлівача.
- Зменіце роль корыстніка з іншай сесіі.
- Праўерніцеся на экран праз навігацыю кліента, не перзагружаючы.
- Паконтролюйце, чы рэндаруемая версія прав у інтэрфейсе паспаўляе таму, што было зберажана ў службе.
У складовых не было жадных багоў. Данные з кэшу працавалі абсолютна так, як было настроена; прызначаная для іх трымкі трываласць проста не падходзіла для данных автарызаціі. Называнне гэтага «багам кэшу з застарелымі даннымі» указвае на памятку з кодам. Рэальныя проблемы выніклі чераз неназваную архітектурную гаранцыю: за які час павінен зникнуць анульованы права з усіх дадзеных, якія можа бачыць корыстнік?
Першая парадка практычна перанесла проблему на базу дадзеных
Найшырэйшы спосаб зменшэння наследкав проблемы — гэта выключэнне кэшування ў всім разделе аблікавання. Застарелыя права зниклі, а навантажэння на базу дадзеных адразу ж вырасла.
Вярсткі та панелі з іерархічным падзелам запрашвалі перакрываючыяся даны аблікавання. Кэшування робіла гэтыя дубліруючыяся запиты непазначальнымі; ўсуненне яго ператворыла інцыдент з правамі на аднойчыны працяглы час чакання падключэнняў да PostgreSQL, што ставіла пад апасценне іншыя сторанцы аблікавання.
У дыялогах працэйкладаў фреймворкаў часта прыходзіцься паскакваць самэ гэты момент. Кэш можа ствараць рызык для правільнасці, а ў той жа час служыць для падтрымкі навантажэння. Яго выключэнне вяртае свежасць дадзеных, адмахнуўшыся ад прыпуску, які падтрымваў ў рошчыні базу дадзеных.
Вяснаванне відбывалася па меньшых, болей безпечных кроках:
- Даныя прывілеў былі анульваны па кожным абліковачу і па кожнай версіі прывілея, а не глобальна.
- Чытанні з правамі автарызацыі сталі спецыфічныя для кожнага запиту, таму яны ніколі не перазбігаліся з часам жыцця адзінаго запиту.
- Дублікаты чытанняў былі адмахнутыя перад тым, як расширваць маршруты без кэшу.
- Малая частка трафіку спачатку праходзіла через новую схему работы, пакуль ставілася задача стежыць за часам чакання.
- Кожны адпаведзень пачаў несці маленькі маркер, який пазначаў, як ён быў створаны, калькі экземпляр яго выдаў, версію дадзеных і чы ранее адпаведзеў кэш.
Этот маркер зміниў падходы да пазнейшых даследжэнняў. Такая падтрымка дазволяла выявіць, звядзе каналася адпаведнае адказа, не афішуячы самых дадзэў прывілеяў, а інжынерам больш не патрабавалася здогадвацца пра шлях выканання, каб з’ясавіць, чый раз падтрымкі дапамагае. Цэе ўсьмохнутая дапамога для будзь-якай системы з калькуляцыйными шарамі.
Чаму саме медленнейшая архітектура павялілася для гэтага продукту
Формат стварэння на кшталт Remix не быў захіщаны ад распрастраненага стану. Калькуляцыі існуюць у браузерах і CDN-сервісах, базы дадзэння запускаюць реплікі, і кожны серйозны прыклад практычна сталкаецца з застарэлымі дадзеннямі, частымі абераннямі і неабходнасцю павторных спроб.
Йго можлівасці былы болей скромныя. Комплекс з элементаў загрузкі, выкарыстоўвання і павтапераканання даў командзе кампактную ментальную модель таго, што выходзіць пасля зберагання дадзеных. Калі якая-небудзь дзеянне зменяла ролю, інжынеры маглі назваць шляхі, дадзеныя якіх трэбавалася апштучыць, і зналі, дзе яго паверыць. Межы запитоў супадалі з тыми, якія вучоркалі аператары. Програма загружала частку дадзеных два разы і патрабавалася дапаможны код браузера для некалькіх віджэтаў, але яе нештатная праця была лёгка аднаведзена.
Таму гэта падходзіла толькі для данага конкретнага продукту, што не робіць яго універсальным. На сайтах, дзе прыважлевае кантэнт, даныя якога рэдкая зміняюцца, і дзе важліва праця браузера, архітектура Next.js застаецца кращым выборам; ўсёлякія яе можлівасці для адрасавання та кэшавання можу запрацаваць і даць значна лепшы досвід. Для інструмента керування, які выкарыстоўвае аутентыкацыю, недастатак прав, які застаецца видным, коштуе больш, чым фактычная разліка ў працэздатнасці. Важнейшымі былі можлівасць відстэйкавання змян і можлівасць прыглядзецца за свежасцю даных.
Бенчмарк не быў некорактны. Ён проста адпаведзеў на непূরны вопыт.
Паўторная ацэнка з узятым у вялікую прычыну невыкарыстоўванням
Другая ацэнка не пачыналася з чыстага браузера і готавых даных. Яна пачалася з ситуацый, якія першы бенчмарк выключыў:
- Змяніцца права доступу, калі экран застаецца ачытым. Чы розмішаныя способы адчынення стороніцы паказваюць змяну за падзеўны час, і чы інжынер можа бачыць, якая версія знаходзіцца на экране?
- Экземпляр прыемлівання зникае ў процесе роботы. Чы корыстнік можа безпечна спробаваць зноў, і чы частковая робота залеяе кліента панэйроўваючым станам?
- Функція кэшавання выключаецца пад час максимальнага наплоўу. Чы база дадзеных можа вытрымаць найбольш прыемныя тварожнія рашэння?
- Две версіі прыемлівання обслужваюць трафік пад час развертання. Чы обе можаць адначасова распрацоўваць даныя з кэшу і адпаведзі на мутацыі?
- Служба падтрымкі воспроістаўляе адзін патракт кліента. Чы яны можаць з’ясаваць, як была атрыбутавана стороніца, калькі кэш і экземпляр адпавялі, і якая версія дадзеных была выканана, усё гэта без дыбагавання жывых сервероў?
Next.js заставаўся быстрейшым на калькі важлівых траектарыяў. Модэль у стыле Remix была простейшаяя для розуміння камандай, калі гэтыя траектарыі переставалі працаваць нормальна. У тым моманті каманда парабягала системы, а не скріншоты.
Рашэнне на адзін крок: што можа пайсці не так з сторанкай
Скарочэнне выбору да „Next.js ўсё ж быстрейшы, а Remix простейшая“ не вытрымае кантакту з рэальным сервісам. Перш чым мераваць выконанасць, адпавяжыце на гэтыя запитанні:
- Какія даны продукт можа супляваць застарэлымі, і на які час?
- Чы рутацыі вплываюць на грошы, правыя, запасы, чы толькі на візуальную прастоту?
- Сколькі меж кэшавання і адрасавання можа выявіць каманда на аднай толькі аналізе звярнення кліента?
- Чы база дадзенаў будзе працаваць, якщо кэш будзе адключаны?
- Як парадульна працуюць две версіі, размешчаныя адночасна?
Потым мераваць „шчаслівы“ траектарый.
Команда дасла рашынку, які складаўся з двух протыярысных падзеяў. Next.js застаўся на тых сторунках, дзе Server Components скасавалі рэальную работу на боку кліента, а кожны фрагмент дадзейнасці меў чыста вялікануты, безпечны тэрмін існавання. Дадзейнасць прав наўласнення, якая часта мутавалася, намерна заставалася простай: ёй быў заданы тэрмін дзеяння на рэгламентны час запыту, і яна ніколі не кэшавалася. А для экрана аперацый, дзе правильнае выкананне змян мела большое значэнне, чым швайна статычная даставка, былі выбраны падходы Remix. Якщо вы застаецеся з Next.js для такіх экранаў, метод неяксальнасці на аднойчынных тэгах, прыказаны ў інструкцыі пра use cache directive і павтарную пераканранасць на аднойчынных тэгах, дае вам больш точны кантроль, а пераканранасці прав наўласнення ў кожным серверскім аперэцыі застаюця апошнім захопам, незалежна ад таго, што паказвае інтэрфейс.
Галоўныя выводы
- Бенчмарк, які запускаецца з чыстай сесіі, не можа адказваць на багі, які існуюць межы разнымі станамі; тэставайце пераходы межы станамі внутрь сесій з довгым трыманням.
- Спрэчвайце даныя автарызаціі як околічную клас: яны должны быць прыязначаныя да конкрэтнага запиту, мець версію і явна анулювацыя.
- Кэш можа несці рэальную навантажэння; планавайце, як яго адчыніць, пры тым, калі інцыдэнт не заставіць вас гэта зрабіць.
- Зробіце вочымаемым выхадны источнік кожнага адказу за дапамою лёгкага маркера, який паказвае режым адрасавання, экземпляр, версію і статус кэша.
- Выберыце фреймворк, чыяі способы абярання які ваша команда можа быстра поясніць. Адной з вароў можа быць больш інструментаў для прыгону; іншая можа спрыяць лёгкам діагнастыкаванню некоректных старонак. Для дзеяных продуктоваў другая характерыстыка ёсць справжнім трэбаваннем.