---
title: 'Uzak Durum ve Kilitleme'
source: 'https://academia.sh/tr/kurslar/kod-olarak-altyapi/uzak-durum-ve-kilitleme'
course: 'Kod Olarak Altyapı'
language: tr
updated: '2026-08-17T18:09:07+00:00'
license: 'CC BY-SA 4.0'
---

# Uzak Durum ve Kilitleme

Aynı duruma iki uygulamanın aynı anda yazması: kilitsiz durumda kaybolan yazma ve kayıt dışı kalan kaynak, kilitle bekleme turu, kilit tutma süresinin kuyruğu, kilit bırakılmadan düşen sürecin artık kilidi ve yerel dosya ile uzak deponun kapsama farkı.

Önceki ders durum dosyasını tek bir dizinde, tek bir sürecin elinde bıraktı. O varsayım bir
kişilik bir ekipte doğrudur. İkinci kişi geldiğinde çöker: iki uygulama aynı durumu okur, ikisi
de kaynak oluşturur, ikisi de aynı dosyaya yazar. Bu ders o çakışmayı gerçekten üretir, bedelini
sayar ve kilidin ne kadarını çözdüğünü ölçer.

Eşzamanlı güncellemenin iyimser ve kötümser çözümleri Veri Erişim Katmanı kursunda ölçüldü;
oradaki mekanik burada tekrarlanmaz. Fark taneciktedir: orada kilitlenen bir satırdı ve farklı
satırlara yazan iki istemci hiç karşılaşmıyordu, burada kilitlenen **durum dosyasının tamamıdır**.
Üç ortam tek dosyada duruyorsa deneme ortamına makine ekleyen kişi, üretimin kuyruğunu büyüten
kişiyi bekler; eşzamanlılık tavanı bire iner.

## Uzak Durum Deposu ve İki Uygulayıcı

Aşağıdaki betik tek bir uygulamayı modeller: durumu okur, uygulama süresi kadar çalışır, kendi
kalemlerini ekler ve durumu geri yazar. **DM7.** Uzak durum deposu bir dizinle modellenir; iki
süreç de aynı `depo/durum.json` dosyasına erişir. **DM8.** Uygulama süresi meşgul döngüyle
modellenir; gerçekte o süre sağlayıcıya yapılan çağrılarla geçer.

