Add idle auto-shutdown for self-hosted deployment
- IDLE_TIMEOUT_MINUTES env var to configure shutdown after inactivity - Background thread monitors activity and exits when timeout reached - Activity tracked via simulation_display fragment (runs while page open) - Set to 0 (default) to disable auto-shutdown
This commit is contained in:
@@ -7,6 +7,8 @@ thermal-electrical coupling in real-time using instrument interfaces.
|
||||
|
||||
import asyncio
|
||||
import atexit
|
||||
import os
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
from collections import deque
|
||||
@@ -27,6 +29,41 @@ from py_dvt_ate.tests.thermal.tempco import TempCoTest
|
||||
# Thread pool for background test execution
|
||||
_test_executor = ThreadPoolExecutor(max_workers=1, thread_name_prefix="test_runner")
|
||||
|
||||
# Idle shutdown configuration
|
||||
# Set IDLE_TIMEOUT_MINUTES=0 to disable auto-shutdown
|
||||
IDLE_TIMEOUT_MINUTES = int(os.environ.get("IDLE_TIMEOUT_MINUTES", "0"))
|
||||
_last_activity_time: float = time.time()
|
||||
_idle_monitor_started = False
|
||||
|
||||
|
||||
def _update_activity() -> None:
|
||||
"""Update the last activity timestamp."""
|
||||
global _last_activity_time
|
||||
_last_activity_time = time.time()
|
||||
|
||||
|
||||
def _idle_monitor() -> None:
|
||||
"""Background thread that exits the app after idle timeout."""
|
||||
global _last_activity_time
|
||||
while True:
|
||||
time.sleep(60) # Check every minute
|
||||
if IDLE_TIMEOUT_MINUTES <= 0:
|
||||
continue
|
||||
idle_minutes = (time.time() - _last_activity_time) / 60
|
||||
if idle_minutes >= IDLE_TIMEOUT_MINUTES:
|
||||
print(f"Idle timeout reached ({IDLE_TIMEOUT_MINUTES} minutes). Shutting down.")
|
||||
os._exit(0)
|
||||
|
||||
|
||||
def _start_idle_monitor() -> None:
|
||||
"""Start the idle monitor thread if timeout is configured."""
|
||||
global _idle_monitor_started
|
||||
if IDLE_TIMEOUT_MINUTES > 0 and not _idle_monitor_started:
|
||||
_idle_monitor_started = True
|
||||
thread = threading.Thread(target=_idle_monitor, daemon=True)
|
||||
thread.start()
|
||||
print(f"Idle auto-shutdown enabled: {IDLE_TIMEOUT_MINUTES} minutes")
|
||||
|
||||
# History buffer size for charts
|
||||
HISTORY_SIZE = 500
|
||||
|
||||
@@ -124,6 +161,10 @@ def start_embedded_server() -> tuple[SimulationServer, threading.Thread]:
|
||||
|
||||
def init_session_state() -> None:
|
||||
"""Initialise Streamlit session state."""
|
||||
# Start idle monitor for auto-shutdown
|
||||
_start_idle_monitor()
|
||||
_update_activity()
|
||||
|
||||
if "server" not in st.session_state:
|
||||
with st.spinner("Starting simulation server..."):
|
||||
st.session_state.server, st.session_state.server_thread = start_embedded_server()
|
||||
@@ -386,6 +427,8 @@ def display_controls() -> None:
|
||||
@st.fragment(run_every=0.1)
|
||||
def simulation_display() -> None:
|
||||
"""Fragment that displays and updates simulation state."""
|
||||
_update_activity() # Track that someone is viewing the dashboard
|
||||
|
||||
if "server" not in st.session_state:
|
||||
st.warning("Initializing simulation server...")
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user