Σενάρια αστοχίας
Αυτές είναι οι καταστάσεις που αντιμετωπίζει ένας scheduler σε ένα πραγματικό cluster. Κάθε περίπτωση παρακάτω δείχνει τι συμβαίνει με ένα lease backend, καθώς και τι συμβαίνει με ένα claim-only backend για σύγκριση.
Δεν αποτελούν νοητικά πειράματα. Το repository περιλαμβάνει ένα test για καθεμία, το οποίο εκτελείται με πραγματικές διεργασίες που τερματίζονται βίαια (kill -9), παγώνουν (SIGSTOP), σταματούν ομαλά (SIGTERM) και επανεκκινούνται, σε Redis, PostgreSQL και SQLite. Μπορείτε να τα παρακολουθήσετε συγκριτικά με:
pnpm scenarios| Τι συμβαίνει | Backend μόνο με διεκδίκηση (Claim-only) | Backend με μίσθωση (Lease) |
|---|---|---|
| Μια εργασία διαρκεί περισσότερο από το μεσοδιάστημά της | Πολλά αντίγραφα εκτελούνται ταυτόχρονα | Ένα τη φορά· οι ενδιάμεσες εκτελέσεις παραλείπονται |
| Ο runner τερματίζεται βίαια κατά την εκτέλεση | Η εκτέλεση χάνεται· το ιστορικό δείχνει running για πάντα |
Ένα backup την ολοκληρώνει ως προσπάθεια 2 |
| Ο runner παγώνει για διάστημα μεγαλύτερο από τη μίσθωσή του | Χωρίς backup· η εκτέλεση αναμένει | Ένα backup αναλαμβάνει· το παγωμένο instance ειδοποιείται όταν ξυπνήσει |
| Άλλο instance επανεκκινείται κατά την εκτέλεση | Κανένα πρόβλημα: εκτελείται μία φορά | Κανένα πρόβλημα: εκτελείται μία φορά, και το επανεκκινημένο instance γίνεται backup |
| Ο runner σταματά ομαλά | Η εκτέλεση χάνεται· το ιστορικό δείχνει interrupted |
Παραδίδεται αμέσως· ένα backup την ολοκληρώνει |
| Όλα τα instances επανεκκινούνται κατά την εκτέλεση | Η εκτέλεση χάνεται | Ένα instance την επανεκτελεί μετά την επανεκκίνηση |
| Όλα τα instances είναι εκτός λειτουργίας όταν αναμένεται εκτέλεση | Η εκτέλεση δεν πραγματοποιείται ποτέ | Ανακτάται (catch-up) κατά την εκκίνηση, αν αναμενόταν πρόσφατα |
Τα παραδείγματα χρησιμοποιούν τρία instances, τα A, B και C, με leaseMs 30 s και heartbeatMs 5 s.
1. Μια εργασία διαρκεί περισσότερο από το μεσοδιάστημά της
Ενότητα με τίτλο «1. Μια εργασία διαρκεί περισσότερο από το μεσοδιάστημά της»Μια εργασία που διαρκεί 7 δευτερόλεπτα, προγραμματισμένη κάθε 3 δευτερόλεπτα.
sequenceDiagram participant A as Instance A participant B as Instance B participant S as Store A->>S: claim εκτέλεσης :00 S-->>A: won Note over A: εκτελείται για 7 s B->>S: claim εκτέλεσης :03 S-->>B: overlap: η εκτέλεση :00 έχει ενεργή μίσθωση Note over B: skip (overlap) A->>S: claim εκτέλεσης :06 S-->>A: overlap Note over A: skip (overlap) Note over A: η εκτέλεση :00 ολοκληρώνεται στο :07 B->>S: claim εκτέλεσης :09 S-->>B: won
Lease backend: μία εκτέλεση τη φορά. Οι εκτελέσεις στο :03 και :06 παραλείπονται, δεν μπαίνουν σε ουρά.
Claim-only backend: κάθε slot διεκδικείται ανεξάρτητα, επομένως οι εκτελέσεις :00, :03 και :06 βρίσκονται όλες σε εξέλιξη στο :06. Τρία αντίγραφα ταυτόχρονα.
Ορίστε overlap: 'allow' αν επιθυμείτε αυτή τη συμπεριφορά σε ένα lease backend.
2. Ο runner τερματίζεται βίαια κατά την εκτέλεση
Ενότητα με τίτλο «2. Ο runner τερματίζεται βίαια κατά την εκτέλεση»Η διεργασία που εκτελεί την εργασία τερματίζεται απροειδοποίητα: kill -9, out-of-memory kill, διακοπή τροφοδοσίας του host.
sequenceDiagram participant A as Instance A participant B as Instance B participant S as Store A->>S: claim: won (προσπάθεια 1) B->>S: claim: running Note over B: γίνεται backup A->>S: heartbeat: η μίσθωση λήγει σε 30 s Note over A: τερματίστηκε. Όχι άλλα heartbeats B->>S: check: running (μίσθωση ενεργή) B->>S: check: running (μίσθωση ενεργή) Note over S: 30 s μετά το τελευταίο heartbeat,<br/>η μίσθωση λήγει B->>S: check S-->>B: won (προσπάθεια 2) Note over B: εκτελεί την εργασία από την αρχή
Lease backend: ένα backup αναλαμβάνει περίπου μία μίσθωση μετά την κατάρρευση, το πολύ leaseMs + 2 × heartbeatMs. Το ιστορικό εμφανίζει την προσπάθεια 1 ως interrupted και την προσπάθεια 2 ως success.
Claim-only backend: κανείς δεν γνωρίζει ότι ο runner πέθανε. Η εκτέλεση δεν ολοκληρώνεται ποτέ και η εγγραφή ιστορικού της παραμένει running.
Το backup ξεκινά την εργασία από την αρχή. Το anarchon δεν γνωρίζει πόσο είχε προχωρήσει η πρώτη προσπάθεια. Δείτε Χρονοβόρες εργασίες για διαχείριση checkpoints.
3. Ο runner παγώνει
Ενότητα με τίτλο «3. Ο runner παγώνει»Η διεργασία δεν έχει πεθάνει. Σταματά να σημειώνει πρόοδο για διάστημα μεγαλύτερο από τη μίσθωσή της: μια πολύ μεγάλη παύση garbage collection, μια εικονική μηχανή που τέθηκε σε παύση ή υποφέρει από έλλειψη πόρων, ένα laptop που μπαίνει σε αναστολή λειτουργίας (sleep).
sequenceDiagram participant A as Instance A participant B as Instance B participant S as Store A->>S: claim: won (προσπάθεια 1, token a1) Note over A: παγωμένο Note over S: η μίσθωση λήγει B->>S: takeover claim S-->>B: won (προσπάθεια 2, token b7) Note over B: εκτελεί την εργασία Note over A: ξυπνά και συνεχίζει τον handler του A->>S: heartbeat (token a1) S-->>A: απορρίφθηκε: η εκτέλεση έχει πλέον token b7 Note over A: ακύρωση ctx.signal, συμβάν 'lost' Note over B: ολοκληρώνεται: success
Lease backend: ένα backup αναλαμβάνει την εκτέλεση. Όταν το παγωμένο instance ξυπνήσει, το επόμενο heartbeat του απορρίπτεται, το ctx.signal του ακυρώνεται και εκπέμπει lost.
Claim-only backend: κανένα backup. Η εκτέλεση απλώς συνεχίζεται όταν το instance ξυπνήσει.
4. Άλλο instance επανεκκινείται κατά την εκτέλεση
Ενότητα με τίτλο «4. Άλλο instance επανεκκινείται κατά την εκτέλεση»Ένα instance που δεν εκτελεί την εργασία τερματίζεται και ξεκινά ξανά ενώ η εργασία εκτελείται αλλού.
sequenceDiagram participant A as Instance A (runner) participant B as Instance B participant S as Store A->>S: claim: won Note over B: τερματίστηκε και επανεκκινήθηκε B->>S: start(): ημιτελείς εκτελέσεις; S-->>B: μία, στο A, μίσθωση ενεργή Note over B: την παρακολουθεί ως backup B->>S: catch-up claim για το ίδιο slot S-->>B: running A->>S: finish: success
Και τα δύο είδη backend: η εκτέλεση πραγματοποιείται μία φορά. Κατά την εκκίνηση, το επανεκκινημένο instance ζητά το πιο πρόσφατο slot, το βρίσκει δεσμευμένο, και δεν το εκτελεί. Σε ένα lease backend γίνεται επίσης ξανά backup.
5. Ο runner σταματά ομαλά
Ενότητα με τίτλο «5. Ο runner σταματά ομαλά»Ένα rolling deploy στέλνει SIGTERM στο instance που εκτελεί την εργασία, και ο κώδικας τερματισμού σας καλεί scheduler.stop().
sequenceDiagram participant A as Instance A participant B as Instance B participant S as Store A->>S: claim: won (προσπάθεια 1) B->>S: claim: running Note over A: SIGTERM, έπειτα stop() Note over A: ακύρωση ctx.signal, ο handler πετάει εξαίρεση A->>S: release: η μίσθωση λήγει τώρα Note over A: συμβάν 'interrupted', handedOver: true B->>S: check (εντός 2 heartbeats) S-->>B: won (προσπάθεια 2) Note over B: εκτελεί την εργασία
Lease backend: η εκτέλεση παραδίδεται χωρίς αναμονή της μίσθωσης. Ένα backup ξεκινά την προσπάθεια 2 εντός δύο heartbeats. Αυτό δεν προσμετράται στο maxAttempts.
Claim-only backend: η εκτέλεση καταγράφεται ως interrupted και κανείς δεν τη συνεχίζει.
Η παράδοση (hand-over) απαιτεί από τον handler να πετάξει εξαίρεση (throw) όταν ακυρωθεί το signal του. Ένας handler που αγνοεί το signal συνεχίζει να εκτελείται μέχρι το stop timeout· στη συνέχεια το instance σταματά την ανανέωση και ένα backup αναλαμβάνει αφού λήξει η μίσθωση. Δείτε Κύκλος ζωής.
6. Όλα τα instances επανεκκινούνται κατά την εκτέλεση
Ενότητα με τίτλο «6. Όλα τα instances επανεκκινούνται κατά την εκτέλεση»Όλα τα instances τερματίζονται ταυτόχρονα ενώ μια εργασία εκτελείται, και στη συνέχεια ξεκινούν ξανά. Κανείς δεν απομένει για να λειτουργήσει ως backup.
sequenceDiagram participant A as Instance A participant B as Instance B participant S as Store A->>S: claim: won (προσπάθεια 1) Note over A,B: όλα τα instances τερματίζονται Note over S: η εκτέλεση παραμένει 'running'.<br/>Η μίσθωσή της λήγει Note over A,B: τα instances ξεκινούν ξανά A->>S: start(): ημιτελείς εκτελέσεις; S-->>A: μία, η μίσθωση έληξε, ακόμα η πιο πρόσφατη εκτέλεση A->>S: takeover claim S-->>A: won (προσπάθεια 2) B->>S: start(): ημιτελείς εκτελέσεις; S-->>B: μία, στο A, μίσθωση ενεργή Note over B: γίνεται backup
Lease backend: η κατάσταση βρίσκεται στο store, όχι στα instances. Όποιο ξεκινήσει πρώτο βρίσκει την ημιτελή εκτέλεση και την αναλαμβάνει, υπό την προϋπόθεση ότι δεν αναμένεται νεότερο slot.
Αν τα instances επανέλθουν πριν λήξει η παλιά μίσθωση, τη βλέπουν ως ενεργή, την παρακολουθούν και αναλαμβάνουν όταν λήξει.
Claim-only backend: η εκτέλεση χάνεται.
7. Όλα τα instances είναι εκτός λειτουργίας όταν αναμένεται εκτέλεση
Ενότητα με τίτλο «7. Όλα τα instances είναι εκτός λειτουργίας όταν αναμένεται εκτέλεση»Ο χρόνος μιας εκτέλεσης παρέρχεται ενώ κανένα instance δεν εκτελείται: μια πλήρης διακοπή (outage), ένα deploy που σταματά τα πάντα πρώτα.
sequenceDiagram participant A as Instance A participant B as Instance B participant S as Store Note over A,B: όλα εκτός λειτουργίας Note over S: 06:00: η εκτέλεση αναμένεται. Κανείς δεν τη διεκδικεί Note over A,B: 06:02: τα instances ξεκινούν A->>S: claim εκτέλεσης 06:00 (catch-up) S-->>A: won Note over A: την εκτελεί, ctx.reason = 'catch-up' B->>S: claim εκτέλεσης 06:00 (catch-up) S-->>B: running
Lease backend: κατά την εκκίνηση, ένα instance εκτελεί το πιο πρόσφατο slot αν κανείς δεν το διεκδίκησε και αναμενόταν πριν από λιγότερο από catchUpMs (προεπιλογή 5 λεπτά). Μόνο το πιο πρόσφατο: μία ώρα downtime ανακτά μία εκτέλεση, όχι εξήντα.
Claim-only backend: η εκτέλεση δεν πραγματοποιείται ποτέ.
Τι παραμένει δική σας ευθύνη
Ενότητα με τίτλο «Τι παραμένει δική σας ευθύνη»Το anarchon διασφαλίζει ότι μια εκτέλεση θα ξεκινήσει ξανά όταν ο runner της έχει χαθεί. Δύο πράγματα παραμένουν ευθύνη του handler:
- Η διπλή εκτέλεση να είναι ακίνδυνη. Οι προσπάθειες 1 και 2 μπορεί να έχουν πραγματοποιήσει και οι δύο μέρος της εργασίας.
- Η διακοπή όταν ζητηθεί. Ένας handler που αγνοεί το
ctx.signalμετατρέπει μια καθαρή παράδοση (hand-over) σε αναμονή για τη λήξη της μίσθωσης.
Ο οδηγός Συγγραφή handlers καλύπτει και τα δύο.
Στη συνέχεια
Ενότητα με τίτλο «Στη συνέχεια»- Μισθώσεις (Leases): οι κανόνες πίσω από αυτά τα σενάρια
- Χρονισμός και ρολόγια