Ders 12 / 14
Önbellekleme ve Çevrimdışı Destek
Tek doğruluk kaynağı kalıbında arayüz yalnız yerel veritabanını okur, ağ yalnız veritabanını yeniler. Ders yenileme sonrası okumanın kökenini, ağ düşünce kökenin aynı kalıp değerin eskidiğini ve diskte hiç olmayan anahtarın neye düştüğünü ölçer.
Yayın Güncelleme
Bu derste
- Tek doğruluk kaynağı kalıbını arayüzün yalnız yerel veritabanını okuması olarak açıklar
- Ağın yalnız veritabanını yenilediğini ve okumanın zincirine girmediğini gösterir
- Yenileme sonrası okumanın kökenini disk olarak ölçer
- Ağ düşünce kökenin disk kaldığını, değerin eskidiğini ve diskte olmayan anahtarın varsayılana düştüğünü gösterir
İçindekiler
Önceki derste ağdan gelen yanıt çözüldü ve zincirde bir basamak oldu; önbellek de eski bir yanıtın çözülmüş hâliydi. Bu derste yanıtın zincirde durmasını değil, kalıcı olarak diske yazılmasını inceliyoruz. Ağ yanıtı diske yazılırsa ekranın ağa hiç bakmasına gerek kalmaz ve ağ düştüğünde de ekran çalışmaya devam eder. Dersin sorusu önceki derslerle aynıdır: bu okumanın yanıtı nereden geliyor?
// Origin.kt — bir okumanin yanitinin hangi basamaktan geldigini siniflandirir
class Tier(val origin: String, private val entries: Map<String, String>) {
init {
require(origin in listOf("bellek", "disk", "ag")) { "gecersiz basamak: $origin" }
}
fun lookup(key: String): String? = entries[key]
}
class Read(val label: String, val key: String, chain: List<Tier>, fallback: String) {
private val hit: Tier? = chain.firstOrNull { it.lookup(key) != null }
val value: String = hit?.lookup(key) ?: fallback
val origin: String = hit?.origin ?: "varsayilan"
fun show() {
println("$label: anahtar=$key | deger=$value -> $origin")
}
}
fun countOrigins(reads: List<Read>) {
for (name in listOf("bellek", "disk", "ag", "varsayilan")) {
println("$name: ${reads.count { it.origin == name }}")
}
}
Tek Doğruluk Kaynağı
Tek doğruluk kaynağı (single source of truth) kalıbı iki kural koyar. Arayüz yalnız yerel veritabanını okur; ağa hiç doğrudan bakmaz. Ağ yalnız veritabanını yeniler; arayüze doğrudan veri vermez. Veri bir yönde akar: ağdan veritabanına, veritabanından arayüze. Böylece ekran tek bir yerden beslenir ve ağ olmasa da o yer çalışır.
// Platform kodu: ProductRepository
class ProductRepository(private val dao: ProductDao, private val api: ProductApi) {
fun products(): Flow<List<ProductEntity>> = dao.observeAll()
suspend fun refresh() {
try {
dao.upsertAll(api.products())
} catch (e: IOException) {
// cevrimdisiysa eski kayitlar kalir
}
}
}
products yalnız veritabanını izler. refresh ağdan alıp veritabanına yazar ve ağ yoksa hiçbir şey yazmaz; veritabanı değişince akış yeni listeyi arayüze iletir. Bu parça platform kodudur ve burada çıktı, süre ya da kayıt sayısı iddia etmiyoruz. Model, platformun bu kuralını kurar; model platformun kendisi değildir.
Modelde yenileme, ağdan istenen anahtarları eski eşleme ekleyen bir işlevdir. Ağ yoksa eşlem değişmez. Okumanın zincirinde yalnız disk vardır; ağ hiçbir okumanın zincirine girmez.
// Refresh.kt — aga ulasilirsa istenen anahtarlari diske yazar, ulasilmazsa diski olduğu gibi birakir
fun refreshFrom(disk: Map<String, String>, network: Tier?, keys: List<String>): Map<String, String> {
if (network == null) return disk
return disk + keys.mapNotNull { key -> network.lookup(key)?.let { key to it } }
}
Ekran üç değer okur ve yalnız diske bakar.
// Screen.kt — arayuz yalniz diski okur
fun readProductScreen(disk: Map<String, String>): List<Read> {
val chain = listOf(Tier("disk", disk))
return listOf(
Read("fiyat", "urun.fiyat", chain, "bilinmiyor"),
Read("stok", "urun.stok", chain, "0"),
Read("kampanya", "kampanya.kod", chain, "yok"),
)
}
Yenileme Diske Yazar
Diskte önceki oturumdan kalan fiyat ve stok var. Sunucu fiyatı güncellemiş. Yenilemeden önce ve sonra ekranın okuması yan yana.
// Main.kt — yenileme ag degerini diske yazar, okuma yine diskten gelir
fun main() {
val disk = mapOf("urun.fiyat" to "120", "urun.stok" to "9")
val network = Tier("ag", mapOf("urun.fiyat" to "125", "urun.stok" to "8"))
println("yenileme oncesi")
readProductScreen(disk).forEach { it.show() }
val refreshed = refreshFrom(disk, network, listOf("urun.fiyat", "urun.stok", "kampanya.kod"))
println("yenileme sonrasi")
val reads = readProductScreen(refreshed)
reads.forEach { it.show() }
countOrigins(reads)
}
yenileme oncesi fiyat: anahtar=urun.fiyat | deger=120 -> disk stok: anahtar=urun.stok | deger=9 -> disk kampanya: anahtar=kampanya.kod | deger=yok -> varsayilan yenileme sonrasi fiyat: anahtar=urun.fiyat | deger=125 -> disk stok: anahtar=urun.stok | deger=8 -> disk kampanya: anahtar=kampanya.kod | deger=yok -> varsayilan bellek: 0 disk: 2 ag: 0 varsayilan: 1
Yenilemeden sonra fiyat 125, stok 8 oldu ve köken değişmedi: hâlâ disk. Ağ bir okumanın kökeni olmadı; ağ yalnız diskin içeriğini değiştirdi. Bu dersin kökeni disktir: ekran her durumda diskten okur ve ağın etkisi yalnızca diskin değerinde görünür. Sunucu kampanya kodunu bilmediği için o anahtar diske yazılmadı ve okuma varsayılanda kaldı.
VK1. Model, yenilemeyi tek adımda ve bütünüyle başarılı varsayar: istenen anahtarlar ya hep birlikte yazılır ya hiç yazılmaz. Platformda yenilemenin yarıda kesilmesi, iki yenilemenin üst üste binmesi ve yazma sırasında veritabanı işleminin başarısız olması modelde yoktur.
Ağ Düşünce
Ağ yoksa refresh hiçbir şey yazmaz. Eski kayıtlar kalır ve ekran onları göstermeye devam eder.
// Main.kt — ag dusunce okuma yine diskten gelir ama deger eskidir
fun main() {
val disk = mapOf("urun.fiyat" to "120", "urun.stok" to "9")
val refreshed = refreshFrom(disk, null, listOf("urun.fiyat", "urun.stok", "kampanya.kod"))
val reads = readProductScreen(refreshed)
reads.forEach { it.show() }
countOrigins(reads)
}
fiyat: anahtar=urun.fiyat | deger=120 -> disk stok: anahtar=urun.stok | deger=9 -> disk kampanya: anahtar=kampanya.kod | deger=yok -> varsayilan bellek: 0 disk: 2 ag: 0 varsayilan: 1
Okumanın kökeni değişmedi: fiyat ve stok disk, sayılar da yenilemeden önceki ölçümle aynı. Ağ yoksa değişen şey değerdir: fiyat 120 kaldı, sunucuda ise 125. Ekran çevrimdışı çalışır ve gösterdiği değer eskidir.
Bu ölçüm M30/K04’teki bayatlık ölçüsünden farklıdır. O kursta bir ekranın gösterdiği değerin modelin değerinden sapması yargılanıyordu: ekran ya güncel ya bayattı. Bu derste tazelik yargılanmaz; kural yalnız okumanın hangi basamaktan geldiğini söyler. İki ölçümde de fiyat 120’dir ve köken disktir; değerin eski olduğunu kural değil, yan yana koyduğumuz sunucu değeri söyler.
Sınırlayıcı Ölçüm
Diskte hiç olmayan bir anahtar ne olur? Uygulama ilk kez çalışıyor, disk boş ve ağ yok. Bu, ağın düştüğü ölçümün en uç hâlidir: ekranın gösterebileceği eski bir değer bile yoktur.
// Main.kt — bos diskte ve agsiz her okuma varsayilana duser, ag gelince diske yazilir
fun main() {
val network = Tier("ag", mapOf("urun.fiyat" to "125", "urun.stok" to "8", "kampanya.kod" to "YAZ10"))
val keys = listOf("urun.fiyat", "urun.stok", "kampanya.kod")
val states = listOf("agsiz" to null, "agli" to network)
for ((name, source) in states) {
println("ilk acilis: $name")
val reads = readProductScreen(refreshFrom(emptyMap(), source, keys))
reads.forEach { it.show() }
countOrigins(reads)
}
}
ilk acilis: agsiz fiyat: anahtar=urun.fiyat | deger=bilinmiyor -> varsayilan stok: anahtar=urun.stok | deger=0 -> varsayilan kampanya: anahtar=kampanya.kod | deger=yok -> varsayilan bellek: 0 disk: 0 ag: 0 varsayilan: 3 ilk acilis: agli fiyat: anahtar=urun.fiyat | deger=125 -> disk stok: anahtar=urun.stok | deger=8 -> disk kampanya: anahtar=kampanya.kod | deger=YAZ10 -> disk bellek: 0 disk: 3 ag: 0 varsayilan: 0
Ağsız ilk açılışta üç okumanın üçü varsayılan. Diskte olmayan anahtar için ağın düşmesi bir değer ortaya çıkarmaz; ekran yedek değerlerle açılır. Ağ geldiğinde yenileme üç anahtarı da diske yazar ve aynı okumalar bu kez diskten gelir. Tek doğruluk kaynağında ağ, okumanın kökeni olmaz; yalnız diskin dolmasını sağlar.
Ölçümün söylemediği şey, hangi değerin doğru olduğudur ve ağsız ilk açılışta ekranın nasıl görünmesi gerektiğidir. İkincisi bir tasarım kararıdır ve kural onu vermez.
VK2. Model, ağın ya hiç erişilemediğini ya da tüm istenen anahtarları verdiğini varsayar. Platformda kısmi yanıt, yavaş bağlantı ve ağın geri gelmesine kadar bekleyen yenilemenin ne zaman yeniden denendiği modelde yoktur.
Özet
- Bu dersin kökeni: disk. Tek doğruluk kaynağında arayüz yalnız veritabanını okur ve ağ okumanın zincirine girmez.
- Yenileme ağdan diske yazar; yenileme sonrası fiyat 125 ve stok 8 olsa da köken disk kalır: disk 2, varsayılan 1.
- Ağ düşünce yenileme bir şey yazmaz; okuma yine disktir, ama fiyat 120 kalır ve eskidir.
- Tazelik burada yargılanmaz; kural değerin güncel olup olmadığını değil, hangi basamaktan geldiğini söyler.
- Diskte hiç olmayan anahtar ağsız ilk açılışta varsayılandır: varsayılan 3; ağ gelince disk 3 olur.
Sonraki Adım
Bu derste ekran tek bir ürünü okudu ve disk onu tek seferde tutabildi. Liste uzadığında hepsini birden getirmek ne ağa ne belleğe uyar; veri parça parça yüklenir. Sıradaki ders, Sayfalı Veri Yükleme, henüz yüklenmemiş bir sayfadaki ögenin hangi kökenden okunduğunu ve sayfa yüklenince kökenin nasıl değiştiğini ölçer.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.