---
title: 'Olay Sıralama ve Yinelenme'
source: 'https://academia.sh/tr/kurslar/servis-mimarileri/olay-siralama-ve-yinelenme'
course: 'Servis Mimarileri'
language: tr
updated: '2026-08-17T18:06:53+00:00'
license: 'CC BY-SA 4.0'
---

# Olay Sıralama ve Yinelenme

Tüketici tarafında etkisizlik: aynı olay akışı yinelenmiş ve sırası bozulmuş olarak teslim edildiğinde düz tüketicinin ürettiği fazladan yan etki ile yanlış biten görünüm, sürüm damgasının eski iletiyi atması ve etkisiz tüketicinin sorgu olarak ölçülen bedeli.

Önceki ders yayıncı tarafını kapattı: giden kutusu hiçbir iletiyi kaybetmedi, ama 24 iletiyi
iki kez teslim etti. Bu artık yayıncının çözebileceği bir sorun değil. Aktarıcının "gönderdim"
kaydı ile tüketicinin "işledim" kaydı ayrı depolarda durduğu sürece ikisi aynı işleme giremez.
Bedel tüketiciye geçer.

Tüketici tarafında iki ayrı bozulma vardır ve etkileri farklıdır. Birincisi **yinelenme**:
aynı ileti iki kez gelir. İkincisi **sıra bozulması**: geç kalan bir ileti, kendinden sonraki
bir iletinin ardından varır. M19/K05'in `dağıtık doğruluk` konusu tekillik anahtarını ve
anahtar defterini kurmuş ve ölçmüştür; o ölçüm burada tekrarlanmıyor. Anahtar defteri
yinelenmeyi keser ama sıra bozulmasına karşı bir şey yapmaz — bu ders ikisini ayırır ve
ikisini birden karşılayan bir gerçekleştirmenin bedelini ölçer.

## Düzenek

**DT7.** Ağın bozulması tohumlu bir üreteçle modellenir: tohum `20260731`, gecikme oranı
`0,20`, yinelenme oranı `0,15`. Aynı oranlarda teslim akışı bütün koşumlarda birebir aynıdır;
dolayısıyla koşumlar arasındaki fark yalnız **tüketicinin** gerçekleştirmesinden gelir.

**DT8.** Tüketici süreç içi bir modüldür ve kendi deposuna yazar. Sorgu sayısı depodan değil,
kodun kendi sayacından okunur; ölçülen şey tüketicinin **yaptığı iş miktarıdır**.

```sh
# kur.sh — tuketici tarafinin kendi deposu: gorunum tablosu ve yan etki gunlugu
rm -f gorunum.db*
sqlite3 gorunum.db "PRAGMA journal_mode=WAL;
  CREATE TABLE kitap_gorunum (kitap_id INTEGER PRIMARY KEY, durumu TEXT, son_surum INTEGER DEFAULT 0);
  CREATE TABLE bildirim (id INTEGER PRIMARY KEY, kitap_id INTEGER, durumu TEXT);" >/dev/null
```

Katalog her kitap için artan sürümlü olaylar üretir: tek sürümler ödünç verme, çift sürümler
iade. Doğru sıra bu yüzden tek bir sayıdan okunabilir; sıra bozulması da öyle.

```js
// akis.mjs — olay gunlugunu uretir, sonra teslim sirasini bozar ve iletileri yineler
export function uretec(tohum) {                     // tohum gorunur, dizi her kosumda ayni
  let d = tohum >>> 0;
  return () => { d = (Math.imul(d, 1103515245) + 12345) >>> 0; return (d >>> 8) / 16777216; };
}

export function gunluk(kitap, surumSayisi) {        // katalogun urettigi dogru sirali gunluk
  const g = [];
  for (let s = 1; s <= surumSayisi; s++)
    for (let k = 1; k <= kitap; k++)
      g.push({ ileti_id: g.length + 1, kitap: k, surum: s, durumu: s % 2 ? "odunc" : "rafta" });
  return g;
}

export function teslimSirasi(g, tohum, gecikme, yineleme) {
  const zar = uretec(tohum), cikti = [], bekleyen = [];
  for (const o of g) {
    if (zar() < gecikme) { bekleyen.push(o); continue; }        // bu ileti gec kalacak
    cikti.push(o);
    if (bekleyen.length && zar() < 0.10) cikti.push(bekleyen.shift());
    if (zar() < yineleme) cikti.push(o);                        // en az bir kez teslim: kopya
  }
  cikti.push(...bekleyen);                                      // en gec kalanlar en sonda
  return cikti;
}
```

