---
title: 'Arayüzlerden Veri Toplama'
source: 'https://academia.sh/tr/kurslar/veri-hazirlama/arayuzlerden-veri-toplama'
course: 'Veri Toplama ve Hazırlama'
language: tr
updated: '2026-08-17T18:08:45+00:00'
license: 'CC BY-SA 4.0'
---

# Arayüzlerden Veri Toplama

Aynı arayüzden üç toplama kararı üç farklı küme çıkarıyor ve üç yanıt da tamam dönüyor: dar kapsamlı belirteç 1.400 kayıt toplar ve geçmiş dönemin 477 satırını hiç istemeden dışarıda bırakır, konum tabanlı sayfalama tam 1.877 satır toplar ama 35 kaydı hiç almaz ve 35 kaydı iki kez alır, imleç tabanlı sayfalama 1.877 kaydın tamamını bir kez toplar. Konum tabanlı kararın genel toplamı 38.690,7 m³ ile birebir tutar çünkü atlanan ve yinelenen kayıtlar aynı okuma değerini taşır; hasar yalnız bölge kırılımında 70,5 m³ sapma olarak görünür. Hız sınırı altında sınır yanıtını boş sayfa sayan sıra 600 kayıtta durup 1.277 kaydı kaybeder; sınıra uyan sıra aynı kümeyi 38 istek ve 3 bekleme birimiyle eksiksiz toplar, sayfa boyu 200'e çıkarıldığında bekleme sıfıra iner.

Dosya da veritabanı da veriyi bir kerede verdi. Dosya okunduğunda kaç satır olduğu belliydi,
sorgu koştuğunda küme elde duruyordu. Bir arayüzden veri toplarken bu güvence yoktur: kayıtlar
parça parça gelir, her parçanın sonrakine nasıl bağlandığını çağıran taraf takip eder, kaynak
çağrı sıklığına bir sınır koyar ve hangi kayıtların görülebileceğini belirteç belirler.

Dersin ölçüsü şudur: kaynaktaki satırın kaçı toplanan kümeye girdi. Arayüz tasarımı, sayfalama ve
hız sınırı **sunan** taraf açısından önkoşuldur ve burada tekrarlanmaz; ölçülen tek şey tüketen
tarafın kaç kaydı aldığı, kaç kaydı iki kez aldığı ve kaç kaydı hiç istemediğidir. Teknik sınırdan
önce bir sınır daha vardır: bir kaynaktan veri toplamak, kaynağın kullanım koşulları ve açık bir
erişim izni olmadan teknik olarak mümkün olsa bile hukuki ve mesleki bir sorundur; burada izin
verilmiş bir toplama varsayılır.

- **VT10.** Arayüz kurgudur ve `node` ile modellenir. Ağ çağrısı yapılmaz: istek bir işlev
  çağrısı, yanıt bir nesnedir. **Modeldir.**
- **VT11.** Kaynaktaki küme önceki dersin deposundaki okuma tablosunun aynısıdır — aynı tohum,
  1.877 satır, 38.690,7 m³.
- **VT12.** Arayüz iki sıralama sunar. `okuma` sıralaması okuma değerine göre azalandır ve eşit
  değerlerin iç sırası her istekte yeniden kurulur; `kimlik` sıralaması abone-dönem anahtarına göre
  artandır ve tekildir. Sayfa boyu en çok 200'dür, fazlası sessizce kırpılır.
- **VT13.** Pencere kotası 12 istektir. Kota aşıldığında yanıt kayıt taşımaz ve durumu `sinir`
  olur. Süre gerçek saatle ölçülmez; kaynağın pencere sayısı **bekleme birimi** olarak sayılır.
- **VT14.** İki toplamın aynı sayılması için eşik 0,05 m³'tür, çünkü okumalar bir ondalıkla
  raporlanıyor ve altındaki bir fark yalnızca toplama sırasından gelebilir.

## Arayüzün Verdiği Sayfa

