SPB Git

spb/findata

Public

public overview: background refresh (inline recompute took 40 s); HTML no-cache, hashed assets immutable

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Simon-Pierre Boucher committed (Nov 2, 2026) parent f85ef07 Browse files

2 changed files +42 −9

modified services/api/src/findata_api/main.py +13 −0
@@ -52,6 +52,12 @@ class Observability(BaseHTTPMiddleware):
52 M.REQUESTS.labels(request.method, _route_label(route), str(response.status_code)).inc() 52 M.REQUESTS.labels(request.method, _route_label(route), str(response.status_code)).inc()
53 M.LATENCY.labels(_route_label(route)).observe(dt) 53 M.LATENCY.labels(_route_label(route)).observe(dt)
54 response.headers["X-Request-ID"] = rid 54 response.headers["X-Request-ID"] = rid
55+ if not route.startswith("/api/") and route != "/metrics":
56+ # static console: hashed assets are immutable, HTML must be revalidated so deployments show up at once
57+ if route.startswith("/_next/static/"):
58+ response.headers["Cache-Control"] = "public, max-age=31536000, immutable"
59+ else:
60+ response.headers["Cache-Control"] = "no-cache"
55 response.headers["X-Content-Type-Options"] = "nosniff" 61 response.headers["X-Content-Type-Options"] = "nosniff"
56 response.headers["X-Frame-Options"] = "DENY" 62 response.headers["X-Frame-Options"] = "DENY"
57 response.headers["Referrer-Policy"] = "no-referrer" 63 response.headers["Referrer-Policy"] = "no-referrer"
@@ -149,6 +155,13 @@ def create_app() -> FastAPI:
149 body, ctype = M.render() 155 body, ctype = M.render()
150 return Response(body, media_type=ctype) 156 return Response(body, media_type=ctype)
151 157
158+ @app.on_event("startup")
159+ def _warm() -> None:
160+ from findata_api.health import start_refresher as _h
161+ from findata_api.routers.public import start_refresher as _p
162+ _h()
163+ _p()
164+
152 web = s.web_dist or Path(s.root) / "apps" / "web" / "out" 165 web = s.web_dist or Path(s.root) / "apps" / "web" / "out"
153 if web.exists(): 166 if web.exists():
154 app.mount("/", StaticFiles(directory=str(web), html=True), name="web") 167 app.mount("/", StaticFiles(directory=str(web), html=True), name="web")
modified services/api/src/findata_api/routers/public.py +29 −9
@@ -66,14 +66,34 @@ def _compute() -> dict[str, Any]:
66 } 66 }
67 67
68 68
69+_started = False
70+
71+
72+def _refresh_loop(period_s: float) -> None:
73+ while True:
74+ try:
75+ v = _compute()
76+ _cache.update(at=time.monotonic(), value=v)
77+ except Exception as e:
78+ _cache.setdefault("error", str(e)[:200])
79+ time.sleep(period_s)
80+
81+
82+def start_refresher(period_s: float = 300.0) -> None:
83+ global _started
84+ if _started:
85+ return
86+ _started = True
87+ threading.Thread(target=_refresh_loop, args=(period_s,), name="public-overview-refresher", daemon=True).start()
88+
89+
69 @router.get("/overview") 90 @router.get("/overview")
70 def overview(): 91 def overview():
71− now = time.monotonic() 92+ """Served from a cache refreshed in the background every 5 minutes; the request path never runs the queries."""
72− if _cache["value"] is not None and now - _cache["at"] < 120: 93+ start_refresher()
73− return _cache["value"] 94+ v = _cache["value"]
74− with _lock: 95+ if v is None:
75− if _cache["value"] is not None and time.monotonic() - _cache["at"] < 120: 96+ return {"generated_at": datetime.now(UTC).isoformat(), "status": "starting", "totals": None, "families": [], "datasets": [],
76− return _cache["value"] 97+ "coverage": [], "options": {}, "fundamentals": {}, "instruments": {}, "freshness": {"jobs_24h": {}}, "storage": {},
77− v = _compute() 98+ "backup": {}, "license": "FirstRate Data — private research use; raw bars and tables are not redistributed. Sign in for data access."}
78− _cache.update(at=time.monotonic(), value=v) 99+ return v
79− return v
80 100
Menu

Theme