glutamatt HF Staff commited on
Commit
956b0ce
·
verified ·
1 Parent(s): 50c0181

chore: rebuild with updated GHCR images (93d7a331a31a22d0e1699e24e9fe200a0c089737)

Browse files
Files changed (3) hide show
  1. Dockerfile +1 -1
  2. README.md +1 -1
  3. session_sync_daemon.py +70 -5
Dockerfile CHANGED
@@ -2,7 +2,7 @@
2
  # Copies pre-built services from GHCR images into single runtime
3
 
4
  # Build arg for image tag (defaults to latest, overridden by deploy workflow with SHA)
5
- ARG IMAGE_TAG=main-sha-f55161c
6
 
7
  # Stage 1: Extract Next.js standalone build
8
  FROM ghcr.io/glutamatt/neural-runner/neural-runner-app:${IMAGE_TAG} AS nextjs-build
 
2
  # Copies pre-built services from GHCR images into single runtime
3
 
4
  # Build arg for image tag (defaults to latest, overridden by deploy workflow with SHA)
5
+ ARG IMAGE_TAG=main-sha-93d7a33
6
 
7
  # Stage 1: Extract Next.js standalone build
8
  FROM ghcr.io/glutamatt/neural-runner/neural-runner-app:${IMAGE_TAG} AS nextjs-build
README.md CHANGED
@@ -68,4 +68,4 @@ Create a **private** Dataset: `{your-username}/neural-runner-sessions`
68
 
