---
title: 'Bilet ve Stok Sistemi'
source: 'https://academia.sh/tr/kurslar/vaka-calismalari/bilet-ve-stok-sistemi'
course: 'Vaka Çalışmaları'
language: tr
updated: '2026-08-17T18:10:56+00:00'
license: 'CC BY-SA 4.0'
---

# Bilet ve Stok Sistemi

Sayılan şeyin geri getirilemez olduğu vaka: kontenjanın tepe hızda 0,179 saniyede tükenmesi, korumasız oku–karar ver–yaz akışının 16 eşzamanlı işçi altında kontenjanın 5,59 katını satması, iyimser ve kötümser kilitlemenin fazla satış ile reddedilen geçerli istek cinsinden ölçülmesi ve rezervasyon penceresinin boşa tutulan kalem-saniyeye etkisi.

Önceki vakada bir sayaç bir eşiği koruyordu ve eşik aşıldığında kaybedilen şey bir istekti;
istemci yeniden dener. Bu vaka aynı sayma sorununu, sayılan şeyin **geri getirilemez** olduğu
yerde sorar. Sınırlı sayıda koltuk vardır ve sayacın iki fazla saymasının karşılığı reddedilmiş
bir istek değil, satılmış ama var olmayan bir kalemdir.

İki fark buradan çıkar. Birincisi, aşımın birimi değişir: hız sınırlayıcıda 1,1 kat kabul
edilebilirdi, burada bir tanenin bile karşılığı yoktur. İkincisi, kaynağı tutmak ile satmak
ayrı işlemlerdir ve aralarında bir pencere vardır.

## Kısıtlar ve Kapsam

İşlevsel gereksinimler: kalan kontenjanı bildirmek, bir rezervasyon açmak, rezervasyonu satışa
çevirmek, süresi dolan rezervasyonu serbest bırakmak, iptali işlemek.

Tasarlanmayan işler: ödeme yürütümü (sonraki vakanın konusu), koltuk yerleşiminin gösterimi,
fiyatlandırma ve ikincil satış.

İşlevsel olmayan gereksinimler eşikle ve kaynağıyla yazılır: **fazla satış sıfırdır** (kaynak:
satılan her kalemin karşılığının bulunması yükümlülüğü), **stok varken reddedilen geçerli istek
sıfırdır** (kaynak: kontenjan bitmeden alıcının eli boş dönmemesi), rezervasyon kararı 100
milisaniyeyi geçmez (kaynak: açılış anında sayfanın yanıtsız kalmaması).

## Varsayımlar ve Ölçek

| Kod | Varsayım | Değer | Gerekçe |
|---|---|---|---|
| BS1 | kontenjan | 5.000 kalem | tek bir satışa açılan sınırlı kaynak |
| BS2 | açılıştaki istek | 200.000 / 10 saniye | açılış anına yığılan talep |
| BS3 | istek başına adet | 1,4 | tek ve çift alımların ortalaması |
| BS4 | rezervasyon penceresi | 120 saniye | ödeme adımına bırakılan süre |
| BS5 | pencereye göre satışa dönme | 0,45 / 0,65 / 0,72 | 30, 120 ve 300 saniyelik pencerede tamamlanan ödeme payı |
| BS6 | eşzamanlı yazma işçisi | 16 | kontenjan satırına aynı anda dokunan iş sayısı |
| BS7 | stok kaydı | 64 bayt | kalem kimliği, durum, rezervasyon damgası |
| BS8 | iyimser çakışmada deneme bütçesi | 3 | yeniden denemenin üst sınırı |

