İçeriğe geç
academia.sh

Ders 07 / 24

Depolama API'leri

Sayfa ömrünü aşan veriyi tutan katmanlar; oturum ve kaynak ömrü ayrımı, dizgi deposunun tip ve serileştirme kayıpları, kota ve tahliye, sekmeler arası bildirim, şema göçü ve depolanmaması gereken veri.

İçindekiler

Önceki ders formun bir gönderimle bittiğini varsaydı. Çoğu form yarım kalır: kullanıcı ölçüm değerini girer, sekmeyi kapatır, ertesi gün geri döner. Sayfa yeniden yüklendiğinde bellekteki her şey silinmiştir — süzgeç alanına yazılan ölçüt de, yarım kalan form da.

Bunları saklamak, tarayıcının sayfa ömrünü aşan bir yere yazmayı gerektirir. Böyle birden çok yer vardır ve aralarındaki fark boyut değil ömür, erişim biçimi ve sunucuya görünürlüktür.

Katmanlar

Oturum depolama (session storage) sekme ömrüyle sınırlıdır. Aynı adres iki sekmede açıldığında iki ayrı depo görülür; sekme kapandığında içerik silinir. Yarım kalan bir form taslağı, çok adımlı bir akışın ara durumu buraya yazılır.

Yerel depolama (local storage) kaynak ömrü boyunca kalır. Sekme kapansa da, tarayıcı kapansa da içerik durur; aynı kaynağın bütün sekmeleri aynı depoyu paylaşır. Kullanıcı tercihleri, süzgeç ölçütü, son görüntülenen ölçüm buraya yazılır.

Yapılandırılmış depolama (IndexedDB) anahtar–değer ikilisinden fazlasını tutar: dizinlenmiş kayıtlar, işlemler, aralık sorguları. Erişimi eşzamansızdır. Çevrimdışı çalışacak bir ölçüm günlüğü bu katmanda durur.

Çerez (cookie) diğerlerinden farklı bir amaca hizmet eder: İnternet Nasıl Çalışır kursunda tanımlandığı gibi her istekle sunucuya gider. Bu, oturum kimliği için istenen davranıştır; arayüz durumu için istenmeyen bir yüktür. Yalnız istemcide kalacak veri çerezde tutulmaz.

Dördünün ortak sınırı kaynaktır (origin). Şema, ana bilgisayar ve bağlantı noktasının oluşturduğu üçlü ayrıysa depo da ayrıdır; aynı sunucuda barınan iki farklı kaynak birbirinin verisini göremez. Çerezin sınırı ayrıca alan adı ve yol bileşenleriyle daraltılabilir.

Dizgi Deposunun Davranışı

İlk iki katman yalnızca dizgi tutar. Bu tek cümle üç ayrı sonuç doğurur.

// depo.mjs — dizgi tabanli anahtar-deger deposunun uc davranisi
class Depo {
  constructor(kota) { this.kota = kota; this.kayit = new Map(); }
  get doluluk() {
    let t = 0;
    for (const [a, d] of this.kayit) t += a.length + d.length;
    return t;
  }
  yaz(anahtar, deger) {
    const metin = String(deger);              // her deger dizgiye cevrilir
    const eski = this.kayit.get(anahtar)?.length ?? 0;
    if (this.doluluk - eski + anahtar.length + metin.length > this.kota)
      throw new Error("KotaAsildi");
    this.kayit.set(anahtar, metin);
  }
  oku(anahtar) { return this.kayit.get(anahtar) ?? null; }
}

const depo = new Depo(100);  // kota, kod birimi sayisiyla olculuyor

// 1. Tip kaybi: yazilan sayi, okunan dizgidir.
depo.yaz("esik", 12.5);
console.log("okunan:", JSON.stringify(depo.oku("esik")), "tip:", typeof depo.oku("esik"));
console.log("olmayan anahtar:", depo.oku("yok"));

// 2. Serilestirme kaybi: JSON turu koruyan bir bicim degildir.
const olcum = { kod: "T-01", an: new Date("2024-02-11T06:00:00Z"), not: undefined,
                etiketler: new Set(["gece"]) };
depo.yaz("olcum", JSON.stringify(olcum));
const geri = JSON.parse(depo.oku("olcum"));
console.log("yazilan:", { an: olcum.an.constructor.name, not: olcum.not, etiketler: olcum.etiketler });
console.log("okunan :", geri);

// 3. Kota: yazma islemi hata atar, sessizce basarisiz olmaz.
try {
  depo.yaz("gunluk", "x".repeat(200));
} catch (hata) {
  console.log("hata:", hata.message, "| doluluk:", depo.doluluk, "/", depo.kota);
}
console.log("kota asildiktan sonra 'esik':", JSON.stringify(depo.oku("esik")));
okunan: "12.5" tip: string
olmayan anahtar: null
yazilan: { an: 'Date', not: undefined, etiketler: Set(1) { 'gece' } }
okunan : { kod: 'T-01', an: '2024-02-11T06:00:00.000Z', etiketler: {} }
hata: KotaAsildi | doluluk: 74 / 100
kota asildiktan sonra 'esik': "12.5"

