Το delta feed σας χάνει καθυστερημένα commits: φτιάξτε έναν αξιόπιστο δείκτη
Μια γραμμή έφτασε αργά επειδή το watermark δεν ήταν όριο commit. Ακολουθήστε την αποτυχία και ορίστε κάλυψη, όριο, διατήρηση και ασφαλή επανεκκίνηση.
Ιδού μια αποτυχία που την έχω δει δύο φορές, σε δύο διαφορετικές μηχανές. Μια εξαγωγή τρέχει στις 10:00 και καταγράφει τη μεγαλύτερη χρονική σήμανση τελευταίας τροποποίησης που είδε. Μια συναλλαγή που ξεκίνησε στις 09:58 είναι ακόμη ανοιχτή, κάνει commit στις 10:03 και φέρει χρονική σήμανση από τη στιγμή που ξεκίνησε. Η επόμενη εξαγωγή ζητά μόνο γραμμές νεότερες από τις 10:00 και δεν τη βλέπει ποτέ. Το μέγεθος μεταφοράς ήταν εξαιρετικό και τα δεδομένα λάθος.
Ακολουθήστε την εξαγωγή που έχασε μια γραμμή
Παρακολουθήστε τη σειρά των γεγονότων, όχι το SQL. Η εφαρμογή γράφει τον χρόνο τροποποίησης της γραμμής όταν ξεκινά η συναλλαγή, όχι όταν γίνεται commit. Ο εξαγωγέας διαβάζει watermark ρολογιού τοίχου ενώ η συναλλαγή είναι ακόμη ανοιχτή. Έπειτα η συναλλαγή κάνει commit με χρόνο τροποποίησης παλαιότερο από το αποθηκευμένο watermark. Το επόμενο batch του καταναλωτή ξεκινά μετά το watermark, άρα η γραμμή μένει πίσω από τον δείκτη και δεν θα επιλεγεί ποτέ ξανά.
Τίποτα δεν καταγράφει σφάλμα, γιατί τίποτα δεν απέτυχε. Αυτό κάνει αυτή την κατηγορία σφαλμάτων ακριβή: το delta feed είναι γρήγορο, οι αριθμοί γραμμών φαίνονται εύλογοι και η γραμμή που λείπει εμφανίζεται εβδομάδες αργότερα ως δελτίο συμφωνίας που κανείς δεν μπορεί να αναπαράγει.
Οι αύξοντες αριθμοί έχουν την ίδια μορφή ελαττώματος για άλλο λόγο. Ένα μονοτονικό ID σας λέει τη σειρά με την οποία εκχωρήθηκαν τιμές, όχι τη σειρά με την οποία έκαναν commit οι συναλλαγές. Μια συναλλαγή που πήρε χαμηλό αριθμό μπορεί να κάνει commit μετά από μια που πήρε υψηλό, οπότε ένας δείκτης βασισμένος στη σειρά εκχώρησης μπορεί επίσης να αφήσει κενό.
Πέντε υποσχέσεις που πρέπει να δώσει το feed
Πριν επιλέξετε μεταφορά, ζητήστε από το σύστημα προέλευσης ένα συμβόλαιο με πέντε ρήτρες. Αν λείπει οποιαδήποτε ρήτρα, έχετε ευρετική και όχι ροή αλλαγών.
- Κάλυψη: εισαγωγές, ενημερώσεις και διαγραφές, καθεμία με το σταθερό κλειδί που χρειάζεται για την εφαρμογή της.
- Όριο: τι σημαίνει μια τιμή δείκτη και τι περιέχει ένα συνεπές batch.
- Συνέπεια: πώς διαβάζονται συσχετισμένοι πίνακες χωρίς ανάμιξη ασύμβατων καταστάσεων προέλευσης.
- Διατήρηση: για πόσο καιρό ένας αποσυνδεδεμένος καταναλωτής μπορεί να συνεχίσει από την αποθηκευμένη θέση του.
- Επαναρχικοποίηση: ένα υποστηριζόμενο πλήρες snapshot για όταν αυτή η θέση δεν ισχύει πια.
Ένας υποστηριζόμενος μηχανισμός μπορεί να ικανοποιήσει και τις πέντε. Η τεκμηρίωση του SQL Server για την παρακολούθηση αλλαγών είναι ένα συγκεκριμένο δημοσιευμένο παράδειγμα: ορίζει μια ελάχιστη έγκυρη έκδοση, καταγράφει διαγραφές καθώς και αλλαγές και περιγράφει μια συναλλαγή snapshot που καλύπτει επικύρωση, καταγραφή έκδοσης και τα σχετικά ερωτήματα αλλαγών. Είναι παράδειγμα του τι να απαιτείτε από μια πηγή, όχι σύνταξη που κολλάτε σε άλλη μηχανή.
Σχεδιάστε το checkpoint ως μέρος του pipeline
Η αποθηκευμένη θέση του καταναλωτή είναι η πραγματική μηχανή καταστάσεων, γι' αυτό γράψτε την ως τέτοια. Ο δείκτης μπορεί να προχωρήσει μόνο μέσω της ροής εργασίας που έχει ήδη εφαρμόσει το batch και έχει κάνει commit τη συναλλαγή προορισμού.
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
Αν η παράδοση αποτύχει στα μισά, συμφιλιώστε το ίδιο batch αντί να επινοήσετε νέα θέση πηγής. Η επανεξαγωγή με νεότερο άνω όριο είναι ο τρόπος με τον οποίο μια μερική εφαρμογή γίνεται μόνιμο κενό. Ο κανόνας του καθρέφτη ισχύει και από την άλλη πλευρά: η idempotent εφαρμογή στον προορισμό αποτρέπει διπλότυπα και δεν κάνει τίποτα για μια αλλαγή που η εξαγωγή δεν είδε ποτέ.
Οι διαγραφές χρειάζονται εγγραφές, όχι συμπεράσματα
Η σύγκριση γραμμών που υπάρχουν ακόμη δεν μπορεί να πει στον καταναλωτή ποια κλειδιά εξαφανίστηκαν. Μια πηγή που δεν καταγράφει ποτέ διαγραφές σας αφήνει δύο ατελείς επιλογές: να κρατάτε ιστορικό κλειδιών αρκετά καιρό για diff ή να εκτελείτε ρητή πλήρη συμφωνία σε πρόγραμμα. Και οι δύο είναι σχεδιαστικές αποφάσεις με κόστος, και καμία δεν υπάρχει αν δεν την έχει κάποιος.
Η διατήρηση είναι η ίδια απόφαση. Κάθε υποστηριζόμενη ροή αλλαγών έχει παράθυρο· δηλώστε το στο συμβόλαιο και προειδοποιήστε όταν το checkpoint ενός καταναλωτή το πλησιάζει. Ένας καταναλωτής που ξυπνά μετά από διακοπή δύο εβδομάδων πρέπει να διαβάζει καθαρό σφάλμα "απαιτείται επαναρχικοποίηση" αντί να λαμβάνει σιωπηλά μερικό batch.
Έπειτα μετρήστε αν το delta άξιζε τον κόπο
Η επιστροφή στη σύγκριση κάθε κλειδιού και hash είναι μερικές φορές σωστή, και αξίζει να ξέρετε τι κοστίζει. Εξακολουθείτε να μεταφέρετε πληροφορία για κάθε γραμμή, τα hash δεν σας δίνουν συνεπές snapshot μεταξύ πινάκων και ένα σχήμα hash φέρνει τις δικές του ερωτήσεις για αναπαράσταση και συγκρούσεις. Λιγότερα byte με επαληθεύσιμο όριο είναι ο στόχος· λιγότερα byte που ρίχνουν σιωπηλά μια committed αλλαγή δεν είναι καθόλου delta.
Μια στήλη τελευταίας τροποποίησης είναι υπόδειξη. Ένας δείκτης που επιβιώνει από καθυστερημένο commit, διαγραφή και μεγάλη διακοπή είναι συμβόλαιο.