```js
// arayuz.mjs — bolgesel olcum aginin kurgu okuma arayuzu. MODELDIR: ag cagrisi
// yapilmaz, "istek" bir islev cagrisi ve "yanit" bir nesnedir. Kayitlar onceki
// dersin deposundaki 1877 okuma satirinin aynisidir: ayni tohum, ayni sayilar.
export const TOHUM = 4711, BOY = 50, ENBUYUKBOY = 200, KOTA = 12;
const uretec = (t) => { let s = t >>> 0;
  return () => { s = (Math.imul(s, 1103515245) + 12345) >>> 0; return s / 4294967296; }; };

// Okuma desi-m3 tamsayisi olarak tutulur; siralama anahtari tamsayi oldugu
// icin kayan noktali degerler esitlik karsilastirmasina hic girmez.
const r = uretec(TOHUM);
export const KAYIT = [];
for (let i = 0; i < 1400; i++)
  KAYIT.push({ abone_no: 10000 + i, donem: "2024-01", desi: 50 + Math.floor(r() * 321) });
for (let i = 0; i < 477; i++)
  KAYIT.push({ abone_no: 10000 + i, donem: "2024-02", desi: 50 + Math.floor(r() * 321) });
export const BOLGE = ["kuzey", "doğu", "merkez", "güney", "batı"];
export const kimlik = (k) => `${k.abone_no}-${k.donem}`;
export const m3 = (k) => k.desi / 10;
export const bolgesi = (k) => BOLGE[(k.abone_no - 10000) % 5];

// "okuma": okumaya gore azalan, esit degerlerin ic sirasi her istekte yeniden
// kurulur. "kimlik": abone-donem anahtarina gore artan ve tekil.
function sirali(sira, istekNo) {
  if (sira === "kimlik") return [...KAYIT].sort((a, b) => (kimlik(a) < kimlik(b) ? -1 : 1));
  const g = uretec(TOHUM + istekNo * 7919);
  for (let i = 0; i < 5; i++) g();
  return KAYIT.map((k) => ({ k, p: g() })).sort((a, b) => b.k.desi - a.k.desi || a.p - b.p)
    .map((x) => x.k);
}

// Kota bir pencerede kabul edilen istek sayisidir; asildiginda yanit kayit
// tasimaz. Kapsam "gecmis" icermiyorsa kaynak yalnizca acik donemi verir.
export function sunucu({ kota = KOTA } = {}) {
  let istek = 0, pencerede = 0, bekleme = 0;
  return {
    sayim: () => ({ istek, bekleme }),
    bekle() { pencerede = 0; bekleme++; },
    iste({ kapsam = ["okuma"], sira = "kimlik", konum = 0, imlec = null, boy = BOY }) {
      istek++; pencerede++;
      if (pencerede > kota) return { durum: "sinir", kayitlar: [], sonraki: imlec };
      const b = Math.min(boy, ENBUYUKBOY);
      let kume = sirali(sira, istek);
      if (!kapsam.includes("gecmis")) kume = kume.filter((k) => k.donem === "2024-01");
      const bas = imlec === null ? konum : kume.findIndex((k) => kimlik(k) > imlec);
      const dilim = bas < 0 ? [] : kume.slice(bas, bas + b);
      return { durum: "tamam", toplam: kume.length,
        kayitlar: dilim.map((k) => ({ abone_no: k.abone_no, donem: k.donem,
          okuma_m3: m3(k), bolge: bolgesi(k) })),
        sonraki: dilim.length < b ? null : kimlik(dilim[dilim.length - 1]) };
    },
  };
}

if (import.meta.url === `file://${process.argv[1]}`) {
  const s = sunucu({ kota: Infinity }), TAM = ["okuma", "gecmis"];
  const oz = (y) => `durum ${y.durum}, toplam ${y.toplam}, sayfada ` +
    `${y.kayitlar.length} kayit, sonraki imlec ${y.sonraki}`;
  console.log(`tohum ${TOHUM}; ${KAYIT.length} okuma satiri, ` +
    `${KAYIT.reduce((t, k) => t + m3(k), 0).toFixed(1)} m3; boy ${BOY}, ust sinir ` +
    `${ENBUYUKBOY}, kota ${KOTA}`);
  console.log(`tam kapsam: ${oz(s.iste({ kapsam: TAM }))}`);
  console.log(`dar kapsam: ${oz(s.iste({ kapsam: ["okuma"] }))}`);
  const u = () => s.iste({ kapsam: TAM, sira: "okuma", konum: 49, boy: 3 })
    .kayitlar.map((x) => `${x.abone_no}/${x.okuma_m3}`).join("  ");
  console.log(`"okuma" sirasinda 50. kayittan uc tane, iki ayri istekte:\n  ${u()}\n  ${u()}`);
}
```

```
tohum 4711; 1877 okuma satiri, 38690.7 m3; boy 50, ust sinir 200, kota 12
tam kapsam: durum tamam, toplam 1877, sayfada 50 kayit, sonraki imlec 10024-2024-02
dar kapsam: durum tamam, toplam 1400, sayfada 50 kayit, sonraki imlec 10049-2024-01
"okuma" sirasinda 50. kayittan uc tane, iki ayri istekte:
  11319/36  10452/36  10828/36
  11319/36  10018/36  10230/36