```js
// uygulayici.mjs — uzak durum deposu uzerinde tek bir uygulamayi modeller.
// modlar: kur | uygula <ad> <adet> <kilit|kilitsiz> <sure_ms> [bekleme_tavani_ms] | ozet
import { readFileSync, writeFileSync, appendFileSync, mkdirSync, rmSync,
  openSync, closeSync, unlinkSync, existsSync } from "node:fs";

const DEPO = "depo", DURUM = "depo/durum.json", KILIT = "depo/kilit.json";
const mod = process.argv[2];
const bekle = (ms) => { const b = Date.now() + ms; while (Date.now() < b); }; // uygulama suresi modeli

if (mod === "kur") {
  rmSync(DEPO, { recursive: true, force: true });
  mkdirSync(DEPO);
  writeFileSync(DURUM, JSON.stringify({ seri: 1, kalemler: [] }));
  process.exit(0);
}

if (mod === "ozet") {
  const d = JSON.parse(readFileSync(DURUM, "utf8"));
  const sahip = {};
  for (const k of d.kalemler) sahip[k[0]] = (sahip[k[0]] ?? 0) + 1;
  const dagilim = Object.keys(sahip).sort().map((a) => `${a}=${sahip[a]}`).join(" ") || "yok";
  const g = existsSync(`${DEPO}/gunluk.txt`) ? readFileSync(`${DEPO}/gunluk.txt`, "utf8") : "";
  for (const s of g.trim().split("\n").sort()) console.log(s);
  const olusan = [...g.matchAll(/(\d+) kalem yazdi/g)].reduce((t, m) => t + Number(m[1]), 0);
  console.log(`durum: ${d.kalemler.length} kalem (${dagilim}), seri ${d.seri}, ` +
    `kilit ${existsSync(KILIT) ? "duruyor" : "yok"}`);
  console.log(`gercekte olusan ${olusan} kaynak, kayitta gorunen ${d.kalemler.length} ` +
    `-> kayit disi ${olusan - d.kalemler.length}`);
  process.exit(0);
}

if (mod !== "uygula") {
  console.log("kullanim: kur | uygula <ad> <adet> <kilit|kilitsiz> <sure_ms> | ozet");
  process.exit(0);
}

const [ad, adet, kilitModu, sureMs] =
  [process.argv[3], Number(process.argv[4]), process.argv[5], Number(process.argv[6])];
let tur = 0;
if (kilitModu === "kilit") {
  const tavan = Date.now() + Number(process.argv[7] ?? 10000);
  for (;;) {
    try {                                        // "wx": dosya varsa hata verir, atomiktir
      const f = openSync(KILIT, "wx");
      writeFileSync(f, JSON.stringify({ sahip: ad, an: "kurgu-zaman-damgasi" }));
      closeSync(f); break;
    } catch {
      tur++;
      if (Date.now() > tavan) { appendFileSync(`${DEPO}/gunluk.txt`,
        `${ad}: kilit alinamadi, ${tur} bekleme turu\n`); process.exit(1); }
      bekle(25);
    }
  }
}
const d = JSON.parse(readFileSync(DURUM, "utf8"));
writeFileSync(`${DEPO}/${ad}-okudu`, "");
if (kilitModu === "kilitsiz")                    // iki uygulama da yazmadan once okusun
  while (!existsSync(`${DEPO}/a-okudu`) || !existsSync(`${DEPO}/b-okudu`)) bekle(5);
bekle(sureMs);
for (let i = 1; i <= adet; i++) d.kalemler.push(`${ad}-kalem-${i}`);
d.seri += 1;
writeFileSync(DURUM, JSON.stringify(d));
if (kilitModu === "kilit") unlinkSync(KILIT);
appendFileSync(`${DEPO}/gunluk.txt`,
  `${ad}: ${adet} kalem yazdi, okudugu seri ${d.seri - 1}, bekleme turu ${tur}\n`);
```

Kurgu ölçüm ağının hazırlık ortamına iki kişi aynı anda kaynak ekliyor: `a` on iki kalem, `b` on
iki kalem. **DM9.** İkisinin de yazmadan önce okuduğundan emin olmak için işaret dosyalarıyla
eşitlenirler; `b`'nin uygulama süresi altmış milisaniye uzun olduğu için son yazan odur.

```sh
# kilitsiz.sh — iki uygulama ayni uzak durumu esanli degistirir, kilit yok
node uygulayici.mjs kur
node uygulayici.mjs uygula a 12 kilitsiz 0 &
node uygulayici.mjs uygula b 12 kilitsiz 60 &
wait
node uygulayici.mjs ozet
```

```
a: 12 kalem yazdi, okudugu seri 1, bekleme turu 0
b: 12 kalem yazdi, okudugu seri 1, bekleme turu 0
durum: 12 kalem (b=12), seri 2, kilit yok
gercekte olusan 24 kaynak, kayitta gorunen 12 -> kayit disi 12
```

Üç sayı üç ayrı zararı gösteriyor. Birincisi **kaybolan yazma**: iki uygulama on ikişer kalem
yazdı, dosyada on iki kalem kaldı; `a`'nın yazması `b` tarafından üzerine yazıldı. İkincisi
**kayıt ile gerçek arasındaki fark**: `a` kaynakları gerçekten oluşturdu, sağlayıcıda yirmi dört
kaynak var, kayıt on iki diyor — on iki kaynak kaydın dışına düştü ve bir daha ne planlanacak ne
silinecek. Üçüncüsü **tutarsız durum**: iki uygulama koştuğu hâlde seri numarası bir arttı;
ikisi de "okuduğu seri 1" diyor. Kayıt, tek bir uygulama olmuş gibi duruyor.

## Kilit

Çözüm, durumu okumadan önce yazma hakkını almaktır. Kilit dosya sisteminin atomik "varsa hata
ver" oluşturmasıyla alınır: iki süreç aynı anda denese bile yalnız biri dosyayı yaratabilir.
Öteki yakalar, yirmi beş milisaniye bekler ve yeniden dener.

