IlmHamroh
JavaScript Full-stack/31-qism. Prodakshn backend va AI integratsiya4/34-dars22 daqiqa
Mundarija (34)

Yuqori mavjudlik (HA) va falokatdan tiklash: failover, RPO, RTO va tiklash mashqlari

Qisqacha: Yuqori mavjudlik (high availability, HA) — server yiqilganda ham baza ishlashda davom etishi: replika yangi asosiy serverga aylanadi — zaxiraga o'tish (failover). Falokatdan tiklash (disaster recovery, DR) — hamma narsa buzilganda ma'lumotni zaxira nusxadan (backup) qaytarish. Ikkalasini ikki raqam o'lchaydi: RPO — qancha ma'lumot yo'qoladi, RTO — qancha vaqt ishlamaymiz. Kanon primary'ni "o'ldirib", replikani qo'lda ko'tardi: tasdiqlangan yozuvlardan 0 tasi yo'qoldi, yozish ~2.2–2.4 soniyada tiklandi. Sinxron replikatsiya yo'qotishni kafolatlab yopadi, lekin har commit'ni taxminan 35 % sekinlashtirdi.

Bu darsda

  • HA va DR farqini, RPO va RTO ni aniq misol bilan tushuntirasiz.
  • Failover qadamlarini va pg_promote() ni kanon tajribasida kuzatasiz.
  • Asinxron replikatsiyada nega ma'lumot yo'qolishi mumkinligini model bilan hisoblaysiz.
  • Sinxron replikatsiyaning narxini o'lchov bilan baholaysiz.
  • Patroni va managed HA nima qilishini, split brain xavfini bilasiz.
  • Tiklash mashqini (PITR) rejalashtirib, natijani o'lchay olasiz.

Oldin bilishingiz kerak: Replikatsiya va read replica'lar, Backup va restore, Postgres arxitekturasi va asosiy sozlamalar, Monitoring va favqulodda vaziyatlar.

Oldingi darsda replikani o'qishni bo'lish uchun ishlatdik. Endi uning ikkinchi, muhimroq vazifasini ko'ramiz: primary yiqilganda o'rnini egallash. Backup va restore darsida o'rgangan PITR va RPO/RTO bu yerda yangi darajada qaytadi — endi o'lchov bilan.

1. Nega bu kerak?

Juma, kechki 20:00. vazifalar-api serveridagi disk ishdan chiqdi. Postgres to'xtadi. Endi ikki savol bor:

  1. Qachon yana ishlaymiz? Foydalanuvchilar ilovani ochadi va xato ko'radi. Har daqiqa — yo'qotilgan ishonch.
  2. Nima yo'qoldi? Kecha 03:00 dagi zaxira bor. 03:00 dan 20:00 gacha qo'shilgan vazifalar-chi?

Birinchi savolga HA javob beradi: tayyor turgan replika bir necha soniyada asosiy serverga aylanadi. Ikkinchisiga — replikatsiya rejimi va DR: zaxira, WAL arxivi va tiklash rejasi.

O'xshatish: kasalxona. Elektr o'chsa, bir necha soniyada zaxira generator yonadi — bu HA. Lekin bino yonib ketsa, generator yordam bermaydi. Bemorlar kartalarining boshqa binodagi nusxasi kerak — bu DR. Kasalxonaga ikkalasi ham kerak.

2. Asosiy atamalar

2.1 HA va DR

Yuqori mavjudlik (HA) Falokatdan tiklash (DR)
Qaysi xavfdan server, disk, jarayon yiqilishi butun ma'lumot markazi, xato DELETE, shifrlovchi virus
Vosita replika + failover zaxira, WAL arxivi, boshqa hududdagi nusxa
Vaqt soniyalar–daqiqalar daqiqalar–soatlar
Xato DELETE dan himoya qiladimi yo'q — u replikaga ham ketadi ha — PITR bilan

Oxirgi qatorni yodda saqlang. Replika — zaxira emas. Kimdir DELETE FROM vazifalar ni WHEREsiz bajarsa, replika uni 5 ms da sodiqlik bilan takrorlaydi. Xato buyruqdan faqat vaqtga qaytish qutqaradi.

2.2 RPO va RTO

Bu ikki atamani Backup va restore darsida uchratgansiz. Qisqa takror:

  • RPO (Recovery Point Objective) — falokatda ko'pi bilan qancha ma'lumot yo'qotishga tayyormiz. Vaqtda ("oxirgi 5 daqiqa") yoki yozuvlarda o'lchanadi.
  • RTO (Recovery Time Objective) — falokatdan keyin qancha vaqtda yana ishlashimiz kerak.

Bu darsda ikkalasini o'lchaymiz: RPO — "tasdiqlangan, lekin yangi serverda yo'q" yozuvlar soni; RTO — primary o'lgan lahzadan yozish qayta ishlagan lahzagacha.

2.3 "To'qqizlar"

