CI/CD'de veritabanı göçlerini (migrations) güvenli nasıl çalıştırırım?
Soru
CI/CD sürecimizde `php artisan migrate` deployment adımları arasında otomatik çalışıyor. Bir migration çok büyük bir tabloya kolon eklerse ve uzun sürerse pipeline timeout'a düşüyor; ya da o sırada canlıda olan eski kod, DB değişikliği yüzünden hata vermeye başlıyor. Büyük projelerde "backward-compatible migration" prensiplerini CI/CD akışında nasıl uygularım?
Cevap
Kısa cevap: Asıl hata, şema değişikliğini eski kod hâlâ canlıyken deploy’a sıkı sıkıya bağlamak — çözüm, geriye uyumlu (expand/contract) migration’ları kod deploy’undan ayırmak.
Kısa cevap
Problemi netleştirelim: ya eski kod yeni şemayla karşılaşıp patlıyor, ya da büyük bir ALTER tabloyu kilitleyip pipeline’ı timeout’a düşürüyor. İkisi de aynı kökten — kuplaj. Aynı geçişin canlı tablo tarafını, yani veri kaybetmeden dual-write ile şema değiştirmeyi ayrı bir kayıtta ele almıştım; buradaki soru o geçişi CI/CD adımlarına nasıl dağıtacağınız.
Neden
-
Eski kod yeni şemayı görmek zorunda kalıyor. Deploy sırasında bir süre boyunca canlıda olan kod, henüz kendisi için yazılmamış bir şemayla konuşur; kolonu tanımadığı ya da beklediği kolonu bulamadığı anda hata vermeye başlar.
-
Büyük ve kilitleyici bir
ALTERdeploy adımının içine sığmaz. Milyonlarca satırlık bir tabloda bazı şema değişiklikleri dakikalar sürer. PostgreSQL 17’de bir kolonun tipini değiştirmek ya da volatileDEFAULT’lu kolon eklemek tabloyuACCESS EXCLUSIVEkilidi altında baştan yazar; düz, nullable birADD COLUMNyeniden yazımı atlar ama yine de o kilidi alır. MySQL 8.4’teADD COLUMNvarsayılan olarakINSTANT’tır, ama her işlem öyle değildir. Ağır iş deploy adımının içindeyse pipeline timeout’a düşer, tablo da o süre boyunca kilitli kalır. -
Kök neden kuplaj. Şema değişikliği ile kod deploy’u aynı ana bağlandığı sürece bu iki hata da kaçınılmazdır; çözüm de tek tek semptomları değil, bu bağı gevşetmektir.
Ne yapmalı
-
Expand/contract disiplinini benimse. Aynı release’de bir kolonu ekleyip onu gerektiren kodu deploy etme; asla rename/drop’u kullanan release ile aynı anda yapma. Önce kolonu nullable ekle, sonra “hem eskiye hem yeniye yazan” kodu deploy et, en son ayrı bir release ile eskiyi kaldır.
-
Backfill’i request yolundan ve deploy adımından çıkar. Büyük tabloyu doldurma işini ayrı, throttle’lı bir adımda yap — deploy adımının transaction’ı içinde değil. Aksi halde tek bir dev
UPDATEhem kilitler hem timeout’a sokar. -
Migration’ı kendi pipeline aşaması yap, timeout baskısı olmadan. Şema değişikliğini app deploy’undan ayrı bir stage’e al. Büyük/kilitleyici değişiklikler için
pt-online-schema-change/gh-ostgibi online-DDL araçlarıyla bant dışı çalıştır; tabloyu kilitlemeden kolonu ekler. -
İdempotent ve geri alınabilir yaz, deploy’u migration’a gate’le. Migration tekrar çalıştığında bozulmasın, geri dönüşü olsun. Deploy’u migration başarısına bağla; ama yıkıcı adımları (drop, rename) yeni kod tamamen yayıldıktan sonraki bir follow-up release’e bırak.
Sonuç: Ben olsam expand/contract’ı kural yapar, migration’ları app deploy’undan ayırır ve büyük/kilitleyici değişiklikleri online araçlarla kritik yolun dışında çalıştırırdım. Şema değişikliğini hiçbir zaman “deploy’un ortasında, eski kod canlıyken” yapma — onu kademeli ve geriye uyumlu yürüt; pipeline timeout’u da, eski kodun patlaması da o zaman ortadan kalkar.
Yorumlar
Yorum yapmak için GitHub hesabınızla giriş yapmanız yeterli. Yorumlar GitHub Discussions üzerinde saklanır.