Merge Strategy — adategyesítési stratégia, amelyben a különböző verziók ütköző változtatásai egyetlen összehangolt állapotba egyesülnek ahelyett, hogy az egyik verziót a másikra cserélnék. A Last Write Wins-szel ellentétben az egyesítés megpróbálja megőrizni a változtatásokat az összes ágból, minimalizálva az adatvesztést. A Apache CouchDB documentation, 2025 szerint a háromirányú egyesítés (three-way merge) a szabványos konfliktusmegoldó mechanizmus a dokumentumorientált adatbázisokban. A háromirányú egyesítés egy közös alapverziót használ annak meghatározására, hogy mely mezőket módosította az egyes kliensek.
Főbb pontok
Merge Strategy — algoritmusok összessége, amelyek az ütköző adatverziókat kombinálják ahelyett, hogy egyet választanának közülük. A mobilalkalmazásokban a Merge akkor használatos, amikor két kliens egymástól függetlenül szerkeszti ugyanazon objektum különböző mezőit vagy tulajdonságait. Ahelyett, hogy a régebbi verziót teljesen elvetné (mint az LWW-ben), a rendszer elemzi az eltéréseket az egyes mezők szintjén, és létrehoz egy eredményobjektumot, amely mindkét verzió változtatásait tartalmazza.
A kulcsfontosságú különbség a Merge és az LWW között — az egyes felhasználók változtatásainak megőrzése, feltéve, hogy nem ellentmondásosak. Ha az A felhasználó megváltoztatta a feladat nevét, a B felhasználó pedig a leírást, a Merge mindkét változtatást megőrzi. Ha mindketten ugyanazt a mezőt változtatták meg — konfliktus kerül rögzítésre, amely megoldást igényel. Ez teszi a Merge-t előnyössé azokban az alkalmazásokban, ahol a felhasználók közösen dolgoznak ugyanazokon az adatokon.
A Stripe Engineering Blog (2025) jelentése szerint a Merge Strategy LWW helyetti bevezetése 76%-kal csökkentette a felhasználói panaszok számát az adatvesztés miatt a mobil projektmenedzsment alkalmazásukban. A konfliktusok feldolgozási ideje azonban 15–30 ms-mal nőtt, ami elfogadható árnak tekinthető az információk megőrzéséért.
Háromirányú egyesítés (three-way merge) — a Merge Strategy legelterjedtebb megvalósítása. A mechanizmus három adatverzióval dolgozik: alap (base — állapot a divergencia előtt), helyi (local — az aktuális kliens verziója) és távoli (remote — a szerverről származó verzió). A rendszer összehasonlítja a helyi és távoli verziók minden mezőjét az alapverzióval annak meghatározására, hogy melyik fél mely mezőket módosította.
A döntéshozatal logikája egyszerű: ha egy mezőt csak egy kliens módosított (az alaphoz képest), a változtatás automatikusan elfogadásra kerül. Ha mindkét kliens ugyanazt a mezőt módosította — konfliktus kerül rögzítésre, amely automatikusan (prioritás szerint) megoldható vagy továbbítható a felhasználónak. Ha egyik kliens sem módosított egy mezőt — az alapérték marad. Ez a megközelítés garantálja, hogy a független változtatások ne vesszenek el és ne ütközzenek.
A háromirányú egyesítés algoritmusa mezőszótár szinten:
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 -> // valódi konfliktus
resolveConflict(key, localVal, remoteVal)
}
}
return result
}
A threeWayMerge függvény egymás után feldolgozza az összes kulcsot a három verzióból. Ha a helyi érték megegyezik az alappal — a távoli változtatás kerül elfogadásra. Ha a távoli érték megegyezik az alappal — a helyi változtatás kerül elfogadásra. Ha mindkettő különbözik az alaptól, de egyenlő egymással — bármelyik. Valódi konfliktus csak akkor kerül rögzítésre, ha mindkét oldalról eltérő változtatások vannak.
Automatikus megoldás akkor alkalmazandó, amikor a változtatások nem fedik egymást, vagy amikor a rendszer szabályok alapján meg tudja határozni a helyes értéket. Például numerikus mezőknél kiválasztható a maximális érték, szöveges mezőknél — konkatenáció vagy újabb verzió. A CouchDB automatikus egyesítést használ a JSON-dokumentum mezőihez, a tömböknél pedig konkatenációt a duplikátumok eltávolításával.
Kézi megoldás akkor szükséges, amikor két felhasználó eltérően módosította ugyanazt a mezőt. Ebben az esetben az alkalmazás egy párbeszédpanelt jelenít meg három opcióval: „helyi verzió elfogadása“, „távoli verzió elfogadása“ vagy „kézi egyesítés“. A CMU (Carnegie Mellon University, 2024) kutatói megjegyzik, hogy a kézi megoldás 40%-kal csökkenti a felhasználói elégedettséget, ezért az automatikus egyesítést maximalizálni kell.
Megoldási stratégiák különböző mezőtípusokhoz:
| Mező típusa | Automatikus stratégia | Kézi alternatíva |
|---|---|---|
| Szám (számláló) | Vedd a maximumot | Mindkét érték mutatása |
| Szöveg (karakterlánc) | Válassz idő szerint | Szerkesztő kiemeléssel |
| Logikai érték | Prioritás szerepek szerint | Három választási lehetőség |
| Tömb (lista) | Egyesítés deduplikációval | Elemenkénti kiválasztás |
| Beágyazott objektum | Rekurzív egyesítés | Diff mutatása |
Tekintsük át a megvalósítást a Merge Strategy-nek egy felhasználói profilhoz egy REST API-n keresztül szinkronizáló mobilalkalmazásban. A profil tartalmazza a nevet, e-mailt, avatárt és értesítési beállításokat. Minden mező függetlenül módosítható a felhasználó különböző eszközein.
A profil adatosztálya mezőszintű verziókezeléssel:
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
)
}
A mergeProfiles függvény egymástól függetlenül feldolgozza a profil minden mezőjét, kiválasztva azt a verziót, amely eltér az alaptól. Konfliktus esetén (mindkettő eltér az alaptól) a prioritást az alkalmazás szabályai határozzák meg. A példában az avatarUrl esetében a távoli verzió kap prioritást, a többi mező esetében — a helyi.
CouchDB és PouchDB — a legismertebb adatbázisok beépített Merge Strategy támogatással. A dokumentumok replikációja során a CouchDB többszálú replikációt használ konfliktusészleléssel dokumentumszinten. Az alapverzió a revíziós előzményekben tárolódik, és konfliktus esetén a rendszer megőrzi az összes ütköző ágat, és API-t biztosít az alkalmazás számára azok egyesítési mechanizmuson keresztüli megoldásához.
A Firebase Firestore-ban a Merge optimista zárolású tranzakciókon keresztül van megvalósítva. A fejlesztő megadhatja, hogy bizonyos mezők atomi módon frissüljenek a FieldValue.serverTimestamp() és FieldValue.arrayUnion() használatával. A Firestore azonban nem támogatja a teljes háromirányú egyesítést — konfliktus esetén a tranzakció új adatokkal ismétlődik, ami egy újrapróbálkozásnak felel meg, nem valódi egyesítésnek.
A Kotlin Multiplatform és React Native platformokon futó mobilalkalmazások esetében a Merge Strategy a kliens oldalon valósul meg. A helyi adatbázis (SQLite, Realm) tárolja minden dokumentum verzióját, és szinkronizáláskor a kliens betölti a verziót a szerverről, és helyben végrehajtja az egyesítést az eredmény elküldése előtt. Ez a megközelítés biztosítja az adatok megőrzését még hosszabb offline munka esetén is, amikor több konfliktus halmozódik fel.
Gyakran Ismételt Kérdések
Merge Strategy — konfliktusmegoldó megközelítés, amelyben a különböző verziók változtatásai egyetlen állapotba egyesülnek. Az LWW-vel ellentétben a Merge mindkét ág változtatásait megőrzi, ha azok nem ellentmondásosak mezőszinten.
Háromirányú egyesítés az alapverziót (a divergencia előtti állapotot) használja annak meghatározására, hogy mely mezőket módosította az egyes kliensek. A kétirányú egyesítés csak két verziót hasonlít össze, nem ismerve a kezdeti állapotot, ami gyakrabban vezet hamis konfliktusokhoz.
CouchDB és PouchDB beépített támogatással rendelkeznek a háromirányú egyesítéshez. Firebase Firestore tranzakció szintű megvalósítást igényel. MongoDB és Realm optimista zárolási mechanizmusokat kínálnak, de nem teljes automatikus egyesítést.
A Merge nem megfelelő olyan adatokhoz, ahol a feldolgozási sebesség fontos (több mint 1000 konfliktus másodpercenként), adatfolyamokhoz (naplók, események) és olyan esetekhez, ahol a változtatások alapvetően összeegyeztethetetlenek (az adatséma különböző verziói). Ezekben az esetekben az LWW vagy a CRDT hatékonyabb lesz.
A megvalósítás három lépésből áll: az alapverzió tárolása az adatok szerverről történő betöltésekor, a változtatások észlelése mezőszinten mentéskor, és az egyesítési algoritmus meghívása szinkronizáláskor. Az egyszerűsítéshez használja a JSON Patch vagy CRDT könyvtárakat.
Összefoglalás
Kulcsrakész mobilalkalmazást fejlesztünk
Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.
Olvassa el is