change backup service

This commit is contained in:
2025-12-20 22:30:18 +07:00
parent 243abe55b5
commit 5c073705d8
2 changed files with 67 additions and 7 deletions

View File

@@ -79,6 +79,8 @@ def create_backup() -> tuple[str, bytes]:
config.DB_NAME, config.DB_NAME,
"--no-owner", "--no-owner",
"--no-acl", "--no-acl",
"--clean", # Add DROP commands before CREATE
"--if-exists", # Use IF EXISTS with DROP commands
"-F", "-F",
"p", # plain SQL format "p", # plain SQL format
] ]

View File

@@ -3,8 +3,9 @@
Restore PostgreSQL database from S3 backup. Restore PostgreSQL database from S3 backup.
Usage: Usage:
python restore.py - List available backups python restore.py - List available backups
python restore.py <filename> - Restore from specific backup python restore.py <filename> - Restore from backup (cleans DB first)
python restore.py <filename> --no-clean - Restore without cleaning DB first
""" """
import gzip import gzip
import os import os
@@ -62,7 +63,48 @@ def list_backups(s3_client) -> list[tuple[str, float, str]]:
return [] return []
def restore_backup(s3_client, filename: str) -> None: def clean_database() -> None:
"""Drop and recreate public schema to clean the database."""
print("Cleaning database (dropping and recreating public schema)...")
env = os.environ.copy()
env["PGPASSWORD"] = config.DB_PASSWORD
# Drop and recreate public schema
clean_sql = b"""
DROP SCHEMA public CASCADE;
CREATE SCHEMA public;
GRANT ALL ON SCHEMA public TO public;
"""
cmd = [
"psql",
"-h",
config.DB_HOST,
"-p",
config.DB_PORT,
"-U",
config.DB_USER,
"-d",
config.DB_NAME,
]
result = subprocess.run(
cmd,
env=env,
input=clean_sql,
capture_output=True,
)
if result.returncode != 0:
stderr = result.stderr.decode()
if "ERROR" in stderr:
raise Exception(f"Database cleanup failed: {stderr}")
print("Database cleaned successfully!")
def restore_backup(s3_client, filename: str, clean_first: bool = True) -> None:
"""Download and restore backup.""" """Download and restore backup."""
key = f"{config.S3_BACKUP_PREFIX}{filename}" key = f"{config.S3_BACKUP_PREFIX}{filename}"
@@ -79,6 +121,10 @@ def restore_backup(s3_client, filename: str) -> None:
print("Decompressing...") print("Decompressing...")
sql_data = gzip.decompress(compressed_data) sql_data = gzip.decompress(compressed_data)
# Clean database before restore if requested
if clean_first:
clean_database()
print(f"Restoring to database {config.DB_NAME}...") print(f"Restoring to database {config.DB_NAME}...")
# Build psql command # Build psql command
@@ -124,20 +170,32 @@ def main() -> int:
s3_client = create_s3_client() s3_client = create_s3_client()
if len(sys.argv) < 2: # Parse arguments
args = sys.argv[1:]
clean_first = True
if "--no-clean" in args:
clean_first = False
args.remove("--no-clean")
if len(args) < 1:
# List available backups # List available backups
backups = list_backups(s3_client) backups = list_backups(s3_client)
if backups: if backups:
print(f"\nTo restore, run: python restore.py <filename>") print(f"\nTo restore, run: python restore.py <filename>")
print("Add --no-clean to skip database cleanup before restore")
else: else:
print("No backups found.") print("No backups found.")
return 0 return 0
filename = sys.argv[1] filename = args[0]
# Confirm restore # Confirm restore
print(f"WARNING: This will restore database from {filename}") print(f"WARNING: This will restore database from {filename}")
print("This may overwrite existing data!") if clean_first:
print("Database will be CLEANED (all existing data will be DELETED)!")
else:
print("Database will NOT be cleaned (may cause conflicts with existing data)")
print() print()
confirm = input("Type 'yes' to continue: ") confirm = input("Type 'yes' to continue: ")
@@ -147,7 +205,7 @@ def main() -> int:
return 0 return 0
try: try:
restore_backup(s3_client, filename) restore_backup(s3_client, filename, clean_first=clean_first)
return 0 return 0
except Exception as e: except Exception as e:
print(f"Restore failed: {e}") print(f"Restore failed: {e}")