```js
// bilet/olcek.mjs — BS varsayim tablosundan cikan kabaca buyukluk hesabi
const BS = { kontenjan: 5_000, acilisIstek: 200_000, acilisSaniye: 10, adet: 1.4,
  pencere: 120, donme: { 30: 0.45, 120: 0.65, 300: 0.72 }, isci: 16, kayitBayt: 64 };

const tepe = BS.acilisIstek / BS.acilisSaniye;
const talep = BS.acilisIstek * BS.adet;
const tukenme = BS.kontenjan / (tepe * BS.adet);
console.log(`tepe istek/s                ${tepe}`);
console.log(`acilistaki toplam talep     ${talep} kalem`);
console.log(`talep / kontenjan           ${(talep / BS.kontenjan).toFixed(1)} kat`);
console.log(`kontenjanin tukenme suresi  ${tukenme.toFixed(3)} s (tepe hizda)`);
console.log(`tukenene kadar gelen istek  ${Math.round(tepe * tukenme)}`);
console.log(`stok tablosu                ${((BS.kontenjan * BS.kayitBayt) / 1e3).toFixed(1)} KB`);
console.log(`rezervasyon sayisi          ${(BS.kontenjan / BS.adet).toFixed(0)} (kontenjan / adet)`);

console.log(`\n${"pencere".padStart(9)}${"donme orani".padStart(13)}${"ilk turda satilan".padStart(19)}` +
  `${"geri donen kalem".padStart(18)}${"bosa kalem-saniye".padStart(19)}`);
for (const [p, d] of Object.entries(BS.donme)) {
  const geri = BS.kontenjan * (1 - d);
  console.log(`${`${p} s`.padStart(9)}${d.toFixed(2).padStart(13)}${(BS.kontenjan * d).toFixed(0).padStart(19)}` +
    `${geri.toFixed(0).padStart(18)}${(geri * Number(p)).toFixed(0).padStart(19)}`);
}
const d120 = BS.donme[BS.pencere], d30 = BS.donme[30];
console.log(`\npencere 30 s -> 120 s: ilk turda satilan ${(BS.kontenjan * d30).toFixed(0)} -> ` +
  `${(BS.kontenjan * d120).toFixed(0)} (+${((d120 - d30) * BS.kontenjan).toFixed(0)} kalem)`);
console.log(`ayni degisim bosa kalem-saniyeyi ${(BS.kontenjan * (1 - d30) * 30).toFixed(0)} -> ` +
  `${(BS.kontenjan * (1 - d120) * BS.pencere).toFixed(0)} yapiyor (` +
  `${((BS.kontenjan * (1 - d120) * BS.pencere) / (BS.kontenjan * (1 - d30) * 30)).toFixed(2)} kat)`);
```

```
tepe istek/s                20000
acilistaki toplam talep     280000 kalem
talep / kontenjan           56.0 kat
kontenjanin tukenme suresi  0.179 s (tepe hizda)
tukenene kadar gelen istek  3571
stok tablosu                320.0 KB
rezervasyon sayisi          3571 (kontenjan / adet)

  pencere  donme orani  ilk turda satilan  geri donen kalem  bosa kalem-saniye
     30 s         0.45               2250              2750              82500
    120 s         0.65               3250              1750             210000
    300 s         0.72               3600              1400             420000

pencere 30 s -> 120 s: ilk turda satilan 2250 -> 3250 (+1000 kalem)
ayni degisim bosa kalem-saniyeyi 82500 -> 210000 yapiyor (2.55 kat)
```

Bu sayılar **hesap** sınıfındadır. Üçü tasarımı belirliyor. Birincisi, talep kontenjanın 56
katıdır ve kontenjan tepe hızda **0,179 saniyede** tükenir; tasarımın çözdüğü sorun kapasite
değil, 0,179 saniyelik bir aralıkta doğru sayabilmektir. İkincisi, bu aralıkta yalnız 3571
istek gelir; geri kalan 196.429 isteğin işi reddedilmektir ve bu iş kontenjan satırına hiç
dokunmadan yapılmalıdır. Üçüncüsü, rezervasyon penceresi iki yönlü bir maliyet taşır: 30
saniyeden 120 saniyeye çıkarmak ilk turda satılanı 1000 kalem artırıyor ama boşa tutulan
kalem-saniyeyi 2,55 katına çıkarıyor.

## Fazla Satış Ölçülüyor

Ölçüm süreç içi bir modeldir: gerçek iş parçacığı, veritabanı ya da kilit yoktur; 16 işçi
tohumlu bir zamanlayıcı tarafından adım adım ilerletilir ve çekişme, bir işçinin okuması ile
yazması arasına başka işçilerin girmesinden doğar. Üç düzen karşılaştırılır: **korumasız**
oku–karar ver–yaz akışı, sürüm sütununa bakan **iyimser** kilitleme ve kararı kilit altında
veren **kötümser** kilitleme. Mekanikleri Veri Erişim Katmanı kursunun İyimser ve Kötümser
Kilitleme dersinde kurulmuştu; burada ölçülen şey bu vakadaki fazla satış sayısıdır.

