Veri göçü neden projenin en riskli adımıdır?
Veri göçü risklidir çünkü tek seferliktir ve hatası çoğu zaman geri alınamaz. Kod hatalıysa düzeltip yeniden yayınlarsınız; veri yanlış taşındıysa ve üstüne yeni kayıtlar girilmişse, geri dönüş bir düzeltme değil bir kurtarma operasyonudur. Üstelik hata çoğu kez sessizdir: sistem çalışır, ekranlar açılır, sadece bazı alanlar boştur ya da bazı ilişkiler kopmuştur.
İkinci zorluk, göçün bir teknik iş sanılmasıdır. Aslında göç önce bir karar işidir: neyi taşıyoruz, neyi taşımıyoruz, hangi kayıt hangi yeni yapıya karşılık geliyor, çakışanları ne yapacağız. Bu kararlar verilmeden yazılan betik, eski veriyi yeni tabloya taşır ama anlamı taşımaz — ve anlam kaybı, veriden çok daha pahalıdır.
Bu yazıda kendi göçlerimizden çıkardığımız dersleri anlatacağız: sabit şemalı bir içerik yapısından esnek bir modele geçiş, canlıda çalıştırılamayan bir kurulum betiğinin nasıl hedefli güncelleyicilere dönüştüğü ve göç doğrulamasının neden ekran değil sayım işi olduğu. Hepsi yaşanmış, bazıları canlı veritabanında.
Önce karar: neyi taşıyorsunuz, neyi bırakıyorsunuz?
Göçün ilk adımı taşımamaya karar vermektir. Eski sistemlerde biriken verinin önemli bir kısmı ölü kayıttır: iptal edilmiş taslaklar, test amaçlı girilmiş satırlar, artık kullanılmayan bir özelliğin geçmişi. Bunları taşımak yalnız yer kaplamaz; yeni sistemin ekranlarını kirletir ve kullanıcıya 'burada bir sürü çöp var' hissi verir. Taşınmayacaklar için ise bir arşiv alıp saklamak yeter — silmekle taşımak arasında üçüncü bir yol vardır.
İkinci karar, hangi verinin gerçekten kaynak olduğudur. Çoğu sistemde aynı bilgi birden çok yerde durur: bir müşteri adı hem sipariş kaydında hem müşteri tablosunda yazılıdır. Göç sırasında hangisinin doğru sayılacağını önceden belirlemezseniz, betik ikisini de taşır ve yeni sistemde iki farklı doğru oluşur. Kaynak seçimini veriye bakarak değil, işe sorarak yaparsınız: 'bu bilgiyi kim, hangi ekranda günceller?'
Üçüncüsü, tarih meselesidir. Geçmiş kayıtların ne kadar geriye taşınacağı bir maliyet kararıdır ve müşteriyle birlikte verilmelidir. 'Hepsi gelsin' cümlesi kolaydır ama on yıllık veriyi yeni modele uydurmak, göç süresini ikiye katlayabilir. Biz genellikle şunu öneriyoruz: aktif veri tam taşınır, geçmiş veri okunabilir bir arşiv olarak saklanır.
Hedef modeli tasarlamak: eski yapıyı kopyalamayın
Göçün en büyük tuzağı, eski tabloların yeni sisteme birebir kopyalanmasıdır. Bu, ilk gün en hızlı yoldur ve ikinci yıl en pahalı yoldur: eski sistemin sınırları, hataları ve sözde-çözümleri yeni sistemin içine taşınmış olur. Yeni yapı, eski verinin şeklinden değil, işin bugünkü ihtiyacından doğmalıdır; göç betiği de aradaki farkı çeviren katmandır.
Kendi ürünümüzde bunu birebir yaşadık. İçerik yönetiminin ilk sürümünde her içerik tipinin kendi tablosu vardı: hizmetler bir tabloda, projeler başka bir tabloda, yazılar üçüncüde. Yeni bir tip eklemek yeni tablo, yeni model, yeni ekran demekti. İkinci sürümde hepsini tek bir esnek yapıya taşıdık: bir kayıt tipini, anahtarını ve serbest şemalı gövdesini taşıyor. Göç bittikten sonra eski tabloları sildik — iki modeli aynı anda yaşatmak, en pahalı seçenek olurdu.
Eski yapıyı silmek cesaret ister ama gereklidir. İki model bir arada yaşadığı sürece her yeni özellik iki kez yazılır, her hata iki yerde aranır ve ekip hangisinin güncel olduğunu unutur. Kural şu olmalı: göç doğrulanana kadar eskisi durur, doğrulandıktan sonra kaldırılır — ve 'ne olur ne olmaz' diye süresiz bekletilmez.
Kalıcı anahtar hangisi: satır numarası mı, adres anahtarı mı?
Göçten sonra da geçerli kalacak anahtar, veritabanının verdiği satır numarası değil, işin verdiği anlamlı anahtardır. Satır numaraları taşıma sırasında değişir; kayıtları silip yeniden yazan bir betik çalıştırdığınızda hepsi yeni numara alır. O numaraya bağladığınız her şey — dış bağlantılar, önbellek anahtarları, başka tablodaki referanslar — sessizce kopar.
Bunu kendi içerik tablomuzda gördük. Yazıları toplu olarak yeniden yazan bir güncelleyici çalıştırdığımızda satır numaraları değişti; ama sitedeki adresler numaraya değil, insan-okur adres anahtarına bağlı olduğu için hiçbir bağlantı kırılmadı. Karar erken verilmişti ve karşılığını o gün aldı. Numaraya bağlı olsaydık, göç sonrası bütün dış bağlantılar 404 verecekti.
Kural olarak: dış dünyaya görünen her şeyin (adres, dosya adı, referans kodu) anlamlı ve değişmeyen bir anahtarı olsun; satır numarası yalnız sistemin içinde kalsın. Bir de göç sırasında eşleme tablosu tutun — eski numara ile yeni kayıt arasındaki karşılık, sorun çıktığında elinizdeki tek haritadır.
Göç betiğini elle yazmayın, üretin
Göç betiği elle yazıldığında iki sorun doğar: tekrar çalıştırılamaz ve içeriğin kendisiyle senkron kalmaz. Bizim yaklaşımımız, betiği kaynağından üretmek oldu — içerik hangi dosyada tanımlıysa, betik o dosyadan otomatik olarak çıkar. Böylece içerik değiştiğinde betiği güncellemek diye bir iş kalmıyor; üreteci yeniden çalıştırıyorsunuz.
Bu yaklaşımın ikinci faydası, üreticinin kurallarını tek yerde tutabilmenizdir. Örneğin bizde bir kural var: üretilen betik yayın bayraklarına dokunmaz. İçerik metnini günceller ama 'bu kayıt yayında mı' bilgisini asla ezmez, çünkü o bilgi yönetim panelinden yönetiliyor. Elle yazılan betiklerde bu kuralı her seferinde hatırlamak gerekirdi; üretilen betikte kural üretecin içinde yaşıyor.
Üçüncü fayda gözden geçirmedir. Üretilen betik metin dosyasıdır; sürüm kontrolüne girer, fark olarak okunur ve uygulamadan önce başka biri bakabilir. 'Canlı veritabanında ne çalıştırıldı' sorusunun cevabı, bir kişinin hafızasında değil geçmişte durur. Veri değiştiren işlerde bu, geriye dönük en değerli kayıttır.
Neden canlıda kurulum betiği çalıştırılmaz?
Kurulum betikleri genellikle 'sıfırdan kur' mantığıyla yazılır: tabloları boşaltır, sonra hazır veriyi yazar. Bu, boş bir geliştirme veritabanında mükemmel çalışır ve canlıda felakettir — çünkü boşaltma adımı, yönetim panelinden girilmiş her şeyi de siler. Aynı dosyayı hem kurulum hem güncelleme için kullanmak, göç kazalarının en yaygın sebebidir.
Biz bu duvara çarptık. Sitenin içerik verisi kurulum betiğiyle üretiliyordu ve o betik, tabloları boşaltarak başlıyordu; dolayısıyla canlıda hiç çalıştırılamıyordu. Bir sektörün metnini düzeltmek gerektiğinde tek yol elle SQL yazmaktı — yani üretecin dışında, kayıt altına alınmayan bir müdahale. Sonunda doğru çözümü kurduk: aynı kaynaktan, hedefli güncelleyiciler üreten ayrı araçlar. Boşaltma yok; kayıt varsa güncelle, yoksa ekle.
Genel kural şudur: canlı veriye dokunan her betik idempotent olmalı, yani iki kez çalıştırıldığında ikinci sefer zarar vermemeli. Bunu sağlamanın en basit yolu, işlemleri 'varsa güncelle, yoksa ekle' biçiminde yazmak ve silme adımlarını betikten tamamen çıkarmaktır. Silinmesi gereken bir şey varsa, o ayrı bir karar ve ayrı bir betiktir.
Yapılı veri tuzakları: sessizce yanlış kaydeden üç şey
Birincisi tip dönüşümüdür. Yapılı bir değeri (iç içe alanlar taşıyan bir gövde) veritabanına yazarken, onu metin olarak değil yapı olarak kaydetmeniz gerekir. Aradaki farkı kaçırdığınızda kayıt hatasız tamamlanır; sorun okuma tarafında ortaya çıkar: beklediğiniz alanlar yerine düz bir metin bulursunuz. Hiçbir istisna fırlamadığı için bu hata, ekranı açana kadar görünmez.
İkincisi otomatik numaralandırmadır. Kayıtları kendi numaralarını vererek eklerseniz, veritabanının sıradaki numarayı üreten sayacı geride kalır. Göç günü her şey yolunda görünür; uygulama ilk yeni kaydı eklemeye çalıştığında 'bu numara zaten var' hatası alırsınız. Çözüm tek satırdır — sayacı en büyük numaranın ötesine taşımak — ama akla gelmesi için bir kez yaşamak gerekiyor.
Üçüncüsü karakter kodlamasıdır ve Türkçe içerikte özellikle acıtır. Eski sistemden alınan dökümün kodlaması ile hedef veritabanının beklediği kodlama uyuşmazsa, veri taşınır ama 'ş', 'ğ', 'İ' gibi harfler bozulur. Bunu göçten sonra fark etmek, elle düzeltilecek binlerce satır demektir. Prova göçünde yalnız sayıları değil, Türkçe karakter içeren birkaç kaydı da gözle kontrol edin.
Eski sistemden dökümü almak: erişim, biçim, izin
Göç projelerinin çoğu teknik bir engelle değil, erişim sorunuyla başlar: eski sistemin veritabanına kimse erişemiyordur, sağlayıcı dökümü vermek istememektedir ya da veri yalnız ekrandan okunabilir hâldedir. Bu yüzden göç planının ilk maddesi kod değil, izindir — dökümü kimin, hangi yetkiyle, hangi biçimde vereceği yazılı hâle gelmeden takvim yapmak boşunadır.
Biçim ikinci meseledir. En iyisi doğrudan veritabanı dökümüdür; ikincisi tablo tablo standart bir dışa aktarımdır; en kötüsü rapor çıktısı ya da elektronik tablodur, çünkü orada ilişkiler kaybolmuş, tarihler biçimlenmiş ve sayılar metne dönmüş olur. Elinizde yalnız kötü biçim varsa, göçün bir bölümü veri temizliği işine dönüşür ve bu ayrı bir emek kalemidir.
Üçüncüsü, dökümün ne zaman alındığıdır. Bir hafta önce alınmış döküm üzerinde çalışıp göç gecesi aynı betiği taze dökümle koşmak doğru yöntemdir; ama iki döküm arasında şema değiştiyse betik patlar. Bu yüzden prova ile gerçek göç arasındaki sürede eski sistemde yapılan her değişikliği bilmeniz gerekir — 'küçük bir alan eklemişler' cümlesi, göç gecesinin en sık duyulan sürprizidir.
Eşleme tablosu: alan alan karşılık çıkarmak
Göçün gerçek belgesi eşleme tablosudur: eski sistemdeki her alanın yeni sistemde hangi alana karşılık geldiğini, dönüşüm gerekiyorsa kuralını ve karşılığı olmayan alanlar için kararı gösteren liste. Bu tablo çıkarılmadan yazılan betik, yazan kişinin o anki varsayımlarını taşır ve altı ay sonra kimse neden öyle yapıldığını hatırlamaz.
Eşleme çıkarırken en çok vakit alan kısım, karşılığı olmayan alanlardır. Eski sistemde bir alan vardır ama yeni modelde yoktur; ya işe yaramadığı için atılacaktır ya da yeni modelde başka bir yapıya dönüşecektir. Bu kararı geliştirici tek başına veremez — 'bu alanı kim dolduruyordu ve neye göre karar veriyordu' sorusunun cevabı işin içindedir.
Bir de kod listeleri vardır: durum, tip, kategori gibi kısıtlı değer kümeleri. Eski sistemdeki 'aktif/pasif/beklemede' değerleri, yeni sistemdeki karşılıklarıyla birebir eşleşmeyebilir. Bu eşlemeyi tabloya yazmak, göç sonrası 'neden bütün kayıtlar beklemede görünüyor' sorusunu baştan engeller.
Kullanıcılar ve dosyalar: veritabanı dışında kalan veri
Kullanıcı verisi göçün en hassas parçasıdır ve parolalar taşınmaz. Doğru kurulmuş bir sistemde parolalar geri döndürülemez biçimde saklanır; farklı bir yöntem kullanan yeni sisteme bunları taşımak çoğu zaman mümkün değildir ve mümkün olduğunda bile doğru değildir. Standart çözüm, kullanıcıları taşıyıp parola belirleme akışını başlatmaktır: herkes ilk girişte yeni parolasını kurar.
Bunun iletişim tarafı teknik tarafından daha önemlidir. Kullanıcılara ne olacağını önceden anlatmazsanız, göç sabahı gelen 'parolam çalışmıyor' çağrıları destek hattını kilitler. Basit bir bilgilendirme ve doğru zamanlanmış bir parola sıfırlama bağlantısı, göçün en görünür kısmının sorunsuz geçmesini sağlar.
Dosyalar ise sıklıkla unutulur: yüklenmiş görseller, belgeler, ekler. Bunlar veritabanında değil dosya sisteminde ya da nesne depolamada durur; veritabanı göçü kusursuz olsa bile dosyalar taşınmadıysa yeni sistemde her yerde kırık bağlantı görürsünüz. Dosya göçünü ayrı bir adım olarak planlayın ve doğrulamasını da ayrı yapın: kaç dosya vardı, kaçı taşındı, kaç kayıt hâlâ olmayan bir dosyayı işaret ediyor.
Göçü nasıl doğrularsınız: ekrana değil sayıma bakın
Göç doğrulaması ekran açarak yapılmaz, sayarak yapılır. Ekran size yalnız açtığınız kaydı gösterir; taşınan on binin içinde kaç tanesinin eksik olduğunu söylemez. Doğru yöntem, göçten önce ve sonra aynı soruları sormaktır: kaç kayıt vardı, kaç tane geldi, kaç tanesinde şu alan boş, kaç tanesinin ilişkisi kayıp. Aradaki fark sıfır değilse göç bitmemiştir.
Biz canlı veriye dokunmadan önce her zaman salt-okunur bir kontrol koşuyoruz: hedef tabloda kaç kayıt var, hangileri elle girilmiş olabilir, betiğin sileceği bir şey var mı. Bu kontrol bir dakikadan kısa sürüyor ve bir kez bile 'iyi ki bakmışız' dedirttiyse kendini ödemiş oluyor. Ardından betik çalıştırılıyor ve aynı sayımlar tekrarlanıyor.
Sayımların yanına iki şey daha ekleyin: rastgele örnekleme ve geri okuma. Örnekleme, taşınan kayıtlardan birkaçını açıp eski sistemdeki hâliyle karşılaştırmaktır. Geri okuma ise yeni sistemin o kaydı gerçekten kullanabildiğini görmek — veri doğru yazılmış olabilir ama uygulama onu okuyamıyor olabilir. Üçü bir arada, 'göç tamam' demek için yeterli kanıttır.
Kirli veriyi göç sırasında mı temizlemeli, sonra mı?
Eski sistemden çıkan veri neredeyse hiçbir zaman temiz değildir: iki kez girilmiş müşteriler, boş bırakılmış zorunlu alanlar, elle yazıldığı için üç farklı biçimde duran telefon numaraları. Bunları göç sırasında düzeltmek cazip gelir ama tehlikelidir — göç betiği aynı anda hem taşıma hem düzeltme yaparsa, bir sorun çıktığında hangisinin bozduğunu ayırt edemezsiniz.
Bizim tercihimiz ikiye ayırmak: göç yalnız taşır, temizlik ayrı bir adımdır ve ayrı betiklerle yapılır. Böylece göçün doğrulaması net kalır — sayılar birebir tutmalıdır. Temizlik adımında ise sayılar bilinçli olarak değişir ve her değişikliğin kaydı ayrı tutulur. İki işi ayırmak toplam süreyi uzatmaz; sadece hata ayıklamayı mümkün kılar.
Bir istisna var: hedef modelin kabul etmediği veri. Yeni yapıda zorunlu olan bir alan eski veride boşsa, o kaydı ya bir varsayılanla taşırsınız ya da hiç taşımazsınız — ortası yoktur. Bu kararı önceden verin ve listesini çıkarın; göç gecesi 'bu kayıtlar geçmedi' diye beliren bir sayı, önceden bilinen bir sayı olsun.
Kesintiyi yönetmek: dondurma penceresi ne kadar olmalı?
Göç sırasında eski sisteme veri girilmeye devam ederse, taşıdığınız veri daha taşırken eskir. Bu yüzden bir dondurma penceresi gerekir: belirlenen saatten sonra eski sisteme kayıt girilmez, göç yapılır, yeni sistem açılır. Pencerenin uzunluğu göçün süresine değil, prova sonucuna göre belirlenir — prova yapılmadan verilen süre tahmindir ve genellikle iyimserdir.
Prova göçü, gerçek verinin kopyası üzerinde baştan sona koşulan bir denemedir ve iki soruyu cevaplar: ne kadar sürüyor ve hangi adımda ne patlıyor. İlk provada patlamayan bir göç neredeyse hiç görmedik; zaten provanın amacı da bu. İkinci ve üçüncü prova hem süreyi kısaltır hem de gerçek gecede kimsenin doğaçlama yapmasına gerek bırakmaz.
Pencereyi tamamen ortadan kaldırmak isteyen bir yaklaşım daha var: çift yazma. Bir süre boyunca hem eski hem yeni sisteme yazılır, sonra okuma yeni sisteme alınır. Güçlüdür ama karmaşıktır ve iki sistemi birden doğru tutmak gerekir. Çoğu kurumsal proje için birkaç saatlik planlı bir pencere, bu karmaşıklığı taşımaktan çok daha ucuzdur — hangisini seçtiğinizi bilerek seçin.
Geri dönüş: yedek almak yetmez, geri yüklemeyi de deneyin
Yedek, geri yüklendiği kanıtlanana kadar yedek sayılmaz. Bir dosyanın var olması onu geri yükleyebileceğiniz anlamına gelmez: sürüm uyuşmazlığı, eksik izin, bozuk kodlama ya da yarım kalmış bir döküm, en kötü anda ortaya çıkar. Göç öncesi alınan yedeği bir kez boş bir veritabanına geri yükleyip açıldığını görmek, bir saatlik iştir ve geri kalan her şeyin sigortasıdır.
İkinci kural, göçün tek bir bütün olarak uygulanmasıdır: ya hepsi olur ya hiçbiri. Yarım uygulanmış bir göç, geri dönüşü en zor durumdur çünkü artık hangi kaydın yeni hangisinin eski olduğunu ayırt edemezsiniz. Uzun göçlerde bunu adım adım, her adımı kendi içinde bütün olacak biçimde kurgulayın ve her adımdan sonra sayımı tekrarlayın.
Üçüncüsü: geri dönüş kararının bir sahibi ve bir saati olsun. 'Biraz daha uğraşalım' cümlesi, gecenin ilerleyen saatlerinde en pahalı cümledir. Önceden konuşun — saat şu olduğunda ve şu adım hâlâ geçmediyse geri dönüyoruz. Kararı önceden vermek, o anda vermekten hem daha kolay hem daha doğrudur.
İlk hafta: iki sistemi karşılaştırarak yürümek
Göçten sonraki ilk hafta, yeni sistemin doğruluğunun gerçek testidir ve bunun en ucuz yöntemi karşılaştırmadır: aynı soruyu iki sisteme sorup cevapların tutmasını beklemek. Günün cirosu, açık kayıt sayısı, bekleyen talepler — hangi rakam işletme için anlamlıysa o. Rakamlar tutuyorsa göç doğrudur; tutmuyorsa fark, kimse şikâyet etmeden bulunur.
Bu karşılaştırmayı bir kişiye görev olarak vermek gerekir, yoksa yapılmaz. Günde beş dakikalık bir kontrol, ilk hafta boyunca sürdürüldüğünde göçün en değerli sigortasıdır — çünkü hatalar genellikle ilk günün değil, ilk ay sonunun ya da ilk kapanışın hataları olur. Ay sonu raporu ilk kez alındığında ortaya çıkan bir fark, göçten üç hafta sonra keşfedilir.
Göçten sonra: eski sistemi ne zaman kapatmalı?
Eski sistem, yeni sistem bir tam iş döngüsünü sorunsuz tamamlayana kadar okunabilir kalmalıdır. Bu döngü işe göre değişir: bir e-ticaret için birkaç gün, aylık kapanışı olan bir muhasebe süreci için bir ay. Kritik nokta, eski sistemin 'okunabilir' kalması ama 'yazılabilir' olmamasıdır; iki sisteme birden veri girilmesi, göçten sonraki en yaygın karışıklık kaynağıdır.
Kapatma kararı verildiğinde iki şey saklanır: verinin tam bir arşiv kopyası ve o veriyi okuyabilecek bir yöntem. Yıllar sonra bir denetim geldiğinde 'yedeğimiz var ama açacak sistem yok' demek, yedeğin olmamasıyla neredeyse aynı şeydir. Basit bir dışa aktarım biçimi — okunabilir, standart bir dosya — çoğu zaman eski sistemi ayakta tutmaktan iyidir.
Son olarak, göçün kendisini belgeleyin: hangi betik ne zaman çalıştı, hangi kararlar verildi, neyi taşımadınız ve neden. Bu belge, altı ay sonra 'şu veri neden yok' sorusunun tek dürüst cevabıdır. Biz bu notları projenin hafıza dosyalarında tutuyoruz; kimsenin hatırlamak zorunda kalmaması, göçün en sessiz faydası.
Sonuç
Veri göçü, teslim sürecinin geri alınamayan tek adımıdır ve bu yüzden en çok hazırlık isteyen adımdır. İyi bir göç, gece boyunca ekran başında beklemekle değil; neyi taşımayacağına önceden karar vermekle, betiği elle yazmak yerine üretmekle, canlıya idempotent işlemler uygulamakla, göçü sayımla doğrulamakla ve geri dönüşü provasını yaparak yazmakla olur.
Bu yazıdaki derslerin hepsi kendi göçlerimizden çıktı — bazıları canlı veritabanında, bazıları bir sürüm gününün ortasında. Elinizde taşınmayı bekleyen bir sistem varsa, önce neyi taşımayacağınızı konuşmakla başlayalım; teslim gününün sakin geçmesinin sırrı çoğu zaman orada saklı.