Mavjudlikni foizda aytish odat: "99.9 % mavjudlik". Bu raqamning ortida ruxsat etilgan to'xtash vaqti turadi. Brauzerda hisoblang:

js
const DAQIQA_YIL = 365 * 24 * 60;

function ruxsatEtilganTushish(foiz) {
  const daqiqa = DAQIQA_YIL * (100 - foiz) / 100;
  return daqiqa >= 60
    ? `${(daqiqa / 60).toFixed(1)} soat`
    : `${daqiqa.toFixed(1)} daqiqa`;
}

for (const foiz of [99, 99.9, 99.95, 99.99, 99.999]) {
  console.log(`${foiz} % → yiliga ${ruxsatEtilganTushish(foiz)}`);
}

Konsolda:

text
99 % → yiliga 87.6 soat
99.9 % → yiliga 8.8 soat
99.95 % → yiliga 4.4 soat
99.99 % → yiliga 52.6 daqiqa
99.999 % → yiliga 5.3 daqiqa

"Uchta to'qqiz" (99.9 %) — yiliga ~8.8 soat. Bu bitta muvaffaqiyatsiz kechki deploy va bitta disk almashtirish. "To'rtta to'qqiz" — yiliga 52 daqiqa: buni qo'lda tiklash bilan ushlab bo'lmaydi, avtomatik failover kerak. Har qo'shimcha to'qqiz narxni bir necha baravar oshiradi. Shuning uchun birinchi savol — "bizga qaysi raqam haqiqatan kerak?".

Tekshirib ko'ring: vazifalar-api uchun 99.9 % va RPO = 0 talab qilindi. Kecha 03:00 dagi pg_dump yetadimi?

Javob

Yo'q, ikkala jihatdan ham. RPO: kunlik dump bilan 24 soatgacha ma'lumot yo'qoladi, 0 emas. RTO: dumpdan tiklash katta bazada soatlab davom etadi, 99.9 % esa yiliga 8.8 soatga ruxsat beradi — bitta tiklash hammasini yeydi. Kerak: replika (RTO) + sinxron replikatsiya yoki WAL arxivi (RPO). Dump esa DR uchun qoladi.

3. Failover: kanon tajribasi

3.1 Tajriba sxemasi

Kanon alohida juftlik qurdi: primary (ha1) va streaming replika (ha2), ikkalasi Postgres 18, bitta kompyuterdagi ikki konteyner. Keyin falokatni o'ynadi:

  1. Yozuvchi dastur har 5 ms da primary'ga bitta INSERT yuboradi. U faqat COMMIT tasdiqlangan id'larni yozib boradi — ya'ni foydalanuvchiga "saqlandi" deb javob berilganlarni.
  2. 2 soniyadan keyin primary "o'ldiriladi": docker kill — elektr o'chgandek, ogohlantirishsiz.
  3. Replika qo'lda asosiy serverga aylantiriladi: pg_promote().
  4. Yozuvchi uzilishni sezadi va har 100 ms da yangi serverga ulanishga urinadi. Ulangach, pg_is_in_recovery() f ekanini tekshirib, yozishni davom ettiradi.
  5. Oxirida tasdiqlangan id'lar yangi primary'dagi id'lar bilan solishtiriladi.
sequenceDiagram
  participant Y as Yozuvchi
  participant P as Primary (ha1)
  participant R as Replika (ha2)
  Y->>P: INSERT, har 5 ms
  P->>R: WAL oqimi
  Note over P: docker kill
  Y--xP: ulanish uzildi
  Note over R: pg_promote()
  Y->>R: qayta ulanish, INSERT

Diagrammaning o'rtasiga qarang: primary o'lgan lahzadan yozuvchi yangi serverga yozgan lahzagacha — bu RTO. Shu oraliqda replikaga yetib bormagan har tasdiqlangan yozuv — RPO.

Kanon skriptidan ikki asosiy buyruq (konteyner nomlari qisqartirilgan):

bash
docker kill ha1
docker exec ha2 psql -U postgres -Atqc "SELECT pg_promote()"

pg_promote() — replikaga "tiklanishni to'xtat, endi sen asosiy server" degan buyruq. Sukut bo'yicha u promote tugaguncha kutadi va t qaytaradi.

3.2 Natija

Tajriba ikki marta yurdi: asinxron va sinxron replikatsiya bilan. Kanon chiqishi:

text
=== B) failover (asinxron): yozuvchi har 5 ms INSERT; 2 s da primary docker kill; replika qo'lda promote
replikatsiya: ha2 streaming async
tasdiqlangan: 285; replikada (promote dan keyin): 285; YO'QOLGAN tasdiqlangan (RPO): 0
kill → promote: 2375 ms; kill → yozuvchi yangi primary'ga yozdi (RTO, qo'lda): 2224 ms
=== B) failover (sinxron): yozuvchi har 5 ms INSERT; 2 s da primary docker kill; replika qo'lda promote
replikatsiya: ha2 streaming sync
tasdiqlangan: 230; replikada (promote dan keyin): 230; YO'QOLGAN tasdiqlangan (RPO): 0
kill → promote: 2536 ms; kill → yozuvchi yangi primary'ga yozdi (RTO, qo'lda): 2401 ms

