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

Deploy'da migratsiya: alohida qadam, advisory lock va ishga tushishdagi tekshiruv

Qisqacha: Migratsiya — deploy'ning eng xavfli qadami, shuning uchun u alohida qadam bo'ladi: avval zaxira, keyin prisma migrate deploy (bir marta, bitta joydan), keyin yangi kod. Ilova migratsiyani o'zi qo'llamaydi — faqat tekshiradi: kanon holat 40 da server prodakshnda baza koddan orqada qolganini ko'rsa, migratsiyalar kutilmoqda … — avval npm run db:migrate deb ishga tushmaydi. Bir nechta nusxa bir vaqtda migratsiya qilsa — Prisma advisory lock bilan ulardan faqat birini o'tkazadi (kanon o'lchadi).

Bu darsda

  • Migratsiya deploy'ning qaysi joyida va kim tomonidan bajarilishini tanlay olasiz.
  • Bir nechta instans poygasini va advisory lock bilan himoyani tushunasiz.
  • Ilova ishga tushishida migratsiya holatini tekshirasiz (kanon migratsiya.ts).
  • Zaxira, CI tekshiruvi va migratsiya tartibini deploy rejasiga kiritasiz.
  • Fayl kodirovkasi (BOM) tuzog'ini haqiqiy xatoda ko'rasiz.

Oldin bilishingiz kerak: Prisma Migrate chuqur, Zero-downtime: expand-contract, Backup va restore, Qulflar: row, table va advisory.

1. Nega bu kerak?

Migratsiya yozildi, review'dan o'tdi, testlar yashil. Endi savol: prodakshnda prisma migrate deploy ni kim va qachon ishga tushiradi?

Uchta odatiy javob — va har birining tuzog'i:

  • "Dasturchi qo'lda, serverga kirib" — bir kun unutiladi. Yangi kod chiqdi, migratsiya yo'q: column "toliq_ism" does not exist.
  • "Ilova ishga tushganda o'zi" — qulay ko'rinadi. Lekin ilova 3 nusxada ishlasa — uchtasi bir vaqtda migratsiya qilmoqchi bo'ladi.
  • "Deploy skriptida, kod bilan birga" — to'g'ri yo'nalish, lekin tartib muhim: avval migratsiyami yoki avval kodmi?

O'xshatish: restoranni ta'mirlash va yangi menyu. Oshxona jihozlari (baza sxemasi) almashtirilmasdan yangi menyu (kod) chiqarilsa — oshpaz yangi taomni pishira olmaydi. Jihozlarni almashtirishni esa uchta ofitsiant bir vaqtda boshlasa — tartibsizlik. Bitta mas'ul usta, aniq vaqtda, menyudan oldin.

2. Migratsiya qayerda bajariladi

2.1 Uch variant

Variant Qanday Afzallik Xavf
Ilova start'ida server.ts boshida migrate deploy Unutilmaydi Ko'p nusxa poygasi; uzun migratsiya — start timeout; ilova DDL huquqiga ega
Konteyner start'ida CMD npm run db:migrate && npm start Oddiy Xuddi shu poyga; har restart'da urinish
Alohida qadam Deploy pipeline'da, kod almashishidan oldin, bitta joydan Bir marta, nazorat, log, to'xtatish oson Qadamni sozlash kerak

Ko'p jamoalar va kanon tanlovi — alohida qadam. Ilova faqat tekshiradi. Docker Compose va CI/CD'ni 33–34-qismlarda o'rganamiz; u yerda bu qadam alohida "migrate" servisi yoki pipeline bosqichi bo'ladi. Hozir buyruqlar va tartib muhim.

2.2 To'g'ri tartib

flowchart TD
  A[CI: testlar, toza bazada migrate deploy] --> B[Zaxira: pg_dump]
  B --> C[npm run db:migrate — bitta joydan]
  C --> D{Muvaffaqiyatlimi?}
  D -->|yo'q| E[To'xtash: kod almashmaydi]
  D -->|ha| F[Yangi kod deploy]
  F --> G[Server start: migratsiya holatini tekshiradi]
  G --> H[Health check]

