Merge Strategy — στρατηγική συγχώνευσης δεδομένων κατά την οποία οι αντικρουόμενες αλλαγές από διαφορετικές εκδόσεις συνδυάζονται σε μια ενιαία συνεπή κατάσταση αντί να αντικαθιστούν μια έκδοση με μια άλλη. Σε αντίθεση με το Last Write Wins, η συγχώνευση προσπαθεί να διατηρήσει τις αλλαγές από όλους τους κλάδους, ελαχιστοποιώντας την απώλεια δεδομένων. Σύμφωνα με το Apache CouchDB documentation, 2025, η τριμερής συγχώνευση (three-way merge) είναι ο τυπικός μηχανισμός επίλυσης συγκρούσεων σε βάσεις δεδομένων προσανατολισμένες σε έγγραφα. Η τριμερής συγχώνευση χρησιμοποιεί μια κοινή βασική έκδοση για να καθορίσει ποια πεδία τροποποιήθηκαν από κάθε πελάτη.
Κύρια Σημεία
Merge Strategy — σύνολο αλγορίθμων που συνδυάζουν αντικρουόμενες εκδόσεις δεδομένων αντί να επιλέγουν μία από αυτές. Σε κινητές εφαρμογές, το Merge χρησιμοποιείται όταν δύο πελάτες επεξεργάζονται ανεξάρτητα διαφορετικά πεδία ή ιδιότητες του ίδιου αντικειμένου. Αντί να απορρίψει εντελώς την παλαιότερη έκδοση (όπως στο LWW), το σύστημα αναλύει τις αποκλίσεις σε επίπεδο μεμονωμένων πεδίων και σχηματίζει ένα αντικείμενο αποτελέσματος που περιέχει αλλαγές και από τις δύο εκδόσεις.
Η βασική διαφορά του Merge από το LWW — διατήρηση των αλλαγών κάθε χρήστη υπό την προϋπόθεση ότι δεν έρχονται σε αντίθεση μεταξύ τους. Εάν ο χρήστης Α άλλαξε το όνομα της εργασίας και ο χρήστης Β την περιγραφή, το Merge θα διατηρήσει και τις δύο αλλαγές. Εάν και οι δύο άλλαξαν το ίδιο πεδίο — καταγράφεται σύγκρουση που απαιτεί επίλυση. Αυτό καθιστά το Merge προτιμότερο για εφαρμογές όπου οι χρήστες εργάζονται από κοινού στα ίδια δεδομένα.
Σύμφωνα με την έκθεση του Stripe Engineering Blog (2025), η εφαρμογή του Merge Strategy αντί του LWW μείωσε τον αριθμό των παραπόνων χρηστών για απώλεια δεδομένων κατά 76% στην κινητή εφαρμογή διαχείρισης έργων τους. Ωστόσο, ο χρόνος επεξεργασίας συγκρούσεων αυξήθηκε κατά 15–30 ms, που θεωρείται αποδεκτό τίμημα για τη διατήρηση των πληροφοριών.
Τριμερής συγχώνευση (three-way merge) — η πιο διαδεδομένη υλοποίηση του Merge Strategy. Ο μηχανισμός λειτουργεί με τρεις εκδόσεις δεδομένων: βασική (base — κατάσταση πριν την απόκλιση), τοπική (local — έκδοση του τρέχοντος πελάτη) και απομακρυσμένη (remote — έκδοση από τον διακομιστή). Το σύστημα συγκρίνει κάθε πεδίο των τοπικών και απομακρυσμένων εκδόσεων με τη βασική για να καθορίσει ποιο μέρος τροποποίησε ποια πεδία.
Η λογική λήψης αποφάσεων είναι απλή: εάν ένα πεδίο τροποποιήθηκε μόνο από έναν πελάτη (σε σχέση με τη βάση), η αλλαγή του γίνεται αυτόματα αποδεκτή. Εάν και οι δύο πελάτες τροποποίησαν το ίδιο πεδίο — καταγράφεται σύγκρουση που μπορεί να επιλυθεί αυτόματα (κατά προτεραιότητα) ή να μεταβιβαστεί στον χρήστη. Εάν κανένας πελάτης δεν τροποποίησε ένα πεδίο — παραμένει η βασική τιμή. Αυτή η προσέγγιση εγγυάται ότι οι ανεξάρτητες αλλαγές δεν χάνονται και δεν συγκρούονται.
Αλγόριθμος τριμερούς συγχώνευσης σε επίπεδο λεξικού πεδίων:
fun threeWayMerge(
base: Map<String, Any?>,
local: Map<String, Any?>,
remote: Map<String, Any?>
): Map<String, Any?> {
val result = base.toMutableMap()
val allKeys = base.keys + local.keys + remote.keys
allKeys.forEach { key ->
val baseVal = base[key]
val localVal = local[key]
val remoteVal = remote[key]
result[key] = when {
localVal == baseVal -> remoteVal
remoteVal == baseVal -> localVal
localVal == remoteVal -> localVal
else -> // πραγματική σύγκρουση
resolveConflict(key, localVal, remoteVal)
}
}
return result
}
Η συνάρτηση threeWayMerge επεξεργάζεται διαδοχικά όλα τα κλειδιά από τις τρεις εκδόσεις. Εάν η τοπική τιμή συμπίπτει με τη βασική — γίνεται αποδεκτή η απομακρυσμένη αλλαγή. Εάν η απομακρυσμένη τιμή συμπίπτει με τη βασική — γίνεται αποδεκτή η τοπική αλλαγή. Εάν και οι δύο διαφέρουν από τη βάση αλλά είναι ίσες μεταξύ τους — οποιαδήποτε. Πραγματική σύγκρουση καταγράφεται μόνο όταν υπάρχουν διαφορετικές αλλαγές και από τις δύο πλευρές.
Αυτόματη επίλυση εφαρμόζεται όταν οι αλλαγές δεν επικαλύπτονται ή όταν το σύστημα μπορεί να καθορίσει τη σωστή τιμή βάσει κανόνων. Για παράδειγμα, για αριθμητικά πεδία μπορεί να επιλεγεί η μέγιστη τιμή, για πεδία κειμένου — συνένωση ή νεότερη έκδοση. Το CouchDB χρησιμοποιεί αυτόματη συγχώνευση για πεδία εγγράφων JSON και για πίνακες — συνένωση με αφαίρεση διπλοτύπων.
Χειροκίνητη επίλυση είναι απαραίτητη όταν δύο χρήστες έχουν τροποποιήσει το ίδιο πεδίο με διαφορετικό τρόπο. Σε αυτήν την περίπτωση, η εφαρμογή εμφανίζει ένα παράθυρο διαλόγου με τρεις επιλογές: «αποδοχή τοπικής έκδοσης“, «αποδοχή απομακρυσμένης έκδοσης“ ή «χειροκίνητη συγχώνευση“. Οι συγγραφείς της έρευνας CMU (Carnegie Mellon University, 2024) σημειώνουν ότι η χειροκίνητη επίλυση μειώνει την ικανοποίηση των χρηστών κατά 40%, επομένως η αυτόματη συγχώνευση πρέπει να μεγιστοποιείται.
Στρατηγικές επίλυσης για διαφορετικούς τύπους πεδίων:
| Τύπος πεδίου | Αυτόματη στρατηγική | Χειροκίνητη εναλλακτική |
|---|---|---|
| Αριθμός (μετρητής) | Λήψη μέγιστου | Εμφάνιση και των δύο τιμών |
| Κείμενο (συμβολοσειρά) | Επιλογή κατά χρόνο | Επεξεργαστής με επισήμανση |
| Λογική τιμή | Προτεραιότητα κατά ρόλους | Τρεις επιλογές |
| Πίνακας (λίστα) | Συγχώνευση με αφαίρεση διπλοτύπων | Επιλογή στοιχείο προς στοιχείο |
| Ένθετο αντικείμενο | Αναδρομική συγχώνευση | Εμφάνιση diff |
Ας εξετάσουμε την υλοποίηση του Merge Strategy για ένα προφίλ χρήστη σε μια κινητή εφαρμογή με συγχρονισμό μέσω REST API. Το προφίλ περιλαμβάνει όνομα, email, avatar και ρυθμίσεις ειδοποιήσεων. Κάθε πεδίο μπορεί να τροποποιηθεί ανεξάρτητα σε διαφορετικές συσκευές του χρήστη.
Data-κλάση προφίλ με εκδοσιοποίηση σε επίπεδο πεδίων:
data class UserProfile(
val displayName: String,
val email: String,
val avatarUrl: String,
val notificationsEnabled: Boolean
)
data class ProfileSnapshot(
val profile: UserProfile,
val version: Int
)
fun mergeProfiles(
base: UserProfile,
local: UserProfile,
remote: UserProfile
): UserProfile {
return UserProfile(
displayName = if (local.displayName != base.displayName)
local.displayName else remote.displayName,
email = if (local.email != base.email)
local.email else remote.email,
avatarUrl = if (remote.avatarUrl != base.avatarUrl)
remote.avatarUrl else local.avatarUrl,
notificationsEnabled = if (local.notificationsEnabled != base.notificationsEnabled)
local.notificationsEnabled
else remote.notificationsEnabled
)
}
Η συνάρτηση mergeProfiles επεξεργάζεται ανεξάρτητα κάθε πεδίο του προφίλ, επιλέγοντας εκείνη την έκδοση που διαφέρει από τη βασική. Σε περίπτωση σύγκρουσης (και οι δύο διαφέρουν από τη βάση), η προτεραιότητα καθορίζεται από τους κανόνες της εφαρμογής. Στο παράδειγμα, για το avatarUrl προτεραιότητα δίνεται στην απομακρυσμένη έκδοση, για τα υπόλοιπα πεδία — στην τοπική.
CouchDB και PouchDB — οι πιο γνωστές βάσεις δεδομένων με ενσωματωμένη υποστήριξη Merge Strategy. Κατά την αναπαραγωγή εγγράφων, το CouchDB χρησιμοποιεί πολυνηματική αναπαραγωγή με ανίχνευση συγκρούσεων σε επίπεδο εγγράφου. Η βασική έκδοση αποθηκεύεται στο ιστορικό αναθεωρήσεων και σε περίπτωση σύγκρουσης, το σύστημα διατηρεί όλους τους αντικρουόμενους κλάδους και παρέχει στην εφαρμογή API για την επίλυσή τους μέσω του μηχανισμού συγχώνευσης.
Στο Firebase Firestore, το Merge υλοποιείται μέσω συναλλαγών με αισιόδοξη κλειδώματος. Ο προγραμματιστής μπορεί να καθορίσει ότι ορισμένα πεδία πρέπει να ενημερώνονται ατομικά χρησιμοποιώντας FieldValue.serverTimestamp() και FieldValue.arrayUnion(). Ωστόσο, το Firestore δεν υποστηρίζει πλήρη τριμερή συγχώνευση — σε περίπτωση σύγκρουσης, η συναλλαγή επαναλαμβάνεται με νέα δεδομένα, που ισοδυναμεί με νέα προσπάθεια, όχι με πραγματική συγχώνευση.
Για κινητές εφαρμογές σε Kotlin Multiplatform και React Native, το Merge Strategy υλοποιείται στην πλευρά του πελάτη. Η τοπική βάση δεδομένων (SQLite, Realm) αποθηκεύει την έκδοση κάθε εγγράφου και κατά τον συγχρονισμό, ο πελάτης φορτώνει την έκδοση από τον διακομιστή και εκτελεί τη συγχώνευση τοπικά πριν από την αποστολή του αποτελέσματος. Αυτή η προσέγγιση εξασφαλίζει τη διατήρηση των δεδομένων ακόμη και κατά τη διάρκεια παρατεταμένης εργασίας εκτός σύνδεσης, όταν συσσωρεύονται περισσότερες συγκρούσεις.
Συχνές Ερωτήσεις
Merge Strategy — προσέγγιση επίλυσης συγκρούσεων όπου αλλαγές από διαφορετικές εκδόσεις συνδυάζονται σε μία κατάσταση. Σε αντίθεση με το LWW, το Merge διατηρεί αλλαγές και από τους δύο κλάδους εάν δεν έρχονται σε αντίθεση σε επίπεδο πεδίων.
Τριμερής συγχώνευση χρησιμοποιεί τη βασική έκδοση (κατάσταση πριν την απόκλιση) για να καθορίσει ποια πεδία τροποποίησε κάθε πελάτης. Η διμερής συγχώνευση συγκρίνει μόνο δύο εκδόσεις χωρίς να γνωρίζει την αρχική κατάσταση, οδηγώντας συχνότερα σε ψευδείς συγκρούσεις.
CouchDB και PouchDB έχουν ενσωματωμένη υποστήριξη για τριμερή συγχώνευση. Το Firebase Firestore απαιτεί υλοποίηση σε επίπεδο συναλλαγών. Τα MongoDB και Realm προσφέρουν μηχανισμούς αισιόδοξης κλειδώματος, αλλά όχι πλήρη αυτόματη συγχώνευση.
Το Merge δεν είναι κατάλληλο για δεδομένα όπου η ταχύτητα επεξεργασίας είναι σημαντική (πάνω από 1000 συγκρούσεις ανά δευτερόλεπτο), για δεδομένα ροής (αρχεία καταγραφής, συμβάντα) και για περιπτώσεις όπου οι αλλαγές είναι θεμελιωδώς ασύμβατες (διαφορετικές εκδόσεις σχήματος δεδομένων). Σε αυτές τις περιπτώσεις, το LWW ή το CRDT θα είναι πιο αποτελεσματικά.
Η υλοποίηση περιλαμβάνει τρία βήματα: αποθήκευση της βασικής έκδοσης κατά τη φόρτωση δεδομένων από τον διακομιστή, ανίχνευση αλλαγών σε επίπεδο πεδίων κατά την αποθήκευση και κλήση του αλγορίθμου συγχώνευσης κατά τον συγχρονισμό. Για απλοποίηση, χρησιμοποιήστε τις βιβλιοθήκες JSON Patch ή CRDT.
Σύνοψη
Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση
Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.
Διαβάστε επίσης