← Όλα τα άρθρα

Optimistic locking σε web apps: πώς δεν χάνεται η δουλειά όταν δύο χρήστες κάνουν edit

Πώς αποφεύγονται lost updates σε CRM και portals με version fields, ETags, conflict screens και ελεγχόμενο merge αλλαγών.

Δύο χρήστες ανοίγουν την ίδια καρτέλα πελάτη. Ο πρώτος αλλάζει το τηλέφωνο και πατά αποθήκευση. Λίγο αργότερα ο δεύτερος, που είχε ανοίξει την παλιότερη έκδοση, αλλάζει τη διεύθυνση και αποθηκεύει ολόκληρη τη φόρμα. Αν το backend δεν ελέγχει version, η δεύτερη αποθήκευση μπορεί να επαναφέρει το παλιό τηλέφωνο χωρίς κανείς να το καταλάβει. Αυτό είναι lost update.

Το optimistic locking επιτρέπει σε πολλούς χρήστες να διαβάζουν ελεύθερα, αλλά πριν από κάθε update ελέγχει ότι το record δεν άλλαξε από τότε που φορτώθηκε. Δεν κλειδώνει τη φόρμα για ώρα· ανιχνεύει τη σύγκρουση τη στιγμή της αποθήκευσης.

Ένα version field αρκεί για τη βασική αρχή

Κάθε record μπορεί να έχει ακέραιο version ή ακριβές updated_at. Ο client φορτώνει την τρέχουσα τιμή και τη στέλνει μαζί με την αλλαγή:

{
  "customerId": 1842,
  "version": 7,
  "address": "Νέα διεύθυνση"
}

Το update εκτελείται μόνο αν η βάση εξακολουθεί να έχει version 7:

UPDATE customers
SET address = ?, version = version + 1
WHERE id = ? AND version = 7

Αν επηρεαστούν μηδέν γραμμές, κάποιος άλλος έχει ήδη αλλάξει το record. Το backend επιστρέφει conflict αντί να γράψει πάνω στη νεότερη έκδοση. Ο έλεγχος και η ενημέρωση πρέπει να γίνουν atomically.

ETag και If-Match σε HTTP APIs

Στα REST APIs το version μπορεί να εκφραστεί ως ETag. Το GET επιστρέφει:

ETag: "customer-1842-v7"

Ο client στέλνει update με:

If-Match: "customer-1842-v7"

Αν το τρέχον ETag διαφέρει, ο server απορρίπτει την αλλαγή συνήθως με 412 Precondition Failed. Η τεκμηρίωση του MDN αναφέρει ρητά ότι το If-Match μπορεί να χρησιμοποιηθεί σε μη ασφαλείς methods, όπως PUT, για να αποτρέψει το lost update problem.

Το ETag δεν πρέπει να βασίζεται σε ασταθή serialization ή σε hash που αλλάζει για άσχετα δεδομένα, εκτός αν αυτή είναι η επιθυμητή granular πολιτική. Ένα explicit revision είναι συχνά πιο κατανοητό.

PATCH αντί για πλήρη αντικατάσταση

Αν ο χρήστης άλλαξε μόνο τη διεύθυνση, είναι προτιμότερο να σταλεί μόνο αυτό το πεδίο. Ένα πλήρες PUT με όλα τα παλιά values αυξάνει την πιθανότητα να γραφτεί ξανά άσχετη παλιά πληροφορία.

Το PATCH δεν καταργεί την ανάγκη locking. Δύο χρήστες μπορεί να αλλάξουν το ίδιο πεδίο ή ένας business rule να εξαρτάται από πολλά fields. Απλώς περιορίζει το εύρος της αλλαγής και επιτρέπει πιο έξυπνο merge όταν τα edits δεν επικαλύπτονται.

Τι βλέπει ο χρήστης στη σύγκρουση

Το μήνυμα «409 Conflict» δεν βοηθά. Το UI πρέπει να εξηγεί ότι η εγγραφή άλλαξε από άλλον χρήστη και να προστατεύει το draft. Καλές επιλογές είναι:

  • εμφάνιση της νεότερης έκδοσης δίπλα στις αλλαγές του χρήστη
  • αυτόματο merge για διαφορετικά fields
  • επιλογή ανά field όταν υπάρχει πραγματική σύγκρουση
  • δυνατότητα αντιγραφής του draft
  • refresh χωρίς απώλεια όσων πληκτρολογήθηκαν

Η επιλογή «overwrite anyway» πρέπει να είναι περιορισμένη και να καταγράφεται. Σε οικονομικά, permissions ή statuses υψηλού ρίσκου μπορεί να μην επιτρέπεται καθόλου.