```sh
# kilitli.sh — ayni iki uygulama, bu kez durum kilidiyle
node uygulayici.mjs kur
node uygulayici.mjs uygula a 12 kilit 250 &
until [ -f depo/kilit.json ]; do :; done   # a kilidi aldi
node uygulayici.mjs uygula b 12 kilit 250 &
wait
node uygulayici.mjs ozet
```

```
a: 12 kalem yazdi, okudugu seri 1, bekleme turu 0
b: 12 kalem yazdi, okudugu seri 2, bekleme turu 10
durum: 24 kalem (a=12 b=12), seri 3, kilit yok
gercekte olusan 24 kaynak, kayitta gorunen 24 -> kayit disi 0
```

Yirmi dört kalem, seri üç, kayıt dışı sıfır. `b` on bekleme turu harcadı — iki yüz elli
milisaniyelik tutmanın yirmi beş milisaniyelik yoklamaya bölümü — ve **güncel** durumu okudu:
"okuduğu seri 2". Kaybolan yazma ile beklemenin takası açıktır — kilidin ürettiği tek
maliyet `b`'nin iki yüz elli milisaniyesidir, kazandığı ise on iki kaynağın kayda girmesidir.

Kilit dosyası uygulamadan sonra siliniyor: kilit durumun bir parçası değil, duruma erişimin
kapısıdır.

Kilidin okuma **öncesinde** alınması da rastgele bir sıralama değildir. `b` kilidi aldığında
durumu yeniden okur ve seri iki değerini görür; önce okuyup sonra kilit almaya çalışsaydı elinde
seri bir kalırdı ve yazdığı anda `a`'nın on iki kalemini silerdi. Kilitsiz ölçümdeki sonuç, kilit
yanlış yerde alınırsa geri gelir. Sıra tektir: kilit, okuma, uygulama, yazma, bırakma.

## Kilit Süresinin Taranması

Bekleme yalnız kilidin tutulduğu süre kadardır. Uygulama uzarsa arkadaki herkes o kadar bekler.

Ölçüm kuralı: ölçülen süre basılmaz, yalnız bir bant denetiminden geçirilir — beklenenin 0,7 ile
2,0 katı arası. Alt sınır kuyruğun gerçekten oluştuğunu doğrular; üst sınır, yirmi beş
milisaniyelik yoklama tanesi ile süreç başlatma yükü kısa tutmalarda beklenenin katına
yaklaşabildiği için geniş bırakılmıştır.

```js
// tarama.mjs — kilit tutma suresi buyudukce dort uygulamanin kuyrugu nasil uzuyor.
// Basilan her sey kosumdan bagimsizdir: beklenen degerler kuyruk aritmetiginden gelir,
// olculen sure ise yalnizca bir bant denetiminden gecirilip evet/hayir olarak basilir.
import { spawn } from "node:child_process";
import { readFileSync } from "node:fs";

const kos = (...a) => new Promise((c) =>
  spawn("node", ["uygulayici.mjs", ...a], { stdio: "ignore" }).on("exit", c));
const N = 4, YOKLAMA = 25, ALT = 0.7, UST = 2.0;
console.log(`kuyruk aritmetigi (${N} uygulama, kosumdan bagimsiz): ` +
  `toplam bekleme = ${(N * (N - 1)) / 2} x tutma, en uzun = ${N - 1} x tutma`);
console.log(`bant: olculen, beklenenin ${ALT}-${UST} kati arasinda olmali`);

const dogruluk = [];
for (const tutma of [50, 200, 800]) {
  await kos("kur");
  await Promise.all(["a", "b", "c", "d"].map(
    (ad) => kos("uygula", ad, "12", "kilit", String(tutma))));
  const turlar = readFileSync("depo/gunluk.txt", "utf8").trim().split("\n")
    .map((s) => Number(/bekleme turu (\d+)/.exec(s)[1]));
  const d = JSON.parse(readFileSync("depo/durum.json", "utf8"));
  dogruluk.push(`${d.kalemler.length} kalem, seri ${d.seri}`);

  const bekToplam = ((N * (N - 1)) / 2) * tutma, bekEnUzun = (N - 1) * tutma;
  const bantta = (olculen, beklenen) =>
    olculen >= ALT * beklenen && olculen <= UST * beklenen ? "evet" : "hayir";
  console.log(`tutma ${String(tutma).padStart(3)} ms | beklenen toplam ` +
    `${String(bekToplam).padStart(4)} ms, en uzun ${String(bekEnUzun).padStart(4)} ms | ` +
    `olculen bantta: ${bantta(turlar.reduce((x, y) => x + y, 0) * YOKLAMA, bekToplam)} / ` +
    `${bantta(Math.max(...turlar) * YOKLAMA, bekEnUzun)}`);
}
console.log(`dogruluk (kosumdan bagimsiz): ` +
  `${new Set(dogruluk).size === 1 ? `uc tutma suresinde de ${dogruluk[0]}` : dogruluk.join(" | ")}`);
```