69
  Built with [Claude Code](https://claude.com/claude-code) 🤖
70
 
71
- # Rebuild: Wed Feb 4 23:39:03 UTC 2026 - Updated GHCR images
 
68
 
69
  Built with [Claude Code](https://claude.com/claude-code) 🤖
70
 
71
+ # Rebuild: Wed Feb 4 23:43:00 UTC 2026 - Updated GHCR images
session_sync_daemon.py CHANGED
@@ -4,11 +4,16 @@ Session sync daemon - Periodically backs up sessions to HF Dataset.
4
 
5
  Runs as a background process managed by process-compose.
6
  Syncs session files every 5 minutes to ensure persistence.
 
 
 
 
7
  """
8
 
9
  import os
10
  import sys
11
  import time
 
12
  import logging
13
  from pathlib import Path
14
 
@@ -27,29 +32,86 @@ logger = logging.getLogger(__name__)
27
  # Configuration
28
  SYNC_INTERVAL_SECONDS = 120 # 2 minutes (increased from 5 for faster persistence)
29
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
 
31
  def main():
32
  """Run periodic session sync."""
33
 
 
 
 
 
 
 
34
  logger.info("=" * 60)
35
  logger.info("Session Sync Daemon - Started")
36
  logger.info("=" * 60)
 
37
 
38
  # Check if HF_TOKEN is set
39
  if not os.environ.get("HF_TOKEN"):
40
  logger.warning("HF_TOKEN not set - daemon will not sync sessions")
41
  logger.info("Set HF_TOKEN in Space settings to enable persistence")
42
  logger.info("Daemon will sleep indefinitely (required by process-compose)")
43
- # Keep daemon running but do nothing
44
- while True:
45
  time.sleep(3600)
46
- return # Never reached, but explicit
47
 
48
  logger.info(f"Syncing sessions every {SYNC_INTERVAL_SECONDS} seconds")
49
 
50
- while True:
51
  try:
52
- time.sleep(SYNC_INTERVAL_SECONDS)
 
 
 
 
 
 
 
 
53
 
54
  logger.info("Starting session backup...")
55
  backup_sessions_to_dataset()
@@ -64,6 +126,9 @@ def main():
64
  logger.warning(f"Session sync failed: {e}")
65
  logger.info("Will retry on next interval...")
66
 
 
 
 
67
 
68
  if __name__ == "__main__":
69
  main()
 
4
 
5
  Runs as a background process managed by process-compose.
6
  Syncs session files every 5 minutes to ensure persistence.
7
+
8
+ Implements graceful shutdown with signal handling:
9
+ - SIGTERM: Docker/process-compose shutdown (triggers final backup)
10
+ - SIGINT: Ctrl+C / manual interrupt (triggers final backup)
11
  """
12
 
13
  import os
14
  import sys
15
  import time
16
+ import signal
17
  import logging
18
  from pathlib import Path
19
 
 
32
  # Configuration
33
  SYNC_INTERVAL_SECONDS = 120 # 2 minutes (increased from 5 for faster persistence)
34
 
35
+ # Global flag for graceful shutdown
36
+ shutdown_requested = False
37
+
38
+
39
+ def signal_handler(signum, frame):
40
+ """
41
+ Handle shutdown signals (SIGTERM, SIGINT).
42
+
43
+ Triggers immediate backup before exit to preserve session state.
44
+
45
+ Signal sources:
46
+ - SIGTERM: Docker stop, process-compose shutdown, HF Space restart
47
+ - SIGINT: Ctrl+C, manual interrupt
48
+ """
49
+ global shutdown_requested
50
+
51
+ signal_name = signal.Signals(signum).name
52
+ logger.info("=" * 60)
53
+ logger.info(f"Received {signal_name} signal - initiating graceful shutdown")
54
+ logger.info("=" * 60)
55
+
56
+ shutdown_requested = True
57
+
58
+ # Perform final backup if HF_TOKEN is available
59
+ if os.environ.get("HF_TOKEN"):
60
+ try:
61
+ logger.info("Triggering final session backup...")
62
+ backup_sessions_to_dataset()
63
+ logger.info("✓ Final backup complete")
64
+ except SessionSyncError as e:
65
+ logger.error(f"🚨 SECURITY ERROR during shutdown: {e}")
66
+ # Don't exit(1) here - we're already shutting down
67
+ except Exception as e:
68
+ logger.warning(f"Final backup failed: {e}")
69
+ else:
70
+ logger.info("HF_TOKEN not set - skipping final backup")
71
+
72
+ logger.info("=" * 60)
73
+ logger.info("Session Sync Daemon - Stopped")
74
+ logger.info("=" * 60)
75
+ sys.exit(0)
76
+
77
 
78
  def main():
79
  """Run periodic session sync."""
80
 
81
+ # Register signal handlers BEFORE starting loop
82
+ # SIGTERM: Docker/process-compose shutdown
83
+ # SIGINT: Ctrl+C / manual interrupt
84
+ signal.signal(signal.SIGTERM, signal_handler)
85
+ signal.signal(signal.SIGINT, signal_handler)
86
+
87
  logger.info("=" * 60)
88
  logger.info("Session Sync Daemon - Started")
89
  logger.info("=" * 60)
90
+ logger.info("Signal handlers registered (SIGTERM, SIGINT)")
91
 
92
  # Check if HF_TOKEN is set
93
  if not os.environ.get("HF_TOKEN"):
94
  logger.warning("HF_TOKEN not set - daemon will not sync sessions")
95
  logger.info("Set HF_TOKEN in Space settings to enable persistence")
96
  logger.info("Daemon will sleep indefinitely (required by process-compose)")
97
+ # Keep daemon running but do nothing (still responsive to signals)
98
+ while not shutdown_requested:
99
  time.sleep(3600)
100
+ return
101
 
102
  logger.info(f"Syncing sessions every {SYNC_INTERVAL_SECONDS} seconds")
103
 
104
+ while not shutdown_requested:
105
  try:
106
+ # Sleep in small intervals to be responsive to shutdown signals
107
+ # Split SYNC_INTERVAL into 1-second chunks for faster signal response
108
+ for _ in range(SYNC_INTERVAL_SECONDS):
109
+ if shutdown_requested:
110
+ break
111
+ time.sleep(1)
112
+
113
+ if shutdown_requested:
114
+ break
115
 
116
  logger.info("Starting session backup...")
117
  backup_sessions_to_dataset()
 
126
  logger.warning(f"Session sync failed: {e}")
127
  logger.info("Will retry on next interval...")
128
 
129
+ # Graceful exit (signal handler already performed final backup)
130
+ logger.info("Main loop exited gracefully")
131
+
132
 
133
  if __name__ == "__main__":
134
  main()