```js
// bilet/cekisme.mjs — sinirli kaynakta cekismenin surec ici modeli. Gercek is parcacigi,
// veritabani ya da kilit yoktur: 16 isci bir zamanlayici tarafindan adim adim ilerletilir.
const KONTENJAN = 5_000, ISTEK = 20_000, DENEME = 3;   // BS1, BS8

function uretec(tohum) {                     // 32 bitlik dogrusal esitlikli uretec
  let s = tohum >>> 0;
  return () => { s = (Math.imul(s, 1103515245) + 12345) >>> 0; return s / 4294967296; };
}

export function kos({ duzen, tohum, ISCI = 16 }) {   // BS6: eszamanli yazma iscisi
  const rnd = uretec(tohum);
  const ortak = { stok: KONTENJAN, surum: 0, kilitli: false };
  const kuyruk = Array.from({ length: ISTEK }, () => (rnd() < 0.6 ? 1 : 2));  // BS3: ortalama 1,4
  const isci = Array.from({ length: ISCI }, () => ({ faz: "bos" }));
  const s = { satilan: 0, red: 0, redGecerli: 0, cakisma: 0, bekleme: 0, adim: 0 };
  let sira = 0;

  while (sira < ISTEK || isci.some((w) => w.faz !== "bos")) {
    const w = isci[Math.floor(rnd() * ISCI)];
    s.adim += 1;
    if (w.faz === "bos") {
      if (sira >= ISTEK) continue;
      w.adet = kuyruk[sira]; sira += 1; w.deneme = 0; w.faz = "oku";
    }
    if (w.faz === "oku") {
      if (duzen === "kotumser") {
        if (ortak.kilitli) { s.bekleme += 1; continue; }
        ortak.kilitli = true;
      }
      w.okunanStok = ortak.stok; w.okunanSurum = ortak.surum; w.faz = "yaz";
      continue;
    }
    if (w.okunanStok < w.adet) {                       // okunan stok yetmiyor
      s.red += 1;
      if (ortak.stok >= w.adet) s.redGecerli += 1;
      if (duzen === "kotumser") ortak.kilitli = false;
      w.faz = "bos"; continue;
    }
    if (duzen === "iyimser" && ortak.surum !== w.okunanSurum) {   // surum degismis
      s.cakisma += 1; w.deneme += 1;
      if (w.deneme >= DENEME) {
        s.red += 1;
        if (ortak.stok >= w.adet) s.redGecerli += 1;
        w.faz = "bos";
      } else w.faz = "oku";
      continue;
    }
    ortak.stok = w.okunanStok - w.adet;
    ortak.surum += 1;
    s.satilan += w.adet;
    if (duzen === "kotumser") ortak.kilitli = false;
    w.faz = "bos";
  }
  s.fazlaSatis = Math.max(0, s.satilan - KONTENJAN);
  s.kalanStok = ortak.stok;
  return s;
}

console.log(`model: kontenjan ${KONTENJAN}, 16 isci, ${ISTEK} istek, deneme butcesi ${DENEME}, tohum 20260730`);
console.log(`\n${"duzen".padEnd(12)}${"satilan".padStart(9)}${"fazla satis".padStart(13)}` +
  `${"kalan stok".padStart(12)}${"cakisma".padStart(9)}${"red (gecerli)".padStart(15)}` +
  `${"bekleme adimi".padStart(15)}${"toplam adim".padStart(13)}`);
const R = {};
for (const d of ["korumasiz", "iyimser", "kotumser"]) {
  const r = (R[d] = kos({ duzen: d, tohum: 20260730 }));
  console.log(`${d.padEnd(12)}${String(r.satilan).padStart(9)}${String(r.fazlaSatis).padStart(13)}` +
    `${String(r.kalanStok).padStart(12)}${String(r.cakisma).padStart(9)}` +
    `${`${r.red} (${r.redGecerli})`.padStart(15)}${String(r.bekleme).padStart(15)}${String(r.adim).padStart(13)}`);
}

const k = R.korumasiz, i = R.iyimser, p = R.kotumser;
console.log(`\nkorumasiz duzen kontenjani ${(k.satilan / KONTENJAN).toFixed(3)} kat satti; ` +
  `fazla satis / kontenjan = %${((100 * k.fazlaSatis) / KONTENJAN).toFixed(2)}`);
console.log(`iyimser: fazla satis ${i.fazlaSatis}, ${i.cakisma} cakisma, ` +
  `${i.redGecerli} gecerli istek deneme butcesi bitince reddedildi`);
console.log(`kotumser: fazla satis ${p.fazlaSatis}, gecerli ret ${p.redGecerli}, ` +
  `adim sayisi iyimserin ${(p.adim / i.adim).toFixed(2)} kati`);

console.log(`\n${"eszamanli isci".padStart(15)}${"iyimser cakisma".padStart(17)}${"gecerli ret".padStart(13)}` +
  `${"kotumser bekleme".padStart(18)}`);
for (const n of [2, 4, 16]) {
  const a = kos({ duzen: "iyimser", tohum: 20260730, ISCI: n });
  const b = kos({ duzen: "kotumser", tohum: 20260730, ISCI: n });
  console.log(`${String(n).padStart(15)}${String(a.cakisma).padStart(17)}${String(a.redGecerli).padStart(13)}` +
    `${String(b.bekleme).padStart(18)}`);
}
```