Tip kaybı. Yazılan sayı okunurken dizgidir. Karşılaştırmayı dönüştürmeden yapan kod sessizce yanlış çalışır. Bulunmayan anahtar ise boş dizgi değil boş değer döndürür; ikisi ayırt edilmelidir, çünkü kullanıcı bir alanı bilerek boş bırakmış olabilir.

Serileştirme (serialization) kaybı. JSON, tipleri koruyan bir biçim değildir. Tarih nesnesi dizgiye döner, tanımsız alan tümüyle düşer, küme boş nesneye dönüşür. Okuma tarafı yalnızca JSON.parse çağırmakla yetinemez; kaydı beklenen tiplere geri çeviren bir katman gerekir. Tarayıcının iş parçacıkları arasında veri taşırken JSON yerine ayrı bir kopyalama düzeneği kullanmasının nedeni de budur; o düzenek Web İşçileri dersinde ele alınır.

Kota. Depo sınırsız değildir ve sınır aşıldığında yazma hata atar. Kotanın büyüklüğü gerçekleştirime bağlıdır; yazılan kodun buna bir sayı varsayarak değil, hatayı yakalayarak uyum sağlaması gerekir. Çıktının son satırı ikinci bir kuralı gösterir: başarısız yazma önceki kaydı bozmaz. Yazma yolu bu nedenle her zaman bir yakalama bloğu içinde durur ve başarısızlıkta kullanıcıya görünür bir davranış üretir.

Kotayı aşmadan da veri kaybolabilir. Tarayıcı, yer daraldığında ya da veri uzun süre kullanılmadığında bir kaynağın depolamasını tahliye edebilir. Kullanıcı da depolamayı temizleyebilir. Bu nedenle depolama bir önbellek gibi ele alınır: içeriği olmadan da çalışan bir sayfa doğru tasarlanmıştır.

Sekmeler Arası Bildirim

Yerel depolama aynı kaynağın bütün sekmelerince paylaşıldığı için bir sekmedeki yazma diğerlerini ilgilendirir. Tarayıcı bunun için bir olay üretir; olay eski değeri, yeni değeri ve anahtarı taşır. Böylece bir sekmede değiştirilen süzgeç ölçütü diğer sekmede de uygulanabilir.

Bu olayın iki özelliği şaşırtıcıdır. Birincisi, yazan sekmede üretilmez; yalnızca diğer sekmeler bildirim alır. Yazan sekmenin kendi arayüzünü güncellemesi kendi sorumluluğudur. İkincisi, oturum depolamada bu bildirim anlamsızdır, çünkü depo zaten sekmeye özeldir.

Eşzamanlılık Maliyeti

İlk iki katmanın erişimi eşzamanlıdır: okuma ve yazma, çağrıldığı anda tamamlanır ve ana iş parçacığı bu sırada başka iş yapamaz. Küçük değerlerde bu görünmez. Büyük bir kaydı her tuş vuruşunda yazan kod ise kullanıcı arayüzünü bekletir; Olay Modeli dersinde anılan kuyruk gecikmesi burada da işler.

Üç kural bu maliyeti sınırlar. Yazma seyrekleştirilir — Form Olayları dersindeki gecikmeli tetikleme burada da geçerlidir. Büyük veri yapılandırılmış depolamaya taşınır; onun eşzamansız arayüzü ana iş parçacığını bekletmez. Okuma, sayfa açılışında bir kez yapılıp bellekte tutulur; her erişimde yeniden okunmaz.

Şema Değişimi

Depolanan veri, onu yazan koddan daha uzun yaşar. Kullanıcının tarayıcısında altı ay önceki biçimle yazılmış bir kayıt durabilir. Bu nedenle her kayıt bir sürüm alanı taşır ve okuma yolu, eski sürümü güncel biçime taşıyan bir zincir içerir.

// goc.mjs — depodaki kaydin surumlenmesi ve ileri goc
const GUNCEL = 3;
const GOC = {
  1: (k) => ({ ...k, birim: "C" }),                                   // 1 -> 2: birim eklendi
  2: ({ deger, ...k }) => ({ ...k, sicaklik: Number(deger) }),        // 2 -> 3: alan adi ve tipi
};