Jadvalga yig'amiz:

Rejim Tasdiqlangan Yo'qolgan (RPO) RTO (qo'lda)
Asinxron 285 0 2 224 ms
Sinxron 230 0 2 401 ms

3.3 Natijani halol o'qish

Uch narsaga e'tibor bering.

RPO 0 — bu isbot emas. Asinxron rejimda ham birorta yozuv yo'qolmadi. Lekin bu ikki konteyner bitta kompyuterda edi: lag millisekundning bir qismi. Haqiqiy tarmoqda va yuk ostida lag kattaroq, va asinxron rejim yo'qotishni kafolatlab yopmaydi. Bu sinov faqat "shu sharoitda yo'qotish kuzatilmadi" deydi.

RTO deyarli butunlay qo'lda bajarilgan ish. Skript primary'ni o'ldirgach yarim soniya kutdi, keyin promote buyrug'ini yubordi. Bizning taxminimizcha, ~2.2 soniyaning katta qismi — shu kutish, buyruq yuborish va konteynerga kirish. Haqiqiy hayotda eng uzoq qism boshqa: "primary haqiqatan o'ldimi yoki tarmoq bir zum uzildimi?" degan savolga javob topish. Bu o'lchovda yo'q, chunki biz o'zimiz o'ldirdik va bilardik.

"kill → promote" RTO'dan katta chiqdi (2 375 > 2 224 ms). Mantiqsiz tuyuladi: yozuvchi promote'dan oldin yozdimi? Sabab — o'lchash usulida. Skript "promote" vaqtini docker exec … pg_promote() buyrug'i to'liq qaytgandan keyin yozdi. Postgres esa promote'ni ichida allaqachon tugatgan, yozuvchi ham ulanib ulgurgan — docker exec jarayoni hali yopilayotgan edi. O'lchov vositasining o'z kechikishi raqamga qo'shildi. Saboq: o'lchovdagi har raqam qayerdan olinganini bilish kerak.

Sinxron rejimda 2 soniyada kamroq yozuv tasdiqlandi (230 < 285) — har commit replikani kutdi. Bu narxni keyingi bo'limda aniq ko'ramiz.

Tekshirib ko'ring: Kanon yozuvchisi faqat COMMIT tasdiqlangan id'larni hisobladi. Nega "yuborilgan" INSERT lar emas?

Javob

Chunki RPO — va'da qilingan ma'lumot yo'qolishi. Primary o'lgan paytda yuborilgan, lekin javob olinmagan INSERT foydalanuvchiga "saqlandi" demagan — ilova xatoni ko'rdi va qayta urinishi mumkin (Idempotency-Key darsidagi g'oya). Yomoni — "saqlandi" deb javob berilgan, lekin yangi serverda yo'q yozuv. Faqat shular haqiqiy yo'qotish.

4. Asinxron va sinxron replikatsiya

4.1 Asinxron rejimda yo'qotish qayerdan keladi

Asinxron rejimda primary commit'ni o'zining WAL'iga yozadi va darhol "tayyor" deydi. WAL replikaga biroz keyin ketadi. Primary shu oraliqda o'lsa, replikaga yetmagan commit'lar yo'qoladi. Taxminiy formula oddiy: yo'qotish ≈ lag × yozish tezligi.

Modelda ko'ramiz. Primary har 5 ms da bitta yozuvni tasdiqlaydi va 2 000 ms da o'ladi:

js
// Primary har `qadam` ms da bitta yozuvni tasdiqlaydi.
// `olim` ms da u yiqiladi. Replika `lag` ms orqada.
function failover({ qadam, olim, lag }) {
  let tasdiqlangan = 0;
  let yoqolgan = 0;
  for (let t = 0; t < olim; t += qadam) {
    tasdiqlangan++;
    if (t + lag > olim) yoqolgan++; // replikaga yetmagan
  }
  return { tasdiqlangan, yoqolgan };
}

for (const lag of [0.5, 50, 500]) {
  const r = failover({ qadam: 5, olim: 2000, lag });
  console.log(`lag ${lag} ms →`, r);
}

Konsolda:

text
lag 0.5 ms → { tasdiqlangan: 400, yoqolgan: 0 }
lag 50 ms → { tasdiqlangan: 400, yoqolgan: 9 }
lag 500 ms → { tasdiqlangan: 400, yoqolgan: 99 }

Birinchi qator — kanon tajribasi sharoiti: lag yarim millisekund, yo'qotish 0. Ikkinchi qator — boshqa ma'lumot markazidagi replika: 50 ms lag, 9 ta yozuv yo'qoldi. Uchinchisi — katta import paytidagi lag (oldingi darsda 200 000 qatorli tranzaksiyada replika 10.9 MB orqada qolgan edi): 99 ta. Model soddalashtirilgan: haqiqiy lag doim o'zgarib turadi. Lekin manzara aniq — asinxron rejimdagi RPO lagga bog'liq va u noldan katta bo'lishi mumkin.

