Mundarija (29)
- Bu darsda
- 1. Nega bu kerak?
- 2. Migratsiya qayerda bajariladi
- 2.1 Uch variant
- 2.2 To'g'ri tartib
- 3. Bir nechta instans poygasi
- 3.1 Muammo
- 3.2 Advisory lock
- 3.3 Prisma'da o'lchov
- 4. Ilova start'ida tekshiruv
- 4.1 Nega faqat tekshiruv
- 4.2 Holatni aniqlash mantig'i
- 4.3 Qaror: production'da to'xtash
- 4.4 Kanon sinovi
- 5. Zaxira va CI
- 5.1 Migratsiyadan oldin zaxira
- 5.2 CI'da tekshirish
- 6. Fayl kodirovkasi: BOM tuzog'i
- 7. Ko'p uchraydigan xatolar
- 8. Mashqlar
- 1-mashq (oson)
- 2-mashq (o'rta)
- 3-mashq (qiyin)
- 9. Loyiha qadami
- 9.1 Holat 40
- 9.2 Tekshiruv
- 10. Real ishda
- Xulosa
- Manbalar
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:migratedeb 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):
-- 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:
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:
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:
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):
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: 1Bittasi 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
transactionrejimi) 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):
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:
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:
SELECT migration_name, finished_at IS NOT NULL AS tugagan,
rolled_back_at IS NOT NULL AS bekor
FROM _prisma_migrations4.3 Qaror: production'da to'xtash
Kanon src/ishga-tushir.ts dagi qaror (izohlar qisqartirildi, uzun qatorlar bo'lindi):
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:migrateqilmagan bo'lishi mumkin, uni to'xtatmaymiz. nomalum(_prisma_migrationsumuman yo'q — bazaqur.shyoki 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:
=== 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 → 200Production'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 deployqilmaydi, 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):
pg_dump -Fc -d "$DATABASE_URL" -f "oldin-$(date +%F-%H%M).dump"
npm run db:migratedown 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:
- Toza bazada hamma migratsiyani noldan qo'llash (
migrate deploy) — tartib va sintaksis xatolari. - Natijani
sql/dan qurilgan baza bilan solishtirish (pg_dumpfarqi — faqat_prisma_migrations). migrate dev --create-only— bo'sh migratsiya bo'lishi kerak (schema.prismava migratsiyalar mos, drift yo'q).- 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,
pgbilan yuborish — boshqa natija.
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):
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
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:
{ 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
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:
CREATE TABLE
oldim
-------
t
(1 row)
INSERT 0 1
bushatdim
-----------
t
(1 row)
qulflar
---------
0
(1 row)
nom
------------
107_expand
(1 row)
DROP TABLEINSERT … 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:
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/migrationspapkalari, tartiblangan) vamigratsiyaHolati(pool, koddagi)— «Holatni aniqlash mantig'i» bo'limidagi qoidalar, bazadan o'qish bilan.src/ishga-tushir.ts—migratsiyaniTekshir(yuqorida to'liq),omborYaratichida ikkala rejimda (postgres,prisma) fail-fast tekshiruvidan keyin chaqiriladi; xato bo'lsapool.end()va xato yuqoriga.test/migratsiya.test.ts— +2 test, vaqtinchalikvazifalar_test_migbazasida:nomalum→kutilmoqda→yiqilgan→ (resolve --rolled-backva 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:
npm run check
npm run db:migrate
NODE_ENV=production npm startOxirgisi 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;
downma'lumotni qaytarmaydi. - BOM: psql uni tashladi, Node
pgesasyntax 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
- PostgreSQL 18 hujjati — Advisory Locks,
pg_advisory_lockfunksiyalari: https://www.postgresql.org/docs/18/explicit-locking.html#ADVISORY-LOCKS - Prisma hujjati — Deploying database changes with Prisma Migrate; advisory locking;
migrate diff --exit-code: https://www.prisma.io/docs/orm/prisma-client/deployment/deploy-database-changes-with-prisma-migrate - Unicode — Byte Order Mark (U+FEFF) FAQ: https://www.unicode.org/faq/utf_bom.html
- Kanon o'lchovi (IlmHamroh, 2026-10): holat 40,
src/umumiy/migratsiya.ts, parallel deploy (Prisma 7.10.1, PostgreSQL 18.6, 2026-10-25). Advisory lock, BOM va JS — biz ishga tushirdik (PostgreSQL 17.6, pg 8.23.1, Node 24.21.0, Windows 11), 2026-10-27.
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!