Тапсырмаларды жоғалтпай агент checkpoint схемасын миграциялау
Аяқталмаған тапсырмаларды жоғалтпай агент checkpoint схемасын миграциялау: жазба нұсқалары, түрлендіргіштер, аренда, идемпотенттілік және процесс тестілері.

Checkpoint-ті кодтағы өріспен бірге жазасыз өзгерте салуға болатын техникалық қоқыс деп қарауға болмайды. Егер агент әрекеттер, воркерлердің қайта іске қосылуы және релиздер арасында күйін сақтаса, осы жазба оның басталған жұмысты жалғастыратынын немесе қайта орындайтынын анықтайды. Мұндағы миграция қатесі әдемі stack trace емес, қайталанған төлем, екі бірдей хат, жоғалған құжат немесе ешкім аша алмайтын тұрып қалған тапсырма болып көрінеді.
Мен мұндай сценарийді тым көп көрдім: команда state құрылымын өзгертеді, сервисті шығарады, ал бірнеше күннен кейін ретрай ескі код жасаған жазбаны көтереді. Жаңа десериализатор не құлайды, не әдепкі мәнді қояды. Екінші жағдай қауіпті. Агент қате позициямен жұмысын жалғастырып, миграцияның бір жолымен байланыстыру қиын із қалдырады.
Сенімді миграция SQL скриптінен басталмайды. Ол қарапайым фактіні мойындаудан басталады: checkpoint - орындаудың ұзақ сақталатын келісімшарты. Онда нұсқа, түрлендірудің нақты жолы, бәсекелес захват ережелері және бір тапсырма бағдарламаның бірнеше нұсқасынан өтетін тестілер болуы керек.
Checkpoint деректерді емес, жұмысты жалғастыру құқығын сақтайды
Checkpoint агент келесі қадам ретінде қай әрекетті орындауға құқылы екенін қауіпсіз анықтауға мүмкіндік беретін фактілерді ғана қамтуы керек. Бұл процестің бүкіл жедел жадының снимогы да, debug журналы да емес.
Өтінімді өңдейтін агентті елестетейік: ол қосымшаларды алады, мәтінді шығарады, модельді шақырады, сыртқы жүйеде жазба жасайды және хабарлама жібереді. Әр қадамнан кейін күйін сақтайды. Нашар checkpoint тек step: 3 мәнін сақтайды. Жақсы checkpoint өтінім идентификаторын, жасалған сыртқы объектілердің идентификаторларын, кіріс нұсқаларын, әрекет нөмірін, келесі іске қосу уақытын және әлі расталмаған шақыруға арналған идемпотентті кілтті сақтайды.
Айырмашылық апаттан кейін көрінеді. Процесс сыртқы жазба жасалғаннан кейін, бірақ оның идентификаторы сақталғанға дейін құласа, бір қадам нөмірі операцияның орындалғанын түсіндірмейді. Агент дубликат жасауы немесе жұмысты өткізіп жіберуі мүмкін. Деректер жоғалғаннан кейін мұндай белгісіздікті қосымша status өрісімен түзету мүмкін емес.
Ақпараттың төрт түрін бөліңіз:
- келесі қадамға қажет фактілер;
- қайта есептеу мүмкін емес сыртқы әрекеттің нәтижесі;
- идемпотенттілік пен салыстыруға арналған деректер;
- зерттеуге көмектесетін, бірақ агент шешімін өзгертпейтін диагностикалық мәліметтер.
Логтар мен трассировка checkpoint-ті алмастырмайды. Лог толық болмауы, қажет мерзімнен аз сақталуы немесе тапсырма жазбасымен транзакциялық байланысы болмауы мүмкін. Checkpoint кезекке де тең емес: кезек жұмысты орындап көру керегін айтады, ал checkpoint оны нақты қай жерден жалғастыру керегін көрсетеді.
Бұл айырмашылық JSON ішіне бәрін тыққанда жиі жоғалады. Бірнеше релизден кейін мұндай құжат уақытша кэштердің, debug жалаушаларының және бизнес күйінің қоспасына айналады. Оны миграциялау JSON күрделі болғандықтан емес, әр өрістің мағынасын ешкім білмейтіндіктен қиын. Схеманы өзгертпес бұрын кесте жасаңыз: өріс, иесі, ақиқат көзі, қайта іске қосылғаннан кейін қажет пе, оны басқа жерден қалпына келтіруге бола ма. Жауабы жоқ өрістер шешім қабылдауға қатыспауы керек.
Нұсқа әр жазбаның ішінде болуы керек
Схема нұсқасы контейнер нұсқасынан, Git тармағынан немесе жасалған күннен шығарылатын константа емес, checkpoint-тің өз бөлігі болуы керек. Хранилищеде бір уақытта бірнеше релиздің жазбалары дерлік әрдайым болады: тапсырма ретрайды күтуі, лимитпен бұғатталуы, карантинге түсуі немесе қолмен тоқтатылғаннан кейін қалуы мүмкін.
Минималды конверт мынадай:
{
"task_id": "job_01J9R8...",
"schema_version": 2,
"revision": 17,
"status": "running",
"lease_until": "2026-07-23T10:17:00Z",
"state": {
"phase": "classify",
"source_document_id": "doc_481",
"classification_request_id": "req_9aa"
}
}
schema_version тек state пішіні мен семантикасына жауап береді. revision бір жазбаны қатар жаңартуға жауап береді. status тапсырманың өмірлік циклін сипаттайды. Бұл сандарды араластырмаңыз. Команда үш міндеттің бәріне бір version өрісін қолданса, оқылмайтын шарт жазады: «нұсқа 12-ден кіші болса, бұл ескі JSON бе, ескі күй ме, әлде басқа біреудің жаңартуы ма?»
Нөмірлеуді 1-ден бастаңыз. Егер мұндай деректер бұрыннан бар болса, нұсқаның жоқтығын уақытша 0 деп түсіндіруге болады. Бірақ бұл барлық кодқа шашылған state.foo ?? state.bar ?? "" тексерулерінің жиыны емес, бөлек түрлендіргіш болуы керек.
Дерекқор мүмкіндік берсе, конверт метадеректерін бөлек бағандарда сақтаңыз. PostgreSQL-де task_id, status, lease_until, revision және schema_version мәндерін типтелген өрістерге шығарып, пәндік күйді jsonb ішінде қалдыру ыңғайлы. Сонда белсенді тапсырмаларды индекстеуге, ескі схема жазбаларын табуға және JSON-ды қолданбада талдамай-ақ таңдау көлемін шектеуге болады.
PostgreSQL нұсқаулығында стандартты READ COMMITTED режимі әр оператордың басында бекітілген деректерді ғана көрсететіні жазылған. Бір транзакциядағы екі тізбекті сұрау әртүрлі нәтиже көруі мүмкін. Сондықтан «алдымен дайын тапсырманы оқып, кейін оны бөлек бос емес деп белгілеу» схемасы воркерлер арасында жарысқа жол береді.
Түрлендіргіштер тек алға жүруі керек
Checkpoint түрлендіргіші жаңа форматты ескіге айналдыра алуға міндетті емес. Ол қолдау көрсетілетін кез келген ескі жазбаны тапсырманы сәтті жалғастыруға дейін бастапқы деректі өзгертпей, ағымдағы ішкі көрініске детерминистік түрде жеткізуі керек.
1-нұсқада агент бір delivery объектісін сақтады делік:
{
"schema_version": 1,
"state": {
"phase": "send",
"delivery": {
"recipient": "[email protected]",
"body": "Дайын",
"sent": false
}
}
}
2-нұсқада хабарлама жіберу ниеті мен расталған сыртқы нәтижені бөлдіңіз. Бұл орынды өзгеріс: message агенттің не істегісі келетінін сипаттайды, ал provider_message_id провайдердің сұрауды қабылдағанын дәлелдейді. Түрлендіргіш ескі сыртқы нәтиже туралы бастапқы жазбада барынан артық білмейтінін анық көрсетуі керек.
function upgradeToV2(v1: CheckpointV1): CheckpointV2 {
if (v1.state.phase !== "send") {
return {
schema_version: 2,
state: { ...v1.state, delivery_attempt: null }
};
}
return {
schema_version: 2,
state: {
phase: "send",
message: {
recipient: v1.state.delivery.recipient,
body: v1.state.delivery.body
},
provider_message_id: null,
delivery_attempt: v1.state.delivery.sent
? { outcome: "unknown", migrated_from: 1 }
: null
}
};
}
Мұнда жағымсыз бір жайт бар: sent: true мәні провайдерде хабарлама идентификаторы бар дегенді білдірмейді. Егер ескі код жалаушаны желілік шақыруға дейін орнатса немесе шақырудан кейін бөлек растаусыз сақтаса, түрлендіргіш жетіспейтін фактіні адал түрде ойдан шығара алмайды. Ол тапсырманы орындаушы идемпотентті кілт бойынша салыстыратын, сыртқы сервистен сұрайтын немесе оператор талдауына жіберетін күйге ауыстыруы керек.
Командалар дәл осы жерде қауіпті шешім қабылдайды: миграция ескі деректердің мағынасын «түзетуі» керек деп ойлайды. Жоқ. Түрлендіргіш көріністі өзгертеді. Ол орындалу тарихын ойдан шығаруға құқық бермейді.
Түрлендіру тізбегін бөлек модуль ретінде рәсімдеңіз:
type AnyCheckpoint = CheckpointV0 | CheckpointV1 | CheckpointV2 | CheckpointV3;
function normalize(raw: AnyCheckpoint): CheckpointV3 {
let current = raw;
while (current.schema_version < 3) {
switch (current.schema_version) {
case 0: current = upgradeV0toV1(current); break;
case 1: current = upgradeV1toV2(current); break;
case 2: current = upgradeV2toV3(current); break;
default: throw new UnsupportedCheckpointVersion(current);
}
}
if (current.schema_version !== 3) {
throw new UnsupportedCheckpointVersion(current);
}
return validateV3(current);
}
Ондаған өріс комбинациясын танитын бір алып migrateToLatest жазбаңыз. Ол тез арада деректердің екінші, бейресми форматына айналады. V1 -> V2 және V2 -> V3 сияқты шағын ауысуларды тестілеу, жою және журнал бойынша зерттеу оңай.
Данило Сато сипаттаған Parallel Change үлгісі үйлесімсіз өзгерісті кеңейтуге, өтпелі кезеңге және ескі жолды жоюға бөледі. Checkpoint үшін бұл алдымен оқырманды екі форматты түсінетіндей ету, кейін жаңа формат жасай бастау, содан соң ғана ескісін алып тастау дегенді білдіреді.
Ескі форматты оқу мен жаңасын жазу әртүрлі міндеттерді шешеді
Оқу үйлесімділігі басталған тапсырмалардың тағдырын анықтайды. Жазу үйлесімділігі жаңа воркерлердің не жасайтынын анықтайды. Оларды бір-бірінен ажыратпайтын бір өзгеріспен шығаруға болмайды.
1-нұсқадан 2-нұсқаға өтуге арналған жұмыс реті:
- Оқырманға 2-нұсқаны және түрлендіргішті қосыңыз, бірақ 1-нұсқаны жасауды жалғастырыңыз.
- Осы кодты шығарып, оның нақты ескі жазбаларды нормализация қатесінсіз өңдейтініне көз жеткізіңіз.
- Оқуды 1-нұсқада сақтай отырып, жазушыны 2-нұсқаға ауыстырыңыз.
- 1-нұсқадағы барлық белсенді жазбалар аяқталғанша немесе өңделгенше күтіңіз.
- Хранилищені және ескі экспорттарға арналған резервтік жоспарды тексергеннен кейін ғана 1-нұсқаны жасауды және оқуды жойыңыз.
Бірінші кезең rollback қажет болғанда артық емес екені көрінеді. Егер жаңа жазушы 2-нұсқаны жасап қойса, ал алдыңғы релиз оны оқи алмаса, rollback бүкіл тапсырмалар пулын тоқтатуға немесе жолдарды қолмен түзетуге айналады. Кейде бұл рұқсат етіледі, бірақ шешім alert-тен кейін емес, релизге дейін қабылдануы керек.
Кері үйлесімділікті екі жақты жазумен шатастырмаңыз. «Тәуекел болмас үшін екі форматты да жазыңыз» деген кеңес жағдайды жиі нашарлатады. Бір күйдің екі көрінісі ішінара қателер кезінде ажырайды: жаңа код provider_message_id мәнін жаңартты, ал ескі sent өрісі бұрынғы күйінде қалды. Содан кейін ескі воркер ескірген нұсқаны оқып, шақыруды қайталайды.
Қосарлы жазу тек негізгі дерек көзі, салыстыру ережесі және осы шешімнің қысқа өмір сүру кезеңі нақты белгіленсе ғана орынды. Көптеген агенттерде ескіні оқып, жадта нормализациялап, келесі сәтті сақтауда тек ағымдағы схеманы жазған дұрыс. Бұл кестені жаппай қайта жазбай-ақ біртіндеп материализация жасауға мүмкіндік береді.
Тапсырманы захваттау мен күйді сақтау бір протокол болуы керек
Егер екі воркер бір тапсырманы қатар өзінікі деп санай алса, формат миграциясы оны құтқармайды. Аренда немесе блокировка протоколы, сондай-ақ жұмысты жаңа иеден кейін аяқтаған ескі жазушыдан қорғаныс қажет.
PostgreSQL-дегі тапсырмалар кезегі үшін аренда мен оптимистік ревизия жиі жеткілікті. Алдымен воркер жазбаны атомарлы түрде захваттайды, кейін қадамды орындайды, одан соң жаңа күйді revision мен аренда иесі сәйкес болғанда ғана сақтайды.
WITH candidate AS (
SELECT task_id
FROM agent_checkpoints
WHERE status IN ('ready', 'retry')
AND run_after <= now()
AND (lease_until IS NULL OR lease_until < now())
ORDER BY run_after, created_at
FOR UPDATE SKIP LOCKED
LIMIT 1
)
UPDATE agent_checkpoints c
SET lease_owner = $1,
lease_until = now() + interval '60 seconds',
status = 'running',
revision = revision + 1
FROM candidate
WHERE c.task_id = candidate.task_id
RETURNING c.task_id, c.schema_version, c.revision, c.state;
SKIP LOCKED тәуелсіз тапсырмаларды воркерлер арасында бөлуге жарайды, бірақ ол бизнес келісімділігін өздігінен шешпейді. Захваттан кейін қайтарылған revision мәнін сақтаңыз. Сақтау кезінде шартты жаңарту жасаңыз:
UPDATE agent_checkpoints
SET schema_version = $2,
state = $3::jsonb,
status = $4,
run_after = $5,
lease_owner = NULL,
lease_until = NULL,
revision = revision + 1,
updated_at = now()
WHERE task_id = $1
AND revision = $6
AND lease_owner = $7
RETURNING revision;
Егер сұрау ешбір жол қайтармаса, воркер нәтижені сақтауға құқығын жоғалтқан. Ол жаңа ревизиямен жаңартуды екінші рет жасамауы керек. Әйтпесе кешігіп қалған процесс басқа воркер алға жылжытқан checkpoint-ті басып кетеді.
PostgreSQL SERIALIZABLE деңгейіндегі транзакциялар сериализация қатесімен аяқталуы мүмкін екенін және қолданба бүкіл транзакцияны қайталауы тиіс екенін ескертеді. Бұл захват пен жаңарту механизміне қатысты: жазбамен жұмыс істейтін қысқа транзакцияны қайталауға болады, бірақ орындалып қойған сыртқы шақыруды ойланбастан қайталауға болмайды.
Сондықтан екі операцияны бөліңіз. Транзакция қадамды орындау құқығын захваттайды. Сыртқы шақыру идемпотентті кілт қолданады. Екінші транзакция расталған нәтижені бекітеді. Осы аралықта процесс өлуі мүмкін, checkpoint сапасын дәл осы орын анықтайды.
Идемпотенттілікті әдемі JSON-нан қалпына келтіру мүмкін емес
Ең жағымсыз нүкте «сұрауды сыртқа жібердік» пен «жауапты сақтадық» аралығында орналасқан. Агент осы аралықта өлсе, қайта іске қосылғанда тек әрекет жасау ниетін көреді. Сыртқы жүйе сұрауды қабылдауы, қабылдамауы немесе қабылдап, жауап қайтаруға үлгермеуі мүмкін.
Идемпотенттілікті қолдайтын операциялар үшін кілтті шақыруға дейін жасаңыз және провайдерге жүгінбей тұрып оны checkpoint-ке салыңыз:
{
"phase": "create_case",
"request": {
"customer_id": "cust_104",
"summary": "Құжатты тексеру"
},
"idempotency": {
"key": "job_01J9R8:create_case:0",
"attempt": 0
},
"external_case_id": null
}
Қайта іске қосылғаннан кейін агент сұрауды сол кілтпен қайталайды. Сыртқы тарап бұрынғы нәтижені қайтарады немесе қайталауды өңделген ретінде қабылдамайды. API мұндай кілтті қолдамаса, өз журналыңыз бен тұрақты бизнес идентификаторы бойынша салыстыруды пайдаланыңыз. Бірақ оны done: true жалаушасымен алмастырмаңыз.
Әр фазада үш анық күйдің бірі болуы керек: әрекет басталмады, әрекет расталды, нәтиже белгісіз. Қосымша контекстсіз «орындалуда» күйі құлағаннан кейін дерлік пайдасыз. Ол кодтың бір кезде функцияға кіргенін ғана айтады, сыртқы тарапта оқиғаның болғанын емес.
Егер сыртқы операция қайтымсыз әрі идемпотенттілігі жоқ болса, талдауға арналған нақты жол қосыңыз. Мысалы, needs_reconciliation күйіндегі тапсырма шексіз ретрай жасамауы керек. Операторға бастапқы сұрау, әрекет уақыты, корреляция идентификаторы, бар болса жауап және қандай өрістерді есептеу мүмкін емес екені қажет. Бұл агент әрекетті екі рет орындағанын дерек иесіне түсіндіруден арзанырақ.
Тестілер функцияны ғана емес, процесті де өткеруі керек
Түрлендіргіштің unit-тесті қажет, бірақ ол қателердің ең ыңғайлы түрін ғана ұстайды. Нақты ақаулар ескі жазба, жаңа бинарлық файл, аренда, желілік шақыру, процесс тоқтауы және келесі релиз түйіскен жерде пайда болады.
Нақты тарихи checkpoint-терден фикстура жинаңыз. Деректерді жасырмас бұрын олардың пішінін сақтаңыз: жоқ өрістер, бос массивтер, фазалардың ескі атаулары, аяқталмаған әрекеттер, арендасы мерзімі біткен жазбалар. Схема өзгергеннен кейін жасалған қолмен мысал дерлік әрдайым тым ұқыпты болады.
Кемінде мына қасиеттерді тексеріңіз:
- қолдау көрсетілетін кез келген фикстура ағымдағы нұсқаға нормализацияланады;
- нормализация қайталанбалы: екінші іске қосу нәтижені өзгертпейді;
- түрлендіргіш кіріс объектісін өзгертпейді;
- белгісіз нұсқа тапсырманы болжамды түрде тоқтатады;
- ағымдағы схеманың міндетті инварианттары түрлендіруден кейін тексеріледі.
Релиздер тізбегіне процесс тесті қажет. Ол толық production стендін көтермеуі мүмкін, бірақ нақты дерекқорды, checkpoint-ті шынайы сақтауды және бөлек процестерді немесе оқшауланған қолданба даналарын пайдалануы керек. Сценарий мынадай:
1. A релизі 1-нұсқадағы тапсырманы жасап, сыртқы ниеттен кейін checkpoint сақтайды.
2. Тест сыртқы нәтиже бекітілмей тұрып A процесін аяқтайды.
3. B релизі сол жолды захваттап, 1-нұсқаны 2-нұсқаға түрлендіреді және сол idempotency key арқылы шақыруды қайталайды.
4. B релизі расталған нәтижені 2-нұсқада сақтайды.
5. C релизі осы тапсырманы оқып, сыртқы тарапты қайта шақырмай аяқтайды.
Тек schema_version = 3 мәнін тексермеңіз. Сыртқы stub бір логикалық сұрау алғанын, соңғы идентификатор сақталғанын және аяқталған тапсырманы қайта іске қосу ештеңе істемейтінін тексеріңіз. API stub шақыруларды идемпотентті кілт бойынша сақтай алса, мұндай тест дубликаттарды жақсы ұстайды.
Аренданың ескірген иесіне арналған тест қосыңыз. A воркері тапсырманы захваттап, тұрып қалады. Аренда мерзімі бітеді, B воркері тапсырманы аяқтайды. Содан кейін A оянып, ескі checkpoint-ті жазуға тырысады. Шартты UPDATE нөл жол қайтаруы керек, ал A әсерді қайта орындамай, арендадан айырылғанын белгілеуі керек.
Командалар тағы бір тесті өткізіп алады: бірнеше схема арқылы өту. Хранилищеде V1 қалуы мүмкін болса, тек V2 -> V3 жолын тестілемеңіз. Нақты жария normalize жолы арқылы V1 -> V2 -> V3 тізбегін тексеріңіз. Аралық түрлендіргішті осындай тексеріссіз жою ұзақ ретрайлар аса қажет болған сәтте оларды бұзады.
Жаппай миграция тек бөлек операция ретінде жасалады
Барлық checkpoint-ті фондық қайта жазу кейде қажет, мысалы типтелген бағандардың түрін өзгерткенде, жаңа индекс құрғыңыз келгенде немесе сезімтал өрісті алып тастау керек болғанда. Бірақ ол үйлесімді оқуды алмастырмайды.
Миграция джобы миллиондаған жолды жаңартса, ол сол жазбалар үшін воркерлермен бәсекелеседі. Шектеусіз бір үлкен UPDATE іске қоспаңыз. Ол ұзақ транзакция жасап, өзгерістер журналын үлкейтеді, кері қайтаруды қиындатады және тірі тапсырмаларға қажет ресурстарды ұстап қалуы мүмкін.
Жазбаларды пакеттермен өңдеңіз, тек нақты ескі нұсқаны таңдаңыз және орындаушыдағы ревизия бақылауын қолданыңыз. Егер воркер оқу мен миграция арасында checkpoint-ті өзгертсе, фондық процесс жолды өткізіп, оған кейін қайта оралуы керек. Оның мақсаты жарыста жеңу емес, өзекті күйді бұзбау.
Expand, migrate, contract тәсілі мұнда да пайдалы: алдымен код жаңа пішінді түсінеді, кейін фон ескі жолдарды өзгертеді, ескі деректердің жоқтығы расталған соң өтпелі жол жойылады. Evolutionary Database Design материалында бұл өтпелі кезең DDL-дің жанама әсері емес, өзгерістің жеке бөлігі деп аталады. Бұл тәртіп күй JSON-да сақталса да, агент күйіне дұрыс келеді.
Жаппай миграциядан бұрын метрикалар туралы келісіңіз. schema_version бойынша белсенді checkpoint санын, нормализация қателерін, needs_reconciliation тапсырмаларын, ең ескі белсенді жазбаның жасын және шартты сақтаудан бас тарту санын есептеңіз. Онсыз ауысудың аяқталғанын немесе ескі деректерге қарауды жай ғана тоқтатқанымызды білмейсіз.
Белгісіз немесе бүлінген жазбаны тоқтату керек, болжауға болмайды
Белгісіз схемаға ең нашар реакция мынадай: қатені ұстап, бос күй объектісін жасап, агентке басынан бастауға мүмкіндік беру. Бұл тек салдарсыз қайталауға болатыны құжатталған операция үшін жарамды. Көптеген бизнес процестері үшін бұл контекстің жасырын жоғалуы.
Қателерді үш санатқа бөліңіз. Қолдау көрсетілетін ескі нұсқадағы жазба түрлендіргіштен өтеді. Болашақ немесе белгісіз нұсқа карантинге түседі, себебі ағымдағы код оның семантикасын түсінбейді. Бүлінген жазба да карантинге түседі, бірақ себебі бөлек көрсетіледі: JSON жарамсыз, инвариант бұзылған, міндетті идентификатор жоқ немесе құрылым жарияланған нұсқаға сәйкес емес.
Бастапқы payload-ты өзгеріссіз, бас тарту себебін, оқырман кодының нұсқасын және тапсырма идентификаторын сақтаңыз. Проблемалы checkpoint-ті «түзетілген» бос объектімен алмастырмаңыз. Операторға оны резервтік көшірмемен, сыртқы сервис журналымен немесе жаңарақ релиз нәтижесімен салыстыру қажет болуы мүмкін.
Егер күй бинарлық форматта сериализацияланса, тәуекел одан да жоғары. Python құжаттамасы pickle сенімсіз деректер үшін қауіпті екенін және өзгертілуі мүмкін кіріс үшін қолданылмауы керегін ескертеді. Тіпті ішкі хранилищеде де нақты схемасы жоқ бинарлық снимок аудит пен ұзақ мерзімді үйлесімділікті қиындатады.
Орындаушы кодынан тәуелсіз тексеруге болатын формат таңдаңыз. JSON Schema бар JSON, эволюция ережелері ойластырылған Protobuf немесе типтелген бағандар runtime объектісін сериализациялаудан жақсы. Форматтың өзі нашар семантикадан қорғамайды, бірақ агент әрекет жасамай тұрып үйлесімсіздікті көруге мүмкіндік береді.
Ескі түрлендіргішті жою дәлелді қажет етеді
Түрлендіргіштер әзірлеушілерді тітіркендіреді, себебі ауысу коды уақытша көрінеді. Бірақ «уақытша» дегеніміз «екі спринттен кейін жоюға болады» деген сөз емес. Ескі жазбалар жүйеде жасалып немесе өмір сүре алатын уақыттың бәрінде ол қажет.
Алдымен ескі нұсқаны жасауды тоқтатыңыз. Содан кейін осы форматтағы белсенді тапсырмалар, соның ішінде кейінге қалдырылған ретрайлар мен карантин аяқталғанша күтіңіз. Одан соң негізгі хранилищені, қалпына келтіру репликаларын, экспорт кезектерін және қолмен қайта іске қосу құралдарын тексеріңіз. Егер инженер архивтен checkpoint алып, оны жаңа воркермен орындауға тырыса алса, бұл жол да келісімшартқа кіреді.
Репозиторийде қолдау шекарасын бекітіңіз: мысалы, ағымдағы 4-схема 2, 3 және 4-нұсқаларды оқиды, ал 1-нұсқа жазбалары жеке талдау рәсімінен өткеннен кейін қолдау көрсетілмейді. Бұл өрістер айналасындағы шексіз if шарттарынан жақсы. Түрлендіргіштің жойылатын күні болуы керек, бірақ оны күнтізбе бойынша емес, деректердің жоқтығы бақыланғаннан кейін жойыңыз.
Келесі релизге дейін жасайтын ең маңызды өзгеріс қарапайым: әр жаңа жазбаға schema_version қосып, оқырманға белгісіз форматты үнсіз қабылдауға тыйым салыңыз. Бұл бұрынғы қателерді шешпейді, бірақ үйлесімсіздікті әдепкі мәндердің артына жасыру әдетін тоқтатады. Одан кейін түрлендіргіштер тізбегін құрып, аренданы тексеріп, тестіні процестің нақты қайта іске қосылуынан өткізіңіз. Сонда аяқталмаған тапсырма ескі кодтың кездейсоқ қалдығы емес, релиздерден өтетін жұмыс объектісі болады.
Жиі қойылатын сұрақтар
Checkpoint JSON форматында сақталса, схема нұсқасы керек пе?
Агент checkpoint деректерін қайта іске қосулар арасында сақтаса, оны қолданбаның ішкі бөлшегі деп санауға болмайды. Жазба процесс, релиз немесе воркер ауысқаннан кейін де сақталса, оның форматы қалпына келтіру келісімшартына айналады. JSON бір бағанда сақталса да, нұсқа қажет.
Checkpoint нұсқасының орнына агент релизінің нөмірін қолдануға бола ма?
Жоқ. Релиз нөмірі кодты, ал schema_version нақты жазбаның пішінін сипаттайды. Кезең-кезеңімен шығарылған релиз кезінде бір нұсқа бірнеше форматты оқуы мүмкін, сондықтан форматты тек сервис нұсқасымен байланыстыру қауіпті.
Ескі күй форматтарын қанша уақыт қолдау керек?
Ескі жазбаны жасауы мүмкін аяқталмаған процесс бар кезде, оған қоса қолмен қалпына келтіру мен кейінге қалдырылған ретрайларға арналған уақыт қоры сақталғанша, ескі форматтарды оқуға болады. Мұндай жазбалар қалмағаны өлшенетін тексеріс арқылы дәлелденгеннен кейін ғана түрлендіргішті жойыңыз. Жұмыс істейтін оқырманы жоқ архив инцидент кезінде тапсырманы қалпына келтіруге көмектеспейді.
Дерекқор транзакциясы қадамның қайталанып орындалуынан қорғай ма?
Жеке транзакция операцияның бір бөлігін ғана қорғайды. Агент сыртқы API-ді шақырып, checkpoint сақталғанға дейін істен шықса, қайта іске қосу сол сұрауды тағы жіберуі мүмкін. Сыртқы жүйеде идемпотентті кілттер немесе қайталау ережесі анық журнал қажет.
Checkpoint миграциясынан кейін релизді кері қайтаруға бола ма?
Иә, егер ескі код жаңа код өзгерткен жазбаны дұрыс жүктей алса. Бір өрісті бірнеше өріске бөлу немесе мағынасын өзгерту кезінде бұл шарт сирек орындалады. Сондықтан алдымен үйлесімді кеңейту енгізіледі, кейін жазбалар ауыстырылады, содан соң ғана ескі формат жойылады.
Барлық ескі checkpoint-терді бірден қайта жазу керек пе?
Бастапқы жазбаны өзгертпей сақтап, оқыған кезде түрлендіруді жадта орындаған дұрыс. Бұл қайта іске қосуды қауіпсіз етеді және ескі мен жаңа мағынаны салыстыруға мүмкіндік береді. Жаңа форматтың жұмыс істейтіні дәлелденгеннен кейін фондық материализация жасауға болады.
Белгісіз нұсқадағы checkpoint-пен не істеу керек?
Белгісіз нұсқаны бос күй немесе ең жақын белгілі формат деп үнсіз түсіндіруге болмайды. Тапсырманы араласуды қажет ететін күйге қойып, бастапқы payload-ты сақтаңыз және дабыл беріңіз. Болжаммен жалғастыру әдетте бір тапсырманы тоқтатудан да қауіпті.
Агент күйінің миграциясына қандай тестілер керек?
Ең аз жиынға ескі фикстураларды оқу, түрлендіргішті қайта іске қосу, захват пен сақтау арасындағы апаттан кейін қалпына келу және бірнеше релизден өтетін тізбек кіреді. Түрлендіру функциясының unit-тесті қажет, бірақ ол жазбаны захваттау, транзакциялар мен воркерлер қателерін анықтамайды. Тек JSON пішінін емес, соңғы бизнес әсерін тексеріңіз.
Агент checkpoint-те қандай деректерді сақтауы керек?
Иә, егер процесс воркердің қайта іске қосылуынан өтуі керек болса. Жадта ғана сақталатын күйді қауіпсіз түрде басынан бастауға болатын қысқа операцияларға қолдануға болады. Сыртқы шақырулары бар көп қадамды әрекеттер үшін тапсырма идентификаторын, позицияны, қадам нәтижесін және идемпотенттілік деректерін сақтаңыз.
Ескі схема түрлендіргішін қашан жоюға болады?
Бұл тапсырманың ең ұзақ өмір сүру уақытына, ретрай саясатына, кезек кідірістеріне және операторлардың инцидентті қанша уақыт талдай алатынына байланысты. Үйлесімділіктің нақты терезесін белгілеңіз және белсенді жазбалардың жасын өлшеңіз. Бірнеше релиз өткендіктен ғана түрлендіргішті жоймаңыз.