Tüketici tek dosyadır ve iki gerçekleştirme içerir. `--duz` geleni olduğu gibi uygular.
`--damgali` her kitap için son işlediği sürümü saklar ve o sürümden büyük olmayan iletiyi
atar.

```js
// tuketici.mjs — ayni akis, iki tuketici gerceklestirmesi: --duz ve --damgali
import { DatabaseSync } from "node:sqlite";
import { gunluk, teslimSirasi } from "./akis.mjs";

const damgali = process.argv.includes("--damgali");
const KITAP = 10, SURUM = 300, TOHUM = 20260731;
const gecikme = Number(process.env.GECIKME ?? 0.20);   // sirayi bozan iletilerin orani
const yineleme = Number(process.env.YINELEME ?? 0.15);  // ikinci kez teslim edilenlerin orani
const dogru = gunluk(KITAP, SURUM);
const akis = teslimSirasi(dogru, TOHUM, gecikme, yineleme);

const db = new DatabaseSync("gorunum.db");
const okuSurum = db.prepare("SELECT son_surum FROM kitap_gorunum WHERE kitap_id = ?");
const yaz = db.prepare(
  "INSERT INTO kitap_gorunum (kitap_id, durumu, son_surum) VALUES (?,?,?)" +
  " ON CONFLICT(kitap_id) DO UPDATE SET durumu = excluded.durumu, son_surum = excluded.son_surum");
const bildir = db.prepare("INSERT INTO bildirim (kitap_id, durumu) VALUES (?,?)");

let islenen = 0, atilan = 0, sorgu = 0, eskiVaris = 0, atilanKopya = 0, atilanEski = 0;
const gorulen = new Map(), gorulenIleti = new Set();
const t0 = performance.now();
db.exec("BEGIN IMMEDIATE");
for (const o of akis) {
  if ((gorulen.get(o.kitap) ?? 0) > o.surum) eskiVaris += 1;    // sirasi bozulmus varis
  gorulen.set(o.kitap, Math.max(gorulen.get(o.kitap) ?? 0, o.surum));
  if (damgali) {
    const s = okuSurum.get(o.kitap); sorgu += 1;                // ek okuma: surum damgasi
    if (s && s.son_surum >= o.surum) {
      atilan += 1; gorulenIleti.has(o.ileti_id) ? (atilanKopya += 1) : (atilanEski += 1);
      continue;
    }
  }
  gorulenIleti.add(o.ileti_id);
  yaz.run(o.kitap, o.durumu, o.surum); sorgu += 1;
  bildir.run(o.kitap, o.durumu); sorgu += 1;
  islenen += 1;
}
db.exec("COMMIT");
const sure = performance.now() - t0;

const sonDurum = new Map(dogru.map((o) => [o.kitap, o.durumu]));
let yanlis = 0;
for (const r of db.prepare("SELECT kitap_id, durumu FROM kitap_gorunum").all())
  if (sonDurum.get(r.kitap_id) !== r.durumu) yanlis += 1;
const yanEtki = db.prepare("SELECT COUNT(*) c FROM bildirim").get().c;

console.log(`${damgali ? "damgali" : "duz    "} (gecikme=${gecikme} yineleme=${yineleme})` +
  ` | gunluk=${dogru.length} teslim=${akis.length}` +
  ` sirasi bozuk varis=${eskiVaris} | islenen=${islenen} atilan=${atilan} (kopya=${atilanKopya} eski=${atilanEski})` +
  ` yan etki=${yanEtki} yanlis biten kitap=${yanlis}/${KITAP}` +
  ` | sorgu=${sorgu} sure=${sure.toFixed(1)} ms (bu kosumda)`);
```

## İki Bozulma, İki Farklı Zarar

İlk iki koşum bozulmaları tek tek uygular; üçüncüsü ikisini birden verir; dördüncüsü aynı
akışı sürüm damgalı tüketiciye verir.

