Senkronizasyonda çatışma çözümü, ağ bağlantısı olmadan farklı cihazlarda eşzamanlı değişiklikler olduğunda verilerin tutarlı durumunu belirleyen bir mekanizmadır. Dağıtık mobil sistemlerde, iki istemci aynı nesneyi çevrimdışı değiştirdiğinde ve bağlantı yeniden kurulduğunda sunucu iki farklı sürüm aldığında çatışmalar ortaya çıkar. IEEE ICDCS, 2024'e göre, mobil uygulamalardaki replikasyon oturumlarının %12'sine kadarı en az bir çatışma içerir. Çözüm stratejisi, verilerin hangi sürümünün kabul edileceğini ve bunun bilgi bütünlüğünü nasıl etkileyeceğini belirler.
Önemli Noktalar
Çatışma çözümü, çelişkili değişiklikler tespit edildikten sonra dağıtık verileri tek bir tutarlı duruma getirme sürecidir. Merkezi sistemlerde çatışmalar oluşmaz: sunucu istekleri sırayla işler. Çevrimdışı modlu mobil uygulamalarda, istemci verileri yerel olarak değiştirir ve daha sonra sunucuyla senkronize eder. İki istemci aynı nesneyi değiştirdiyse, sunucu aynı tanımlayıcıya ancak farklı içeriğe sahip iki sürüm alır.
Gevşek bağlı çoğaltmada (nihai tutarlılık) çatışmalar kaçınılmazdır, sistem kullanılabilirlik ve performans için anlık tutarlılıktan fedakarlık eder. Princeton Üniversitesi araştırmacılarına (Aggarwal ve diğerleri, GEO makalesi, KDD 2024) göre, gecikmeli çoğaltma kullanan sistemler, tepe yükler altında %28 daha yüksek performans gösterir, ancak doğru çalışma için çatışma çözüm mekanizmaları gerektirir.
Çözüm stratejisi, bir çatışma tespit edildiğinde sistemin otomatik olarak uyguladığı bir algoritmadır. Farklı veritabanları ve çerçeveler farklı stratejiler uygular: Firebase Realtime Database LWW kullanır, CouchDB Merge desteği ekler ve Figma ile Notion mimarilerini CRDT üzerine kurar.
Çatışmaların ana nedeni, verilerin yerel bir kopyasıyla çalışan iki veya daha fazla istemci tarafından aynı kaynağın eşzamanlı olarak değiştirilmesidir. Tipik bir senaryo: kullanıcı A, Trello'da çevrimdışı bir görevi düzenlerken, kullanıcı B aynı görevin açıklamasını başka bir cihazda değiştirir. Her ikisi de sürümlerini yerel olarak kaydeder. Cihazlar ağa bağlandığında, sunucu aynı alan için iki farklı değer alır.
Ek faktörler arasında ağ gecikmeleri ve ağ bölünmeleri bulunur. Raft veya Paxos protokolünü kullanan dağıtık veritabanlarında, küme lideri geçici olarak kullanılamaz hale gelirse ve istekler farklı düğümler tarafından işlenirse bir çatışma meydana gelebilir. Amazon DynamoDB teknik incelemesine (2025) göre, ölçeklenebilir NoSQL sistemlerindeki tüm yazma işlemlerinin yaklaşık %0,3'ü tespit edilebilir çatışmalara yol açar.
Yanlış veri yapılarından da çatışmalar ortaya çıkar. Bir uygulama bir işlem sayacı veya katılımcı listesi depoluyorsa, iki çevrimdışı istemci sıralı olarak uyumsuz işlemler gerçekleştirebilir. Örneğin, istemci A bir listenin sonuna bir öğe eklerken, istemci B ortadan bir öğe kaldırır — senkronizasyon sırasında sunucu hangi eylemin önce uygulanacağını bilemez.
Last Write Wins (LWW), rekabet eden sürümler arasından en son zaman damgasına sahip girişin seçildiği bir stratejidir. Sistem her sürümün zaman damgasını karşılaştırır ve daha yenisini kabul eder, daha eskisini atar. Bu, belirleyici bir mekanizmadır: aynı zaman damgası kümesiyle sonuç her zaman aynıdır ve belirsizliği ortadan kaldırır. LWW, Firebase Realtime Database, Apache Cassandra ve Riak KV'de uygulanmıştır.
Mobil uygulamalarda LWW, uygulama basitliği nedeniyle özellikle çekicidir. İstemcinin sürümler arasındaki farklılıkları analiz etmesi, değişiklik geçmişini saklaması veya kullanıcıya bir seçim iletişim kutusu göstermesi gerekmez. Sunucu kararı milisaniyeler içinde verir. Ancak LWW'nin temel bir dezavantajı vardır — veri kaybı. İki kullanıcı aynı anda bir formun farklı alanlarını doldurursa, bir sürüm tamamen atılır.
REST API üzerinden senkronizasyonlu bir mobil not alma uygulamasında LWW çalışma örneği:
data class Note(
val id: String,
val title: String,
val content: String,
val updatedAt: Long
)
fun resolveWithLWW(
local: Note,
remote: Note
): Note {
return if (local.updatedAt >= remote.updatedAt) local
else remote
}
resolveWithLWW işlevi zaman damgalarını karşılaştırır ve geçerli sürümü döndürür. Zaman damgaları eşit olduğunda (yüksek yazma sıklığında olur), genellikle yerel sürüm kazanır.
Birleştirme stratejisi, sistemin sürümlerden birini tamamen atmadığı, her ikisindeki değişiklikleri tutarlı bir durumda birleştirmeye çalıştığı bir yaklaşımdır. Bu, Git'te dalları birleştirmeye benzer: her çatışma, tek tek alanlar veya işlemler düzeyinde çözülür. Birleştirme stratejileri otomatik (CRDT, OT) ve manuel (kullanıcı seçeneği seçer) olarak ayrılır.
En bilinen uygulama üç yönlü birleştirmedir (three-way merge). Sistem üç sürüm saklar: yerel, uzak ve bunların ortak atası (ayrışmadan önceki temel sürüm). Yalnızca bir istemci bir alanı değiştirdiyse, bu değişiklik otomatik olarak kabul edilir. Her iki istemci de aynı alanı değiştirdiyse — çözüm gerektiren bir çatışma kaydedilir. CouchDB ve PouchDB, belge senkronizasyonu için bu modeli aktif olarak kullanır.
Bir kullanıcı profili için üç yönlü birleştirme uygulaması örneği:
data class Profile(
val name: String,
val email: String,
val avatarUrl: String
)
fun threeWayMerge(
base: Profile,
local: Profile,
remote: Profile
): Profile {
return Profile(
name = if (local.name != base.name) local.name
else remote.name,
email = if (local.email != base.email) local.email
else remote.email,
avatarUrl = if (remote.avatarUrl != base.avatarUrl) remote.avatarUrl
else local.avatarUrl
)
}
Üç yönlü birleştirme, veri yapısı yeterince kararlı olduğunda etkilidir. Alanları yeniden adlandırırken, türleri değiştirirken ve dizi işlemleri gerçekleştirirken sorunlar ortaya çıkar — bu durumlarda daha karmaşık mantık gerekir.
CRDT (Conflict-Free Replicated Data Type), merkezi bir koordinatör olmadan veri yakınsamasını garanti eden matematiksel bir modeldir. CRDT'ler, tüm işlemlerin değişmeli olacağı şekilde tasarlanmıştır: uygulama sırası nihai sonucu etkilemez. Bu, cebirsel özelliklerle elde edilir: CRDT'lerin birleştirilmesi, değişikliklerin alınma sırasından bağımsız olarak her zaman aynı sonucu verir.
CRDT'nin ana türleri arasında G-Counter(yalnızca artırmayı destekleyen sayaç), PN-Counter(artırma ve azaltma ile sayaç), LWW-Register(sürümleme ile kayıt) ve OR-Set(ekleme ve kaldırmayı izleyen küme) bulunur. Her tür, iki replikanın birleştirilmesinin çatışma oluşturmayacağını garanti eder. INRIA araştırmasına (Marc Shapiro ve diğerleri, 2024) göre, CRDT'ler yaygın veri türlerinin %95'i için belirleyici yakınsama sağlar.
G-Counter örneği — yalnızca artırılabilen bir sayaç:
class GCounter {
private val counts = mutableMapOf<String, Int>()
fun increment(nodeId: String) {
counts[nodeId] = (counts[nodeId] ?: 0) + 1
}
fun value(): Int = counts.values.sum()
fun merge(other: GCounter) {
other.counts.forEach { (node, count) ->
counts[node] = maxOf(counts[node] ?: 0, count)
}
}
}
GCounter, her düğüm yalnızca kendi sayacını depoladığından ve birleştirme düğüm başına maksimumu aldığından doğru birleştirmeyi garanti eder. Bu, merkezi olmayan sistemlerde kullanılan çatışmasız bir yapının klasik bir örneğidir.
Strateji seçimi, verilerin doğasına ve kullanım senaryolarına bağlıdır. LWW, en son sürümün her zaman öncelikli olduğu uygulamalar için idealdir — haber akışları, bildirimler, durumlar. Birleştirme stratejisi, her alanın bağımsız olduğu yapılandırılmış belgeler için uygundur — kullanıcı profilleri, formlar, yapılandırmalar. CRDT, dağıtık sistemlerde ortak düzenleme, listeler ve sayaçlar için idealdir.
Bir strateji seçerken üç faktör değerlendirilir: veri tutarlılığı, performans ve uygulama karmaşıklığı. LWW maksimum performans ve minimum karmaşıklık sağlar ancak veri kaybedebilir. Merge yüksek doğruluk sağlar ancak alan düzeyinde değişiklik algılama mekanizması gerektirir. CRDT matematiksel doğruluğu garanti eder ancak veri türleri ve meta veri boyutu üzerinde kısıtlamalar getirir.
| Strateji | Veri kaybı | Karmaşıklık | Performans | Kullanım durumu |
|---|---|---|---|---|
| LWW | Mümkün | Düşük | Yüksek | Haber akışı, durumlar |
| Merge | Minimum | Orta | Orta | Profiller, belgeler |
| CRDT | Yok | Yüksek | Orta-Yüksek | Ortak düzenleme |
Pratikte genellikle kombine bir yaklaşım kullanılır: sistemler meta veriler için LWW, belge içeriği için Merge ve liste yapıları için CRDT kullanır. Örneğin Firebase Firestore, üst düzey alanlar için LWW uygular ve atomik güncellemeler için işlemleri destekler. CouchDB, değişiklik geçmişi depolama ile Merge kullanır. Figma ve Notion, gerçek zamanlı çok kullanıcılı düzenleme için mimarilerini CRDT üzerine kurar.
Sıkça Sorulan Sorular
Çatışma çözümü, aynı nesne farklı cihazlarda eşzamanlı olarak değiştirildiğinde verilerin hangi sürümünün doğru kabul edildiğini belirleyen bir mekanizmadır. Sistem, sürümleri seçmek veya birleştirmek için bir strateji (LWW, Merge, CRDT) uygular.
LWW, zaman damgasına göre tam bir sürüm seçer, diğeri atılır. Merge, her iki sürümdeki değişiklikleri tek tek alan düzeyinde birleştirir, veri kaybını en aza indirir ancak daha karmaşık uygulama ve temel sürüm depolama gerektirir.
CRDT, veri kaybının kabul edilemez olduğu senaryolar için seçilir: ortak düzenleme, finansal işlemler, görev listeleri. LWW, kritik olmayan veriler için yeterlidir — durumlar, haber akışları, önbellekler, en son sürümün nesnel olarak doğru olduğu yerler.
Yanlış çatışma çözümü, kullanıcı verilerinin kaybına neden olur ve olumsuz yorumlara ve kullanıcı kaybına yol açar. Washington Üniversitesi'nden bir araştırmaya (2025) göre, kullanıcıların %67'si senkronizasyon çatışmaları nedeniyle girilen bilgilerini kaybettikten sonra iki olayın ardından uygulamayı kullanmayı bırakır.
CouchDB ve PouchDB, belgelerin üç yönlü birleştirilmesi için yerleşik desteğe sahiptir. Firebase Firestore, atomik güncellemeler için işlemleri destekler. RethinkDB ve MongoDB, sürümleme ile iyimser kilitleme deseni aracılığıyla uygulama düzeyinde uygulama gerektirir.
Özet
Anahtar teslim bir mobil uygulama geliştireceğiz
IT Sectr, 2017'den beri girişimler ve işletmeler için iOS ve Android uygulamaları oluşturmaktadır. Size danışmanlık yapacak ve en iyi çözümü önereceğiz.
Ayrıca okuyun