Diagrammaning asosiy g'oyasi: migratsiya koddan oldin. Bu expand-contract bilan birga ishlaydi: expand migratsiyasi eski kodni buzmaydi, shuning uchun u eski kod ishlab turgan paytda qo'llanishi mumkin (Zero-downtime: expand-contract). Contract esa yangi kod hamma joyda bo'lgandan keyin — keyingi deploy'da.

Migratsiya yiqilsa — deploy to'xtaydi, eski kod eski sxemada ishlashda davom etadi. Hech kim xato ko'rmaydi.

Tekshirib ko'ring: Nega "avval kod, keyin migratsiya" tartibi xavfli?

Javob

Yangi kod toliq_ism ni so'raydi, bazada hali faqat ism bor — kod deploy bo'lgan zahoti so'rovlar column does not exist bilan yiqiladi, migratsiya tugaguncha. Teskari tartibda (expand migratsiyasi avval) eski kod yangi sxemada ham ishlaydi — trigger ikkala ustunni sinxron ushlaydi.

3. Bir nechta instans poygasi

3.1 Muammo

Ilova 3 nusxada. Har biri start'ida migrate deploy qilsa: uchtasi bir vaqtda _prisma_migrations ni o'qiydi, uchtasi ham 107 ni "qo'llanmagan" deb ko'radi, uchtasi ham ALTER TABLE … ADD COLUMN toliq_ism ni boshlaydi. Biri o'tadi, qolganlari — column already exists bilan yiqiladi, yoki yomonroq — yarim holatda qoladi.

3.2 Advisory lock

PostgreSQL'da buning uchun maxsus vosita bor — advisory lock (maslahat qulfi): jadvalga emas, raqamga olinadigan qulf (Qulflar: row, table va advisory). Bitta sessiya pg_advisory_lock(N) ni oldi — boshqa sessiya xuddi shu N ni ololmaydi, bo'shashini kutadi (yoki pg_try_… bilan darhol f oladi).

Biz ikki terminalda sinadik (PostgreSQL 17.6, Windows 11). Vaqt bo'yicha:

Qadam A sessiya B sessiya Natija
1 pg_advisory_lock(27) — A qulfni oldi
2 pg_sleep(3) — ushlab turibdi pg_try_advisory_lock(27) B: f, kutmasdan
3 sessiya tugadi — qulf o'zi bo'shadi
4 — yana pg_try_…(27) B: t

