Κάπου σε κάθε συμβόλαιο ενοποίησης υπάρχει μια πρόταση όπως «κάθε παραγγελία θα μεταδίδεται ακριβώς μία φορά». Δεν έχει σταθεί αληθινή ούτε μία φορά. Το δίκτυο διασπάστηκε στη μέση ενός αιτήματος και ο αποστολέας ξαναπροσπάθησε· η ουρά παρέδωσε ξανά το μήνυμα μετά από κατάρρευση ενός καταναλωτή· ένας χειριστής αποθήκης πάτησε το κουμπί εξαγωγής δύο φορές. Τα διπλότυπα δεν είναι μια κατάσταση αποτυχίας - είναι καιρικές συνθήκες.
Κλειδιά ιδεμποτεντίας (idempotency), επιλεγμένα προσεκτικά
Η λύση είναι παλιά και άχαρη: κάθε μήνυμα φέρει ένα κλειδί, και η επεξεργασία δεν κάνει τίποτα (no-op) όταν το κλειδί έχει ήδη εμφανιστεί. Οι ενδιαφέρουσες αποφάσεις βρίσκονται όλες στην επιλογή του κλειδιού.
- Τα φυσικά κλειδιά (natural keys) υπερτερούν των παραγόμενων. Ένας συγχρονισμός παραγγελιών με κλειδί το
order_id + statusεπιβιώνει όταν ο αποστολέας αναδημιουργεί το outbox του· ένα τυχαίο UUID που δημιουργείται τη στιγμή της αποστολής δεν επιβιώνει. - Ορίστε το εύρος του κλειδιού στο αποτέλεσμα, όχι στο μήνυμα. Αν ένα μήνυμα δημιουργεί ένα τιμολόγιο και δεσμεύει απόθεμα, αυτά είναι δύο αποτελέσματα με δύο κλειδιά - διαφορετικά μια μερική αποτυχία σας αφήνει χωρίς τη δυνατότητα να ξαναπροσπαθήσετε με ασφάλεια για κανένα από τα δύο.
- Λήξτε τα κλειδιά σκόπιμα. Ένα αποθετήριο αποεπανάληψης (dedup store) που μεγαλώνει για πάντα είναι ωρολογιακή βόμβα· ένα που λήγει πολύ νωρίς επανεισάγει διπλότυπα. Εξ ορισμού χρησιμοποιούμε 3× τον μέγιστο ορίζοντα επανάληψης του αποστολέα.
Η δοκιμή που έχει σημασία
Κάθε ενοποίηση που παραδίδουμε περνά αυτό που εσωτερικά ονομάζουμε δοκιμή double-tap: επανάληψη ολόκληρου του χθεσινού log μηνυμάτων πάνω στο σημερινό σύστημα, δύο φορές, και σύγκριση της προκύπτουσας κατάστασης. Αν κάτι άλλαξε στο δεύτερο πέρασμα, το pipeline δεν είναι έτοιμο.
Είναι μια αδυσώπητη δοκιμή και αποτυγχάνει συνεχώς κατά την ανάπτυξη - σε timestamps, σε πεδία ελέγχου «created by», σε μετρητές ακολουθίας. Κάθε αποτυχία είναι ένα σημείο όπου μια επανάληψη παράδοσης στις 3 τα ξημερώματα θα είχε αθόρυβα καταστρέψει δεδομένα. Είναι πολύ φθηνότερο να εντοπίσετε το πρόβλημα στη φάση της ανάπτυξης παρά στην παραγωγή.