4.2 Sinxron replikatsiya

Sinxron rejimda primary commit'ni replika tasdiqlamaguncha "tayyor" demaydi. Sozlash — primary'da ikki parametr (Postgres arxitekturasi darsidagi synchronous_commit bilan tanishsiz). Bizning oddiy serverimizda ularning sukut qiymatlari (biz ishga tushirdik, PostgreSQL 17.6):

sql
SHOW synchronous_commit;
SHOW synchronous_standby_names;
text
 synchronous_commit
--------------------
 on
(1 row)

 synchronous_standby_names
---------------------------

(1 row)

synchronous_standby_names bo'sh — sinxron replika yo'q, demak synchronous_commit = on faqat lokal diskni kutadi. Kanon unga replika nomini yozdi:

sql
ALTER SYSTEM SET synchronous_standby_names = 'ha2';
SELECT pg_reload_conf();

Shundan keyin pg_stat_replication replikani sync deb ko'rsatdi (yuqoridagi chiqishdagi ha2 streaming sync). synchronous_commit ning qiymatlari "replika qayergacha yetsin" degan savolga javob beradi:

Qiymat Commit qachon tasdiqlanadi
off lokal WAL diskka yozilishini ham kutmaydi
local faqat lokal diskda
remote_write replika WAL'ni oldi (OS xotirasiga yozdi)
on replika WAL'ni o'z diskiga yozdi
remote_apply replika WAL'ni qo'lladi — undan o'qisa ko'rinadi

remote_apply — oldingi darsdagi read-your-writes muammosini ham yopadigan qiymat.

4.3 Narxi

Kanon bitta-qatorli INSERT ni 500 marta har rejimda o'lchadi (oldingi darsdagi replika juftligida):

text
5) asinxron (sukut)                             p50 7.64 ms, p99 12.66 ms
   pg_stat_replication: [ { application_name: 'r1', sync_state: 'sync' } ]
