|
45 | 45 | mcp_app = mcp.http_app(path="/", stateless_http=True, json_response=True) |
46 | 46 |
|
47 | 47 |
|
| 48 | +# ── Background-loop tunables ────────────────────────────────────── |
| 49 | +# These are defined *here* (above the functions that reference them) |
| 50 | +# so the f-string in ``lifespan`` doesn't depend on the rest of the |
| 51 | +# module having loaded first. lifespan is invoked by uvicorn during |
| 52 | +# app startup, after the module is fully imported, so the old layout |
| 53 | +# worked — but it was fragile to any refactor that called the |
| 54 | +# function during import. Putting the constants up top makes the |
| 55 | +# dependency direction obvious. |
| 56 | + |
| 57 | +# Fallback retention for orgs whose plan can't be resolved (Clerk lookup |
| 58 | +# failed AND no cached Setting). The per-org tiered retention — |
| 59 | +# 30d / 90d / 365d for Free / Pro / Pro Plus — is sourced from |
| 60 | +# ``app.core.plans.PLAN_LIMITS[plan]["log_retention_days"]`` and applied |
| 61 | +# in ``_log_cleanup_loop`` below. This env var only matters when plan |
| 62 | +# resolution breaks entirely; we keep a 90-day default so a transient |
| 63 | +# Clerk outage doesn't silently wipe a paid customer's logs. |
| 64 | +LOG_RETENTION_DAYS = int(os.getenv("LOG_RETENTION_DAYS", "90")) |
| 65 | +LOG_CLEANUP_INTERVAL_HOURS = 24 # Run once per day |
| 66 | +# Cameras offline for longer than this get their in-memory caches freed. |
| 67 | +# HLS segments are live-only fragments — useless once streaming stops. |
| 68 | +INACTIVE_CAMERA_CLEANUP_HOURS = int(os.getenv("INACTIVE_CAMERA_CLEANUP_HOURS", "24")) |
| 69 | + |
| 70 | +# How often to sweep for stale "online" entities and flip them to offline. |
| 71 | +# Needs to be shorter than the heartbeat-miss threshold (90s) for |
| 72 | +# timely notifications but longer than a few seconds to keep DB load low. |
| 73 | +OFFLINE_SWEEP_INTERVAL_SECONDS = int(os.getenv("OFFLINE_SWEEP_INTERVAL_SECONDS", "30")) |
| 74 | +# If a node/camera hasn't heart-beat in this many seconds, the sweep |
| 75 | +# marks it offline. Matches the 90s threshold used by the model's |
| 76 | +# ``effective_status`` property so the UI and DB agree. |
| 77 | +OFFLINE_HEARTBEAT_TIMEOUT_SECONDS = 90 |
| 78 | + |
| 79 | + |
48 | 80 | @asynccontextmanager |
49 | 81 | async def lifespan(app): |
50 | 82 | """Application lifespan: startup and shutdown hooks.""" |
@@ -188,27 +220,9 @@ async def security_headers(request: Request, call_next): |
188 | 220 | app.mount("/mcp", mcp_app) |
189 | 221 |
|
190 | 222 |
|
191 | | -# Fallback retention for orgs whose plan can't be resolved (Clerk lookup |
192 | | -# failed AND no cached Setting). The per-org tiered retention — |
193 | | -# 30d / 90d / 365d for Free / Pro / Pro Plus — is sourced from |
194 | | -# ``app.core.plans.PLAN_LIMITS[plan]["log_retention_days"]`` and applied |
195 | | -# in ``_log_cleanup_loop`` below. This env var only matters when plan |
196 | | -# resolution breaks entirely; we keep a 90-day default so a transient |
197 | | -# Clerk outage doesn't silently wipe a paid customer's logs. |
198 | | -LOG_RETENTION_DAYS = int(os.getenv("LOG_RETENTION_DAYS", "90")) |
199 | | -LOG_CLEANUP_INTERVAL_HOURS = 24 # Run once per day |
200 | | -# Cameras offline for longer than this get their in-memory caches freed. |
201 | | -# HLS segments are live-only fragments — useless once streaming stops. |
202 | | -INACTIVE_CAMERA_CLEANUP_HOURS = int(os.getenv("INACTIVE_CAMERA_CLEANUP_HOURS", "24")) |
203 | | - |
204 | | -# How often to sweep for stale "online" entities and flip them to offline. |
205 | | -# Needs to be shorter than the heartbeat-miss threshold (90s) for |
206 | | -# timely notifications but longer than a few seconds to keep DB load low. |
207 | | -OFFLINE_SWEEP_INTERVAL_SECONDS = int(os.getenv("OFFLINE_SWEEP_INTERVAL_SECONDS", "30")) |
208 | | -# If a node/camera hasn't heart-beat in this many seconds, the sweep |
209 | | -# marks it offline. Matches the 90s threshold used by the model's |
210 | | -# ``effective_status`` property so the UI and DB agree. |
211 | | -OFFLINE_HEARTBEAT_TIMEOUT_SECONDS = 90 |
| 223 | +# Background-loop constants moved up to just above the ``lifespan`` |
| 224 | +# function (search "Background-loop tunables") so the f-string in |
| 225 | +# ``lifespan`` doesn't reference a name defined later in the module. |
212 | 226 |
|
213 | 227 |
|
214 | 228 | async def _log_cleanup_loop(): |
|
0 commit comments