EF CORE MIGRATIONS STRATEJİSİ

Veri Akademi7 dk okuma
Üst üste binmiş veritabanı katmanları ve mor versiyon noktalarıyla EF Core migration sürüm hattı

Cuma akşamı saat 19:00. Üretim deploy'u başladı. dotnet ef database update komutu çalıştı, transaction yarıda kaldı, veritabanı yarı-migrated halde donuk. Müşteri telefon ediyor, sistem yarın açık olmak zorunda. Bu sahne neredeyse her .NET takımının bir noktada yaşadığı klasik bir migration kazasıdır. Sorunun kaynağı genellikle EF Core'un kendisi değildir; migration'ların stratejisiz biçimde üretim ortamına bırakılmış olmasıdır. Bu yazıda hangi senaryoda hangi yaklaşımın seçileceğini, dev/prod farkını ve veri kaybı riskinin nasıl kontrol altına alındığını ele alıyoruz.

1. EF Core Migrations Ne Yapar

Migration, kod tarafındaki DbContext ve entity sınıflarındaki değişikliklerden veritabanı şemasına karşılık gelen SQL'i üretir. Klasik akış üç komutla yürür: dotnet ef migrations add <Ad> ile değişikliği yakalar, dotnet ef migrations script ile SQL'i çıkarır, dotnet ef database update ile uygular. Üretilen migration dosyası iki metot içerir: Up() şemayı ileri taşır, Down() geri alır.

Bu mekanizma yerel makinede çalıştırırken çok rahattır. Bir property ekledin, migration üret, çalıştır, bitti. Sorun, aynı rahatlığı üretime taşımaya kalktığında başlar.

2. Geliştirme ve Üretim Ortamı Aynı Şey Değildir

Geliştirme ortamında veritabanı tek kullanıcılıdır, downtime kabul edilebilir, veri kaybı umursanmaz çünkü kolayca yeniden seed edilir. Üretimde ise tam tersi: çok kullanıcı, sıfıra yakın downtime, gerçek müşteri verisi. Aynı komutu (database update) iki ortamda çalıştırmak yaygın bir hatadır.

Üretime giden her migration'ın önceden gözden geçirilmesi gerekir. Auto-migrate akışı — yani uygulamanın açılışta db.Database.Migrate() çağırması — geliştirmede idealdir, üretimde tehlikelidir. Birden çok instance aynı anda ayağa kalkarsa race condition oluşur. Migration'ın yaratacağı kilit pattern'i, özellikle SQL Server'da büyük tablolarda ALTER TABLE, saatlerce sürebilir ve uygulama bu süre boyunca açılış aşamasında takılı kalır.

3. Üretim İçin Üç Sağlıklı Strateji

Pratikte üç temel yaklaşım yaygındır. Hangisinin seçileceği takım büyüklüğüne, deploy frekansına ve veritabanı boyutuna bağlıdır.

  • Idempotent SQL script: dotnet ef migrations script --idempotent komutu, hangi migration'ların uygulandığını kontrol eden, defalarca çalıştırılabilen bir SQL dosyası üretir. DBA bu scripti inceler, gerekirse manuel düzenler, ayrı bir adımda çalıştırır. Üretim için en güvenli yaklaşım budur.
  • Bundle (EF Core 6 ve sonrası): dotnet ef migrations bundle standalone bir executable üretir. .NET SDK'sı kurulu olmayan sunucuda da çalışır. CI/CD adımlarında kullanışlıdır, deploy artifact'ı tek dosyadır.
  • Otomatik Migrate (yalnızca küçük ölçek): Uygulama başlangıcında db.Database.Migrate(). Tek instance, küçük tablolar, kabul edilebilir downtime varsa kullanılabilir. Çoklu instance veya kritik üretim için uygun değildir.

Konunun detaylarını uçtan uca işleyen Entity Framework Core eğitimi migration stratejileri, performans tuning ve LINQ optimizasyonunu birlikte ele alır.

Soldaki açık kapılı geliştirme kasası ve sağdaki kilitli üretim kasası ile dev ve prod ortamı karşılaştırması

4. Veri Kaybına Karşı Önlemler

EF Core bazı şema değişikliklerini destructive olarak işaretler ve uyarı verir. Sütun silme, sütun tipi daraltma, NOT NULL ekleme bu sınıfa girer. Auto-generated migration veri kaybı taşıyorsa Up() metodu migrationBuilder.DropColumn gibi çağrılar içerir. Bunu üretimde fark etmemek tamamen mümkündür.

Rename işlemi özellikle tuzaklıdır. EF Core property adının değiştiğini anlayamaz — drop ve add olarak yorumlar. Bunun yerine migration dosyasını manuel açıp migrationBuilder.RenameColumn çağrısıyla değiştirmek gerekir. Aksi takdirde eski sütundaki veri tamamen kaybolur.

Pratik bir kural: her destructive migration için önce expand sonra contract adımı yapılır. Yeni sütun eklenir, uygulama iki sütuna da yazar, eski sütun ayrı bir deploy'da temizlenir. Bu yaklaşım Martin Fowler'ın yıllar önce yazdığı evolutionary database design prensiplerine dayanır ve resmi dokümantasyonda da bu sırayla önerilir.

5. CI/CD Pipeline'a Entegrasyon

Migration deploy'u uygulama deploy'undan ayrılmalıdır. Tipik bir pipeline'da sıralama şu olur:

  1. Build aşamasında dotnet ef migrations script --idempotent --output migrate.sql üretilir, artifact olarak saklanır
  2. Staging veritabanında otomatik çalıştırılır, smoke test koşulur
  3. Üretim deploy'undan önce migration adımı çalışır (uygulama instance'ları henüz yeni kodu görmüyor)
  4. Yeni kod deploy edilir