```
model: kontenjan 5000, 16 isci, 20000 istek, deneme butcesi 3, tohum 20260730

duzen         satilan  fazla satis  kalan stok  cakisma  red (gecerli)  bekleme adimi  toplam adim
korumasiz       27963        22963        1798        0          0 (0)              0        40047
iyimser          5000            0           0    10038   16397 (2409)              0        55267
kotumser         5000            0           0        0      16427 (0)         304276       344446

korumasiz duzen kontenjani 5.593 kat satti; fazla satis / kontenjan = %459.26
iyimser: fazla satis 0, 10038 cakisma, 2409 gecerli istek deneme butcesi bitince reddedildi
kotumser: fazla satis 0, gecerli ret 0, adim sayisi iyimserin 6.23 kati

 eszamanli isci  iyimser cakisma  gecerli ret  kotumser bekleme
              2             1519          130             20043
              4             3577          520             60109
             16            10038         2409            304276
```

Bu sayılar **ölçüm** sınıfındadır; tohum 20260730 ile yeniden üretilir.

Birinci satır korumasız düzenin ölçüsüdür ve iki sayı taşır. Kontenjanın 5,593 katı satıldı,
fazla satış 22.963 kalem. İkinci sayı daha öğreticidir: **kalan stok 1798 görünüyor.** Sayaç
yalnız yanlış değil, yanlış yönde yanlıştır — sistem hâlâ satacak yer olduğunu bildiriyor.
Hiçbir istek reddedilmedi, çünkü hiçbir işçi stoku yetersiz görmedi; kaybedilen yazmalar
sayacı geri yükseltti.

İkinci ve üçüncü satır iki korumanın da fazla satışı sıfırladığını gösteriyor. Ayrım öteki
sütunlardadır. İyimser kilitleme 10.038 çakışma üretiyor ve deneme bütçesi biten **2409 geçerli
isteği** reddediyor: stok dururken eli boş dönen alıcı. Bu, ikinci işlevsel olmayan
gereksinimin ihlalidir. Kötümser kilitleme geçerli ret üretmiyor, karşılığında 304.276 bekleme
adımı ve iyimserin 6,23 katı toplam adım harcıyor.

Dördüncü tablo iki maliyetin de aynı değişkene bağlı olduğunu gösteriyor: eşzamanlı işçi
sayısı. İşçi 2 iken iyimser kilitlemenin geçerli reddi 130'a, kötümserin bekleme adımı
20.043'e iniyor. Çekişme bir sabit değil, bir tasarım parametresidir.

## Tasarım ve Elenen Alternatif

Kontenjan satırı **kötümser kilitle** korunur; seçim gerekçesi 2409'a karşı 0 geçerli
rettir. Kilidin tutulma süresi bir kira olarak verilir ve Uygulama Katmanı ve Servis Etkileşimi
kursunun Lider Seçimi dersindeki kira mekaniği burada kilit sahipliğine uygulanır; asılı kalan
kilit kira dolunca düşer.