```

İki belirteç arasındaki fark üçüncü satırdadır. Dar kapsamlı belirteç `tamam` durumu döndürüyor,
sayfa dolu geliyor, imleç ilerliyor ve hiçbir yerde bir yetki uyarısı yok. Tek işaret `toplam`
alanının 1.877 yerine 1.400 yazmasıdır ve bu sayının doğru olup olmadığı yanıtın içinden
anlaşılamaz. Kapsam hata üreten bir kısıt değildir; kaynağın verdiği kümeyi küçültür ve geri
kalanını hiç var olmamış gibi gösterir.

Son iki satır ikinci sorunu gösteriyor: aynı konumdan aynı üç kayıt iki kez istendi ve gelen
kayıtlar farklı. Sıralama anahtarı okuma değeridir ve o değer tekil değildir — 1.877 kayıt en çok
321 farklı okuma değerine dağılır, yani her değeri ortalama altı kayıt paylaşır. Eşit değerlerin
kendi aralarındaki sırası kaynağın kararına kalmıştır. Sabit görünen tek şey listedeki konumdur,
ama o konumda hangi kaydın durduğu sabit değildir.

## Aynı Arayüz, Üç Toplama Kararı

```js
// topla.mjs — ayni arayuzden uc toplama karari uc farkli kume cikarir. Hicbir
// karar hata almaz, butun yanitlar "tamam" doner. Olculen sey donen kayit
// sayisi degil, kaynaktaki 1877 satirin kacinin toplanan kumeye girdigidir.
import { KAYIT, kimlik, m3, bolgesi, BOLGE, sunucu, BOY } from "./arayuz.mjs";
const TAM = ["okuma", "gecmis"];

// Imlec tabanli sayfalama: her sayfa son anahtardan sonrasini ister, sinir
// kaymaz. T1 bunu dar, T3 tam kapsamli belirtecle yapar.
function imlecli(kapsam) {
  const s = sunucu({ kota: Infinity }), toplanan = []; let imlec = null;
  do { const y = s.iste({ kapsam, sira: "kimlik", imlec, boy: BOY });
    toplanan.push(...y.kayitlar); imlec = y.sonraki; } while (imlec !== null);
  return { toplanan, istek: s.sayim().istek };
}
// T2. Konum tabanli sayfalama, "okuma" sirasinda: anahtar tekil olmadigi icin
// sayfa siniri her istekte baska bir kaydin uzerine duser.
function konumla() {
  const s = sunucu({ kota: Infinity }), toplanan = []; let konum = 0, toplam = Infinity;
  while (konum < toplam) {
    const y = s.iste({ kapsam: TAM, sira: "okuma", konum, boy: BOY });
    toplam = y.toplam; toplanan.push(...y.kayitlar); konum += BOY;
  }
  return { toplanan, istek: s.sayim().istek };
}