Auto-save και background updates

Το auto-save αυξάνει τις πιθανότητες concurrent edits, επειδή η εφαρμογή γράφει συχνά. Κάθε save πρέπει να χρησιμοποιεί το τελευταίο version που έλαβε. Μετά από επιτυχία ο server επιστρέφει το νέο version και ο client ενημερώνει το local state.

Αν υπάρχει WebSocket ή server-sent events, η οθόνη μπορεί να ειδοποιείται ότι το record άλλαξε όσο ο χρήστης γράφει. Αυτό βελτιώνει την εμπειρία, αλλά ο τελικός server-side έλεγχος παραμένει απαραίτητος: real-time notifications μπορεί να καθυστερήσουν ή να χαθούν.

Διαφορετική granularity ανά οντότητα

Ένα version για ολόκληρο το customer record είναι απλό, αλλά μπορεί να δημιουργεί conflicts ανάμεσα σε άσχετες αλλαγές. Σε σύνθετες εφαρμογές μπορεί να υπάρχουν ξεχωριστές υπο-οντότητες: contact details, billing settings και notes.

Δεν χρειάζεται version ανά field. Η υπερβολική granularity κάνει το σύστημα δύσκολο. Χωρίζουμε μόνο boundaries που έχουν ανεξάρτητο lifecycle και permissions. Τα append-only comments ή activities δεν χρειάζονται conflict με την αλλαγή διεύθυνσης.

Pessimistic locking πότε έχει νόημα

Σε ορισμένες εργασίες, όπως αποκλειστική επεξεργασία μιας αίτησης για λίγα λεπτά, μπορεί να χρειάζεται προσωρινό lock ή lease. Το σύστημα δείχνει ότι ο φάκελος είναι υπό επεξεργασία και το lease λήγει αυτόματα αν ο χρήστης φύγει.

Τα μεγάλης διάρκειας database locks δεν είναι κατάλληλα για browser sessions. Μπορούν να μείνουν ορφανά και να μπλοκάρουν άλλους. Συχνά χρησιμοποιείται soft lock για UX μαζί με optimistic version check για πραγματική ασφάλεια.

Integrations και background jobs

Οι συγκρούσεις δεν προέρχονται μόνο από ανθρώπους. ERP sync, imports και background automations μπορεί να ενημερώνουν το ίδιο record. Κάθε writer πρέπει να σέβεται version ή να χρησιμοποιεί explicit merge policy.

Για source-of-truth fields, η εφαρμογή μπορεί να απαγορεύει manual edit. Για άλλα πεδία, το integration ενημερώνει μόνο όσα κατέχει. Το audit log καταγράφει actor, source, old value, new value και version.

Tests που χρειάζονται

Το lost update δεν φαίνεται πάντα σε απλό manual test. Χρειάζονται integration tests που φορτώνουν δύο αντίγραφα, αποθηκεύουν το πρώτο και επιβεβαιώνουν ότι το δεύτερο απορρίπτεται. Δοκιμάζονται auto-save retries, network timeout μετά από επιτυχία, overwrite permission και merge διαφορετικών fields.

Επίσης ελέγχεται ότι το version δεν παρακάμπτεται από admin endpoints, imports ή mobile clients. Ένα μόνο unguarded update μπορεί να επαναφέρει το πρόβλημα.

Πώς το υλοποιεί η Ai Foundry

Στην Ai Foundry προσθέτουμε concurrency control στα records που επεξεργάζονται πολλοί χρήστες ή integrations. Χρησιμοποιούμε revisions ή ETags, atomic conditional updates και σαφείς HTTP responses, ενώ το frontend διατηρεί το draft και εμφανίζει κατανοητό conflict view αντί για τεχνικό error.

Σχεδιάζουμε τη σωστή granularity ανά domain και συνδυάζουμε optimistic locking με audit logs, real-time notifications ή leases όπου χρειάζεται. Ελέγχουμε παράλληλες αποθηκεύσεις και retries με automated tests. Έτσι τα CRM και portals που αναπτύσσει η Ai Foundry προστατεύουν την εργασία των χρηστών χωρίς να τους εμποδίζουν να συνεργάζονται.

Συμπέρασμα

Η τελευταία αποθήκευση δεν πρέπει να κερδίζει σιωπηλά. Με version checks, ETags και σωστό conflict UX, η εφαρμογή εντοπίζει τη σύγκρουση πριν χαθούν δεδομένα και δίνει στον χρήστη ασφαλή τρόπο να συνεχίσει.

Πηγή