IlmHamroh
JavaScript Full-stack/27-qism. Ma'lumotlar bazasi chuqur: Postgres, ORM, Mongo, Redis16/65-dars17 daqiqa
Mundarija (31)

Monitoring va favqulodda vaziyatlar: pg_stat_activity, qulflar va timeout'lar

Qisqacha: Baza "qotib qolsa", avval kim nima qilayotganini ko'ring: pg_stat_activity (holat, kutish sababi, so'rov) va pg_blocking_pids(pid) (kim kimni to'syapti). Keyin eng yumshoq choradan boshlang: pg_cancel_backend faqat joriy so'rovni bekor qiladi, pg_terminate_backend esa ulanishni uzadi va uning tranzaksiyasini qaytaradi. Takrorlanmasligi uchun timeout'lar: statement_timeout, lock_timeout, idle_in_transaction_session_timeout.

Bu darsda

  • pg_stat_activity dan "kim, qaysi holatda, nimani kutyapti" ni o'qiysiz.
  • pg_blocking_pids va pg_locks bilan qulf zanjirini topasiz.
  • pg_cancel_backend va pg_terminate_backend farqini bilib, to'g'risini tanlaysiz.
  • To'rt timeout sozlamasini qaysi rolga qanday qo'yishni bilasiz.
  • Disk to'lganda Postgres o'zini qanday tutishini va nimani kuzatish kerakligini tushuntirasiz.

Oldin bilishingiz kerak: Qulflar: row, table va advisory, VACUUM, autovacuum va bloat, Rollar va huquqlar, Sekin so'rovlarni topish.

1. Nega bu kerak?

Seshanba, soat 11:15. Telegram guruhga xabar keladi: "Sayt ochilmayapti". Server ishlayapti, protsessor bo'sh, xotira yetarli. Lekin API'ga kelgan har so'rov 5 soniyadan keyin statement timeout bilan qaytyapti.

Sababi ko'pincha shunday: bitta jarayon tranzaksiya ochdi, vazifa qatorini o'zgartirdi va... "unutdi". Masalan dasturchi psql'da BEGIN; UPDATE … yozib, tushlikka chiqib ketdi. Yoki kodda ROLLBACK chaqirilmaydigan xato yo'li bor. U qator qulflangan qoladi (Qulflar), shu qatorni o'zgartirmoqchi bo'lgan har kim navbatda turadi, navbat esa hovuzdagi hamma ulanishni yeydi.

Bunday paytda vahima — eng yomon maslahatchi. Serverni qayta ishga tushirish hammani uzadi va baribir sababini ko'rsatmaydi. Bu darsda tartibli yo'lni o'rganamiz: ko'r → tushun → eng yumshoq chora → oldini ol.

O'xshatish: chorrahada tirbandlik. Hamma mashinani evakuator bilan olib ketish shart emas. Avval qaysi mashina buzilib, yo'lni to'sib turganini topasiz — qolganlari o'zi yuradi.

Diqqat: Ikki sessiyali (A to'sadi, B kutadi) tajriba va disk to'lishi — kanondan: PostgreSQL 18.6 Docker'da, mon bazasi, 2026-10-24. Bitta sessiyada takrorlash mumkin bo'lgan misollarni biz o'z PostgreSQL 17.6 klasterimizda holat 11 bazasida ishga tushirdik. Prometheus, Grafana va boshqa monitoring tizimlari — sinalmagan, hujjat bo'yicha.

2. Kim nima qilyapti: pg_stat_activity

2.1 O'z sessiyamiz

pg_stat_activity — har ulanish (backend jarayoni) uchun bitta qator: kim ulangan, qaysi bazaga, hozir nima qilyapti. Avval o'zimizni ko'raylik:

sql
SET application_name = 'dars16';
SELECT application_name, state, backend_type,
       pg_blocking_pids(pid) AS tosuvchi
FROM pg_stat_activity
WHERE pid = pg_backend_pid();

Natija:

text
SET
 application_name | state  |  backend_type  | tosuvchi
------------------+--------+----------------+----------
 dars16           | active | client backend | {}
(1 row)

application_name — ulanish o'zini tanishtirgan nom. Kanon API hovuzi (pool) uni vazifalar-api qilib qo'yadi — shunda 40 ta ulanish ichidan "bular API'niki" deb darhol ajratasiz. pg_backend_pid() — joriy sessiya jarayonining raqami (PID). {} — bizni hech kim to'smayapti.

2.2 Muhim ustunlar

Ustun Nimani ko'rsatadi
state active (bajaryapti), idle (bo'sh), idle in transaction (tranzaksiya ochiq, lekin hech narsa qilmayapti)
wait_event_type, wait_event nimani kutyapti: Lock / transactionid, Client / ClientRead, IO …
xact_start, query_start tranzaksiya va joriy so'rov qachon boshlangan
query oxirgi (yoki joriy) so'rov matni

Eng xavfli holat — idle in transaction: tranzaksiya ochiq, qulflar ushlab turilibdi, lekin sessiya mijozdan buyruq kutyapti (ClientRead). U o'zi hech qachon tugamaydi.

3. Qulf zanjirini topish

3.1 Kanon tajribasi: A unutdi, B kutyapti

Kanon ikki sessiya ochdi. A — BEGIN; UPDATE vazifalar … WHERE id = 1; va shu bilan to'xtadi. B — xuddi shu qatorni yangilamoqchi. Vaqt bo'yicha:

Qadam A sessiya B sessiya Natija
1 BEGIN; UPDATE … id = 1 — A qatorni qulfladi
2 jim (COMMIT yo'q) UPDATE … id = 1 B navbatda kutyapti
3 jim kutyapti admin zanjirni ko'radi
4 jim cancel → ERROR B navbatdan chiqdi, ulanish qoldi
5 terminate → ROLLBACK — qator qulfi bo'shadi

Uchinchi, admin sessiyasidan (uzun chiqishda state, wait_event_type va tosuvchi ustunlariga qarang):

sql
SELECT pid, application_name AS ilova, state,
       wait_event_type, wait_event,
       pg_blocking_pids(pid) AS tosuvchi,
       now() - xact_start > interval '1 second' AS uzoq_tx
FROM pg_stat_activity
WHERE datname = 'mon' AND pid <> pg_backend_pid()
ORDER BY backend_start;

Kanon natijasi (sorov ustuni olib tashlandi):

text
 pid  |    ilova     |        state        | wait_event_type |  wait_event   | tosuvchi | uzoq_tx
------+--------------+---------------------+-----------------+---------------+----------+---------
 3057 | A_unutgan    | idle in transaction | Client          | ClientRead    | {}       | t
 3064 | B_kutayotgan | active              | Lock            | transactionid | {3057}   | t

O'qiymiz: A — idle in transaction, mijozdan buyruq kutyapti, uni hech kim to'smayapti. B — active, lekin aslida ishlamayapti: Lock / transactionid — boshqa tranzaksiya tugashini kutyapti. tosuvchi = {3057} — aynan A. PID'lar har safar boshqacha bo'ladi.

sequenceDiagram
  participant A as A (3057)
  participant B as B (3064)
  participant M as Admin
  A->>A: BEGIN, UPDATE id=1
  Note over A: idle in transaction
  B->>B: UPDATE id=1 (kutyapti)
  M->>M: pg_blocking_pids(B) = {A}
  M->>B: pg_cancel_backend
  M->>A: pg_terminate_backend

Diagrammada tartib: A qatorni qulflab "uxlab qoldi", B navbatga turdi, admin avval zanjirni ko'rdi, keyin chora ko'rdi. Choralarni quyida birma-bir ko'ramiz.

3.2 pg_locks: qulflarning o'zi

pg_blocking_pids "kim" ni aytadi, pg_locks esa "qanday qulf" ni. Kanon natijasi:

text
 pid  |   locktype    |       mode       | granted | application_name
------+---------------+------------------+---------+------------------
 3057 | relation      | RowExclusiveLock | t       | A_unutgan
 3057 | transactionid | ExclusiveLock    | t       | A_unutgan
 3064 | relation      | RowExclusiveLock | t       | B_kutayotgan
 3064 | transactionid | ShareLock        | f       | B_kutayotgan
 3064 | tuple         | ExclusiveLock    | t       | B_kutayotgan

granted = f — berilmagan, kutilayotgan qulf. B A ning tranzaksiya raqamiga ShareLock so'rayapti: "A tugaganda meni uyg'ot". Qator darajasidagi qulf Postgres'da xotirada emas, qatorning o'zida yoziladi (MVCC) — shuning uchun kutish tranzaksiya orqali bo'ladi. tuple ExclusiveLock — B navbatda birinchi turganini belgilaydi.

Jadval darajasidagi og'ir qulfni o'zimiz ham ko'ra olamiz — bitta sessiyada:

sql
BEGIN;
LOCK TABLE teglar IN ACCESS EXCLUSIVE MODE;
SELECT mode, granted
FROM pg_locks
WHERE relation = 'teglar'::regclass;
ROLLBACK;

Natija:

text
BEGIN
LOCK TABLE
        mode         | granted
---------------------+---------
 AccessExclusiveLock | t
(1 row)

ROLLBACK

AccessExclusiveLock — eng kuchli qulf: hatto oddiy SELECT ham kutadi. Uni ALTER TABLE, DROP, TRUNCATE va VACUUM FULL oladi. Kanon VACUUM darsidagi tajribada buni o'lchadi: 300 000 qatorli jadvalda VACUUM FULL 2 497 ms ishladi, shu paytda oddiy SELECT 1 459 ms kutdi.

Tekshirib ko'ring: B active, lekin protsessor yuklanmagan. state ga qarab "B ishlayapti" deyish to'g'rimi?

Javob

Yo'q. active — "so'rov bajarilish jarayonida", ichida kutish ham bor. Haqiqatda nima bo'layotganini wait_event_type aytadi: Lock — qulfni kutyapti. Shuning uchun monitoring so'rovida state bilan birga doim wait_event_type ni ham chiqaring.

4. Choralar: cancel va terminate

4.1 pg_cancel_backend — faqat so'rov

Eng yumshoq chora — kutib turgan so'rovni bekor qilish:

sql
SELECT pg_cancel_backend(3064);

Kanonda B sessiyasi shu xatoni oldi:

text
ERROR:  canceling statement due to user request
CONTEXT:  while locking tuple (0,1) in relation "vazifalar"

"So'rov foydalanuvchi talabi bilan bekor qilindi; vazifalar dagi (0,1) qatorni qulflash paytida". Ulanish qoldi — B keyingi buyruqni yubora oladi. Xuddi shu xabarni bitta sessiyada o'zimiz ham olamiz — o'z PID'imizga cancel yuborib:

sql
SELECT pg_cancel_backend(pg_backend_pid());

Natija:

text
ERROR:  canceling statement due to user request

Lekin B'ni bekor qilish muammoni hal qilmaydi: A hali ham qatorni ushlab turibdi, keyingi so'rov yana navbatga turadi. Cancel — kutayotganlarni ozod qilish uchun, sababni yo'qotish uchun emas.

4.2 pg_terminate_backend — ulanishni uzish

Sabab — A. U idle in transaction holatida: bekor qiladigan so'rovi yo'q, shuning uchun cancel unga ta'sir qilmaydi. Yagona yo'l — ulanishni uzish:

sql
SELECT pg_terminate_backend(3057);

A sessiyasida:

text
FATAL:  terminating connection due to administrator command
server closed the connection unexpectedly

"Administrator buyrug'i bilan ulanish uzilmoqda". A ning tranzaksiyasi ROLLBACK bo'ldi: kanon tekshirdi — bajarildi o'zgarmagan (f). Qator qulfi bo'shadi: endi shu qatorga kelgan har yangi UPDATE kutmasdan bajariladi. (Kanonda B oldinroq cancel qilingan edi; agar cancel qilinmaganida, terminate'dan keyin B ning UPDATE'i o'zi davom etardi.)

pg_cancel_backend pg_terminate_backend
Nimani to'xtatadi joriy so'rovni butun ulanishni
Tranzaksiya xato holatiga o'tadi (keyin ROLLBACK) darhol ROLLBACK
idle in transaction ga ta'sir qilmaydi ishlaydi
Kim chaqira oladi superuser yoki pg_signal_backend a'zosi (o'z rolining sessiyalari — har kim) xuddi shunday

Diqqat: Terminate qilingan ulanish API hovuziga tegishli bo'lsa, hovuz error hodisasini oladi. Kanon API'si buni log'ga yozadi (pg pool: bo'sh ulanish uzildi) va keyingi so'rov uchun yangi ulanish ochadi — jarayon yiqilmaydi. Bu himoya 27-qism loyiha qadamida qo'shilgan (ORM, query builder va raw SQL).

4.3 Zanjir uzun bo'lsa

Real hayotda zanjir ko'pincha shunday: A → B'ni to'sadi, B → C, D, E'ni to'sadi (ular B ushlagan qatorlarni kutyapti). Hammasini o'ldirish shart emas — bosh tosuvchini (o'zi hech kimni kutmaydigan, lekin boshqalarni to'sadigan) topish kerak:

sql
SELECT pid, state, now() - xact_start AS davomiylik,
       left(query, 60) AS sorov
FROM pg_stat_activity
WHERE pid IN (SELECT unnest(pg_blocking_pids(pid))
              FROM pg_stat_activity)
  AND cardinality(pg_blocking_pids(pid)) = 0;

"Kimdir uni tosuvchi deb ko'rsatgan, lekin uning o'zini hech kim to'smayapti" — shu bosh. 3-mashqda xuddi shu mantiqni JS'da yozasiz.

5. Oldini olish: timeout'lar

5.1 To'rt sozlama

Sukut bo'yicha Postgres hech narsani o'zi uzmaydi:

sql
SELECT name, setting, unit
FROM pg_settings
WHERE name IN ('statement_timeout', 'lock_timeout',
               'idle_in_transaction_session_timeout',
               'transaction_timeout', 'deadlock_timeout')
ORDER BY name;

Natija:

text
                name                 | setting | unit
-------------------------------------+---------+------
 deadlock_timeout                    | 1000    | ms
 idle_in_transaction_session_timeout | 0       | ms
 lock_timeout                        | 0       | ms
 statement_timeout                   | 0       | ms
 transaction_timeout                 | 0       | ms
(5 rows)

0 — "cheksiz". Har biri nima qiladi:

Sozlama Qachon ishlaydi Natija
statement_timeout bitta so'rov shundan uzoq yursa so'rov bekor (ERROR)
lock_timeout qulfni shundan uzoq kutsa so'rov bekor (ERROR)
idle_in_transaction_session_timeout tranzaksiya ichida shuncha bo'sh tursa ulanish uziladi (FATAL)
transaction_timeout (PG 17+) butun tranzaksiya shundan uzoq bo'lsa ulanish uziladi (FATAL)

deadlock_timeout — boshqacha: u hech narsani bekor qilmaydi, faqat "qancha kutgandan keyin deadlock tekshiruvini boshlash" (Deadlock darsi).

5.2 Ularni ko'rib chiqamiz

statement_timeout — o'zimiz:

sql
SET statement_timeout = '100ms';
SELECT pg_sleep(1);
RESET statement_timeout;

Natija:

text
SET
ERROR:  canceling statement due to statement timeout
RESET

idle_in_transaction_session_timeout = 2s — kanonda sessiya BEGIN; SELECT 1; dan keyin jim turdi:

text
FATAL:  terminating connection due to idle-in-transaction timeout

lock_timeout = 500ms — kanonda ALTER TABLE band jadvalni kutmasdan xato berdi:

text
ERROR:  canceling statement due to lock timeout

Oxirgisi migratsiyalar uchun juda muhim. Agar ALTER TABLE qulf navbatida tursa, uning ortidagi hamma kutadi — oddiy SELECT lar ham. lock_timeout bilan migratsiya tez yiqiladi va qayta urinadi, sayt esa to'xtamaydi — bu Xavfli migratsiyalar va qulflar darsida chuqur.

5.3 Qayerga qo'yish

Bitta global qiymat hamma uchun yaramaydi: API so'rovi 5 soniyadan oshmasin, hisobot esa 30 soniya ishlashi mumkin. Shuning uchun rol darajasida (Rollar va huquqlar):

sql
ALTER ROLE vazifalar_api SET statement_timeout = '5s';
ALTER ROLE vazifalar_api
  SET idle_in_transaction_session_timeout = '30s';
ALTER ROLE vazifalar_hisobot SET statement_timeout = '30s';

Rol sozlamasi faqat yangi ulanishlarga ta'sir qiladi. Kanondagi tuzoqni eslang: SET ROLE bilan rolga o'tilganda rol sozlamalari qo'llanmaydi — faqat shu rol bilan haqiqiy LOGIN qilinganda.

Tekshirib ko'ring: API'da statement_timeout = 5s bor. Nega baribir idle_in_transaction_session_timeout kerak?

Javob

statement_timeout faqat bajarilayotgan so'rovni o'lchaydi. idle in transaction sessiyada hech qanday so'rov bajarilmayapti — taymer ishlamaydi. Kod BEGIN dan keyin xatoga uchrab, ROLLBACK chaqirmay qolsa, qulflarni faqat idle_in_transaction_session_timeout (yoki transaction_timeout) bo'shatadi.

6. Disk to'lishi

6.1 Ma'lumot diski

Kanon buni xavfsiz sinash uchun 16 MB lik alohida disk (tablespace) yaratdi va unga 100 000 qator yozishga urindi:

text
ERROR:  could not extend file "pg_tblspc/16398/PG_18_202506291/5/16399": No space left on device
HINT:  Check free disk space.

"Faylni kengaytirib bo'lmadi: qurilmada joy qolmadi. Diskdagi bo'sh joyni tekshiring". Muhim kuzatuvlar:

  • INSERT to'liq bekor bo'ldi — jadvalda 0 qator, yarim yozilgan ma'lumot yo'q (tranzaksiya kafolati).
  • Server ishlashda davom etdi — boshqa jadvallarga so'rovlar o'tdi.
  • DROP TABLE joyni darhol bo'shatdi (16 MB → 0).

6.2 WAL diski — boshqacha

WAL yoziladigan diskning to'lishi ancha jiddiyroq: Postgres hech narsani yoza olmaydi, hujjatga ko'ra server PANIC bilan to'xtaydi va joy bo'shatilmaguncha ishga tushmaydi. Bu kanonda sinalmagan. WAL'ni to'ldiradigan odatiy sabablar: ishlamay qolgan archive_command (Backup va restore — arxivlanmagan WAL o'chirilmaydi) va unutilgan replikatsiya sloti.

Qoida: disk 100 % ga yetishini kutmang — 80 % da ogohlantirish oling.

6.3 WAL arxivini kuzatish

Oldingi darsda PITR WAL zanjiriga tayanishini ko'rdik. Arxivlash holatini pg_stat_archiver ko'rsatadi:

sql
SELECT archived_count, failed_count, last_failed_wal
FROM pg_stat_archiver;

Natija (arxivlash o'chiq):

text
 archived_count | failed_count | last_failed_wal
----------------+--------------+-----------------
              0 |            0 |
(1 row)

failed_count o'sib borsa va last_failed_wal to'lsa — arxiv to'xtagan: PITR imkoniyati yo'qolyapti va WAL diskda to'planyapti. Bu ikkala muammoning eng erta belgisi.

7. Nimani doim kuzatish kerak

Har kuni qo'lda so'rov yozish o'rniga, monitoring tizimi shu ko'rsatkichlarni yig'ib, chegaradan oshsa xabar beradi:

Ko'rsatkich Qayerdan Ogohlantirish
Ulanishlar soni pg_stat_activity max_connections ning 80 % i
Uzoq tranzaksiyalar now() - xact_start masalan > 5 daqiqa
idle in transaction state > 1 daqiqa
Disk band joyi operatsion tizim > 80 %
WAL arxivi xatolari pg_stat_archiver failed_count o'sdi
Deadlock, vaqtinchalik fayllar pg_stat_database o'sish tezligi
Sekin so'rovlar pg_stat_statements Sekin so'rovlarni topish

Kanondagi pg_stat_database qatori: deadlocks 0, temp_files 1 (4 102 kB) — work_mem ga sig'magan saralash diskka tushgan. Vositalar (hammasi sinalmagan, 2026-10-25 dagi hujjat bo'yicha): Prometheus + postgres_exporter + Grafana — eng keng tarqalgan juftlik; pgwatch — Postgres uchun tayyor panellar; managed xizmatlar (RDS, Neon) o'z panelini beradi.

8. Favqulodda vaziyat tartibi

Qisqa yo'riqnoma — uni loyiha hujjatiga yozib qo'yish foydali:

  1. Ko'r. pg_stat_activity — state, wait_event_type, pg_blocking_pids, now() - xact_start.
  2. Bosh tosuvchini top. Kim boshqalarni to'syapti, lekin o'zini hech kim to'smayapti.
  3. Kim ekanini aniqla. application_name, client_addr, query — bu API'mi, migratsiyami, kimningdir psql'imi?
  4. Eng yumshoq chora. Faol so'rov — pg_cancel_backend; idle in transaction — pg_terminate_backend.
  5. Natijani tekshir. Navbat bo'shadimi, API javob beryaptimi?
  6. Sababini yoz va oldini ol. Kodda ROLLBACK siz yo'l, timeout yo'q rol, kunduzi ishga tushirilgan migratsiya.

Serverni qayta ishga tushirish — oxirgi chora: u hammani uzadi, va sabab (masalan kodda qaytib keladigan xato) besh daqiqadan keyin yana paydo bo'ladi.

9. Ko'p uchraydigan xatolar

Belgi Sabab Davosi
Hamma so'rov sekin, CPU bo'sh qulf navbati pg_blocking_pids, bosh tosuvchi
pg_cancel_backend "ishlamadi" sessiya idle in transaction pg_terminate_backend
Migratsiya saytni to'xtatdi ALTER TABLE qulf navbatida lock_timeout + qayta urinish
Rol timeout'i ishlamayapti SET ROLE yoki eski ulanish haqiqiy LOGIN, hovuzni yangilash
Server o'zi to'xtadi WAL diski to'ldi arxiv va disk monitoringi
current transaction is aborted timeout'dan keyin tranzaksiya davom ettirildi ROLLBACK

10. Mashqlar

Mashqlar kurs/mashqlar/27/16-monitoring/ da. 2-mashq holat 11 bazasida (biz ishga tushirdik, PostgreSQL 17.6), 3-mashq Node 24 da tekshirilgan.

1-mashq (oson): qaysi chora

  • active, Lock / transactionid kutayotgan so'rovni bekor qilish — pg__backend
  • idle in transaction sessiyani to'xtatish — pg__backend
  • Tranzaksiya ichida jim turgan sessiyani avtomatik uzish —
  • Qulfni 500 ms dan ortiq kutmaslik —
  • Kim kimni to'syapti — pg_
Yechim
  • cancel — faqat joriy so'rov; ulanish qoladi.
  • terminate — ulanish uziladi, tranzaksiya ROLLBACK.
  • idle_in_transaction_session_timeout — statement_timeout bo'sh sessiyada ishlamaydi.
  • lock_timeout — migratsiyalar uchun ayniqsa muhim.
  • blocking_pids — natija massiv: bir nechta tosuvchi bo'lishi mumkin.

2-mashq (o'rta): timeout'dan keyingi tranzaksiya

Bitta tranzaksiyada: SET LOCAL statement_timeout = '200ms', vazifalarni sanang, keyin pg_sleep(1), keyin yana sanashga urining va ROLLBACK qiling. Har qadamda nima chiqishini oldindan yozing, keyin tekshiring.

Yechim
sql
BEGIN;
SET LOCAL statement_timeout = '200ms';
SELECT count(*) FROM vazifalar;
SELECT pg_sleep(1);
SELECT count(*) FROM vazifalar;
ROLLBACK;

Natija:

text
BEGIN
SET
 count
-------
    48
(1 row)

ERROR:  canceling statement due to statement timeout
ERROR:  current transaction is aborted, commands ignored until end of transaction block
ROLLBACK

Timeout so'rovni bekor qildi, lekin tranzaksiya xato holatida qoldi: keyingi oddiy SELECT ham bajarilmadi ("joriy tranzaksiya bekor qilingan, buyruqlar tranzaksiya oxirigacha e'tiborsiz"). Ilovada timeout xatosidan keyin albatta ROLLBACK — aks holda ulanish hovuzga shu holatda qaytadi. SET LOCAL — faqat shu tranzaksiya uchun, ROLLBACK dan keyin o'chadi.

3-mashq (qiyin): bosh tosuvchilar

pg_stat_activity dan olingan qatorlar massivi berilgan: har birida pid va tosuvchi (PID'lar massivi). boshTosuvchilar(qatorlar) funksiyasi kimnidir to'sadigan, lekin o'zini hech kim to'smaydigan PID'larni o'sish tartibida qaytarsin.

Ishora: avval hamma tosuvchilarni bitta Set ga yig'ing (Set); keyin ular ichidan tosuvchi massivi bo'sh bo'lganlarini qoldiring.

Yechim
js
function boshTosuvchilar(qatorlar) {
  const tosuvchilar = new Set(qatorlar.flatMap((q) => q.tosuvchi));
  return qatorlar
    .filter((q) => tosuvchilar.has(q.pid) && q.tosuvchi.length === 0)
    .map((q) => q.pid)
    .sort((a, b) => a - b);
}

const qatorlar = [
  { pid: 3057, tosuvchi: [] },
  { pid: 3064, tosuvchi: [3057] },
  { pid: 3070, tosuvchi: [3064] },
  { pid: 3071, tosuvchi: [3064] },
  { pid: 3080, tosuvchi: [] },
  { pid: 3090, tosuvchi: [3095] },
  { pid: 3095, tosuvchi: [] },
];
console.log(boshTosuvchilar(qatorlar));

Konsolda:

text
[ 3057, 3095 ]

3064 ham tosuvchi, lekin o'zini 3057 to'syapti — uni o'ldirish yordam bermaydi. 3080 — hech kimni to'smaydi (oddiy bo'sh ulanish). Ikki mustaqil zanjirning boshlari — 3057 va 3095.

11. Loyiha qadami: kanon tajribasi

Bu darsda vazifalar-api kodi o'zgarmaydi va commit yo'q. Monitoring — server sozlamasi va operatsiya ishi; serverga chiqarish 33-qismda. Lekin API'da nima borligini bilib qo'yamiz:

  • Hovuzda application_name: "vazifalar-api" — pg_stat_activity da darhol ajraladi.
  • statement_timeout — PG_STATEMENT_TIMEOUT (sukut 5 000 ms). Bo'sh qiymat bilan uni jim o'chirib yuborish teshigi 27-qismda yopilgan (ORM, query builder va raw SQL darsining loyiha qadami).
  • Hovuz error → log — terminate qilingan ulanish iz qoldiradi.
  • idle_in_transaction_session_timeout va lock_timeout — API'da yo'q. Sababi: tranzaksiyalar faqat ombor ichida, try/finally bilan, va bitta foydalanuvchi. Serverga chiqarishda ularni vazifalar_api roliga ALTER ROLE … SET bilan qo'yish — «Qayerga qo'yish» bo'limidagi namuna.

O'zingiz takrorlang: ikki terminalda docker exec -it kanon27-pg psql -U postgres -d vazifalar oching. Birinchisida BEGIN; UPDATE vazifalar SET bajarildi = NOT bajarildi WHERE id = 1; (COMMIT'siz), ikkinchisida xuddi shu qatorni yangilang. Uchinchi terminalda «Kanon tajribasi: A unutdi, B kutyapti» bo'limidagi so'rov bilan zanjirni toping va avval cancel, keyin terminate qilib ko'ring.

12. Real ishda

  • "Sayt qotdi" ning yarmi — qulf navbati, ko'pincha unutilgan tranzaksiya yoki kunduzi yurgizilgan migratsiya.
  • Katta jamoalarda yuqoridagi tartib "runbook" (navbatchi yo'riqnomasi) bo'lib yoziladi, pg_terminate_backend kim va qachon chaqirishi kelishiladi.
  • Intervyuda: "idle in transaction nima va nega xavfli?", "cancel va terminate farqi?", "Migratsiya saytni qanday to'xtatishi mumkin?"

Xulosa

  • pg_stat_activity: state + wait_event_type + pg_blocking_pids — kim, nima qilyapti, kim to'syapti.
  • idle in transaction — qulf ushlab, hech narsa qilmayotgan eng xavfli holat.
  • Avval bosh tosuvchini toping; cancel — so'rovni, terminate — ulanishni (tranzaksiya ROLLBACK).
  • Sukut bo'yicha hamma timeout 0; ularni rol darajasida qo'ying, lock_timeout — migratsiyalarga.
  • Ma'lumot diski to'lsa — so'rov bekor, server ishlaydi; WAL diski to'lsa — server to'xtaydi: 80 % da ogohlantirish.

Keyingi dars: Postgres versiyasini yangilash — 17 dan 18 ga: dump/restore, pg_upgrade --link va Docker image tuzog'i.

Manbalar

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
Monitoring va favqulodda vaziyatlar: pgstatactivity, qulflar va timeout'lar — IlmHamroh