// Esik 0.05 m3: okumalar bir ondalikla tutuluyor, altindaki bir fark yalnizca
// toplama sirasindan gelebilir.
const ESIK = 0.05, HEDEF = KAYIT.reduce((t, k) => t + m3(k), 0);
const BHEDEF = new Map(BOLGE.map((b) => [b,
  KAYIT.filter((k) => bolgesi(k) === b).reduce((t, k) => t + m3(k), 0)]));
const sapma = (kume) => {                  // bolge kiriliminin en buyuk sapmasi
  const t = new Map(BOLGE.map((b) => [b, 0]));
  for (const x of kume) t.set(x.bolge, t.get(x.bolge) + x.okuma_m3);
  return Math.max(...BOLGE.map((b) => Math.abs(t.get(b) - BHEDEF.get(b))));
};
const y1 = (h) => h.map((v, j) => (j ? String(v).padStart([0, 7, 7, 7, 8, 9, 11, 11, 9][j])
  : String(v).padEnd(20))).join("");
console.log(y1(["toplama karari", "giren", "istek", "cikan", "farkli", "atlanan",
  "yinelenen", "toplam m3", "sapma m3"]));
const sonuc = {};
for (const [ad, f] of [["T1 dar kapsam", () => imlecli(["okuma"])],
  ["T2 konum tabanli", konumla], ["T3 imlec tabanli", () => imlecli(TAM)]]) {
  const r = sonuc[ad] = f(), farkli = new Set(r.toplanan.map(kimlik)).size;
  r.farkli = farkli; r.toplam = r.toplanan.reduce((t, x) => t + x.okuma_m3, 0);
  console.log(y1([ad, KAYIT.length, r.istek, r.toplanan.length, farkli,
    KAYIT.length - farkli, r.toplanan.length - farkli, r.toplam.toFixed(1),
    sapma(r.toplanan).toFixed(1)]));
}
const t2 = sonuc["T2 konum tabanli"];
console.log(`\nT2 kaynagin bildirdigi ${t2.toplanan.length} satiri tutturdu, ama ` +
  `${KAYIT.length - t2.farkli} kayit hic gelmedi ve ${t2.toplanan.length - t2.farkli} kayit ` +
  `iki kez geldi; atlanan ve yinelenen ayni okuma degerini tasidigi icin genel toplam ` +
  `hedeften ${Math.abs(t2.toplam - HEDEF).toFixed(3)} m3 ayrisiyor (esik ${ESIK}), bolge ` +
  `kiriliminda ise esigin ${Math.round(sapma(t2.toplanan) / ESIK)} kati sapiyor`);
```

```
toplama karari        giren  istek  cikan  farkli  atlanan  yinelenen  toplam m3 sapma m3
T1 dar kapsam          1877     29   1400    1400      477          0    28790.8   2016.2
T2 konum tabanli       1877     38   1877    1842       35         35    38690.7     70.5
T3 imlec tabanli       1877     38   1877    1877        0          0    38690.7      0.0

