Refactor of caching & data storage part 2 #118

This commit is contained in:
Ian Renton
2026-07-31 15:18:23 +01:00
parent d26ddff7d1
commit 818fd2d504
17 changed files with 186 additions and 243 deletions
+14 -58
View File
@@ -6,7 +6,7 @@ import tornado
from tornado.web import StaticFileHandler
from core.config import ALLOW_SPOTTING, WEB_SERVER_PORT, API_ONLY_MODE, LOG_WEB_REQUESTS, BASE_URL
from core.utils import empty_queue
from core.data_store import DATA_STORE
from server.handlers.api.addspot import APISpotHandler
from server.handlers.api.alerts import APIAlertsHandler, APIAlertsStreamHandler
from server.handlers.api.dxstats import APIDxStatsHandler
@@ -18,6 +18,7 @@ from server.handlers.api.status import APIStatusHandler
from server.handlers.manifesthandler import ManifestHandler
from server.handlers.metrics import PrometheusMetricsHandler
from server.handlers.pagetemplate import PageTemplateHandler
from server.sse_broadcaster import SSEBroadcaster
_HERE = os.path.dirname(__file__ or "")
@@ -25,12 +26,12 @@ _HERE = os.path.dirname(__file__ or "")
class WebServer:
"""Provides the public-facing web server."""
def __init__(self, data_store):
def __init__(self):
"""Constructor"""
self._data_store = data_store
self._sse_spot_queues = []
self._sse_alert_queues = []
self._data_store = DATA_STORE
self._spot_broadcaster = SSEBroadcaster()
self._alert_broadcaster = SSEBroadcaster()
self._port = WEB_SERVER_PORT
self._api_only_mode = API_ONLY_MODE
self._shutdown_event = asyncio.Event()
@@ -42,6 +43,10 @@ class WebServer:
"status": "Starting"
}
# Listen for new spots and alerts being added to the cache, so we can notify SSE clients immediately
DATA_STORE.spots.add_listener(self._spot_broadcaster.publish)
DATA_STORE.alerts.add_listener(self._alert_broadcaster.publish)
def start(self):
"""Start the web server"""
@@ -64,13 +69,13 @@ class WebServer:
(r"/api/v1/spots", APISpotsHandler, {"spots": self._data_store.spots, **handler_opts}),
(r"/api/v1/alerts", APIAlertsHandler, {"alerts": self._data_store.alerts, **handler_opts}),
(r"/api/v1/spots/stream", APISpotsStreamHandler,
{"sse_spot_queues": self._sse_spot_queues, **handler_opts}),
{"sse_spot_broadcaster": self._spot_broadcaster, **handler_opts}),
(r"/api/v1/alerts/stream", APIAlertsStreamHandler,
{"sse_alert_queues": self._sse_alert_queues, **handler_opts}),
{"sse_alert_broadcaster": self._alert_broadcaster, **handler_opts}),
(r"/api/v1/solar", APISolarConditionsHandler, {"solar_conditions": self._data_store.solar, **handler_opts}),
(r"/api/v1/dxstats", APIDxStatsHandler, {"spots": self._data_store.spots, **handler_opts}),
(r"/api/v1/options", APIOptionsHandler, {"status_data": self._data_store.status, **handler_opts}),
(r"/api/v1/status", APIStatusHandler, {"status_data": self._data_store.status, **handler_opts}),
(r"/api/v1/options", APIOptionsHandler, {"status_data": self._data_store.status_data, **handler_opts}),
(r"/api/v1/status", APIStatusHandler, {"status_data": self._data_store.status_data, **handler_opts}),
(r"/api/v1/lookup/call", APILookupCallHandler, {**handler_opts}),
(r"/api/v1/lookup/sigref", APILookupSIGRefHandler, {**handler_opts}),
(r"/api/v1/lookup/grid", APILookupGridHandler, {**handler_opts}),
@@ -118,55 +123,6 @@ class WebServer:
logging.info("You can access your copy of Spothole at " + BASE_URL)
await self._shutdown_event.wait()
def notify_new_spot(self, spot):
"""Internal method called when a new spot is added to the system. This is used to ping any SSE clients that are
awaiting a server-sent message with new spots."""
for queue in self._sse_spot_queues:
try:
queue.put(spot)
except:
# Cleanup thread was probably deleting the queue, that's fine
pass
pass
def notify_new_alert(self, alert):
"""Internal method called when a new alert is added to the system. This is used to ping any SSE clients that are
awaiting a server-sent message with new spots."""
for queue in self._sse_alert_queues:
try:
queue.put(alert)
except:
# Cleanup thread was probably deleting the queue, that's fine
pass
pass
def clean_up_sse_queues(self):
"""Clean up any SSE queues that are growing too large; probably their client disconnected and we didn't catch it
properly for some reason."""
for q in self._sse_spot_queues:
try:
if q.full():
logging.warning(
"A full SSE spot queue was found, presumably because the client disconnected strangely. It has been removed.")
self._sse_spot_queues.remove(q)
empty_queue(q)
except:
# Probably got deleted already on another thread
pass
for q in self._sse_alert_queues:
try:
if q.full():
logging.warning(
"A full SSE alert queue was found, presumably because the client disconnected strangely. It has been removed.")
self._sse_alert_queues.remove(q)
empty_queue(q)
except:
# Probably got deleted already on another thread
pass
pass
def request_log(handler):
"""Custom log function to provide more data about requests when enabled, and to provide the ability to turn off