Sıfır Kesintili Veritabanı Geçişleri: Expand-Contract Pattern'i
DB schema değişiklikleri, çoğu sistemin en korkulan deploy'larıdır. Doğru yaklaşımla sıfır downtime mümkün — yıllarca üretimde uyguladığım expand-contract pattern'i ve adım adım çalışma planı.
Bir DB migration ne zaman korkulur? Eski kod ile yeni şema arasında uyumsuzluk olduğunda. Deploy anında bir tarafta eski kod, diğerinde yeni şema — bir saniyelik bir uyumsuzluk window'u sistemi düşürebilir.
Çözüm: o window'u sıfırlamak. Bunu yapmanın adı "expand-contract" pattern'i.
Yanlış yaklaşım — neden işe yaramıyor
Bir kolonun adını değiştirmek istiyorsun. Klasik yol:
- Migration yaz:
ALTER TABLE users RENAME COLUMN name TO full_name - Kodu güncelle:
user.full_namekullan - Deploy
Bu basit görünür ama canlıda kırılır:
- Deploy sırasında 30 saniye boyunca eski instance'lar
namekolonunu okumaya çalışır → DB hata verir - Migration uzun sürer (10 milyon satırlık tablo) → 5 dakika boyunca hiçbir yazma çalışmaz
- Rollback gerekirse, "Migration'ı geri al" çoğu zaman pratik değildir
Doğru yaklaşım — expand-contract
İdeasını değişikliği 3 deploy'a yay:
Faz 1: Expand (genişlet)
Yeni alanı ekle, ESKİSİNİ DOKUNMA.
ALTER TABLE users ADD COLUMN full_name TEXT;
Bu deploy'da:
- Eski kod hâlâ
nameile çalışır - Yeni kod (henüz deploy edilmedi)
full_name'i kullanacak - Hiçbir kullanıcı etkilenmez
Şema iki sürümle de uyumlu — expand.
Faz 2: Migrate + dual-write
İki şey aynı anda:
a. Backfill — eski veriyi yeni kolona kopyala. Asenkron, batch halinde. 1 milyon satır için 30 dakika sürebilir, bekle:
UPDATE users SET full_name = name WHERE full_name IS NULL;
Bunu prod'da tek seferde yapma — 1000'lik batch'lerle, geceleri çalıştırma. pt-online-schema-change veya gh-ost gibi araçlar burada kullanışlı.
b. Dual-write — yeni kod hem name hem full_name'a yazsın:
user.name = value
user.full_name = value
db.save(user)
Bu deploy:
- Hâlâ eski kullanıcılar eski kodla çalışıyor (ama yazıyorlarsa eski kolon yazılıyor)
- Yeni kullanıcılar yeni kod, dual-write yapıyor
- Tutarsızlık yok
Faz 3: Cutover
Tüm okumayı full_name'e çevir:
return user.full_name # name değil
Deploy. Eski kod artık production'da yok. Bir hafta bekle, monitoring temiz mi bak.
Faz 4: Contract (daralt)
Şimdi eski alanı sil:
ALTER TABLE users DROP COLUMN name;
Hiçbir kod artık name'i okumuyor veya yazmıyor. Silmek güvenli. Contract.
Uzun süren migration'lar
10 milyon+ satırlık bir tablo için ALTER TABLE dakikalarca lock tutabilir. Production'da kabul edilemez.
Çözüm araçları:
- MySQL/MariaDB:
pt-online-schema-change— yeni tabloyu yan yana oluşturur, trigger'larla senkronize tutar, atomik swap yapar - PostgreSQL: çoğu schema değişikliği zaten lock-free veya kısa lock'lu.
CONCURRENTLYkeyword'ü index'lerde işe yarar - AWS Aurora / RDS:
gh-ostözellikle replikasyon-aware
Hiç tool kullanmadan da yapılabilir ama dikkatli olun:
- Index oluşturmak: PostgreSQL'de
CREATE INDEX CONCURRENTLY - Kolon eklemek (default'suz): hızlı, lock kısa
- Default ile kolon eklemek: PostgreSQL 11+ hızlı, eski versiyon ya da MySQL yavaş
Foreign key ve constraint değişiklikleri
Bu, migration'ın gizli kâbusu. Yeni constraint eklemek:
ALTER TABLE orders ADD CONSTRAINT fk_user FOREIGN KEY (user_id) REFERENCES users(id);
Eğer mevcut veri constraint'i ihlal ediyorsa, ALTER hata verir. Önce veriyi temizle, sonra constraint ekle. Aynı expand-contract:
- Önce sorunlu kayıtları bul, sil veya düzelt
- Sonra constraint ekle
Geri alma planı
Her faz'ın bir geri alma yolu olmalı:
- Faz 1 (expand): yeni kolonu drop et — kolayrebellion
- Faz 2 (dual-write): yeni kodu rollback et — eski kod hâlâ çalışır
- Faz 3 (cutover): yeni kodu rollback et — eski kod hâlâ çalışır (eski kolonu okur)
- Faz 4 (contract): geri alma yok. Bu noktaya geldiğinde 1 hafta beklemiş, monitoring'e güvenmişsindir.
Gerçek bir örnek
Üretim platformunda user_session.expires_at kolonunu Unix timestamp'ten TIMESTAMP WITH TIME ZONE'a çevirdik. Tablo 50 milyon satır.
- Faz 1:
expires_at_v2 TIMESTAMP WITH TIME ZONEekle (5 saniye lock) - Faz 2:
pt-online-schema-change-tarzı script ile gece batch'lerle backfill (3 gün) - Faz 3: Yeni kod,
expires_at_v2'yi okuyup yazar,expires_at'a sadece yazar (1 hafta) - Faz 4: Yeni kod sadece
expires_at_v2'yi yazar/okur (2 hafta sonra) - Faz 5:
expires_atkolonunu drop et
Tüm süreç boyunca sıfır incident. Bir kullanıcı bile etkilenmedi.
Sonuç
Sıfır kesintili migration mümkün, ama "tek deploy"a sığmıyor. Gerçek maliyeti sabır — 3-4 deploy'a yayılan değişiklik, fast-track yapılan migration'dan haftalar uzun sürer ama incident riski sıfır.
Bu bir trade-off: hız mı, güvenlik mi. Production sistemde her zaman güvenlik kazanır. Hız geri kazanılır, downtime kazanılmaz.
Distributed sistemler, üretim stabilitesi, AI destekli geliştirme üzerine uzun yazılar. Spam yok, çıkmak kolay.
Yorumlar
Henüz yorum yok. İlk yorumu sen bırak.