T2 kaynagin bildirdigi 1877 satiri tutturdu, ama 35 kayit hic gelmedi ve 35 kayit iki kez geldi; atlanan ve yinelenen ayni okuma degerini tasidigi icin genel toplam hedeften 0.000 m3 ayrisiyor (esik 0.05), bolge kiriliminda ise esigin 1410 kati sapiyor
```

Son sütun bölge kırılımındaki en büyük sapmadır.

T1 sayfalamayı doğru tüketiyor ve yine de kümenin dörtte birini kaçırıyor. Yirmi dokuz isteğin
hepsi başarılı döndü, imleç düzgün ilerledi ve döngü doğru yerde durdu. Kayıp tamamen kapsamdan
gelir: geçmiş dönemin **477 satırı** hiç istenmedi ve toplam 28.790,8 m³, yani gerçeğin 0,74 katı
oldu. Bir toplayıcının başarıyla bitmesi, hangi kümeyi görmeye yetkili olduğunu söylemez; kapsam
sorusu toplamadan **önce** sorulur.

T2 dersin asıl bulgusudur. Toplanan satır sayısı **1.877**, yani kaynağın bildirdiği sayıyla
birebir aynı; "beklenen kadar kayıt geldi mi" diye bakan bir denetim burada geçer. Oysa kayıt
kimlikleri sayılınca **35 kayıt hiç gelmemiş**, **35 kayıt iki kez gelmiştir**. Sayfa sınırı, eşit
okumaların iç sırası her istekte yeniden kurulduğu için her seferinde başka bir kaydın üzerine
düştü: biri önceki sayfanın sonuna geçip atlandı, bir başkası sonraki sayfanın başına düşüp
yeniden geldi.

Hatanın ölçüde neden görünmediği, sessiz kaybın en temiz örneğidir. Atlanan ve yinelenen kayıtlar
aynı eşit değer öbeğinden geldiği için **aynı okuma değerini taşır**: toplam 38.690,7 m³'te kalır
ve fark 0,05 m³'lük eşiğin altındadır. Hasar yalnızca kimliğe bağlı bir kırılımda ortaya çıkar —
bölge kırılımında en büyük sapma **70,5 m³**, eşiğin 1.410 katıdır.

T3 aynı 1.877 kaydı 38 istekte bir kez toplar; farkı yaratan tek şey imlecin **tekil ve kararlı**
bir anahtar üzerinde ilerlemesidir. Konum tabanlı sayfalama listenin istekler arasında sabit
kaldığını varsayar ve bu varsayım tekil olmayan bir sıralama anahtarında da, toplama sürerken
kayıt eklenen bir kaynakta da yanlıştır.

## Hız Sınırına Uyan Sıra

```js
// sira.mjs — hiz siniri altinda toplama sirasi. Sinir asma teknigi yoktur:
// olculen sey sinira uyan siranin kac bekleme birimi harcadigi ve sinira
// uymayan siranin kac kaydi disarida biraktigidir. Sure gercek saatle olculmez;
// kaynagin pencere sayisi bir bekleme birimi olarak sayilir.
import { KAYIT, kimlik, sunucu, BOY, ENBUYUKBOY, KOTA } from "./arayuz.mjs";
const TAM = ["okuma", "gecmis"];

// H1: sinir yanitini bos sayfa sayan sira. Kayit gelmeyince "kume bitti" der.
function h1() {
  const s = sunucu(), toplanan = []; let imlec = null, y;
  do { y = s.iste({ kapsam: TAM, imlec, boy: BOY });
    toplanan.push(...y.kayitlar); imlec = y.sonraki;
  } while (y.kayitlar.length > 0 && imlec !== null);
  return { toplanan, ...s.sayim() };
}
// H2: sinira uyan sira. Pencere kotasini kendisi sayar ve dolunca bekler.
function h2(boy) {
  const s = sunucu(), toplanan = []; let imlec = null, pencerede = 0;
  do { if (pencerede === KOTA) { s.bekle(); pencerede = 0; }
    const y = s.iste({ kapsam: TAM, imlec, boy });
    pencerede++; toplanan.push(...y.kayitlar); imlec = y.sonraki;
  } while (imlec !== null);
  return { toplanan, ...s.sayim() };
}
const y1 = (h) => h.map((v, j) => (j ? String(v).padStart([0, 7, 7, 9, 8, 9][j])
  : String(v).padEnd(28))).join("");