```
kuyruk aritmetigi (4 uygulama, kosumdan bagimsiz): toplam bekleme = 6 x tutma, en uzun = 3 x tutma
bant: olculen, beklenenin 0.7-2 kati arasinda olmali
tutma  50 ms | beklenen toplam  300 ms, en uzun  150 ms | olculen bantta: evet / evet
tutma 200 ms | beklenen toplam 1200 ms, en uzun  600 ms | olculen bantta: evet / evet
tutma 800 ms | beklenen toplam 4800 ms, en uzun 2400 ms | olculen bantta: evet / evet
dogruluk (kosumdan bagimsiz): uc tutma suresinde de 48 kalem, seri 5
```

İlk satır ölçümden değil, kuyruğun kendisinden gelir: dört uygulama sıraya girdiğinde birincisi
hiç beklemez, ikincisi bir, üçüncüsü iki, dördüncüsü üç kilit tutma süresi bekler. Toplam altı,
en uzunu üç tutma süresidir. Dersin iddiası bu aritmetiğe dayanır — sıranın uzunluğu uygulama
süresiyle doğru orantılıdır — ve elli milisaniyelik tutmada üç yüz milisaniye olan toplam bekleme,
sekiz yüzde dört bin sekiz yüze çıkar: on altı kat tutma, on altı kat bekleme.

Ölçüm bunu doğrulamakla yetinir: üç tutma süresinde de **evet**. Sapmanın kaynağı sabit bir
yüktür — süreç başlatma ve yoklama tanesi — ve tutma büyüdükçe payı erir; bu koşumda sekiz yüz
milisaniyelik satırda ölçülen, beklenenin yüzde ikisi kadar üstündeydi.

Son satır da koşumdan bağımsızdır: kırk sekiz kalem, seri beş. Kilit doğruluğu bedava vermez,
**sıraya** karşılığında verir; sıranın boyu ise ölçülmeden önce hesaplanabilir.

Buradan işletme kuralı çıkar: uzun uygulamalar bir ekibin tamamını bekletir. Sırayı kısaltmanın
yolu kilidi hızlandırmak değil, kilidin kapsadığı alanı küçültmektir.

## Artık Kilit

Kilidi alan süreç düşerse kilit dosyası ardında kalır. **DM10.** Aşağıdaki düzenek bunu doğrudan
kurar: kilit dosyası yazılır, sahibi hiç yazmadan yok olur.

```js
// artik-kilit.mjs — kilidi birakmadan dusen surecin ardinda biraktigi kilit ve elle acma.
import { writeFileSync, readFileSync, existsSync, unlinkSync, rmSync, mkdirSync } from "node:fs";
import { spawnSync } from "node:child_process";

rmSync("depo", { recursive: true, force: true });
mkdirSync("depo");
writeFileSync("depo/durum.json", JSON.stringify({ seri: 1, kalemler: [] }));
// a kilidi aldi ve yazmadan dustu; kilit dosyasi ardinda kaldi.
writeFileSync("depo/kilit.json", JSON.stringify({ sahip: "a", an: "kurgu-zaman-damgasi" }));

const sonSatir = () => readFileSync("depo/gunluk.txt", "utf8").trim().split("\n").at(-1);
const deneB = () => spawnSync("node",
  ["uygulayici.mjs", "uygula", "b", "12", "kilit", "0", "1000"]).status;

console.log(`artik kilit: ${readFileSync("depo/kilit.json", "utf8")}`);
console.log(`b denedi (cikis ${deneB()}) -> ${sonSatir()}`);
console.log(`durum: ${JSON.parse(readFileSync("depo/durum.json", "utf8")).kalemler.length} kalem`);

unlinkSync("depo/kilit.json");                   // elle acma
console.log(`kilit elle acildi; dosya duruyor mu: ${existsSync("depo/kilit.json")}`);
console.log(`b yeniden denedi (cikis ${deneB()}) -> ${sonSatir()}`);
console.log(`durum: ${JSON.parse(readFileSync("depo/durum.json", "utf8")).kalemler.length} kalem`);
console.log(`kilit kaydindaki alan: 2 (sahip, an) | sahibin hala calisip calismadigini soyleyen alan: 0`);
```