```sh
sh kur.sh; GECIKME=0 node tuketici.mjs --duz
sh kur.sh; YINELEME=0 node tuketici.mjs --duz
sh kur.sh; node tuketici.mjs --duz
sh kur.sh; node tuketici.mjs --damgali
```

```
duz     (gecikme=0 yineleme=0.15) | gunluk=3000 teslim=3452 sirasi bozuk varis=0 | islenen=3452 atilan=0 (kopya=0 eski=0) yan etki=3452 yanlis biten kitap=0/10 | sorgu=6904 sure=2.8 ms (bu kosumda)
duz     (gecikme=0.2 yineleme=0) | gunluk=3000 teslim=3000 sirasi bozuk varis=585 | islenen=3000 atilan=0 (kopya=0 eski=0) yan etki=3000 yanlis biten kitap=4/10 | sorgu=6000 sure=2.3 ms (bu kosumda)
duz     (gecikme=0.2 yineleme=0.15) | gunluk=3000 teslim=3359 sirasi bozuk varis=585 | islenen=3359 atilan=0 (kopya=0 eski=0) yan etki=3359 yanlis biten kitap=4/10 | sorgu=6718 sure=2.5 ms (bu kosumda)
damgali (gecikme=0.2 yineleme=0.15) | gunluk=3000 teslim=3359 sirasi bozuk varis=585 | islenen=2415 atilan=944 (kopya=359 eski=585) yan etki=2415 yanlis biten kitap=0/10 | sorgu=8189 sure=3.4 ms (bu kosumda)
```

Birinci satır yalnız yinelenmedir. Üç bin olay 3452 kez teslim edildi ve tüketici 3452 yan
etki üretti: **452 fazladan bildirim**. Buna karşılık son durum doğru — çünkü aynı iletinin
ikinci kez uygulanması görünümü değiştirmez, yalnız bildirimi tekrarlar. Yinelenmenin zararı
**yan etkidedir**, durumda değil.

İkinci satır yalnız sıra bozulmasıdır. Teslim sayısı günlüğe eşit, yan etki sayısı doğru
(3000), ama 585 ileti kendinden sonrakinin ardından vardı ve **10 kitabın 4'ü yanlış durumda
bitti**. Sıra bozulmasının zararı **durumdadır**, yan etki sayısında değil.

İki bozulma birlikte verildiğinde ikisi de görünür: 3359 yan etki ve yine 4 yanlış kitap.
Anahtar defteriyle yinelenme kesilse bile bu 4 kitap yanlış kalırdı; defter aynı iletiyi ikinci
kez tanır, geç kalan **başka** bir iletiyi tanımaz.

## Sürüm Damgası

Dördüncü satır aynı akışı sürüm damgalı tüketiciye verir. Sonuç: yanlış biten kitap **0**, yan
etki 3359'dan 2415'e iniyor, 944 ileti atılıyor.

Atılanların ayrımı önemlidir: `944 = 359 kopya + 585 eski`. Kopyalar aynı iletinin ikinci
gelişidir; atılmaları saf kazançtır. **Eskiler ise gerçek olaylardır** — sadece geç kalmış
oldukları için üzerlerine daha yeni bir sürüm yazılmıştır. Sürüm damgası onları da atar,
çünkü bir sürümü geriye almanın anlamı yoktur.

Bu yüzden yan etki sayısı 3000 değil 2415'tir. Görünüm doğru bitti, ama 585 gerçek durum
değişikliği için bildirim **hiç gönderilmedi**. Bu, tek bir mekanizmayla iki bozulmayı birden
karşılamanın bedelidir: sıra bozulmasını düzelten kural, geç kalan bilgiyi de siler.

Ayrım, tüketicinin ne yaptığına bağlıdır. Görünüm tablosu gibi yalnız **son durumu** taşıyan
bir tüketici için bu doğru davranıştır; atılan ileti hiçbir şey kaybettirmez. Her geçişe tepki
vermek zorunda olan bir tüketici — gecikme ücreti hesaplayan ya da her hareketi deftere yazan
bir servis — için 585 kayıp gerçek bir kayıptır ve o tüketici sürüm damgasıyla değil, her
iletiyi ayrı ayrı tanıyan bir kayıtla çalışmak zorundadır.

