Yedeğiniz gerçekten çalışıyor mu? Geri yükleme tatbikatı, RPO ve RTO
"Yedeğimiz var" cümlesini çok duydum. "Geçen ay yedekten geri yükleme denedik, şu kadar sürdü" cümlesini neredeyse hiç duymadım. Bu ikisi arasındaki fark, kötü günde işinizi kurtarmakla kurtaramamak arasındaki fark.
Yedeğin çalıştığını bilmenin tek yolu, ondan geri yükleme yapmak. Bu yazıda neyi ne sıklıkla yedeklemeniz gerektiğini nasıl belirleyeceğinizi, yedeği nasıl doğrulayacağınızı ve portföyümdeki eğitim platformu çalışmasında tatbikat sırasında çıkan beklenmedik bir sorunu anlatacağım.
Önce hedef: RPO ve RTO
Yedekleme planı iki soruyla başlıyor.
RPO (recovery point objective): En fazla ne kadarlık veri kaybını kabul edebilirsiniz? Gece bir kez yedek alıyorsanız, sabah altıda gelen bir arızada gece yarısından sonraki her şey gider. RPO'nuz fiilen 24 saat.
RTO (recovery time objective): Arızadan sonra sistem en geç ne zaman tekrar çalışıyor olmalı? Buna yalnızca dosyanın kopyalanma süresi değil; sorunu fark etme, karar verme, yedeği indirme, sunucuyu kurma ve doğrulama da giriyor.
Bu iki sayıyı işin sahibiyle birlikte koymak gerekiyor, çünkü cevaplar teknik değil, iş kararı. Bir e-ticaret sitesi için sipariş kaybı çok pahalı olabilir; bir tanıtım sitesinin içeriği için bir günlük kayıp önemsizdir. Hedefler netleşince yöntem de netleşiyor:
| Hedef RPO | Tipik yöntem |
|---|---|
| 24 saat | Günlük tam yedek (dump ya da dosya kopyası) |
| Birkaç saat | Gün içinde birden fazla yedek |
| Dakikalar | Sürekli günlük (WAL) arşivleme, zamana göre geri dönüş |
PostgreSQL'de dakikalık RPO için temel yedek (pg_basebackup) ve WAL arşivlemeyle belirli bir ana geri dönülebiliyor. SQLite tarafında Litestream gibi araçlar WAL'i sürekli olarak nesne depolamaya aktarıyor.
Canlı veritabanını doğru yedeklemek
En sık görülen hata, çalışan veritabanının dosyasını cp ile kopyalamak. Kopyalama sürerken veritabanına yazılırsa dosyanın başı eski, sonu yeni hâlde kalabilir. SQLite WAL modundaysa son yazmalar ana dosyada değil -wal dosyasında durur; yalnızca .db dosyasını kopyalarsanız onları kaybedersiniz.
Doğru yol, veritabanının kendi tutarlı yedek aracını kullanmak:
- PostgreSQL:
pg_dumptek bir işlem içinde tutarlı bir anlık görüntü alır. - MySQL (InnoDB):
mysqldump --single-transactionyazmaları durdurmadan tutarlı döküm alır. - SQLite:
.backupkomutu (çevrimiçi yedek API'si) ya daVACUUM INTO.
SQLite için bu iki seçenek aynı görünüyor. Ölçene kadar ben de öyle düşünüyordum.
Eğitim platformunda ilk tatbikat neden bitmedi
Söz konusu proje, 400 bin sınav gönderimli kurgusal bir eğitim platformu. Tatbikat senaryosu şuydu: sunucu gerçek yazma trafiği altındayken yedek almak, sonra o yedekten yeni bir sunucu kaldırmak.
İlk denemede SQLite'ın sayfa sayfa yedek API'sini kullandım. Yedek, yük devam ettiği sürece bir türlü tamamlanmadı ve ancak yük bittikten sonra, 9,2 saniyede bitebildi. Sebebi SQLite'ın belgelenmiş bir davranışı: yedek sürerken kaynak veritabanına başka bir bağlantıdan yazılırsa, yedek işlemi baştan başlıyor. Bu platformda her gönderim bir yazma demek. Sınav saatinde yedek hiç bitmeyecekti.
Çözüm VACUUM INTO oldu:
const bk = new Database(dbPath);
bk.pragma("busy_timeout = 5000");
bk.prepare("VACUUM INTO ?").run(backupFile);
bk.close();
VACUUM INTO, tek bir okuma işlemi içinde veritabanının tutarlı bir anlık görüntüsünü yeni bir dosyaya yazıyor. WAL modunda okuma işlemi yazıcıları durdurmadığı için uygulama çalışmaya devam ediyor. Sonuç: saniyede 3.816 isteklik yük altında 61 MB'lık yedek 254 ms'de alındı. Yedek sırasında hiç hata olmadı, p95 yanıt süresi 17,3 ms'de kaldı.
Buradan çıkan ders SQLite'a özel değil. Bir yedekleme yöntemini boş bir sunucuda test edip "çalışıyor" demek yetmiyor. Gerçek yük altında davranışı farklı olabilir.
Yedeği doğrulamak: dört katman
Dosyanın var olması, içinin sağlam olduğunu göstermez. Eğitim platformundaki tatbikat betiği yedeği dört adımda doğruluyor.
1. Bütünlük kontrolü
const b = new Database(backupFile, { readonly: true });
const integrity = b.pragma("integrity_check", { simple: true }); // "ok" beklenir
PostgreSQL'de bunun en basit karşılığı, dökümü pg_restore --list ile okuyabilmek. Ama asıl kanıt geri yüklemenin kendisi.
2. Satır sayıları ve tutarlılık
Her tablonun satır sayısı yedekte kontrol ediliyor. Canlı yazma altında alınan bir yedek için güzel bir tutarlılık testi de var: yedekteki gönderim sayısı, yedek başlamadan önceki sayıdan az, yük bittikten sonraki sayıdan fazla olmamalı. Bu projede yedek öncesi 404.373, yedekte 404.373, yük sonrası 424.735 gönderim sayıldı. Sayı beklenen aralıkta; yedek, başladığı andaki durumu yakalamış.
3. Özet (hash)
Her yedeğin SHA-256 özeti kaydediliyor. Yedeği başka bir yere taşıdığınızda ya da aylar sonra geri yüklemeniz gerektiğinde, dosyanın yolda bozulmadığını ya da değiştirilmediğini bununla anlıyorsunuz.
4. Gerçek geri yükleme
Asıl test bu. Yedek yeni bir konuma kopyalanıyor, üzerinde yeni bir sunucu başlatılıyor ve ilk sağlıklı yanıta kadar geçen süre ölçülüyor. Sonra gerçek bir kullanıcı işlemi deneniyor: bir öğrencinin ders listesi okunuyor ve bir sınav gönderimi yapılıyor. Gönderim 201 dönmeli.
Eğitim platformu çalışmasında yedekten kalkan sunucu 514 ms'de sağlıklı yanıt verdi ve örnek gönderim başarıyla kaydedildi. Bu sayıyı dürüst okumak lazım: aynı makinede, yerel diskte ölçülen bir süre. Gerçek bir felakette RTO'ya yedeği uzak depolamadan indirme süresi, sunucu hazırlama ve en çok da insanın sorunu fark edip karar vermesi eklenir. Tatbikat bu adımların ne kadar sürdüğünü görmenin de fırsatı.
Yedeği nerede tutuyorsunuz?
Veritabanıyla aynı sunucuda duran yedek, sunucunun diski bozulduğunda onunla birlikte gidiyor. Aynı bulut hesabında, aynı yetkilerle silinebilen yedek de fidye yazılımı ya da ele geçirilmiş bir hesap karşısında koruma sağlamıyor.
Bildik 3-2-1 kuralı hâlâ iyi bir başlangıç: en az üç kopya, iki farklı ortam, biri başka bir yerde. Buna iki şey eklemek gerekiyor:
- Yedeği yazan kimlik yedeği silemesin. Nesne depolamada sürümleme ya da değiştirilemezlik (object lock) seçeneklerini kullanın.
- Yedekler şifreli dursun ve şifre çözme anahtarı yedekle aynı yerde olmasın. Anahtarı kaybederseniz yedeğiniz de yok demektir.
Saklama süresini de baştan belirleyin. Sadece son yedeği tutmak tehlikeli; fark edilmeden bozulan bir veri, birkaç gün sonra tek sağlam yedeğin üzerine yazılabilir. Günlük, haftalık ve aylık yedekleri farklı sürelerle tutmak bu riski azaltıyor.
Tatbikatı takvime koyun
Eğitim platformunun nöbet kılavuzunda yedek ve geri yükleme tatbikatı haftalık bir iş olarak duruyor ve sonucu aylık rapora giriyor. Veritabanı bozulma şüphesinde ilk adım da "son doğrulanmış yedekten dön". "Son yedek" değil, "son doğrulanmış yedek"; bu ayrım önemli.
Kendi projeniz için kontrol listesi:
- RPO ve RTO'yu işin sahibiyle birlikte yazılı hâle getirin.
- Canlı veritabanını dosya kopyasıyla değil, veritabanının tutarlı yedek aracıyla yedekleyin.
- Yedeklemeyi gerçek yük altında en az bir kez deneyin.
- Her yedekte bütünlük kontrolü, satır sayısı ve özet kaydedin.
- Düzenli olarak yeni bir sunucuya geri yükleyin, bir kullanıcı işlemi deneyin ve süreyi ölçün.
- Yedeği başka bir yerde, silinmeye karşı korumalı ve şifreli tutun.
- Tatbikat sonucunu bir yere yazın. Yazılmayan tatbikat, yapılmamış sayılır.