Spaces:
Running
Running
Fix: db_sync.py - smart restore + WAL-aware backup
Browse files- db_sync.py +29 -6
db_sync.py
CHANGED
|
@@ -10,7 +10,9 @@ Usage:
|
|
| 10 |
|
| 11 |
import os
|
| 12 |
import shutil
|
|
|
|
| 13 |
import sys
|
|
|
|
| 14 |
import time
|
| 15 |
|
| 16 |
HF_TOKEN = os.environ.get("HF_TOKEN", "")
|
|
@@ -27,6 +29,13 @@ def _api():
|
|
| 27 |
return HfApi(token=HF_TOKEN)
|
| 28 |
|
| 29 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
def restore():
|
| 31 |
"""Download DB from HF backup repo if it exists. Returns True if restored."""
|
| 32 |
try:
|
|
@@ -59,26 +68,40 @@ def restore():
|
|
| 59 |
|
| 60 |
|
| 61 |
def backup():
|
| 62 |
-
"""Upload current DB to HF backup repo."""
|
| 63 |
if not os.path.exists(DB_PATH):
|
| 64 |
print(f"[db_sync] No DB at {DB_PATH} — skipping backup.", flush=True)
|
| 65 |
return False
|
|
|
|
| 66 |
try:
|
| 67 |
api = _api()
|
| 68 |
-
|
| 69 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 70 |
api.upload_file(
|
| 71 |
-
path_or_fileobj=
|
| 72 |
path_in_repo=REMOTE_FILE,
|
| 73 |
repo_id=BACKUP_REPO,
|
| 74 |
repo_type="dataset",
|
| 75 |
-
commit_message="Auto-backup from HF Space",
|
| 76 |
)
|
| 77 |
-
print(f"[db_sync] Backup complete.", flush=True)
|
| 78 |
return True
|
| 79 |
except Exception as e:
|
| 80 |
print(f"[db_sync] Backup failed: {e}", flush=True)
|
| 81 |
return False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 82 |
|
| 83 |
|
| 84 |
def watch():
|
|
|
|
| 10 |
|
| 11 |
import os
|
| 12 |
import shutil
|
| 13 |
+
import sqlite3
|
| 14 |
import sys
|
| 15 |
+
import tempfile
|
| 16 |
import time
|
| 17 |
|
| 18 |
HF_TOKEN = os.environ.get("HF_TOKEN", "")
|
|
|
|
| 29 |
return HfApi(token=HF_TOKEN)
|
| 30 |
|
| 31 |
|
| 32 |
+
def _safe_copy(src, dst):
|
| 33 |
+
"""Copy a SQLite DB using the backup API so WAL data is included."""
|
| 34 |
+
with sqlite3.connect(src) as src_conn:
|
| 35 |
+
with sqlite3.connect(dst) as dst_conn:
|
| 36 |
+
src_conn.backup(dst_conn)
|
| 37 |
+
|
| 38 |
+
|
| 39 |
def restore():
|
| 40 |
"""Download DB from HF backup repo if it exists. Returns True if restored."""
|
| 41 |
try:
|
|
|
|
| 68 |
|
| 69 |
|
| 70 |
def backup():
|
| 71 |
+
"""Upload current DB to HF backup repo using SQLite backup API (handles WAL)."""
|
| 72 |
if not os.path.exists(DB_PATH):
|
| 73 |
print(f"[db_sync] No DB at {DB_PATH} — skipping backup.", flush=True)
|
| 74 |
return False
|
| 75 |
+
tmp = None
|
| 76 |
try:
|
| 77 |
api = _api()
|
| 78 |
+
# Use SQLite backup API to create a consistent snapshot that includes WAL data.
|
| 79 |
+
# Uploading the raw .sqlite3 file misses uncommitted WAL transactions.
|
| 80 |
+
tmp = DB_PATH + ".upload_tmp"
|
| 81 |
+
_safe_copy(DB_PATH, tmp)
|
| 82 |
+
size = os.path.getsize(tmp)
|
| 83 |
+
# Verify the copy has data
|
| 84 |
+
with sqlite3.connect(tmp) as check:
|
| 85 |
+
n = check.execute("SELECT COUNT(*) FROM htx_user").fetchone()[0]
|
| 86 |
+
print(f"[db_sync] Backing up DB ({size:,} bytes, {n} users) → {BACKUP_REPO}...", flush=True)
|
| 87 |
api.upload_file(
|
| 88 |
+
path_or_fileobj=tmp,
|
| 89 |
path_in_repo=REMOTE_FILE,
|
| 90 |
repo_id=BACKUP_REPO,
|
| 91 |
repo_type="dataset",
|
| 92 |
+
commit_message=f"Auto-backup from HF Space ({n} users)",
|
| 93 |
)
|
| 94 |
+
print(f"[db_sync] Backup complete ({n} users).", flush=True)
|
| 95 |
return True
|
| 96 |
except Exception as e:
|
| 97 |
print(f"[db_sync] Backup failed: {e}", flush=True)
|
| 98 |
return False
|
| 99 |
+
finally:
|
| 100 |
+
if tmp and os.path.exists(tmp):
|
| 101 |
+
try:
|
| 102 |
+
os.unlink(tmp)
|
| 103 |
+
except Exception:
|
| 104 |
+
pass
|
| 105 |
|
| 106 |
|
| 107 |
def watch():
|