console.log(y1(["sira", "giren", "istek", "bekleme", "cikan", "atlanan"]));
for (const [ad, f] of [["H1 sinir yanitini bos sayan", h1], [`H2 sinira uyan, boy ${BOY}`,
  () => h2(BOY)], ["H2 sinira uyan, boy 20", () => h2(20)],
  ["H2 sinira uyan, boy 100", () => h2(100)],
  [`H2 sinira uyan, boy ${ENBUYUKBOY}`, () => h2(ENBUYUKBOY)]]) {
  const r = f(), farkli = new Set(r.toplanan.map(kimlik)).size;
  console.log(y1([ad, KAYIT.length, r.istek, r.bekleme, r.toplanan.length,
    KAYIT.length - farkli]));
}
```

```
sira                          giren  istek  bekleme   cikan  atlanan
H1 sinir yanitini bos sayan    1877     13        0     600     1277
H2 sinira uyan, boy 50         1877     38        3    1877        0
H2 sinira uyan, boy 20         1877     94        7    1877        0
H2 sinira uyan, boy 100        1877     19        1    1877        0
H2 sinira uyan, boy 200        1877     10        0    1877        0
```

H1 hız sınırının en pahalı yanlış anlaşılmasıdır. On üçüncü istek kotayı aştığı için kayıt
taşımadan döndü, toplayıcı bunu "küme bitti" diye okudu ve süreç sıfır çıkış koduyla bitti:
**600 kayıt** toplandı, **1.277 kayıt** dışarıda kaldı. Boş sayfa ile reddedilmiş istek birbirine
benzer; ayırt edilmezse fark yalnızca satır sayısında görünür.

H2 aynı kümeyi eksiksiz topluyor ve bedeli **3 bekleme birimidir**. Son üç satır bu bedelin
nereden geldiğini gösteriyor: bekleme birimi, istek sayısının pencere kotasına bölümünden çıkar.
Sayfa boyu 20 iken 94 istek ve 7 bekleme birimi gerekir; boy 200 iken 10 istek yeter ve toplama
tek pencerede biter. Toplanan kayıt dört satırda da 1.877'dir. **Sayfa boyu bir ayrıntı değil,
doğrudan bir toplama süresi kararıdır** ve kaynağın üst sınırı bu sürenin alt sınırını belirler. VT12'ye göre bu üst sınır aşılmaya çalışıldığında bir hata da
gelmez: 400 kayıtlık sayfa isteyip konumunu 400 ilerleten bir sıra, kaynak 200 verdiği için her
turda 200 kaydı sessizce atlar. Gelen sayfadaki kayıt sayısı sayılır, istenen boy varsayılmaz.

## Özet

- Bir arayüzden toplanan kümede doğrulanacak sayı, dönen kayıt sayısı değil, kaynaktaki satırların
  kaçının kümeye bir kez girdiğidir.
- Dar kapsamlı belirteç hiçbir uyarı üretmeden 477 satırı dışarıda bırakır ve toplam gerçeğin 0,74
  katına iner; kapsam sorusu toplamadan önce sorulur.
- Konum tabanlı sayfalama tekil olmayan bir anahtarda 35 kaydı atlar ve 35 kaydı iki kez toplar;
  satır sayısı ve genel toplam doğru görünür, hasar bölge kırılımında 70,5 m³ sapma olarak çıkar.
- Tekil ve kararlı bir anahtar üzerinde ilerleyen imleç aynı kümeyi 1.877 kayıt, sıfır atlanan ve
  sıfır yinelenen olarak toplar.
- Hız sınırı yanıtını boş sayfa sayan sıra 600 kayıtta durup 1.277 kaydı kaybeder; sınıra uyan sıra
  38 istek ve 3 bekleme birimi harcar ve sayfa boyu 200'e çıkarıldığında bekleme sıfıra iner.

## Sonraki Adım

Bu dersin kaynağı verisini alan için tasarlanmış bir arayüzden verdi: alanların adı belliydi,
sayfa sınırı ilan edilmişti, kayıt sayısı yanıtın içinde yazıyordu. Verinin bir kısmı ise böyle
bir arayüzün arkasında değil, insan gözü için düzenlenmiş bir sayfanın yapısının içinde durur.
Orada alan adı yoktur; yalnızca konum ve görünüm işaretleri vardır ve bunlar bir sözleşme
değildir. Sonraki ders kurgu bir yayın sayfasından ayrıştırma yapar ve tek şeyi ölçer: yapı
değiştiğinde kaç seçici kırılır, kaç satır düşer, kaç satır yanlış alandan okunmuş olarak hayatta
kalır.
