DedSec Project

Χρονικά όρια και επαναπροσπάθειες δικτύου — DedSec Project

Αποτυχίες δικτύου — περιορισμένες επαναπροσπάθειες και αποφυγή διπλών εγγραφών Τι σημαίνει πραγματικά ένα χρονικό όριο Όταν τελειώσει ο χρόνος αναμονής, ο πελάτης γνωρίζει μόνο ότι δεν…

Αποτυχίες δικτύου — περιορισμένες επαναπροσπάθειες και αποφυγή διπλών εγγραφών

Τι σημαίνει πραγματικά ένα χρονικό όριο

Όταν τελειώσει ο χρόνος αναμονής, ο πελάτης γνωρίζει μόνο ότι δεν παρέλαβε απάντηση. Ο διακομιστής μπορεί να έχει ήδη ολοκληρώσει την ενέργεια. Αν χαθεί η απάντηση μετά από πληρωμή ή καταχώριση, η επόμενη προσπάθεια δεν πρέπει να δημιουργήσει δεύτερη πράξη. Ξεχώρισε μόνιμα σφάλματα, όπως 400 και 401, από ενδεχομένως προσωρινά 429 και 503. Σε κανονικό πρόγραμμα χρειάζονται επίσης όρια χρόνου σύνδεσης και ανάγνωσης, μέγιστο πλήθος προσπαθειών, συνολική προθεσμία και τήρηση του Retry-After όταν είναι έγκυρο.

Εκτελέσιμο εργαστήριο (Python 3)

Αποθήκευσε ως retry_lab.py και εκτέλεσε python3 retry_lab.py. Όλα γίνονται στη μνήμη: δεν αποστέλλονται HTTP αιτήματα και δεν πραγματοποιούνται συναλλαγές. Η πρώτη απάντηση χάνεται σκόπιμα αφού έχει αποθηκευτεί η ενέργεια.

def plan(statuses, max_attempts=3):
    visits = []
    for status in statuses[:max_attempts]:
        visits.append(status)
        if status == 200:
            return ("OK", visits)
        if status not in (429, 503):
            raise ValueError("permanent HTTP failure")
    raise TimeoutError("attempt budget exhausted")

assert plan([503, 429, 200]) == ("OK", [503, 429, 200])
for bad in ([400, 200], [503, 503, 503]):
    try:
        plan(bad)
    except (ValueError, TimeoutError):
        pass
    else:
        raise AssertionError("bad retry accepted")

ledger = {}
def post(key, fail_response=False):
    if not key:
        raise ValueError("idempotency key required")
    if key not in ledger:
        ledger[key] = len(ledger) + 1
    if fail_response:
        raise TimeoutError("reply lost after commit")
    return ledger[key]

try:
    post("order-7", True)
except TimeoutError:
    pass
else:
    raise AssertionError("missing simulated timeout")
assert post("order-7") == 1 and len(ledger) == 1
print("PASS: bounded retries and no double commit")

Η ακεραιότητα δεν επιτυγχάνεται με τυφλή επανάληψη

Το παράδειγμα σταματά σε μόνιμο σφάλμα και αποτυγχάνει όταν εξαντληθούν οι προσπάθειες. Το σταθερό κλειδί order-7 συνδέει δύο προσπάθειες με την ίδια λογική πράξη. Σε πραγματικό διακομιστή το κλειδί και το αποτέλεσμα πρέπει να αποθηκεύονται με ατομικό τρόπο μαζί με την αλλαγή δεδομένων. Ένα λεξικό στη μνήμη δεν επιβιώνει από επανεκκίνηση. Αν τα κλειδιά λήγουν ύστερα από χρονικό διάστημα, η εφαρμογή πρέπει να περιορίζει τις επαναπροσπάθειες μέσα σε αυτό το παράθυρο.

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

  1. Βάλε απαντήσεις [503, 400, 200]. Η δεύτερη πρέπει να διακόψει την προσπάθεια και η τρίτη να μη χρησιμοποιηθεί.
  2. Προσομοίωσε δύο εγγραφές χωρίς σταθερό κλειδί, κατάγραψε το διπλό αποτέλεσμα και έπειτα αποκατάστησε την αποφυγή διπλοτύπων.
  3. Υπολόγισε εκθετικά αυξανόμενες καθυστερήσεις με μέγιστο όριο και ψευδοτυχαία διακύμανση σταθερής αρχικής τιμής. Μην χρησιμοποιείς πραγματική αναμονή μέσα στις δοκιμές.
  4. Σχεδίασε συνολική προθεσμία δύο δευτερολέπτων και ξεχωριστό χρονικό όριο ανά αίτημα. Δείξε πότε απομένει χρόνος για άλλη προσπάθεια.

Παραδοτέα: PASS, πίνακας χειρισμού 429/503/400, ίχνος χαμένης απάντησης και εξήγηση γιατί η μόνιμη αποθήκευση του κλειδιού είναι ευθύνη του διακομιστή.