Ikki sessiyaning buyruqlari bitta blokda (bu blokni bitta terminalda ketma-ket bajarib bo'lmaydi):

sql
-- A sessiya
SELECT pg_advisory_lock(27) IS NOT NULL AS a_oldi;
SELECT pg_sleep(3);
-- B sessiya (A ushlab turganda)
SELECT pg_try_advisory_lock(27) AS b_oldimi;
SELECT locktype, objid, granted FROM pg_locks
WHERE locktype = 'advisory';

2-qadamda B ning natijasi:

text
 b_oldimi 
----------
 f
(1 row)

 locktype | objid | granted 
----------+-------+---------
 advisory |    27 | t
(1 row)

f — B ololmadi; pg_locks da A ning qulfi ko'rinadi. A sessiya tugagach, B yana urindi — t. Sessiya darajasidagi advisory lock sessiya yopilganda o'zi bo'shaydi.

Bitta sessiya ichida qulf qayta kiriluvchan: o'z qulfini yana olishi mumkin, lekin har olish uchun bir bo'shatish kerak:

sql
SELECT pg_try_advisory_lock(27) AS birinchi,
       pg_try_advisory_lock(27) AS ikkinchi;
SELECT objid, mode, granted FROM pg_locks
WHERE locktype = 'advisory';
SELECT pg_advisory_unlock(27) AS bir, pg_advisory_unlock(27) AS ikki,
       pg_advisory_unlock(27) AS uch;

Natija:

text
 birinchi | ikkinchi 
----------+----------
 t        | t
(1 row)

 objid |     mode      | granted 
-------+---------------+---------
    27 | ExclusiveLock | t
(1 row)

WARNING:  you don't own a lock of type ExclusiveLock
 bir | ikki | uch 
-----+------+-----
 t   | t    | f
(1 row)

Ikki marta olindi, ikki marta bo'shatildi, uchinchi bo'shatish — f va ogohlantirish: "sizda ExclusiveLock turidagi qulf yo'q".

3.3 Prisma'da o'lchov

Prisma migrate deploy advisory lock'ni o'zi oladi. Kanon ikki deploy ni bir vaqtda ishga tushirdi (kanon o'lchovi, IlmHamroh, 2026-10):

text
1-deploy: No pending migrations to apply.
2-deploy: The following migration(s) have been applied: All migrations have been successfully applied.
qo'llangan migratsiyalar: 1

Bittasi qulfni olib qo'lladi; ikkinchisi kutdi, keyin holat jadvalini o'qib, "qo'llanadigan narsa yo'q" dedi. Yozuvlar soni — 1, dublikat yo'q. (Qaysi deploy birinchi bo'lishi — tasodif; chiqishda "2-deploy" yutgan.)

Bu himoya yaxshi, lekin alohida qadamning o'rnini bosmaydi: advisory lock poygani to'xtatadi, lekin "qaysi nusxa uzun migratsiya paytida start timeout bilan o'ldirildi?" kabi muammolarni hal qilmaydi.

Diqqat: Advisory lock bitta ulanishga bog'langan. Tranzaksiya rejimidagi pooler (PgBouncer transaction rejimi) orqali ulanilsa, sessiya qulfi boshqa so'rovda boshqa ulanishga tushadi va ishlamaydi (PgBouncer va tashqi poolerlar). Migratsiyani pooler'ni chetlab, to'g'ridan-to'g'ri bazaga ulanib bajaring.

4. Ilova start'ida tekshiruv

4.1 Nega faqat tekshiruv

Migratsiya alohida qadam bo'lsa ham, uni unutish mumkin: kimdir deploy skriptini o'zgartirdi, yoki migratsiya qadami yiqildi-yu pipeline davom etdi. Natijada yangi kod eski sxemada ishlaydi — va eng yomoni, ba'zi so'rovlar ishlaydi, ba'zilari yo'q, ma'lumot yarim yoziladi.

Kanon holat 40 bu teshikni yopdi: server ishga tushganda baza migratsiyalari koddagi bilan mosligini tekshiradi.

4.2 Holatni aniqlash mantig'i

Kanon src/umumiy/migratsiya.ts ikki manbani solishtiradi: koddagi prisma/migrations/ papkalari va bazadagi _prisma_migrations qatorlari. Natija — to'rt holatdan biri: mos, kutilmoqda, yiqilgan, nomalum. Mantiqni bazasiz, toza funksiya sifatida yozamiz (kanondagi bilan bir xil qoidalar):

js
function holatniAniqla(qatorlar, koddagi) {
  if (qatorlar === null) {
    return { tur: "nomalum", sabab: "_prisma_migrations yo'q" };
  }
  const yiqilgan = qatorlar
    .filter((r) => !r.tugagan && !r.bekor).map((r) => r.nom);
  if (yiqilgan.length > 0) {
    return { tur: "yiqilgan", nomlar: yiqilgan };
  }
  const bazada = new Set(
    qatorlar.filter((r) => r.tugagan).map((r) => r.nom));
  const kutilmoqda = koddagi.filter((m) => !bazada.has(m));
  return kutilmoqda.length > 0
    ? { tur: "kutilmoqda", nomlar: kutilmoqda }
    : { tur: "mos", soni: koddagi.length };
}

const kod = ["0_init", "1_a", "2_b"];
const q = (nom, tugagan, bekor = false) => ({ nom, tugagan, bekor });
console.log(holatniAniqla(null, kod).tur);
console.log(holatniAniqla([q("0_init", true), q("1_a", true)], kod));
console.log(holatniAniqla(
  [q("0_init", true), q("1_a", true), q("2_b", false)], kod));
console.log(holatniAniqla([q("0_init", true), q("1_a", true),
  q("2_b", false, true), q("2_b", true)], kod));

Konsolda:

text
nomalum
{ tur: 'kutilmoqda', nomlar: [ '2_b' ] }
{ tur: 'yiqilgan', nomlar: [ '2_b' ] }
{ tur: 'mos', soni: 3 }

Oxirgi holat — eng nozik: 2_b yiqilgan edi, migrate resolve --rolled-back bilan "bekor" deb belgilandi va qayta qo'llandi. Bazada 2_b uchun ikki qator: biri bekor, biri tugagan. Natija — mos. Kanon testi (test/migratsiya.test.ts) aynan shu to'rt holatni haqiqiy bazada tekshiradi. Bazadan o'qiydigan SQL — kanonda:

sql
SELECT migration_name, finished_at IS NOT NULL AS tugagan,
  rolled_back_at IS NOT NULL AS bekor
FROM _prisma_migrations

4.3 Qaror: production'da to'xtash

Kanon src/ishga-tushir.ts dagi qaror (izohlar qisqartirildi, uzun qatorlar bo'lindi):

ts
async function migratsiyaniTekshir(pool: pg.Pool,
  production: boolean, log?: OmborLogi): Promise<void> {
  const h = await migratsiyaHolati(pool,
    await koddagiMigratsiyalar());
  if (h.tur === "mos") return;
  const xabar = h.tur === "nomalum" ? h.sabab
    : `migratsiyalar ${h.tur}: ${h.nomlar.join(", ")}` +
      " — avval npm run db:migrate";
  if (production && h.tur !== "nomalum") throw new Error(xabar);
  log?.warn?.({ migratsiya: h }, xabar);
}
  • Production + kutilmoqda/yiqilgan → xato, server ishga tushmaydi. Yaxshiroq: eski nusxalar ishlashda davom etadi, yangi nusxa health check'dan o'tmaydi, deploy to'xtaydi.
  • Development → faqat ogohlantirish: dasturchi hali db:migrate qilmagan bo'lishi mumkin, uni to'xtatmaymiz.
  • nomalum (_prisma_migrations umuman yo'q — baza qur.sh yoki test bilan qurilgan) → hech qayerda to'xtatmaydi.

Bu tekshiruv postgres va prisma omborlarining ikkalasida ham ishlaydi — u pg.Pool orqali o'qiydi.

4.4 Kanon sinovi

Kanon o'lchovi: baza eski kod bilan qurilgan (faqat 0_init), unga yangi kod (holat 40 — 107, 108 bilan) keldi. Uch joyga qarang: [exit 1], GET … → 200 va oxirgi GET:

text
=== yangi kod, migratsiyasiz — NODE_ENV=production
Error: migratsiyalar kutilmoqda: 20261025120000_toliq_ism_expand, 20261025120100_toliq_ism_contract — avval npm run db:migrate
[exit 1]
=== yangi kod, migratsiyasiz — development (ogohlantirish bilan ishga tushadi, lekin…)
"msg":"migratsiyalar kutilmoqda: 20261025120000_toliq_ism_expand, 20261025120100_toliq_ism_contract — avval npm run db:migrate"
GET /v1/vazifalar → 200
=== deploy qadami: npm run db:migrate
Applying migration `20261025120000_toliq_ism_expand`
Applying migration `20261025120100_toliq_ism_contract`
The following migration(s) have been applied:
All migrations have been successfully applied.
=== yana production
GET /v1/vazifalar → 200

Production'da server aniq xabar bilan to'xtadi; development'da ishladi (API ism ga tegmaydi, shuning uchun GET 200 — "lekin…" shu: boshqa endpoint tegsa, yiqilardi). db:migrate dan keyin production server ishga tushdi.

Tekshirib ko'ring: Nega server o'zi migrate deploy qilmaydi, faqat tekshiradi — axir u kutilayotgan migratsiyalarni biladi-ku?

Javob

Uch sabab. (1) Bir nechta nusxa — poyga (advisory lock yordam beradi, lekin har start'da urinish ortiqcha). (2) Huquq — ilova foydalanuvchisiga ALTER TABLE huquqini berish xavfsizlik nuqtai nazaridan yomon; migratsiya alohida, kuchliroq rol bilan bajariladi (Rollar va huquqlar). (3) Nazorat — uzun migratsiya start timeout'iga uriladi, uni kuzatish va to'xtatish alohida qadamda oson.

5. Zaxira va CI

5.1 Migratsiyadan oldin zaxira

Har prodakshn migratsiyasidan oldin — yangi zaxira nusxa (Backup va restore):

bash
pg_dump -Fc -d "$DATABASE_URL" -f "oldin-$(date +%F-%H%M).dump"
npm run db:migrate

down migratsiya ma'lumotni qaytarmaydi (Migratsiya tushunchasi); haqiqiy "orqaga" — shu zaxira yoki PITR. Katta bazada pg_dump uzoq — u holda doimiy PITR zaxirasi (kanon #14 da o'lchangan) va migratsiyadan oldin aniq vaqt belgisini yozib qo'yish.

5.2 CI'da tekshirish

Kanon tekshiruv skripti har holatda nima qiladi — bu CI'ning minimal ro'yxati:

  1. Toza bazada hamma migratsiyani noldan qo'llash (migrate deploy) — tartib va sintaksis xatolari.
  2. Natijani sql/ dan qurilgan baza bilan solishtirish (pg_dump farqi — faqat _prisma_migrations).
  3. migrate dev --create-only — bo'sh migratsiya bo'lishi kerak (schema.prisma va migratsiyalar mos, drift yo'q).
  4. Testlar shu bazada.

Prisma'da 3-qadamning bazasiz varianti ham bor — prisma migrate diff --from-migrations prisma/migrations --to-schema prisma/schema.prisma --exit-code: farq bo'lsa exit kodi 2 (Prisma hujjati; biz sinamadik, --from-migrations uchun shadow baza kerak).

IlmHamroh egasi CI'ga pul sarflamaydi — kursda ham shu tekshiruvlar lokal skript bilan (npm run check) deploy'dan oldin bajariladi. CI'ni 34-qismda ko'ramiz.

6. Fayl kodirovkasi: BOM tuzog'i

BOM (byte order mark) — ba'zi Windows muharrirlari UTF-8 fayl boshiga qo'yadigan ko'rinmas uch bayt (EF BB BF, belgi U+FEFF). Ko'zga ko'rinmaydi, git diff da ham deyarli sezilmaydi. Biz BOM'li bom.sql faylini ikki usulda qo'lladik (PostgreSQL 17.6):

  • psql -f bom.sql — CREATE TABLE, muammosiz: psql BOM'ni o'zi tashlab yubordi.
  • Node'da faylni o'qib, pg bilan yuborish — boshqa natija.
js
import { readFile } from "node:fs/promises";
import pg from "pg";
const client = new pg.Client(process.env.DATABASE_URL);
await client.connect();
const sql = await readFile("bom.sql", "utf8");
console.log("birinchi belgi:", sql.codePointAt(0).toString(16));
try {
  await client.query(sql);
  console.log("bajarildi");
} catch (e) {
  console.log(`${e.code} ${e.message}`);
}
await client.end();

Natija (biz ishga tushirdik, pg 8.23.1, Node 24.21.0):

text
birinchi belgi: feff
42601 syntax error at or near "CREATE"

Xabarda "CREATE" — lekin haqiqiy chiqishda qo'shtirnoq ichida CREATE dan oldin ko'rinmas U+FEFF turibdi (darsda uni olib tashladik — sahifada baribir ko'rinmaydi). Tarjimasi: "CREATE yaqinida sintaksis xatosi" — so'z to'g'ri ko'rinadi, lekin oldida ko'rinmas belgi. BOM'siz xuddi shu fayl — bajarildi. Ya'ni bir vosita (psql) bilan lokal sinovdan o'tgan fayl boshqa vosita (Node skripti, boshqa migratsiya vositasi) bilan prodakshnda yiqilishi mumkin.

Himoya:

  • .editorconfig: charset = utf-8 (BOM'siz); .gitattributes: *.sql text eol=lf — CRLF tuzog'i ham shu yerda (kanon #15 da o'lchangan).
  • CI'da tekshiruv: grep -rl $'\xEF\xBB\xBF' prisma/migrations — bo'sh bo'lishi kerak.
  • Migratsiyani bajaruvchi kodda: sql.replace(/^\uFEFF/, "") — fayl boshidagi BOM'ni olib tashlash.

7. Ko'p uchraydigan xatolar

1. column … does not exist deploy'dan keyin. Migratsiya qadami unutildi yoki kod migratsiyadan oldin chiqdi. Tuzatish: alohida qadam, "migratsiya → kod" tartibi, start'da tekshiruv.

2. already exists bir nechta nusxada. Poyga. Tuzatish: migratsiya bitta joydan; vositada advisory lock (Prisma bor).

3. Start timeout. Ilova start'ida uzun migratsiya — orkestrator nusxani "o'lik" deb o'ldiradi, migratsiya yarmida uziladi. Tuzatish: migratsiyani start'dan chiqarish.

4. P3009 — oldingi deploy'dan yiqilgan migratsiya. Tuzatish: Prisma Migrate chuqur dagi resolve tartibi; start tekshiruvi buni yiqilgan deb ko'rsatadi.

5. syntax error at or near "…". BOM. Tuzatish: faylni BOM'siz saqlash, CI tekshiruvi.

6. Pooler orqali migratsiya. Advisory lock ishlamaydi, SET sessiya sozlamalari yo'qoladi. Tuzatish: migratsiya uchun pooler'siz, to'g'ridan-to'g'ri ulanish. Prisma 7 da CLI ulanishni prisma.config.ts dagi URL'dan oladi — unga pooler'siz manzilni bering (eski Prisma versiyalarida bu directUrl edi).

8. Mashqlar

Yechimlar tekshirilgan (biz ishga tushirdik, PostgreSQL 17.6, Node 24.21.0).

1-mashq (oson)

Deploy qadamlarini to'g'ri tartibga qo'ying: (a) yangi kod nusxalarini almashtirish; (b) pg_dump; (c) health check; (d) npm run db:migrate; (e) CI: toza bazada migratsiyalar va testlar.

Yechim

(e) → (b) → (d) → (a) → (c). CI birinchi — xato bo'lsa prodakshnga umuman yetib bormaydi. Zaxira migratsiyadan oldin. Migratsiya koddan oldin. Health check oxirida — yangi nusxa haqiqatan ishlayotganini tasdiqlaydi; start tekshiruvi (migratsiya mosligi) shu yerda o'zini ko'rsatadi.

2-mashq (o'rta)

holatniAniqla ga yangi qoida qo'shing: bazada bor, lekin kodda yo'q tugagan migratsiya bo'lsa (masalan eski versiyaga qaytildi), natija { tur: "notanish", nomlar } bo'lsin — bu ham production'da to'xtatishi kerak. Qoida tartibi: yiqilgan → notanish → kutilmoqda → mos. Sinang: kod ["0_init", "1_a"], bazada 0_init, 1_a, 2_b tugagan. Ishora: Set (Set).

Yechim
js
function holatniAniqla(qatorlar, koddagi) {
  if (qatorlar === null) return { tur: "nomalum" };
  const yiqilgan = qatorlar
    .filter((r) => !r.tugagan && !r.bekor).map((r) => r.nom);
  if (yiqilgan.length > 0) {
    return { tur: "yiqilgan", nomlar: yiqilgan };
  }
  const kodda = new Set(koddagi);
  const notanish = qatorlar
    .filter((r) => r.tugagan && !kodda.has(r.nom))
    .map((r) => r.nom);
  if (notanish.length > 0) {
    return { tur: "notanish", nomlar: notanish };
  }
  const bazada = new Set(
    qatorlar.filter((r) => r.tugagan).map((r) => r.nom));
  const kutilmoqda = koddagi.filter((m) => !bazada.has(m));
  return kutilmoqda.length > 0
    ? { tur: "kutilmoqda", nomlar: kutilmoqda }
    : { tur: "mos", soni: koddagi.length };
}
const q = (nom) => ({ nom, tugagan: true, bekor: false });
console.log(holatniAniqla([q("0_init"), q("1_a"), q("2_b")],
  ["0_init", "1_a"]));
console.log(holatniAniqla([q("0_init")], ["0_init", "1_a"]));

Konsolda:

text
{ tur: 'notanish', nomlar: [ '2_b' ] }
{ tur: 'kutilmoqda', nomlar: [ '1_a' ] }

notanish — eski kod yangi sxemada ishlamoqchi. Expand migratsiyasi uchun bu xavfsiz bo'lishi mumkin (eski kod ishlaydi), contract'dan keyin esa — yo'q. Shuning uchun ba'zi jamoalar notanish ni production'da ogohlantirish bilan o'tkazadi; kanon bu holatni hisobga olmaydi — bu siz uchun qaror nuqtasi.

3-mashq (qiyin)

Migratsiya skriptini "bir vaqtda faqat bittasi" qiladigan SQL naqshini yozing: sessiya pg_try_advisory_lock(27) bilan qulfni olishga urinsin; olmasa — hech narsa qilmasin. Bitta sessiyada tekshiring: qulfni oling, migratsiya_jurnali jadvaliga yozuv qo'shing, qulfni bo'shating; keyin pg_locks da advisory qulf qolmaganini ko'rsating. Jadvalni oxirida o'chiring.

Yechim
sql
CREATE TABLE migratsiya_jurnali (nom text, vaqt timestamptz
  DEFAULT now());
SELECT pg_try_advisory_lock(27) AS oldim;
INSERT INTO migratsiya_jurnali (nom)
SELECT '107_expand' WHERE EXISTS (
  SELECT 1 FROM pg_locks WHERE locktype = 'advisory'
    AND objid = 27 AND pid = pg_backend_pid() AND granted);
SELECT pg_advisory_unlock(27) AS bushatdim;
SELECT count(*) AS qulflar FROM pg_locks
WHERE locktype = 'advisory';
SELECT nom FROM migratsiya_jurnali;
DROP TABLE migratsiya_jurnali;

Natija:

text
CREATE TABLE
 oldim 
-------
 t
(1 row)

INSERT 0 1
 bushatdim 
-----------
 t
(1 row)

 qulflar 
---------
       0
(1 row)

    nom     
------------
 107_expand
(1 row)

DROP TABLE

INSERT … WHERE EXISTS (… pid = pg_backend_pid()) — yozuv faqat qulf shu sessiyada bo'lsa qo'shiladi. Haqiqiy skriptda bu shart odatda kodda: if (!oldim) { console.log("boshqa nusxa migratsiya qilyapti"); process.exit(0); }. pg_backend_pid() — joriy sessiyaning jarayon raqami.

9. Loyiha qadami

9.1 Holat 40

Kanon commit'i:

bash
git add src/umumiy/migratsiya.ts src/ishga-tushir.ts
git add test/migratsiya.test.ts
M='feat: server ishga tushishda migratsiya holati tekshiruvi'
M+=" — production'da mos kelmasa ishga tushmaydi"
git commit -m "$M"

O'zgarishlar:

  • src/umumiy/migratsiya.ts — koddagiMigratsiyalar() (prisma/migrations papkalari, tartiblangan) va migratsiyaHolati(pool, koddagi) — «Holatni aniqlash mantig'i» bo'limidagi qoidalar, bazadan o'qish bilan.
  • src/ishga-tushir.ts — migratsiyaniTekshir (yuqorida to'liq), omborYarat ichida ikkala rejimda (postgres, prisma) fail-fast tekshiruvidan keyin chaqiriladi; xato bo'lsa pool.end() va xato yuqoriga.
  • test/migratsiya.test.ts — +2 test, vaqtinchalik vazifalar_test_mig bazasida: nomalum → kutilmoqda → yiqilgan → (resolve --rolled-back va qayta qo'llash) mos.

Server migratsiyani o'zi qo'llamaydi — deploy qadami npm run db:migrate.

9.2 Tekshiruv

Kanon tekshiruvi: tip/lint 0, testlar 101/101, migrate deploy == sql/, jonli 9/9 × 2. O'zingizda:

bash
npm run check
npm run db:migrate
NODE_ENV=production npm start

Oxirgisi xatosiz ishga tushishi kerak (baza mos). Tekshiruvni sinash uchun alohida bazada faqat 0_init ni qo'llab, NODE_ENV=production bilan ishga tushiring — "migratsiyalar kutilmoqda" xabari va exit 1.

10. Real ishda

  • Kubernetes da migratsiya odatda alohida Job (bir marta). "Init container" ham uchraydi, lekin u har pod'da ishga tushadi — ya'ni bu yana poyga, faqat advisory lock himoya qiladi.
  • Platformalar kod almashishidan oldin bir marta bajariladigan buyruq beradi: Heroku — "release phase", Render va Railway — "pre-deploy command", Fly.io — release_command (hujjatlarga ko'ra, 2026-10; sinalmagan). Hammasi — "alohida qadam" g'oyasining shakllari.
  • Katta jamoalarda migratsiya va kod deploy'i butunlay ajratiladi: migratsiya — alohida PR, alohida reliz (expand), kod — keyingi relizda.
  • Intervyuda: "Migratsiyani qachon va kim qo'llaydi?", "3 ta pod bir vaqtda start bo'lsa nima bo'ladi?", "Deploy yarmida yiqilsa qanday tiklanasiz?".

Xulosa

  • Migratsiya — alohida deploy qadami: CI → zaxira → migrate deploy (bir joydan) → kod → health check; tartib — migratsiya koddan oldin.
  • Bir nechta nusxa poygasi — advisory lock bilan; kanon: ikki parallel deploy, yozuv 1, ikkinchisi "No pending migrations".
  • Ilova migratsiyani qo'llamaydi, tekshiradi: production'da kutilmoqda/yiqilgan → ishga tushmaydi (kanon: exit 1), development'da — ogohlantirish.
  • Zaxira har prodakshn migratsiyasidan oldin; down ma'lumotni qaytarmaydi.
  • BOM: psql uni tashladi, Node pg esa syntax error at or near "CREATE" berdi — fayllarni BOM'siz saqlang va CI'da tekshiring.

Keyingi dars: ORM'siz migratsiya vositalari — SQL-first jamoalar uchun node-pg-migrate, dbmate, graphile-migrate va Atlas: up/down, tranzaksiya rejimlari va CONCURRENTLY.

Manbalar

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
Deploy'da migratsiya: alohida qadam, advisory lock va ishga tushishdagi tekshiruv — IlmHamroh