Bugün, bir Laravel uygulaması için yedekleme ve geri yükleme özelliği inşa ettim. Bu uygulama birden fazla API geçidi bağlantısını yönetiyor — her bağlantı ayrı bir geçit ortamı (prod, staging, bir müşterinin kümesi gibi). Kong, tüm yönlendirme, eklentiler, tüketiciler ve kimlik bilgilerini kendi veritabanında saklar. Birden fazla geçidi bir kontrol düzleminden yönetmeye başladığınızda, “geçidin kendi yedeğine güvenmek” yeterli olmaktan çıkar. Siz, sizin anlık görüntünüzü, sizin programınıza uygun bir şekilde, bir bağlantıya göre belirlemiş olmalısınız; böylece diff yapabilir ve geri dönebilirsiniz.
Burada, durumu belirleyen ve önemli kararları sunan yapı var.
Yedek, bir satırdır, bir dosya dökümü değildir
Yedek, bir satırdır, bir dosya dökümü değildir
Basit bir versiyon, kong config db_export > backup.yml komutunu çalıştırmak ve günü kurtarmaktan ibarettir. Bu, bir geçide SSH ile bağlandığınızda işe yarar. Ancak, N bağlantıyı uzaktan yönetiyorsanız ve geçmiş, durum ve “tam olarak bunu geri yükle” gibi özellikler istiyorsanız, bu yöntem işe yaramaz.
Bu nedenle yedek, birinci sınıf bir model haline geldi — KongConnectionBackup; UUID ile bir genel kimlik, otomatik artan bir dahili kimlik, bağlantıya yönelik bir yabancı anahtar, yakalanan yük ve durumunu taşıyan iki enum içerir. Enums, açıklığın büyük kısmını sunar:
enum KongBackupStatus: string
{
case Pending = 'pending';
case Running = 'running';
case Completed = ;
case Failed = ;
public function label(): string
{
return match ($this) {
self::Pending => ,
self::Running => ,
self::Completed => ,
self::Failed => ,
};
}
public function color(): string
{
return match ($this) {
self::Pending => ,
self::Running => ,
self::Completed => ,
self::Failed => ,
};
}
}
label() ve color() metotları, Blade/Livewire katmanını sade tutar; görünüm sadece enum’dan nasıl render edileceğini sorar, durum dizelerini badge renklerine eşleyen @if merdivenlerine gerek yoktur. Aynı desen KongBackupSection için de geçerlidir ve yedekleme kapsamını (hizmetler, yönlendirmeler, eklentiler, tüketiciler…) belirler, böylece bir geri yükleme kısmi yapılabilir.
Yakalama, bir işte çalışır; komut sadece bildirir
Yakalama, bir işte çalışır; komut sadece bildirir
Canlı bir geçidin yedeğini almak yavaş ve hataya eğilimlidir — ağ çağrıları, büyük yükler, oran limitleri. Bunların hiçbiri senkron isteklerde veya bir Artisan komutunu engellemeye dair değildir. Bu nedenle gerçek iş, bir iş kuyruklanmış işte bulunur:
class BackupKongConnection implements ShouldQueue
{
public function __construct(
public KongConnectionBackup $backup,
) {}
public function handle(KongConnectionBackupService $service): void
{
$this->backup->update([=> KongBackupStatus::Running]);
try {
$service->capture($this->backup);
$this->backup->update([=> KongBackupStatus::Completed]);
} catch (Throwable $e) {
$this->backup->update([=> KongBackupStatus::Failed]);
throw $e; // kuyruk hatayı kaydetsin ve yeniden deneme politikası düşünsün
}
}
}
Statü geçişleri sözleşmeyi oluşturur: bir yedek satırı Pending olarak oluşturulur, iş onu aldığında Running durumuna geçer ve daha sonra Completed veya Failed olarak sonuçlanır. Kullanıcı arayüzü buna abone olduğu için, canlı bir ilerleme rozetine sahip olursunuz. Failed durumuna geçtikten sonra yeniden fırlatmanın önemli olması — hala işlem hatasında failed_jobs içinde bir hata yığın izini ile birlikte olması gerek, yoksa kaybolan bir istisna kırık bir yedekte yeşil bir onay işareti bırakmış olur.
Komut satırı komutu (KongBackupCommand) ve planlı varyantı aynı basit şeyi yapar: bir satır oluştur, işi bildir, geri dön. İnce komut, kalın servis — komut, tek bir hizmet üzerinde UI butonu ve zamanlayıcıya ek bir tetikleyici işlevi görür.
Zamanlama, her bağlantı için değil, genel olarak değil
Zamanlama, her bağlantı için değil, genel olarak değil
Tek bir genel “her şeyi 2’de yedekle” önerisi cazip görünse de yanlıştır. Bağlantıların farklı patlama alanları vardır — bir müşterinin prod geçidini her gece yedeklemek istersiniz ama kullanmadığınız bir sandbox’ın depolama alanını kirletmesini istemezsiniz. Bu nedenle, zamanlama her bağlantının kendi ayarlarına göre ayarlanır ve KongScheduledBackupCommand katılıp katılmadığına göre bağlantıları döner:
$connections->each(function (KongConnection $connection) {
BackupKongConnection::dispatch(
$connection->backups()->create([
=> KongBackupStatus::Pending,
=> $connection->backup_sections,
])
});
Zamanlama girişi kendisi aslında amaçsızdır — routes/console.php içinde komutu kaydedin (veya schedule() metodunu kullanın) ve komutun hangi bağlantıların çalışacağını belirlemesine izin verin.
Geri yükleme, işinizin ona bağımlıymış gibi test edilmesi gereken kısımdır
Geri yükleme, işinizin ona bağımlıymış gibi test edilmesi gereken kısımdır
Yedeklemek hoşgörülüdür — kötü durumda bir tane daha alırsınız. Ancak geri yüklemek tehlikeli bir durumdur: canlı bir geçide durumu geri itiyorsunuz, bu bazen daha yeni yapılandırmayı bozabilir. İki güvenlik duvarı eklendi.
İlk olarak, geri yükleme bölüm bilgilidir. plugins’ı geri yükleyebilir, consumers’ı etkilemeden yapabilirsiniz, çünkü KongBackupSection sınırları modellemiştir. İkincisi, servis geri yüklemeyi “bu anlık görüntüye doğru uzlaş” olarak değerlendirir; “kör üst yazma” şeklinde değil — içeriklerin halihazırda mevcut olduğunu bilirken sürpriz yaşamaz.
Bu, bir Pest testinin gerçek bir geçide dokunmadan önce sabitlemesi gereken türden bir şeydir:
it(, function () {
$connection = KongConnection::factory()->create();
$backup = $connection->backups()->create([
=> KongBackupStatus::Pending,
]);
$this->mock(KongConnectionBackupService::class)
->shouldReceive()
->once()
->with($backup);
(new BackupKongConnection($backup))->handle(app(KongConnectionBackupService::class));
expect($backup->fresh()->status)->toBe(KongBackupStatus::Completed);
});
it(, function () {
$backup = KongConnection::factory()
->create()
->backups()->create([=> KongBackupStatus::Pending]);
$this->mock(KongConnectionBackupService::class)
->shouldReceive()
->andThrow(new RuntimeException());
expect(fn () => (new BackupKongConnection($backup))->handle(app(KongConnectionBackupService::class)))
->toThrow(RuntimeException::class);
expect($backup->fresh()->status)->toBe(KongBackupStatus::Failed);
});
Statü makinesini izole bir şekilde test etmek — gerçek bir geçit olmadan — özelliği üzerinde iterasyon yapmayı güvenli hale getiren şeydir. HTTP düzeyindeki yakalama/geri yükleme, bir disposable geçit üzerinde kendi entegrasyon testlerine sahipken, durum geçişleri saf ve hızlı bir şekilde doğrulanabilir.
Çıkarım
Çıkarım
Sıklıkla tekrarlanan ders: eseri modelleyin (yedek, durum + kapsam ile bir satırdır), yavaş/başarısız olma eğilimindeki işleri bir işe yerleştirin, komut/UI/zamanlayıcıyı ince tetikleyiciler olarak tutun, ve enum’ların kendi sunumlarını yönetmelerine izin verin. Geri yükleme yönünde test bütçenizi harcarsınız — yedekler yeniden denenebilir, geri yüklemeler değil.
Sonraki aşama, koruma ve budama (bir Completed yedeği, bir sandbox bağlantısı için N günden daha eski olmamalıdır; sonsuza kadar yaşamamalıdır) şeklinde bir küçük Observer odaklı takip olacaktır.
Kaynak: Orijinal Makale


