Spaces:
AHC-Devs
/
Running on CPU Upgrade

Timothy S. Phan commited on
Commit
f72161b
Β·
1 Parent(s): 5cdab22

syncs atomic .backup sqlite db to S3

Browse files
Files changed (1) hide show
  1. entrypoint.sh +21 -33
entrypoint.sh CHANGED
@@ -1,24 +1,6 @@
1
  #!/bin/bash
2
  # =============================================================================
3
  # entrypoint.sh – Label Studio wrapper for Hugging Face Spaces
4
- #
5
- # What this script does:
6
- # 1. On startup : restores SQLite DB + exports from S3
7
- # 2. After restore: verifies DB integrity before starting Label Studio
8
- # 3. Starts Label Studio in the background
9
- # 4. Every 60 s : checkpoints WAL then syncs /data β†’ S3
10
- # 5. On SIGTERM : does one final sync before the pod dies
11
- #
12
- # Required HF Space secrets:
13
- # BACKUP_S3_BUCKET – bucket name (no s3:// prefix)
14
- # LABEL_STUDIO_USERNAME – admin username / email
15
- # LABEL_STUDIO_PASSWORD – admin password
16
- #
17
- # Optional HF Space secrets (with defaults shown):
18
- # BACKUP_S3_PREFIX – labelstudio
19
- # AWS_DEFAULT_REGION – us-east-1
20
- # AWS_ENDPOINT_URL – https://s3.amazonaws.com (change for R2/MinIO)
21
- # SYNC_INTERVAL_SECONDS – 60
22
  # =============================================================================
23
 
24
  set -euo pipefail
@@ -31,27 +13,31 @@ ENDPOINT="${AWS_ENDPOINT_URL:-https://s3.amazonaws.com}"
31
  SYNC_INTERVAL="${SYNC_INTERVAL_SECONDS:-60}"
32
 
33
  DATA_DIR=/data
34
- # Label Studio uses label_studio.sqlite3 (not .db) β€” confirmed from live logs
35
  DB_FILE="$DATA_DIR/label_studio.sqlite3"
 
36
  S3_PATH="s3://${BUCKET}/${PREFIX}"
37
 
38
  export AWS_DEFAULT_REGION="$REGION"
39
 