Bu sıralama, "uygulama yeni şemayı bekliyor ama şema eski" senaryosunu engeller. Tersi durumda — kod eski, şema yeni — genellikle daha az hasarlı olur çünkü iyi yazılmış bir migration backward-compatible olur. Şema değişiklikleri ve uygulama sürümleri arasındaki bu pencere SQL pratiklerini de ilgilendirir; konuyu derinleştirmek için SQL eğitimi içeriklerinden yararlanabilirsiniz.

Build, script, staging ve prod aşamalı CI pipeline akışı yanında geri alma okuyla yedek kalkanı sembolü

6. Yaygın Tuzaklar

  • Migration dosyasını sonradan düzenlemek: Üretimde uygulanmış bir migration'ın Up() metodu değişmez. Yeni bir migration ekleyerek düzelt.
  • Aynı feature branch'te birden çok migration: Merge çakışmaları kaçınılmaz. Branch'i merge etmeden önce migration'ları squash et veya tek bir migration olarak yeniden üret.
  • Migration history tablosunu manuel düzenlemek: __EFMigrationsHistory tablosuna elle row eklemek kısa vadede sorunu örter, uzun vadede kaos yaratır.
  • Test ortamında migration test edilmemesi: CI'da prod benzeri veri hacmiyle migration süresi ölçülmelidir. 10 kayıtla 200 ms süren bir ALTER TABLE 5 milyon kayıtla 40 dakika sürebilir.
  • Connection string'i build-time'da hard-code etmek: Bundle veya script üretilirken farklı bir veritabanına bağlandığını sanmak yaygın bir hata kaynağıdır. Environment variable üzerinden geçilmelidir.

Migration stratejisi bir kez kurulduktan sonra takım için disipline edilmiş bir akış haline gelir. Önemli olan üretimi "yerel makineden bir komut" olarak görmemek, deploy zincirinin denetlenebilir bir parçası haline getirmektir. İlk birkaç migration'da harcanan zaman, ileride yaşanmayacak gece nöbetlerinin karşılığıdır.

Sıkça Sorulan Sorular

Add-Migration ile dotnet ef migrations add komutu arasında fark var mı?

İkisi aynı işi yapar. Add-Migration, Visual Studio'nun Package Manager Console'unda çalışan PowerShell cmdlet'idir. dotnet ef migrations add ise terminalden çalışan CLI komutudur. Çıktıları aynı migration dosyasıdır. Ekipler genellikle CI uyumu için CLI versiyonunu tercih eder.

Üretimde db.Database.Migrate() çağırmak hangi durumlarda kabul edilebilir?

Tek instance çalışan küçük uygulamalarda, dakikalar süren downtime'ı tolere eden iç araçlarda ve veritabanı kilitlenmesinin kritik olmadığı senaryolarda kabul edilebilir. Yüksek trafikli web API'leri, çoklu instance'lı uygulamalar veya 7/24 SLA'sı olan sistemler için uygun değildir; race condition ve uzun süren ALTER işlemleri risk yaratır.

Idempotent SQL script ne anlama gelir?

Script, hangi migration'ların uygulandığını __EFMigrationsHistory tablosundan kontrol eder ve sadece eksik olanları çalıştırır. Aynı scripti birden çok kez çalıştırmak güvenlidir. Bu özellik DBA'ya elle inceleme ve tekrar deneme imkânı verir. Standart script çıktısının aksine sadece bekleyen migration'ları işler, kontrol mantığı SQL'in içine gömülüdür.

EF Core Bundle ile Idempotent Script arasında nasıl seçim yapılır?

Bundle, deploy hedefinde .NET SDK'sı bulunmadığında veya migration'ı tek artifact olarak taşımak istediğinde uygundur. Idempotent script, DBA'nın SQL'i okuyup inceleyebilmesi gereken kurumsal ortamlarda tercih edilir. Bundle daha kapalıdır, script daha şeffaftır. Regülasyon ağır ortamlar genellikle script'i seçer.

Migration sırasında uzun süren ALTER TABLE nasıl yönetilir?

Büyük tablolarda ALTER TABLE saatlerce sürebilir ve tabloyu kilitler. Çözüm, online schema change araçları (pt-online-schema-change, gh-ost) veya expand-then-contract yaklaşımıdır. Yeni sütun eklenir, uygulama yeni sütunu doldurur, arka planda eski veri taşınır, son adımda eski sütun düşürülür. EF Core'un default migration'ı bunu otomatik yapmaz, manuel kurgu gerektirir.

Birden fazla geliştirici aynı anda migration üretirse merge conflict nasıl çözülür?

EF Core migration'ları timestamp ile sıralanır. İki kişi aynı zaman diliminde migration üretirse model snapshot dosyasında çakışma olur. Çözüm: branch'lerden biri merge edildikten sonra ikincisi migration'ı drop edip yeniden üretir. Bu, migration sırasının doğru kalmasını sağlar. Pratikte, ekipler migration'ları küçük ve sık tutarak çakışma riskini azaltır.

__EFMigrationsHistory tablosu bozulursa ne yapılır?

Önce yedeklemek şart. Bozulma durumunda iki yol vardır: yeni boş bir veritabanına migration'ları sıfırdan uygulayıp şemayı karşılaştırmak veya tablodaki eksik kayıtları manuel eklemek. İkinci yol risklidir; sadece DBA gözetiminde, hangi migration'ların gerçekten uygulandığını DDL üzerinden teyit ederek yapılır. Manuel müdahale her zaman son çare olmalıdır.