tolgacelik.net
Tüm yazılar
·3 dk

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ı.

databasemigrationschemadeploymentproduction

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:

  1. Migration yaz: ALTER TABLE users RENAME COLUMN name TO full_name
  2. Kodu güncelle: user.full_name kullan
  3. Deploy

Bu basit görünür ama canlıda kırılır:

  • Deploy sırasında 30 saniye boyunca eski instance'lar name kolonunu 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â name ile ç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. CONCURRENTLY keyword'ü 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:

  1. Önce sorunlu kayıtları bul, sil veya düzelt
  2. 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 ZONE ekle (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_at kolonunu 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.

PaylaşLinkedInX
Bir sonraki yazı doğrudan kutuna düşsün

Distributed sistemler, üretim stabilitesi, AI destekli geliştirme üzerine uzun yazılar. Spam yok, çıkmak kolay.

Sadece e-posta. Bu sunucuda saklanır. Hiçbir yerde paylaşılmaz, satılmaz.

Yorumlar

Henüz yorum yok. İlk yorumu sen bırak.

Yorum bırak

Hiçbir yerde gösterilmez. Sadece gerekirse cevap vermek için.