```
artik kilit: {"sahip":"a","an":"kurgu-zaman-damgasi"}
b denedi (cikis 1) -> b: kilit alinamadi, 42 bekleme turu
durum: 0 kalem
kilit elle acildi; dosya duruyor mu: false
b yeniden denedi (cikis 0) -> b: 12 kalem yazdi, okudugu seri 1, bekleme turu 0
durum: 12 kalem
kilit kaydindaki alan: 2 (sahip, an) | sahibin hala calisip calismadigini soyleyen alan: 0
```

`b` kırk iki tur bekledi, bir saniyelik tavanı doldurdu ve sıfır kalem yazabildi. Kilit
kendiliğinden açılmaz; onu bırakacak süreç artık yok. Buradan sonrası kaydın **kendi riskidir**:
altyapıya hiç dokunulmadığı hâlde altyapı yönetilemez durumda ve tek çıkış kilidi elle açmaktır.

Elle açma iki adımda biter, ama kararı verecek kişinin elinde iki alan vardır: sahibin adı ve bir
zaman damgası. Sahibin **hâlâ çalışıp çalışmadığını** söyleyen alan sayısı sıfırdır. Karar yanlış
verilirse — sahip uzun bir uygulamanın ortasındaysa — kilit anlamını yitirir ve ilk bölümdeki
ölçüm geri gelir: on iki kalem kaybolur, on iki kaynak kayıt dışı kalır.

## Yerel Dosya ile Uzak Deponun Farkı

Kilidin çalışabilmesi için herkesin **aynı** duruma bakması gerekir. Durum her uygulayıcının
kendi diskinde durursa kilitlenecek ortak bir şey yoktur.

```js
// yerel.mjs — durumun her uygulayicinin kendi diskinde durmasi: iki kayit, tek gercek.
import { writeFileSync, readFileSync, rmSync, mkdirSync } from "node:fs";

rmSync("yerel", { recursive: true, force: true });
mkdirSync("yerel");
const gercek = [];
for (const ad of ["a", "b"]) {                   // iki kisi kendi kopyasinda calisti
  const kendi = [];
  for (let i = 1; i <= 12; i++) { kendi.push(`${ad}-kalem-${i}`); gercek.push(`${ad}-kalem-${i}`); }
  writeFileSync(`yerel/durum-${ad}.json`, JSON.stringify({ seri: 2, kalemler: kendi }));
}
const oku = (ad) => JSON.parse(readFileSync(`yerel/durum-${ad}.json`, "utf8")).kalemler;
const a = oku("a"), b = oku("b");
const oran = (n) => `%${Math.round((100 * n) / gercek.length)}`;

console.log(`saglayicidaki gercek kaynak: ${gercek.length}`);
console.log(`yerel dosya: 2 -> durum-a ${a.length} kalem (kapsama ${oran(a.length)}), ` +
  `durum-b ${b.length} kalem (kapsama ${oran(b.length)})`);
console.log(`ortak kalem: ${a.filter((k) => b.includes(k)).length}, ` +
  `tamamini bilen dosya: 0, iki dosyanin toplami: ${a.length + b.length}`);
console.log(`sapma taramasi durum-a ile kosarsa kayit disi gorunen: ` +
  `${gercek.filter((k) => !a.includes(k)).length} kalem (hepsi yanlis)`);
console.log(`uzak depo ile: 1 dosya, ${gercek.length} kalem, kapsama %100, yanlis kayit disi 0`);
```