function oku(ham) {
  let kayit;
  try { kayit = JSON.parse(ham); }
  catch { return { sonuc: "cozumlenemedi", kayit: null }; }

  const surum = kayit.surum ?? 1;
  if (surum > GUNCEL) return { sonuc: "gelecek surum, yok sayildi", kayit: null };

  let s = surum;
  while (s < GUNCEL) { kayit = { ...GOC[s](kayit), surum: s + 1 }; s += 1; }
  return { sonuc: `surum ${surum} -> ${GUNCEL}`, kayit };
}

const ornekler = [
  '{"kod":"T-01","deger":"-4.2"}',                                  // surum alani yok: 1 sayilir
  '{"surum":2,"kod":"T-02","deger":"3.5","birim":"C"}',
  '{"surum":3,"kod":"T-03","sicaklik":11.8,"birim":"C"}',
  '{"surum":9,"kod":"T-04"}',
  '{kod:"T-05"}',
];

for (const ham of ornekler) {
  const { sonuc, kayit } = oku(ham);
  console.log(sonuc.padEnd(26), JSON.stringify(kayit));
}
surum 1 -> 3               {"kod":"T-01","birim":"C","surum":3,"sicaklik":-4.2}
surum 2 -> 3               {"surum":3,"kod":"T-02","birim":"C","sicaklik":3.5}
surum 3 -> 3               {"surum":3,"kod":"T-03","sicaklik":11.8,"birim":"C"}
gelecek surum, yok sayildi null
cozumlenemedi              null

Zincir dört durumu birden karşılar. Sürüm alanı olmayan en eski kayıt birinci sürüm sayılır ve iki adımda güncel biçime taşınır. Ara sürüm kalan adımı atlar. Güncel kayıt dokunulmadan geçer.

Son iki satır savunma tarafıdır. Gelecekten gelen bir kayıt — kullanıcının başka bir cihazdan eşitlediği ya da daha yeni bir sürümle yazılmış olan — geriye taşınamaz; okunmaz ve yok sayılır. Çözümlenemeyen içerik de aynı biçimde ele alınır. Depodan okunan her şey dış girdidir: bozuk olabilir, elle değiştirilmiş olabilir, hiç yazılmamış olabilir. Çözümleme her zaman bir yakalama bloğu içinde yapılır.

Ne Saklanmaz

Depolama, kullanıcının cihazında ve kullanıcının denetiminde duran bir alandır. Buradan üç kural çıkar.

Sayfada çalışan her kod aynı depoyu okuyabilir; bu nedenle kimlik belirteci, parola ve kişisel veri oraya yazılmaz. Bir betik enjeksiyonu, depodaki her şeye erişir.

Depo kaydın kaynağı değildir. Sunucudaki veriye göre ikincildir; çakışma durumunda hangisinin kazanacağı açıkça kararlaştırılır.

Kullanıcı depoyu her an temizleyebilir ve tarayıcı tahliye edebilir. Bu nedenle depolanan veri yeniden üretilebilir olmalıdır; kaybı bir işlevi kalıcı olarak bozmamalıdır.

Özet

  • Oturum depolama sekme ömrüyle, yerel depolama kaynak ömrüyle sınırlıdır; yapılandırılmış depolama dizinli kayıtları eşzamansız arayüzle tutar, çerez ise her istekle sunucuya gider.
  • Dizgi depoları yazılan değeri dizgiye çevirir ve JSON tipleri korumaz; okuma tarafı kaydı beklenen tiplere geri çeviren bir katman gerektirir.
  • Kota aşımı hata atar ve önceki kaydı bozmaz; kotanın büyüklüğü varsayılmaz, hata yakalanır.
  • Yerel depolamadaki değişiklik diğer sekmelere olayla bildirilir; yazan sekme bu olayı görmez ve kendi arayüzünü kendisi günceller.
  • Eşzamanlı erişim ana iş parçacığını bekletir; yazma seyreltilir, büyük veri yapılandırılmış depolamaya taşınır, okuma bir kez yapılıp bellekte tutulur.
  • Kayıtlar sürüm alanı taşır ve okuma yolunda göç zincirinden geçer; gelecek sürümlü ya da çözümlenemeyen içerik yok sayılır.

Sonraki Adım

Bu ders durumu sakladı ama durumun adresle ilişkisine dokunmadı. Süzgeç ölçütü depoda tutulduğunda sayfa yeniden açıldığında geri gelir; ancak o hâlin bir adresi yoktur. Kullanıcı süzülmüş listeyi paylaşamaz, yer imine ekleyemez, geri düğmesiyle önceki süzgece dönemez. Bunlar depolamanın değil gezinmenin sorunlarıdır ve tarayıcının adres çubuğu ile geçmiş yığıtı üzerinden çözülür. Sonraki ders adresin parçalarını, geçmiş yığıtının nasıl değiştirildiğini ve istemci tarafı gezinmenin hangi noktada sunucuyla anlaşmak zorunda olduğunu inceler.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat