Cancellation — DedSec Project
Ακύρωση εργασίας — αίτημα διακοπής, όχι αναίρεση πράξεων Ακριβής ορισμός Η ακύρωση ζητά από μία συνεργατική εργασία να σταματήσει σε κατάλληλο σημείο αναμονής. Δεν αναστρέφει αυτόματα μία…
Ακύρωση εργασίας — αίτημα διακοπής, όχι αναίρεση πράξεων
Ακριβής ορισμός
Η ακύρωση ζητά από μία συνεργατική εργασία να σταματήσει σε κατάλληλο σημείο αναμονής. Δεν αναστρέφει αυτόματα μία συναλλαγή βάσης, ένα αίτημα HTTP που ήδη παραλήφθηκε ή αλλαγή αρχείου που ολοκληρώθηκε. Διαφέρει και από το timeout: ο πελάτης μπορεί να σταματήσει να περιμένει ενώ ο εξυπηρετητής συνεχίζει. Η εφαρμογή πρέπει να ορίζει τι καθαρίζει, ποια εξαίρεση διαδίδεται και τι γίνεται όταν έχει ήδη υπάρξει εξωτερική επίδραση.
Εκτελέσιμο asyncio εργαστήριο
Αποθήκευσε ως cancel_lab.py και εκτέλεσε python3 cancel_lab.py. Τα asyncio.Event συγχρονίζουν τη δοκιμή χωρίς αυθαίρετη αναμονή πολλών δευτερολέπτων. Η εργασία διακόπτεται σε await, το finally σηματοδοτεί cleanup και ξεχωριστή αποτυχία αποδεικνύει ότι ένα πραγματικό bug δεν εξαφανίζεται μέσα σε γενικό χειρισμό εξαιρέσεων.
import asyncio
async def work(started, cleaned):
try:
started.set()
await asyncio.Event().wait()
finally:
cleaned.set()
async def failing():
raise RuntimeError('real failure')
async def main():
started, cleaned = asyncio.Event(), asyncio.Event()
task = asyncio.create_task(work(started, cleaned))
await started.wait()
assert task.cancel()
try:
await task
except asyncio.CancelledError:
pass
else:
raise AssertionError('cancellation not propagated')
assert cleaned.is_set() and task.cancelled()
try:
await failing()
except RuntimeError:
pass
else:
raise AssertionError('unexpected exception was swallowed')
finished = asyncio.create_task(asyncio.sleep(0, result='done'))
assert await finished == 'done'
assert not finished.cancel() # Cancellation cannot undo completed work.
print('PASS asyncio cancellation, cleanup, unrelated errors')
asyncio.run(main())
Πώς ερμηνεύουμε τις εγγυήσεις
Το started.wait αποδεικνύει ότι η εργασία έφτασε στο σημείο αναμονής πριν καλέσουμε cancel. Η αναμονή της ακυρωμένης εργασίας προκαλεί asyncio.CancelledError και ο σωστός κάτοχος της εργασίας πρέπει να τη χειριστεί. Το finally εκτελείται και θέτει το cleaned. Μία ήδη ολοκληρωμένη εργασία δεν μπορεί να ακυρωθεί εκ των υστέρων: η cancel επιστρέφει false. Αν η απομακρυσμένη αλλαγή έχει ολοκληρωθεί, απαιτείται έλεγχος κατάστασης, ασφαλής επανάληψη με μοναδικό κλειδί ή αντισταθμιστική ενέργεια.
Ασκήσεις διάγνωσης
- Αφαίρεσε προσωρινά το finally και έλεγξε γιατί το cleanup δεν επιβεβαιώνεται.
- Χρησιμοποίησε context manager για προστατευμένο πόρο και εξέτασε την αποδέσμευσή του όταν προκύψει ακύρωση κατά τη διάρκεια await.
- Κάνε ένα δοκιμαστικό αντικείμενο να προκαλεί RuntimeError αντί CancelledError. Μην αποκρύπτεις μη σχετικές εξαιρέσεις.
- Προσομοίωσε εξυπηρετητή που ολοκλήρωσε αίτημα πριν λήξει η αναμονή του πελάτη και γράψε τι μπορεί να γνωρίζει κάθε πλευρά.
Παραδοτέα: PASS, ακολουθία δημιουργίας/εκτέλεσης/ακύρωσης/καθαρισμού και κανόνας επανεκτέλεσης ήδη ολοκληρωμένων ενεργειών.