40
- # ------------- helper: WAL checkpoint + S3 upload --------------------------
41
  sync_to_s3() {
42
  local reason="${1:-periodic}"
43
  echo "[sync] Starting $reason sync to $S3_PATH/ ..."
44
 
45
- # Checkpoint the WAL so S3 always receives a single self-consistent file.
46
- # TRUNCATE: folds all WAL frames into the main DB file and zeros the WAL.
47
- # This briefly blocks writers (not readers) β€” safe for ≀10 concurrent users.
48
  if [ -f "$DB_FILE" ]; then
49
- sqlite3 "$DB_FILE" "PRAGMA wal_checkpoint(TRUNCATE);" 2>/dev/null || \
50
- echo "[sync] WAL checkpoint skipped (DB may be locked) – proceeding anyway"
51
  fi
52
 
 
53
  aws s3 sync "$DATA_DIR/" "$S3_PATH/" \
54
  --endpoint-url "$ENDPOINT" \
 
 
 
 
55
  --no-progress \
56
  && echo "[sync] $reason sync complete" \
57
  || echo "[sync] WARNING: $reason sync failed – will retry next cycle"
@@ -79,22 +65,28 @@ echo "============================================================"
79
  mkdir -p "$DATA_DIR"
80
 
81
  echo "[restore] Syncing data DOWN from $S3_PATH/ ..."
82
- # Use || true so a fresh bucket (nothing to restore) doesn't abort the script
83
  aws s3 sync "$S3_PATH/" "$DATA_DIR/" \
84
  --endpoint-url "$ENDPOINT" \
85
  --no-progress \
86
  && echo "[restore] Restore complete" \
87
  || echo "[restore] Restore returned non-zero (may be first run) – continuing"
88
 
89
- # ------------- 2. DB integrity check before starting LS -------------------
 
 
 
 
 
 
90
  if [ -f "$DB_FILE" ]; then
91
  echo "[integrity] Checking DB integrity ..."
92
- INTEGRITY=$(sqlite3 "$DB_FILE" "PRAGMA integrity_check;" 2>&1 | head -1)
 
 
93
  if [ "$INTEGRITY" != "ok" ]; then
94
  echo "[integrity] ERROR: DB integrity check failed: $INTEGRITY"
95
  echo "[integrity] The restored database is corrupt."
96
  echo "[integrity] β†’ Check your S3 bucket for a healthy backup."
97
- echo "[integrity] β†’ If you have bucket versioning enabled, restore an earlier version."
98
  exit 1
99
  fi
100
  echo "[integrity] DB is healthy: $INTEGRITY"
@@ -111,21 +103,17 @@ label-studio user create \
111
  --preserve-case 2>/dev/null || true
112
 
113
  # ------------- 4. start Label Studio in the background --------------------
114
- # Use $SPACE_HOST exactly as the official HF Dockerfile does β€” this is what
115
- # makes HF's reverse proxy route traffic correctly to the container.
116
  echo "[start] Starting Label Studio ..."
117
  label-studio --host="$SPACE_HOST" &
118
  LS_PID=$!
119
  echo "[start] Label Studio PID: $LS_PID"
120
 
121
- # Register shutdown handler NOW (after we have LS_PID)
122
  trap shutdown_handler EXIT INT TERM
123
 
124
  # ------------- 5. periodic sync loop (runs forever) -----------------------
125
  echo "[loop] Entering sync loop (every ${SYNC_INTERVAL}s) ..."
126
  while true; do
127
  sleep "$SYNC_INTERVAL"
128
- # Verify LS is still alive before syncing
129
  if ! kill -0 "$LS_PID" 2>/dev/null; then
130
  echo "[loop] Label Studio process has exited unexpectedly – stopping container"
131
  exit 1
 
1
  #!/bin/bash
2
  # =============================================================================
3
  # entrypoint.sh – Label Studio wrapper for Hugging Face Spaces
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
  # =============================================================================
5
 
6
  set -euo pipefail
 
13
  SYNC_INTERVAL="${SYNC_INTERVAL_SECONDS:-60}"
14
 
15
  DATA_DIR=/data
 
16
  DB_FILE="$DATA_DIR/label_studio.sqlite3"
17
+ SAFE_BACKUP_FILE="$DATA_DIR/ls_safe_backup.sqlite3"
18
  S3_PATH="s3://${BUCKET}/${PREFIX}"
19
 
20
  export AWS_DEFAULT_REGION="$REGION"
21
 
22
+ # ------------- helper: Atomic SQLite Backup + S3 upload --------------------
23
  sync_to_s3() {
24
  local reason="${1:-periodic}"
25
  echo "[sync] Starting $reason sync to $S3_PATH/ ..."
26
 
27
+ # 1. Create a safe, atomic backup of the SQLite DB.
28
+ # This prevents the race condition of syncing live/journal files.
 
29
  if [ -f "$DB_FILE" ]; then
30
+ sqlite3 "$DB_FILE" ".backup '$SAFE_BACKUP_FILE'" 2>/dev/null || \
31
+ echo "[sync] Warning: SQLite backup failed, syncing rest of data."
32
  fi
33
 
34
+ # 2. Sync to S3, explicitly excluding the live database and its lock/journal files
35
  aws s3 sync "$DATA_DIR/" "$S3_PATH/" \
36
  --endpoint-url "$ENDPOINT" \
37
+ --exclude "label_studio.sqlite3" \
38
+ --exclude "label_studio.sqlite3-journal" \
39
+ --exclude "label_studio.sqlite3-wal" \
40
+ --exclude "label_studio.sqlite3-shm" \
41
  --no-progress \
42
  && echo "[sync] $reason sync complete" \
43
  || echo "[sync] WARNING: $reason sync failed – will retry next cycle"
 
65
  mkdir -p "$DATA_DIR"
66
 
67
  echo "[restore] Syncing data DOWN from $S3_PATH/ ..."
 
68
  aws s3 sync "$S3_PATH/" "$DATA_DIR/" \
69
  --endpoint-url "$ENDPOINT" \
70
  --no-progress \
71
  && echo "[restore] Restore complete" \
72
  || echo "[restore] Restore returned non-zero (may be first run) – continuing"
73
 
74
+ # ------------- 2. Restore DB & Integrity Check ----------------------------
75
+ # If we have a safe backup from a previous session, make it the live DB
76
+ if [ -f "$SAFE_BACKUP_FILE" ]; then
77
+ echo "[restore] Overwriting live DB with safe backup from S3..."
78
+ cp "$SAFE_BACKUP_FILE" "$DB_FILE"
79
+ fi
80
+
81
  if [ -f "$DB_FILE" ]; then
82
  echo "[integrity] Checking DB integrity ..."
83
+ # The `|| true` prevents set -e from silently crashing the script if it fails
84
+ INTEGRITY=$(sqlite3 "$DB_FILE" "PRAGMA integrity_check;" 2>&1 | head -1 || true)
85
+
86
  if [ "$INTEGRITY" != "ok" ]; then
87
  echo "[integrity] ERROR: DB integrity check failed: $INTEGRITY"
88
  echo "[integrity] The restored database is corrupt."
89
  echo "[integrity] β†’ Check your S3 bucket for a healthy backup."
 
90
  exit 1
91
  fi
92
  echo "[integrity] DB is healthy: $INTEGRITY"
 
103
  --preserve-case 2>/dev/null || true
104
 
105
  # ------------- 4. start Label Studio in the background --------------------
 
 
106
  echo "[start] Starting Label Studio ..."
107
  label-studio --host="$SPACE_HOST" &
108
  LS_PID=$!
109
  echo "[start] Label Studio PID: $LS_PID"
110
 
 
111
  trap shutdown_handler EXIT INT TERM
112
 
113
  # ------------- 5. periodic sync loop (runs forever) -----------------------
114
  echo "[loop] Entering sync loop (every ${SYNC_INTERVAL}s) ..."
115
  while true; do
116
  sleep "$SYNC_INTERVAL"
 
117
  if ! kill -0 "$LS_PID" 2>/dev/null; then
118
  echo "[loop] Label Studio process has exited unexpectedly – stopping container"
119
  exit 1