```
saglayicidaki gercek kaynak: 24
yerel dosya: 2 -> durum-a 12 kalem (kapsama %50), durum-b 12 kalem (kapsama %50)
ortak kalem: 0, tamamini bilen dosya: 0, iki dosyanin toplami: 24
sapma taramasi durum-a ile kosarsa kayit disi gorunen: 12 kalem (hepsi yanlis)
uzak depo ile: 1 dosya, 24 kalem, kapsama %100, yanlis kayit disi 0
```

**DM11.** İki kişi hiç haberleşmeden on ikişer kaynak açıyor. Sonuç iki ayrı kayıt: her biri
kendi içinde tutarlı, hiçbiri gerçeğin yarısından fazlasını bilmiyor. Kaybolan yazma yok — çünkü
paylaşılan bir dosya yok — ama fark daha kötüdür: iki kayıt da kendi kapsamında doğru olduğu için
kimse yanlış olduğunu fark etmez. Bir sapma taraması `durum-a` ile koşarsa on iki kalemi kayıt
dışı ilan eder; on ikisi de yanlış alarmdır, kaynakların sahibi vardır, sahibi başka bir dosyadır.

Uzak durum deposunun verdiği tek şey budur: **tek kayıt**. Kapsama yüzde yüze çıkar, kilitlenecek
bir nesne oluşur, yanlış kayıt dışı sayısı sıfırlanır. **DM12.** Depo, dosya oluşturmayı atomik
yapabilen bir hizmet varsayar; bu garanti yoksa kilit de yoktur.

Kazanç bedavaya gelmez ve bedeli kaydın kendi riskidir. İki yerel dosya iki ayrı arıza noktasıydı;
tek uzak kayıt tek arıza noktasıdır. Yarım kalan bir yazma dosyayı bozarsa önceki dersin sayısı
geri gelir: yirmi altı kalem içe aktarmayla kurtarılır, üç kalem kurtarılamaz. Depo sürüm
tutmuyorsa dönülecek bir kopya da yoktur. İkinci risk erişimdir: dosya artık bir kişinin diskinde
değil, ekibin tamamının okuyabildiği bir yerdedir ve önceki derste sayılan seksen alan değerinin
tamamı oradadır.

## Özet

- Kilitsiz iki eşzamanlı uygulama on ikişer kalem yazdı, kayıtta on iki kalem kaldı: bir yazma
  kayboldu, on iki kaynak kayıt dışına düştü, seri iki uygulamaya rağmen bir arttı.
- Atomik dosya oluşturmayla kurulan kilit aynı ölçümde yirmi dört kalem, seri üç ve sıfır kayıt
  dışı üretti; bedeli ikinci uygulamanın on bekleme turudur.
- Dört uygulamanın kuyruğu koşumdan bağımsız olarak altı kilit tutma süresi bekler, en uzun
  bekleyen üç; ölçüm üç tutma süresinde de bandın içinde kaldı, doğruluk değişmedi.
- Kilidi bırakmadan düşen süreç bir saniyelik tavanı dolduran kırk iki bekleme turu ve sıfır
  yazılmış kalem üretti; kilit kaydında sahibin canlı olup olmadığını söyleyen alan yok.
- Durum yerel dosyada tutulunca iki kayıt oluştu, ikisinin de kapsaması yüzde elli kaldı ve bir
  sapma taraması on iki yanlış kayıt dışı üretti; uzak depo kapsamayı yüzde yüze çıkardı.

## Sonraki Adım

Kilit doğruluğu kurtardı ama bir maliyet getirdi: kilidin kapsadığı alan tek bir dosyaysa, o
dosyaya yazan herkes sıraya girer. Ölçüm ağının tamamı tek bir durumda duruyorsa bir güvenlik
kuralını değiştirmek isteyen kişi, veri deposu ölçeklendiren kişinin uygulamasını bekler. Sonraki
ders bu tek dosyayı böler: tek durumla çalışan altyapıda plan süresini ve bir değişikliğin
kaç kalemi kilitlediğini ölçer, aynı ölçümleri bölünmüş hâlde tekrarlar ve bölmenin bedelini —
parçalar arası bağımlılıkları — sayar.
