Veri tabanı şema değişiklikleri (kolon ekleme/çıkarma, index, constraint, tip dönüşümü) çoğu ekipte “DDL çalıştırıp geçmek” gibi görülür. Kurumsal ölçekte ise şema değişikliği; deploy, trafik, replikasyon, lock davranışı ve rollback ile birlikte ele alınmadığında bir anda incident’a dönüşür.
Bu yazı; “downtime istemiyorum ama güvenli değişiklik yapmak istiyorum” diyen ekipler için, üretimde defalarca işe yarayan bir çerçeveyi anlatır: expand/contract, backfill ve gerektiğinde dual write.
Hedef: “kesintisiz” değil, “kontrollü risk”
Benim gerçekçi hedefim şudur:
- Kullanıcıya görünür kesinti olmasın (ya da çok kısa ve planlı olsun)
- Değişiklik geri alınabilir olsun
- Sinyaller ve guardrail’ler tanımlı olsun
Expand/Contract nedir?
Expand/contract (genişlet/daralt) yaklaşımı; şema değişikliğini tek adımda değil, iki (bazen üç) fazda yapmaktır.
- Expand: Yeni alanı ekle, eskiyi bozma
- Migrate: Veri taşı, uygulamayı yeni alana geçir
- Contract: Eski alanı kaldır / constraint’i sıkılaştır
Bu yaklaşımın gücü şurada:
- Uygulama ile DB aynı anda “keskin dönüş” yapmaz.
- Rollback mümkün olur: uygulamayı eski alana geri alabilirsin.
1) Expand fazı: Yeni alanı ekle, kırma
Tipik adımlar:
- Yeni kolon ekle (
NULLkabul eden) - Yeni index’i “non-blocking” yöntemle oluştur (DB motoruna göre)
- Yeni tablo/ilişki ekle (eski path’i bozmadan)
Bu fazın kuralı: eski uygulama sürümü çalışmaya devam edebilmelidir.
Risk noktaları:
- “DEFAULT + NOT NULL” gibi DDL’ler bazı DB’lerde uzun süre lock alabilir
- Büyük tabloda index oluşturma, replikasyonu geriye düşürebilir
2) Backfill: Veriyi taşımak bir ETL değildir
Backfill, çoğu zaman asıl maliyettir. Sahada şu hataları çok gördüm:
- “tek seferde update” ile tabloyu kilitlemek
- Backfill sırasında replication lag’i patlatmak
- Backfill’i izlememek (ne kadar kaldı, rate nedir, hata oranı nedir?)
Benim pratik önerim:
- Backfill’i batch yap (ör. 1k–10k satır)
- Her batch arasında kısa bekleme (DB nefes alsın)
- İlerlemeyi idempotent bir şekilde kaydet (checkpoint)
- Hata durumunda devam edebil (resume)
Örnek “batch backfill” mantığı (pseudocode):
-- son_islenen_id'yi bir yerde tut
UPDATE my_table
SET new_col = transform(old_col)
WHERE id > :last_id
ORDER BY id
LIMIT :batch_size;
3) Dual write: Son çare, ama bazen gerekli
Dual write; uygulamanın bir süre hem eski hem yeni alana yazmasıdır. Güçlüdür ama risklidir:
- İki yere yazma = iki farklı failure mode
- Partial write (birine yazdı, diğerine yazamadı)
- Ordering ve retry ile tutarsızlık
Dual write kullanacaksan:
- “source of truth” belirle (hangisi esas?)
- Tutarsızlık tespit sinyali üret (reconciliation job)
- Dual write süresini kısa tut (haftalarca sürmesin)
Contract fazı: Eskiyi kaldırmadan önce “kanıt” üret
Contract aşamasına geçmeden önce ben şu kanıtları ararım:
- Yeni kolon/tabela kullanım oranı %99+ (read path)
- Eski alan artık okunmuyor (log/metric ile)
- Reconciliation job 0 mismatch veriyor
- Rollback planı son bir kez gözden geçirildi
Contract örnekleri:
- Eski kolonu kaldır
NOT NULLconstraint’i ekle- Unique constraint’i sıkılaştır
Observability: Migrasyon sırasında izlemen gerekenler
Minimum izleme listem:
- DB: CPU, I/O, lock waits, slow queries
- Replication lag (varsa)
- Uygulama: error rate, latency (özellikle write path)
- Backfill job: batch/s, remaining rows, failures
Alarm prensibi:
- Migrasyon sırasında “normalde sessiz” olan metrikleri alarm eşiğini geçici ayarla
- Geri dönüş (rollback) için tetikleyici eşiği belirle
Rollback planı: Kağıt üzerinde değil, adım adım
Rollback planı şunları içermeli:
- Uygulama: eski sürüme dönüş (feature flag / config)
- DB: yeni kolon durabilir (expand fazı rollback genelde “şemayı silmek” değildir)
- Veri: backfill geri alınacak mı, yoksa bırakılacak mı?
Benim sık kullandığım yaklaşım:
- Expand fazında rollback = uygulamayı geri al, şema kalsın
- Contract fazından önce rollback = contract’ı yapma, borçla yaşamaya devam etme
Sonuç
Online schema migrasyonu; DDL komutlarının toplamı değil, operasyonel bir süreçtir. Expand/contract ile riskini küçült, backfill’i kapasite bütçesiyle yönet, dual write’ı kontrollü ve kısa süreli kullan, contract ile borcu kapat. En önemlisi: her fazı ölç, kanıtla ve rollback’i “gerçekten uygulanabilir” hale getir.