Ваш дельта-потік губить пізні коміти: збудуйте надійний курсор
Один рядок прийшов пізно, бо водяний знак не був межею коміту. Пройдіть збій, потім визначте охоплення, межу, зберігання й безпечне відновлення.
Ось збій, який я бачив двічі, у двох різних рушіях. Експорт запускається о 10:00 і записує найбільшу побачену позначку часу останньої зміни. Одна транзакція, що почалася о 09:58, досі відкрита, фіксується о 10:03 і несе позначку часу з моменту свого початку. Наступний експорт запитує лише рядки, новіші за 10:00, і ніколи її не бачить. Розмір передавання був чудовий, а дані — хибні.
Пройдіть експорт, який втратив рядок
Стежте за порядком подій, а не за SQL. Застосунок записує час зміни рядка на початку транзакції, а не під час коміту. Експортер читає водяний знак за настінним годинником, поки транзакція ще відкрита. Потім транзакція фіксується з часом зміни, старішим за збережений водяний знак. Наступна партія споживача починається після водяного знака, тож рядок залишається за курсором і більше ніколи не буде вибраний.
Ніщо не записує помилку, бо ніщо не зламалося. Саме це робить цей клас збоїв дорогим: дельта-потік швидкий, кількості рядків виглядають правдоподібно, а зниклий рядок спливає тижнями пізніше як заявка на звірку, яку ніхто не може відтворити.
Порядкові номери мають ту саму форму дефекту з іншої причини. Монотонний ID повідомляє порядок виділення значень, а не порядок фіксації транзакцій. Транзакція, що взяла низький номер, може зафіксуватися після тієї, що взяла високий, тому курсор, заснований на порядку виділення, теж може залишити діру.
П'ять обіцянок, які потік мусить дати
Перш ніж обирати транспорт, попросіть у системи-джерела договір із п'яти пунктів. Якщо бракує будь-якого пункту, у вас евристика, а не потік змін.
- Охоплення: вставки, оновлення й видалення, кожне зі стійким ключем, потрібним для застосування.
- Межа: що означає значення курсора і що містить узгоджена партія.
- Узгодженість: як читаються пов'язані таблиці, не змішуючи несумісні стани джерела.
- Зберігання: скільки часу від'єднаний споживач може продовжувати зі своєї збереженої позиції.
- Переініціалізація: підтримуваний повний знімок, коли ця позиція вже недійсна.
Підтримуваний механізм може задовольнити всі п'ять. Документація SQL Server щодо відстеження змін — один конкретний опублікований приклад: вона визначає мінімальну чинну версію, записує видалення поряд зі змінами й описує транзакцію знімка, що охоплює перевірку, захоплення версії та пов'язані запити змін. Це приклад того, що вимагати від джерела, а не синтаксис для вставлення в інший рушій.
Проєктуйте контрольну точку як частину конвеєра
Збережена позиція споживача — це справжня машина станів, тож так її й опишіть. Курсор може просуватися лише через робочий процес, який уже застосував партію й зафіксував цільову транзакцію.
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 або запускати явну повну звірку за розкладом. Обидва — проєктні рішення з ціною, і жодного не існує, поки хтось ним не володіє.
Зберігання — таке саме рішення. Кожен підтримуваний потік змін має вікно; зазначте його в договорі й попереджайте, коли контрольна точка споживача наближається до нього. Споживач, який прокидається після двотижневої перерви, має прочитати чітку помилку «потрібна переініціалізація», а не тихо отримати часткову партію.
Потім виміряйте, чи варта була дельта
Повернення до порівняння кожного ключа й хешу іноді правильне, і варто знати, скільки це коштує. Ви все ще передаєте інформацію про кожен рядок, хеші не дають узгодженого знімка між таблицями, а схема хешів приносить свої питання про подання й колізії. Менше байтів із перевірюваною межею — це мета; менше байтів, які тихо гублять зафіксовану зміну, — зовсім не дельта.
Стовпець останньої зміни — це підказка. Курсор, що переживає пізній коміт, видалення й довгу перерву, — це договір.