From 5d7e452aafa62e5044bb24894116e71da8f75e17 Mon Sep 17 00:00:00 2001 From: nessi Date: Fri, 21 Aug 2026 20:55:19 +0200 Subject: [PATCH] feat: add non-blocking datastore refresh with dedicated lock and improve service worker offline handling Add datastore_refresh_lock to prevent concurrent refresh operations. Extract refresh_datastore_storage() function with non-blocking lock acquisition using acquire(blocking=False). Update datastore_snapshot() to spawn background thread for refresh when cache is stale but return immediately with cached data. Add offline fallback HTML page to service worker for navigate requests with 503 status for --- app.py | 27 +++++++++++++++++++++------ static/sw.js | 9 +++++++-- templates/index.html | 4 ++-- 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/app.py b/app.py index 782f3b1..f653878 100644 --- a/app.py +++ b/app.py @@ -82,6 +82,7 @@ previous_cpu = None service_status_lock = threading.Lock() service_check_executor = ThreadPoolExecutor(max_workers=16) datastore_cache_lock = threading.Lock() +datastore_refresh_lock = threading.Lock() datastore_cache = {"stored_at": 0.0, "snapshot": None, "raid": None, "raid_at": 0.0} raid_log_state = None hardware_log_once = set() @@ -532,18 +533,32 @@ def raid_status(): "finish": format_remaining_time(finish_match.group(1)) if finish_match else "—", "speed": format_transfer_rate(speed_match.group(1)) if speed_match else "—"}, "error": None if array_line or mdadm_output else (mdadm_error or mdstat_error or "RAID-Status nicht verfügbar"), "updated_at": time.time()} -def datastore_snapshot(): - now = time.time() - with datastore_cache_lock: - if datastore_cache["snapshot"] is None or now - datastore_cache["stored_at"] >= max(30, STORAGE_CACHE_SECONDS): - fresh_storage = scan_datastore_storage() +def refresh_datastore_storage(): + if not datastore_refresh_lock.acquire(blocking=False): + return + try: + fresh_storage = scan_datastore_storage() + with datastore_cache_lock: if fresh_storage.get("error") and datastore_cache["snapshot"]: datastore_cache["snapshot"] = dict(datastore_cache["snapshot"]) datastore_cache["snapshot"]["stale"] = True datastore_cache["snapshot"]["warning"] = fresh_storage["error"] else: datastore_cache["snapshot"] = fresh_storage - datastore_cache["stored_at"] = now + datastore_cache["stored_at"] = time.time() + finally: + datastore_refresh_lock.release() + +def datastore_snapshot(): + now = time.time() + with datastore_cache_lock: + storage = datastore_cache["snapshot"] + refresh_due = storage is None or now - datastore_cache["stored_at"] >= max(30, STORAGE_CACHE_SECONDS) + if storage is None: + refresh_datastore_storage() + elif refresh_due: + threading.Thread(target=refresh_datastore_storage, name="datastore-refresh", daemon=True).start() + with datastore_cache_lock: storage = datastore_cache["snapshot"] current_raid = raid_status() with datastore_cache_lock: diff --git a/static/sw.js b/static/sw.js index b34e762..6a62cb3 100644 --- a/static/sw.js +++ b/static/sw.js @@ -1,4 +1,4 @@ -const CACHE_NAME = 'media-max-shell-v5'; +const CACHE_NAME = 'media-max-shell-v6'; const SHELL = ['/static/media-mark.svg', '/static/media-mark-192.png', '/static/media-mark-512.png', '/static/manifest.json']; self.addEventListener('install', event => { @@ -16,5 +16,10 @@ self.addEventListener('fetch', event => { if (request.method !== 'GET') return; const url = new URL(request.url); if (url.pathname.startsWith('/api/')) return; - event.respondWith(fetch(request).catch(() => caches.match(request).then(response => response || caches.match('/static/manifest.json')))); + event.respondWith(fetch(request).catch(() => { + if (request.mode === 'navigate') { + return new Response('Media Max

Media Max

Die Verbindung ist momentan unterbrochen. Bitte erneut laden.

', {headers: {'Content-Type': 'text/html; charset=utf-8'}}); + } + return caches.match(request).then(response => response || new Response('', {status: 503})); + })); }); diff --git a/templates/index.html b/templates/index.html index e8844a1..3c841ae 100644 --- a/templates/index.html +++ b/templates/index.html @@ -29,7 +29,7 @@ document.addEventListener('DOMContentLoaded',()=>{const grid=document.querySelec @@ -50,7 +50,7 @@ if ('serviceWorker' in navigator) { -Media Max +Media Max