Rezervasyon penceresi 120 saniyedir ve süresi dolan rezervasyonu serbest bırakan denetçi aynı
kursun Zamanlayıcı–Aracı–Denetçi dersindeki denetçidir. Açılış anındaki 20.000 istek/s'nin
kontenjan satırına ulaşmaması için önde iki katman durur: önceki vakanın hız sınırlayıcısı ve
aynı kursun Kuyruk Tabanlı Yük Dengeleme dersindeki kuyruk. Önceki vakada kuyruk **bilerek
kullanılmamıştı**, çünkü orada sözleşme reddetmekti; burada sözleşme sıraya koymaktır ve aynı
kalıp bu yüzden yer değiştirir.

Tekillik anahtarı kapsamı `(alıcı, kontenjan, istemci istek kimliği)` üçlüsüdür; alıcının
yeniden denemesi ikinci bir rezervasyon açmaz.

**Bilerek kullanılmayan kalıp.** Kontenjan sayacının parçalara bölünmesi (Veri Katmanı
Ölçekleme, Parçalama) kullanılmaz: bu, önceki vakada ölçülen bölünmüş kotanın aynısıdır ve
başka parçada kalem dururken ret üretir — orada bu, sınırının tam üstündeki istemcinin yüzde
4,17'sini reddetmişti.

Elenen alternatif iyimser kilitlemedir ve eleme sayısı 2409 geçerli rettir. Kazandığı da
yazılır: 6,23 kat daha az adım, yani kilit tutmayan bir yazma yolu. Hangi kısıt değişirse
kazandığı da bellidir: eşzamanlı işçi 16 yerine 2 olsaydı geçerli ret 130'a, kontenjanın yüzde
2,6'sına inerdi ve deneme bütçesi 3'ten 6'ya çıkarılarak bu da düşürülebilirdi.

## Arıza Davranışı ve Feda Edilen

Kilidi tutan işçi düşerse kontenjan satırı kira süresi boyunca kilitli kalır ve bu süre
boyunca hiçbir satış yapılamaz; kira 0,179 saniyelik tükenme penceresinden uzunsa açılışın
tamamı durur. Kuyruk tarafında ise Dayanıklılık ve Güvenilirlik kursunun Zarif Bozulma dersi
işler: kontenjan tükendiğinde istekler kontenjan satırına hiç dokunmadan reddedilir ve bu,
196.429 isteğin normal yoludur.

Feda edilen tek cümlededir: bu tasarım fazla satışı sıfırlamak için verimi feda eder — 304.276
bekleme adımı ve 6,23 kat işlem, tek bir kalemin bile fazladan satılmaması karşılığında
ödenir.

## Özet

- Talep kontenjanın 56 katı ve kontenjan tepe hızda 0,179 saniyede tükeniyor; bu aralıkta
  yalnız 3571 istek geliyor, kalan 196.429 isteğin işi reddedilmektir.
- Korumasız düzen kontenjanın 5,593 katını sattı, fazla satış 22.963; üstelik sayaç sonunda
  1798 kalan stok gösteriyor, yani yanlış yönde yanlış.
- İyimser kilitleme fazla satışı sıfırladı ama 10.038 çakışma üretti ve deneme bütçesi biten
  2409 geçerli isteği reddetti.
- Kötümser kilitleme fazla satışı ve geçerli reddi birden sıfırladı; bedeli 304.276 bekleme
  adımı ve iyimserin 6,23 katı toplam adımdır.
- İki maliyet de eşzamanlı işçi sayısına bağlı: 16 yerine 2 işçide geçerli ret 2409'dan 130'a,
  bekleme adımı 304.276'dan 20.043'e iniyor.
- Rezervasyon penceresini 30 saniyeden 120 saniyeye çıkarmak ilk turda satılanı 1000 kalem
  artırıyor, boşa tutulan kalem-saniyeyi 2,55 katına çıkarıyor.

## Sonraki Adım

Rezervasyon bir söz verdi ama sözü tutan adım bu vakanın dışında bırakıldı: ödemenin kendisi.
O adım iki tarafı olan bir işlemdir ve iki taraf birbirinin kayıtlarını göremez. Sonraki vaka
bu ayrımı ele alır. Sorulacak sayı iki kaydın birbirinden ne kadar ayrıştığıdır: eşleşmeyen
kayıt sayısı, farkın nereden doğduğu ve geç gelen bir onayın bu farkı ne kadar sürede
kapattığı.
