Veritabanı Migrasyon Betikleri: Versionlama ve Rollback
Veritabanı migrasyon betikleri üretim ortamında en kritik araçlardan biri. Yanlış bir adım veri kaybına yol açabilir. Versionlama ve rollback mekanizması olmadan migrasyon yönetimi kaos olur.
Basit bir migrasyon sistemi kuralım: Her betik bir versiyon numarası taşısın, başarılı olanlar kaydedilsin, hata durumunda geri dönüş yapılsın.
1# migrations/001_create_users.sql2CREATE TABLE users (3id SERIAL PRIMARY KEY,4email VARCHAR(255) UNIQUE NOT NULL,5created_at TIMESTAMP DEFAULT NOW()6);78# migrations/002_add_username.sql9ALTER TABLE users ADD COLUMN username VARCHAR(100);1011# migrations/003_create_posts.sql12CREATE TABLE posts (13id SERIAL PRIMARY KEY,14user_id INTEGER REFERENCES users(id),15title VARCHAR(255),16created_at TIMESTAMP DEFAULT NOW()17);Şimdi Python ile migrasyon yöneticisi yazalım:
Reklam · TEST
AdSense Test ReklamıYazı arası (gönderi içi) · Yazı arası
1import sqlite32import os3from datetime import datetime45class MigrationManager:6def __init__(self, db_path, migrations_dir):7self.db_path = db_path8self.migrations_dir = migrations_dir9self.init_db()1011def init_db(self):12conn = sqlite3.connect(self.db_path)13cursor = conn.cursor()14cursor.execute('''15CREATE TABLE IF NOT EXISTS migrations (16version TEXT PRIMARY KEY,17executed_at TIMESTAMP,18status TEXT19)20''')21conn.commit()22conn.close()2324def get_applied_migrations(self):25conn = sqlite3.connect(self.db_path)26cursor = conn.cursor()27cursor.execute('SELECT version FROM migrations WHERE status = "success"')28result = [row[0] for row in cursor.fetchall()]29conn.close()30return result3132def migrate_up(self):33applied = self.get_applied_migrations()34migration_files = sorted(os.listdir(self.migrations_dir))3536for file in migration_files:37version = file.split('_')[0]38if version in applied:39continue4041filepath = os.path.join(self.migrations_dir, file)42try:43with open(filepath, 'r') as f:44sql = f.read()4546conn = sqlite3.connect(self.db_path)47cursor = conn.cursor()48cursor.executescript(sql)4950cursor.execute(51'INSERT INTO migrations VALUES (?,?,?)',52(version, datetime.now(), 'success')53)54conn.commit()55conn.close()56print(f" {version} migrated successfully")57except Exception as e:58print(f" {version} failed: {e}")59conn.execute(60'INSERT INTO migrations VALUES (?,?,?)',61(version, datetime.now(), 'failed')62)63conn.commit()64conn.close()65break6667def rollback(self, steps=1):68conn = sqlite3.connect(self.db_path)69cursor = conn.cursor()70cursor.execute(71'SELECT version FROM migrations WHERE status = "success" ORDER BY version DESC LIMIT?',72(steps,)73)74versions = [row[0] for row in cursor.fetchall()]7576for version in versions:77rollback_file = os.path.join(self.migrations_dir, f"{version}_rollback.sql")78if os.path.exists(rollback_file):79with open(rollback_file, 'r') as f:80sql = f.read()81cursor.executescript(sql)82cursor.execute('DELETE FROM migrations WHERE version =?', (version,))83print(f"↻ {version} rolled back")8485conn.commit()86conn.close()8788# Kullanım89if __name__ == '__main__':90manager = MigrationManager('app.db', 'migrations')91manager.migrate_up()92# manager.rollback(1) # Son 1 migrationu geri alBeklenen çıktı:
1001 migrated successfully2002 migrated successfully3003 migrated successfullyÖnemli ipucu: Rollback betikleri (001_rollback.sql) her zaman UP betiğinin tersi olmalı. DROP için CREATE'in backup'ını saklı tut, ALTER için eski şemayı dokümante et. Üretimde test ortamında önce çalıştır.
0 yorum