Делта потокът ви пропуска късни комити: изградете надежден курсор
Един ред пристигна късно, защото водният знак не беше граница на комит. Проследете отказа, после дефинирайте покритие, граница, задържане и безопасно възобновяване.
Ето отказ, който наблюдавах два пъти, в два различни двигателя. Експортът се изпълнява в 10:00 и записва най-високата марка за последна промяна, която е видял. Една транзакция, започнала в 09:58, е още отворена, прави комит в 10:03 и носи марка за време от началото си. Следващият експорт пита само за редове по-нови от 10:00 и никога не я вижда. Размерът на трансфера беше отличен, а данните — грешни.
Проследете експорта, който загуби ред
Проследете реда на събитията, а не SQL. Приложението записва времето на промяна на реда при началото на транзакцията, не при комита. Експортът чете воден знак по стенния часовник, докато транзакцията е още отворена. После транзакцията прави комит с време на промяна, по-старо от запазения воден знак. Следващата партида на консуматора започва след водния знак, така че редът остава зад курсора и никога повече няма да бъде избран.
Нищо не записва грешка, защото нищо не се провали. Това прави този клас дефекти скъп: делта потокът е бърз, броят на редовете изглежда правдоподобен, а липсващият ред изплува седмици по-късно като заявка за сверка, която никой не може да възпроизведе.
Поредните номера имат същата форма на дефект по друга причина. Монотонният ID ви казва реда, в който са раздадени стойностите, а не реда, в който транзакциите са направили комит. Транзакция, взела нисък номер, може да направи комит след такава с висок номер, затова курсор, основан на реда на раздаване, също може да остави дупка.
Пет обещания, които потокът трябва да даде
Преди да изберете транспорт, поискайте от изходната система договор с пет клаузи. Ако липсва която и да е клауза, имате евристика, а не поток от промени.
- Покритие: вмъквания, обновявания и изтривания, всяко със стабилния ключ, нужен за прилагането му.
- Граница: какво означава стойността на курсора и какво съдържа последователна партида.
- Съгласуваност: как се четат свързани таблици, без да се смесват несъвместими състояния на източника.
- Задържане: колко дълго изключен консуматор може да възобнови работа от запазената си позиция.
- Повторна инициализация: поддържан пълен snapshot за случаите, когато тази позиция вече не е валидна.
Поддържан механизъм може да изпълни и петте. Документацията на SQL Server за проследяване на промени е един конкретен публикуван пример: тя дефинира минимална валидна версия, записва изтриванията наред с промените и описва snapshot транзакция, обхващаща валидация, заснемане на версия и свързаните заявки за промени. Това е пример какво да изисквате от източник, а не синтаксис, който да поставите в друг двигател.
Проектирайте контролната точка като част от конвейера
Запазената позиция на консуматора е истинската машина на състоянията, затова я опишете като такава. Курсорът може да напредва само чрез работния поток, който вече е приложил партидата и е направил комит на целевата транзакция.
cursor = durable_checkpoint() # last fully applied position
batch = extract(cursor, upper_bound) # one consistent batch
apply(batch) # idempotent at the target
commit()
checkpoint(batch.upper_bound) # only now
Ако доставката се провали по средата, сверявайте същата партида, вместо да измисляте нова позиция в източника. Повторното извличане с по-нова горна граница е начинът, по който частично прилагане се превръща в трайна празнина. Огледалното правило важи и от другата страна: идемпотентното прилагане в целта предотвратява дубликати и не прави нищо за промяна, която извличането никога не е видяло.
Изтриванията искат записи, не изводи
Сравняването на редове, които още съществуват, не може да каже на консуматора кои ключове са изчезнали. Източник, който никога не записва изтривания, ви оставя два несъвършени варианта: да пазите история на ключовете достатъчно дълго за diff, или да пускате явна пълна сверка по график. И двете са проектни решения с цена и нито едно не съществува, освен ако някой не го притежава.
Задържането е същото решение. Всеки поддържан поток от промени има прозорец; посочете го в договора и алармирайте, когато контролната точка на консуматор наближи него. Консуматор, който се събуди след двуседмично прекъсване, трябва да прочете ясна грешка "нужна е повторна инициализация", а не тихо да получи частична партида.
После измерете дали делтата си е струвала
Връщането към сравняване на всеки ключ и хеш понякога е правилно и си струва да знаете колко струва. Вие пак прехвърляте информация за всеки ред, хешовете не ви дават съгласуван snapshot между таблици, а схемата с хешове носи своите въпроси за представяне и колизии. По-малко байтове с проверима граница е целта; по-малко байтове, които тихо изпускат комитната промяна, изобщо не са делта.
Колоната за последна промяна е подсказка. Курсорът, който оцелява при късен комит, изтриване и дълго прекъсване, е договор.