DedSec Project

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. Αν η απομακρυσμένη αλλαγή έχει ολοκληρωθεί, απαιτείται έλεγχος κατάστασης, ασφαλής επανάληψη με μοναδικό κλειδί ή αντισταθμιστική ενέργεια.

Ασκήσεις διάγνωσης

  1. Αφαίρεσε προσωρινά το finally και έλεγξε γιατί το cleanup δεν επιβεβαιώνεται.
  2. Χρησιμοποίησε context manager για προστατευμένο πόρο και εξέτασε την αποδέσμευσή του όταν προκύψει ακύρωση κατά τη διάρκεια await.
  3. Κάνε ένα δοκιμαστικό αντικείμενο να προκαλεί RuntimeError αντί CancelledError. Μην αποκρύπτεις μη σχετικές εξαιρέσεις.
  4. Προσομοίωσε εξυπηρετητή που ολοκλήρωσε αίτημα πριν λήξει η αναμονή του πελάτη και γράψε τι μπορεί να γνωρίζει κάθε πλευρά.

Παραδοτέα: PASS, ακολουθία δημιουργίας/εκτέλεσης/ακύρωσης/καθαρισμού και κανόνας επανεκτέλεσης ήδη ολοκληρωμένων ενεργειών.