## Etkisiz Tüketicinin Bedeli

Bedelin koşumdan bağımsız ölçüsü sorgu sayısıdır. Düz tüketici teslim başına iki yazma yapar:
`3359 × 2 = 6718`. Damgalı tüketici her teslim için önce bir okuma yapar (`3359`), sonra yalnız
işlediklerine iki yazma (`2415 × 2 = 4830`); toplam `8189`. Fark `+1471` sorgu, yani teslim
başına yaklaşık `0,44` ek sorgu. Süre farkı da aynı yöndedir (2,5 ms'e karşı 3,4 ms), ama asıl
sabit maliyet okumadır: **her ileti, atılacak olsa bile önce sorgulanır.**

Depo tarafındaki maliyet de görünürdür: `kitap_gorunum` tablosuna `son_surum` sütunu eklendi ve
her yazmada güncellendi. Sürüm damgası, tüketicinin şemasına giren kalıcı bir alandır; iletiyi
üreten servisin de her olayda artan bir sürüm yayımlamayı **sözleşmesinin parçası** yapması
gerekir.

**Neyi ucuzlattı:** sürüm damgası, yinelenmiş ve sırası bozulmuş bir akıştan doğru son durumu
çıkardı — 4 yanlış kitap 0'a indi — ve fazladan bildirimleri sıfırladı. Yayıncı tarafında
hiçbir garanti yükseltilmeden yapıldı; giden kutusunun "en az bir kez" teslimi olduğu gibi
kaldı.

**Neyi pahalılaştırdı:** tüketici şemasına bir sütun, her iletiye bir okuma (`+1471` sorgu)
eklendi; üstelik yayıncı artık her olayda artan bir sürüm yayımlamakla yükümlü.

**Hangi yeni arıza kipi doğdu:** *sessiz atma*. Tüketici 585 gerçek olayı hiçbir hata
üretmeden yuttu. Görünüm doğru olduğu için bu kayıp durumdan anlaşılmaz; yalnız yan etki
sayısı ile günlük uzunluğu karşılaştırılırsa görünür. Sürüm alanı yanlış üretilirse — örneğin
iki servis aynı kayda ayrı sayaçlarla yazarsa — atılan iletiler artar ve kimse fark etmez.

## Özet

- Yinelenme ile sıra bozulması farklı zararlar verir: yalnız yinelenmede yan etki 3000 yerine
  3452 çıktı ama son durum doğruydu; yalnız sıra bozulmasında yan etki sayısı doğruydu ama 10
  kitabın 4'ü yanlış durumda bitti.
- Bu yüzden yinelenmeyi kesen bir kayıt tek başına yetmez; geç kalan ileti farklı bir iletidir
  ve tanınamaz.
- Sürüm damgalı tüketici aynı akıştan doğru son durumu çıkardı (0/10 yanlış) ve 944 iletiyi
  attı: 359 kopya, 585 eski.
- Atılan eskiler gerçek olaylardır; yan etki 3000 yerine 2415 kaldı. Son durumu taşıyan bir
  tüketici için bu doğru, her geçişe tepki veren bir tüketici için kayıptır.
- Bedel teslim başına yaklaşık 0,44 ek sorgu (`6718` yerine `8189`), tüketici şemasında bir
  sütun ve yayıncı sözleşmesinde artan sürüm yükümlülüğüdür.

## Sonraki Adım

Bu dersin çözümü sessizce bir söz verdi: yayıncı her olayda artan bir `surum` alanı
yayımlayacak, tüketici o alanı okuyacak. Aynı söz önceki derslerde de verilmişti — `ileti_id`
kalıcı olacak, `kitap` alanı hep aynı anlamı taşıyacak. Bu alanlar artık iki ayrı ekibin
birlikte tuttuğu bir sözleşmedir ve sistemin doğruluğu ona bağlıdır. Servisler ayrı dağıtım
birimleri olduğu için sözleşme tek bir anda değişemez: bir süre iki sürüm birlikte koşar.
Sonraki ders sözleşmenin nasıl değişebileceğini ele alır — alan ekleme ve kaldırma sırası,
iki sürümün birlikte koşması ve tüketicinin korunması.