5) sinxron, synchronous_commit=on (WAL replikada) p50 10.50 ms, p99 18.56 ms
5) sinxron, remote_apply (replikada ko'rinadi)  p50 10.27 ms, p99 14.08 ms

Ikkinchi qatorni adashtirmang: u asinxron o'lchovdan keyin chiqqan. Skript birinchi o'lchovni tugatgach synchronous_standby_names = 'r1' ni yoqdi va replika holatini tekshirdi — sync_state: 'sync'. Keyingi ikki qator shu sinxron rejimda o'lchandi. Bu juftlikda replika nomi r1 (oldingi darsdagi), failover tajribasida esa ha2 edi.

Grafikda — commit vaqtining medianasi asinxron rejimga nisbatan:

Bitta commit vaqti (mediana): asinxron = 100 %
  • asinxron100 %
  • sinxron, on137 %
  • sinxron, remote_apply134 %

Manba: IlmHamroh kanon o'lchovi, 2026-11-02: PostgreSQL 18.6, primary va replika — bitta mashinadagi ikki Docker konteyneri; 500 ta INSERT, p50: 7.64 / 10.50 / 10.27 ms; bitta mashina, bir vaqtda

Sinxron rejim har commit'ni taxminan 35 % sekinlashtirdi. on va remote_apply deyarli teng chiqdi — bu tajribada replika WAL'ni yozish bilan qo'llash orasidagi farq shovqin ichida. Muhimi: bu raqam bitta kompyuterda, lag deyarli nol bo'lganda. Replika boshqa shaharda bo'lsa, har commit'ga tarmoqning ikki tomonlama yo'li (round trip) qo'shiladi — 10, 30, 100 ms.

4.4 Sinxron rejimning yashirin xavfi

Sinxron replika bitta bo'lsa va u o'chsa nima bo'ladi? Primary commit'ni tasdiqlay olmaydi va yozish osilib qoladi — Postgres hujjati buni ochiq ogohlantiradi. RPO = 0 ni HA hisobiga sotib oldingiz: endi ikki serverdan biri o'lsa ham yozish to'xtaydi.

Yechim — replikalar to'plamidan "istalgan bittasi" yoki "ko'pchiligi":

sql
ALTER SYSTEM SET synchronous_standby_names = 'ANY 1 (r1, r2)';

Bu yozuv uchun hujjatga ko'ra ikki replikadan istalgan bittasining tasdig'i yetadi. Biri o'lsa, ikkinchisi bilan ishlash davom etadi. Kanon buni sinamadi — u bitta replika bilan ishladi.

Tekshirib ko'ring: To'lov tizimi va vazifalar-api — qaysi biriga sinxron replikatsiya, qaysi biriga asinxron mos?

Javob

To'lov — sinxron: "pul o'tdi" deb javob berilgan yozuv yo'qolishi qabul qilinmaydi, 35 % sekin commit esa to'lov tezligida sezilmaydi. Yaxshisi — ikki replika va ANY 1, toki bitta replika o'chsa to'lovlar to'xtamasin. vazifalar-api — asinxron yetarli: failover paytida oxirgi bir necha millisekunddagi vazifa yo'qolishi noxush, lekin halokatli emas, commit tezligi esa saqlanadi.

5. Avtomatik failover: Patroni va managed HA

5.1 Qo'lda failover nega yetmaydi

Kanon tajribasida inson (skript) "primary o'ldi" deb bildi va promote qildi. Haqiqatda kechasi 03:00 da buni kim qiladi? Navbatchi dasturchi uyg'onguncha 20 daqiqa o'tadi — "to'rtta to'qqiz" yillik byudjeti yarmi ketdi.

Avtomatlashtirishning qiyin qismi — promote emas, qaror: primary haqiqatan o'ldimi? Ehtimol tarmoq 3 soniyaga uzilgan va primary tirik. Shunda replikani ko'tarsangiz, ikkita primary paydo bo'ladi.

5.2 Split brain

Split brain (miyaning bo'linishi) — ikki server bir vaqtda o'zini asosiy deb hisoblaydi va ikkalasi yozuv qabul qiladi. Ba'zi foydalanuvchilar eskisiga, ba'zilari yangisiga yozadi. Tarix ikkiga bo'linadi va ularni qayta birlashtirishning avtomatik yo'li yo'q.

Bu — HA tizimlaridagi eng qo'rqinchli xato. Himoya — fencing (o'rab qo'yish): yangi primary ko'tarilishidan oldin eskisi albatta to'xtatiladi yoki tarmoqdan uziladi. Eski primary qaytsa, u yangisining replikasi sifatida qayta ulanadi. Buning uchun Postgres'da pg_rewind vositasi bor — u eski serverning bo'lingan tarixini yangisiga moslaydi. Kanon pg_rewind ni sinamadi.

5.3 Patroni

Patroni — Postgres uchun eng mashhur ochiq manbali HA vositasi. Quyidagilar rasmiy hujjat bo'yicha (sinalmagan, 2026-11):

flowchart TB
  D[("etcd / Consul: lider kaliti")]
  P1["Patroni + Postgres 1 (lider)"] --> D
  P2["Patroni + Postgres 2"] --> D
  P3["Patroni + Postgres 3"] --> D
  H["HAProxy / pooler"] --> P1
  I["Ilova"] --> H

Diagrammani pastdan o'qing. Ilova to'g'ridan serverga emas, oraliq HAProxy (yoki ulanish hovuzi) ga ulanadi — u har doim joriy liderni biladi. Har Postgres yonida Patroni agenti ishlaydi. Ular umumiy kelishuv xizmatida (DCS — etcd, Consul yoki ZooKeeper) bitta "lider kaliti" uchun bellashadi. Kalitning amal qilish muddati (TTL) bor; lider uni muntazam yangilab turadi. Lider o'lsa yoki uzilsa, kalit muddati tugaydi va eng oldinda turgan replika uni egallab, promote qilinadi. Eski lider kalitni yo'qotgani uchun o'zini to'xtatadi — fencing shu.

Muqobillar ham bor: repmgr, pg_auto_failover, Kubernetes uchun CloudNativePG. G'oya hammasida bir: tashqi "hakam" kim lider ekanini hal qiladi.

5.4 Managed HA

Ko'p jamoalar bu ishni o'zlari qilmaydi. AWS RDS (Multi-AZ), Google Cloud SQL (HA), Azure, Neon, Supabase kabi managed xizmatlar (Managed va serverless Postgres) tayyor turgan replikani (standby) boshqa ma'lumot markazida ushlaydi va failover'ni o'zi bajaradi. Siz bitta katakchani belgilaysiz va har oy ko'proq to'laysiz.

Lekin javobgarlik to'liq ketmaydi:

  • Failover paytida ulanishlar uziladi — ilovangiz qayta ulanishni bilishi shart.
  • Failover bir necha soniyadan bir necha daqiqagacha davom etishi mumkin — aniq raqamni provayder hujjatidan o'qing va o'zingiz sinab ko'ring.
  • Xato DELETE dan HA himoya qilmaydi — PITR va zaxira baribir kerak.

6. Falokatdan tiklash: PITR mashqi

6.1 Zaxira tiklab ko'rilmaguncha zaxira emas

Backup va restore darsida PITR'ni o'rgandik: asosiy nusxa + WAL arxivi + maqsad vaqti. U yerdagi asosiy qoida — zaxirani muntazam tiklab ko'rish. Kanon 31-qismda shu mashqni yangi juftlikda qaytadan o'tkazdi — bu safar vaqtni o'lchab:

  1. Primary'da WAL arxivi yoqildi (archive_mode = on, archive_command arxiv papkasiga nusxalaydi).
  2. 1 000 ta "eski" yozuv → pg_basebackup bilan asosiy nusxa.
  3. 500 ta "muhim" yozuv — ular faqat WAL arxivida.
  4. Maqsad vaqti eslab qolindi, keyin falokat: DELETE FROM hodisa (WHEREsiz) va yana 10 ta "keyin" yozuvi.
  5. Yangi konteynerda asosiy nusxa + restore_command + recovery_target_time bilan tiklash.

Tiklash sozlamalari (kanon skriptidan, maqsad vaqti o'rniga belgi):

text
restore_command = 'cp /arxiv/wal/%f %p'
recovery_target_time = '<maqsad vaqti>'
recovery_target_action = 'promote'

6.2 Natija

text
=== A) PITR: WAL arxivi + asosiy nusxa (pg_basebackup) → DELETE dan oldingi vaqtga tiklash
asosiy nusxa (pg_basebackup): 122939 ms, 47M
primary: DELETE dan keyin qator soni = 10; maqsad vaqti = 2026-11-02 15:21:53.299484+00; arxivda WAL fayllar: 5
PITR tiklash (konteyner + WAL qayta o'ynatish): 4642 ms; tiklangan bazada: 1000 eski + 500 muhim + 0 keyin
  log: LOG:  starting point-in-time recovery to 2026-11-02 15:21:53.299484+00
  log: LOG:  recovery stopping before commit of transaction 757, time 2026-11-02 17:19:31.502312+00
  log: LOG:  selected new timeline ID: 2
  log: LOG:  archive recovery complete

Asosiysi — uchinchi qator: 1000 eski + 500 muhim + 0 keyin. Asosiy nusxadagi 1 000 qator ham, faqat WAL'da bo'lgan 500 qator ham qaytdi. DELETE va undan keyingi 10 qator — yo'q: tiklash ular commit qilinishidan oldin to'xtadi. Butun tiklash 4.6 soniya.

Log qatorlari Backup va restore darsidagidek o'qiladi: "aniq vaqtga tiklash boshlandi" → "757-tranzaksiya commit'idan oldin to'xtadi" (bu aynan DELETE) → "yangi vaqt chizig'i (timeline) 2".

6.3 Ishonchsiz raqamni tanib olish

Ikkinchi qatorga qayting: asosiy nusxa 122 939 ms — ikki daqiqadan ko'p. 47 MB lik baza uchun bu g'alati. Log'dagi vaqtlarga qarang: maqsad vaqti 15:21:53, DELETE commit'i esa 17:19:31. Skript esa maqsad vaqtini yozgandan keyin atigi bir soniya kutib DELETE qilgan edi. Ikki soatlik sakrash. Kanon yozuvlariga ko'ra, tajriba paytida kompyuter uyqu rejimiga ketgan va soat oldinga sakragan.

Bu yugurishdagi zaxira vaqti shuning uchun ishonchsiz — biz uni RTO hisobiga kiritmaymiz. Tiklash natijasi (1000 + 500 + 0) esa ishonchli: u vaqtga emas, WAL tarkibiga bog'liq. Saboq tiklash mashqlari uchun ham muhim: har o'lchovda "sharoit normalmidi?" deb log'ni tekshiring.

6.4 Tiklash mashqi rejasi

Ko'p jamoalar zaxirani faqat falokat kunida birinchi marta tiklaydi — va o'shanda zaxira ishlamasligini bilib oladi. Tiklash mashqi (DR drill) — falokatni rejali ravishda o'ynash. Minimal reja:

Qadam Nima o'lchanadi
Oxirgi asosiy nusxa va WAL arxivini toping arxivda uzilish yo'qmi (pg_stat_archiver)
Alohida serverda tanlangan vaqtga tiklang tiklash vaqti — haqiqiy RTO
Tiklangan bazada nazorat so'rovlarini bajaring qatorlar soni, oxirgi yozuv vaqti — haqiqiy RPO
Ilovani tiklangan bazaga ulab ko'ring rollar, parollar, kengaytmalar joyidami
Natijani yozib qo'ying keyingi mashq bilan solishtirish uchun

To'rtinchi qator ko'p unutiladi: Backup va restore darsida rollar pg_dump ga tushmasligini ko'rgandik. Tiklangan bazaga ilova ulana olmasa — RTO hali tugamagan.

Bu rejaning yozma shakli — runbook (amallar qo'llanmasi): kechasi 03:00 da charchagan odam ham qadamma-qadam bajara oladigan hujjat. Monitoring signallari va navbatchilik — Monitoring va favqulodda vaziyatlar darsida.

7. Ko'p uchraydigan xatolar

Noto'g'ri tasavvur Haqiqat
"Replikamiz bor — zaxira kerak emas" Xato DELETE replikaga ham ketadi; PITR kerak
"Asinxron replika — RPO 0" RPO ≈ lag × yozish tezligi; kafolat yo'q
"Sinxron replika qo'ydik — endi HA" Yagona sinxron replika o'chsa, yozish osiladi
"Failover'ni Patroni qiladi — ilova bilmaydi" Ulanishlar uziladi; qayta ulanish va qayta urinish kerak
"Zaxira har kuni olinadi — demak bor" Tiklab ko'rilmagan zaxira — taxmin
"Eski primary qaytdi — yana ishlataveramiz" Split brain xavfi; avval fencing, keyin pg_rewind

8. Mashqlar

Mashqlar kurs/mashqlar/31/04-ha/ da. 2- va 3-mashq yechimlari Node 24.21.0 da tekshirilgan (biz ishga tushirdik).

1-mashq (oson): bo'shliqlarni to'ldiring

  • Replikani asosiy serverga aylantiruvchi funksiya — pg_()
  • "Qancha ma'lumot yo'qotishga tayyormiz" savoliga javob beradigan raqam —
  • Replika WAL'ni qo'llaguncha commit'ni tasdiqlamaydigan synchronous_commit qiymati —
  • Ikki server bir vaqtda o'zini asosiy deb hisoblashi — split
  • Kanon o'lchovida sinxron rejim commit medianasini taxminan necha foiz sekinlashtirdi? —
Yechim
  • promote — pg_promote(); replika tiklanishni to'xtatib, yozishni qabul qila boshlaydi.
  • RPO — Recovery Point Objective; RTO esa "qancha vaqt ishlamaymiz".
  • remote_apply — read-your-writes'ni ham yopadi.
  • brain — split brain; himoyasi — fencing.
  • 35 — 7.64 ms dan 10.27–10.50 ms ga (134–137 %).

2-mashq (o'rta): oylik to'xtash byudjeti

Jamoa vazifalar-api uchun "99.9 % mavjudlik" va'da qildi. oyHisoboti(foiz, hodisalar) funksiyasini yozing: 30 kunlik oyda ruxsat etilgan to'xtash daqiqalari (byudjet), hodisalarda sarflangani, qolgani va holat ("me'yorda" yoki "BUZILDI"). Raqamlarni bitta kasr xonagacha yaxlitlang. Ishora: reduce (massivning HOF metodlari), Number(x.toFixed(1)).

Yechim
js
function byudjetDaqiqa(foiz, kunlar = 30) {
  return (kunlar * 24 * 60 * (100 - foiz)) / 100;
}

function oyHisoboti(foiz, hodisalar) {
  const byudjet = byudjetDaqiqa(foiz);
  const sarflandi = hodisalar.reduce((s, h) => s + h.daqiqa, 0);
  const qoldi = byudjet - sarflandi;
  return {
    byudjet: Number(byudjet.toFixed(1)),
    sarflandi,
    qoldi: Number(qoldi.toFixed(1)),
    holat: qoldi >= 0 ? "me'yorda" : "BUZILDI",
  };
}

const noyabr = [
  { sabab: "deploy xatosi", daqiqa: 12 },
  { sabab: "disk to'ldi", daqiqa: 25 },
];
console.log(oyHisoboti(99.9, noyabr));
console.log(oyHisoboti(99.95, noyabr));

Konsolda:

text
{ byudjet: 43.2, sarflandi: 37, qoldi: 6.2, holat: "me'yorda" }
{ byudjet: 21.6, sarflandi: 37, qoldi: -15.4, holat: 'BUZILDI' }

99.9 % da oyiga 43.2 daqiqa bor — ikki hodisa 37 daqiqani yedi, 6.2 qoldi. Xuddi shu hodisalar 99.95 % va'dasini buzadi. SRE amaliyotida bu xato byudjeti (error budget) deyiladi: byudjet tugasa, jamoa yangi xususiyatlarni chiqarishni to'xtatib, barqarorlikka o'tadi. holat birinchi qatorda qo'shtirnoqda chiqdi — ichida apostrof bor.

3-mashq (qiyin): failover jurnalidan RPO va RTO

Kanon yozuvchisi failover'dan keyin jurnal qoldiradi: tasdiqlangan id'lar, yangi primary'dagi id'lar, primary yiqilgan va yozuv qayta ishlagan vaqt (ms). hisobla(jurnal) funksiyasini yozing: rpo — tasdiqlangan, lekin yangi serverda yo'q yozuvlar soni; yoqolganIdlar — o'sha id'lar; rtoMs. Ikki jurnalni tekshiring: asinxron (oxirgi ikki yozuv replikaga yetmagan) va sinxron (hammasi yetgan). Ishora: new Set(...) va has (Set), filter (filter), spread bilan obyekt nusxasi (spread).

Yechim
js
function hisobla(jurnal) {
  const bor = new Set(jurnal.replikada);
  const yoqolgan = jurnal.tasdiqlangan.filter((id) => !bor.has(id));
  return {
    rpo: yoqolgan.length,
    yoqolganIdlar: yoqolgan,
    rtoMs: jurnal.qaytaYozdi - jurnal.yiqildi,
  };
}

const asinxron = {
  tasdiqlangan: [1, 2, 3, 4, 5, 6, 7],
  replikada: [1, 2, 3, 4, 5],
  yiqildi: 10_000,
  qaytaYozdi: 41_500,
};
const sinxron = { ...asinxron, replikada: [1, 2, 3, 4, 5, 6, 7] };
console.log(hisobla(asinxron));
console.log(hisobla(sinxron));

Konsolda:

text
{ rpo: 2, yoqolganIdlar: [ 6, 7 ], rtoMs: 31500 }
{ rpo: 0, yoqolganIdlar: [], rtoMs: 31500 }

Asinxron jurnalda 6 va 7 foydalanuvchiga "saqlandi" deyilgan, lekin yangi primary'da yo'q — RPO 2. RTO 31.5 soniya — bu misolda failover avtomatik vosita orqali bo'lgandek: lider kalitining muddati tugashini kutish soniyalar oladi. Kanonning hisob skripti ham xuddi shu Set + filter usuli bilan hisoblagan. Set muhim: 10 000 ta id bo'lsa, massivdagi includes har id uchun butun ro'yxatni aylanib chiqardi, Set.has esa darhol topadi.

9. Loyiha qadami: kanon tajribasi

Bu darsda vazifalar-api o'zgarmaydi va commit yo'q. HA va DR — ilova kodi emas, infratuzilma: replikalar, Patroni yoki managed xizmat, zaxira jadvali. Loyiha hozir bitta lokal bazada ishlaydi; uni serverga chiqarish va zaxira rejasini qurish — 33-qism (deploy). Ilova tomonidan kerak bo'ladigan narsa esa allaqachon bor: 26–27-qismlardagi ulanish hovuzi uzilgan ulanishni tashlab, yangisini ochadi, xato bo'lsa so'rov 5xx bilan qaytadi va mijoz qayta urina oladi.

Kanon tajribasi alohida juftlikda o'tdi: (A) WAL arxivi va PITR, (B) asinxron va sinxron rejimda docker kill + pg_promote(). Oxirida hamma konteyner va volume o'chirildi. Avtomatik failover (Patroni, managed HA), pg_rewind va pgBackRest — sinalmagan: kanon qo'shimcha o'rnatishsiz, image'dagi vositalar bilan ishladi.

O'zingiz takrorlaydigan qism Docker'siz: darsdagi failover modeliga rejim parametrini qo'shing ("sinxron" da yo'qotish doim 0, lekin har qadam 35 % uzunroq). Keyin 2 000 ms da ikki rejimda nechtadan yozuv tasdiqlanishini hisoblang va kanon nisbati (285 ga 230) bilan solishtiring. Docker bilan ishlay olsangiz — Backup va restore darsidagi PITR mashqini «Tiklash mashqi rejasi» jadvali bo'yicha takrorlab, tiklash vaqtini yozib qo'ying.

10. Real ishda

  • Qayerda uchraydi: har jiddiy prodakshn bazasida. Bank va to'lov tizimlarida — sinxron replika va boshqa hududdagi DR nusxasi; startaplarda — managed Postgres'ning HA katakchasi.
  • Intervyu savollari: "RPO va RTO nima, ular qanday o'lchanadi?", "Sinxron va asinxron replikatsiya farqi va narxi?", "Split brain nima va undan qanday himoyalanasiz?", "Replika zaxira o'rnini bosadimi?"
  • Hujjatlarda: SLA shartnomalarida "99.9 % mavjudlik" va'dasi; ichki hujjatlarda — runbook va tiklash mashqlari jurnali.
  • Jamoada: incident'dan keyin "postmortem" — nima bo'ldi, RPO/RTO qancha chiqdi, keyingi safar nimani o'zgartiramiz.

Xulosa

  • HA — server yiqilganda ishlashni davom ettirish (replika + failover); DR — falokatdan keyin ma'lumotni qaytarish (zaxira + WAL + PITR).
  • RPO — yo'qolgan tasdiqlangan ma'lumot, RTO — to'xtash vaqti; ikkalasi ham o'lchanadi, taxmin qilinmaydi.
  • Kanon failover'i: RPO 0 (lokal tarmoqda — isbot emas), RTO ~2.2–2.4 s (qo'lda, aniqlash vaqtisiz).
  • Asinxron RPO ≈ lag × yozish tezligi; sinxron rejim uni yopadi, lekin commit ~35 % sekin va yagona sinxron replika o'chsa yozish osiladi.
  • Avtomatik failover'ning qiyin qismi — qaror va split brain; Patroni buni DCS'dagi lider kaliti bilan hal qiladi.
  • Replika zaxira emas; zaxira esa tiklab ko'rilmaguncha zaxira emas — log'ni ham tekshiring.

Keyingi dars: Partitioning — katta jadvalni oyma-oy bo'limlarga ajratamiz va vazifalar-api ga birinchi yangi jadvalni — vazifa hodisalarining vaqt qatorini — qo'shamiz.

Manbalar

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
Yuqori mavjudlik (HA) va falokatdan tiklash: failover, RPO, RTO va tiklash mashqlari — IlmHamroh