Compare commits

...
3 Commits
Author SHA1 Message Date
Ian Renton f45b7d51b0 Give threads less time to shut down gracefully. Attempt shutdown() rather than stop() on the telnet connections to see if that improves things 2026-09-19 15:07:31 +01:00
Ian Renton 0eb4553406 Merge remote-tracking branch 'origin/main'
# Conflicts:
#	templates/add_spot.html
#	templates/alerts.html
#	templates/bands.html
#	templates/base.html
#	templates/conditions.html
#	templates/map.html
#	templates/spots.html
#	templates/status.html
2026-09-19 10:49:36 +01:00
Ian Renton 682e2c267c Improve handling of empty JSON repsonses from ParksNPeaks (and potentially others) 2026-09-19 10:49:09 +01:00
23 changed files with 52 additions and 32 deletions
+1 -1
View File
@@ -35,7 +35,7 @@ class CleanupTimer:
self._stop_event.set()
if self._thread:
self._thread.join(timeout=15)
self._thread.join(timeout=5)
if self._thread.is_alive():
logger.warning("Cleanup worker thread did not exit on time and will be killed.")
+1 -1
View File
@@ -91,7 +91,7 @@ class DataProviders:
for t in threads:
t.start()
deadline = time.monotonic() + 40
deadline = time.monotonic() + 15
for t in threads:
t.join(timeout=max(0.0, deadline - time.monotonic()))
still_running = [t for t in threads if t.is_alive()]
+7 -2
View File
@@ -4,7 +4,7 @@ from threading import Event, Thread
import pytz
import requests
from requests.exceptions import ConnectionError, ConnectTimeout, ReadTimeout
from requests.exceptions import ConnectionError, ConnectTimeout, JSONDecodeError, ReadTimeout
from core.constants import HTTP_HEADERS
from providers.alert.alert_provider import AlertProvider
@@ -33,7 +33,7 @@ class HTTPAlertProvider(AlertProvider):
def stop(self):
self._stop_event.set()
if self._thread:
self._thread.join(timeout=35)
self._thread.join(timeout=12)
if self._thread.is_alive():
logger.warning(f"{self.name} alert worker thread did not exit on time and will be killed.")
@@ -64,9 +64,14 @@ class HTTPAlertProvider(AlertProvider):
logger.warning(f"HTTP {http_response.status_code} when calling {self.name} alerts API.")
except ConnectionError:
self.status = "Error"
logger.warning(f"Connection error when accessing {self.name} alerts API.")
except (ConnectTimeout, ReadTimeout):
self.status = "Error"
logger.warning(f"Timeout when accessing {self.name} alerts API.")
except JSONDecodeError:
self.status = "Error"
logger.warning(f"Invalid or empty JSON response from {self.name} alert API.")
except Exception:
self.status = "Error"
logger.exception(f"Exception in HTTP JSON Alert Provider ({self.name})")
@@ -38,7 +38,7 @@ class FileDownloadCallsignDataProvider(CallsignDataProvider):
def stop(self):
self._stop_event.set()
if self._thread:
self._thread.join(timeout=35)
self._thread.join(timeout=12)
if self._thread.is_alive():
logger.warning(f"{self.name} callsign data worker thread did not exit on time and will be killed.")
@@ -33,7 +33,7 @@ class FileDownloadSIGRefDataProvider(SIGRefDataProvider):
def stop(self):
super().stop()
if self._thread:
self._thread.join(timeout=35)
self._thread.join(timeout=12)
if self._thread.is_alive():
logger.warning(f"{self.sig_name} SIG ref data worker thread did not exit on time and will be killed.")
+1 -1
View File
@@ -73,7 +73,7 @@ class GIROIonosonde(SolarConditionsProvider):
def stop(self):
self._stop_event.set()
if self._thread:
self._thread.join(timeout=35)
self._thread.join(timeout=12)
if self._thread.is_alive():
logger.warning("GIRO ionosonde worker thread did not exit on time and will be killed.")
@@ -31,7 +31,7 @@ class HTTPSolarConditionsProvider(SolarConditionsProvider):
def stop(self):
self._stop_event.set()
if self._thread:
self._thread.join(timeout=35)
self._thread.join(timeout=12)
if self._thread.is_alive():
logger.warning(f"{self.name} solar conditions worker thread did not exit on time and will be killed.")
+1 -1
View File
@@ -38,7 +38,7 @@ class KC2GProp(SolarConditionsProvider):
def stop(self):
self._stop_event.set()
if self._thread:
self._thread.join(timeout=35)
self._thread.join(timeout=12)
if self._thread.is_alive():
logger.warning("KC2G ionosonde worker thread did not exit on time and will be killed.")
+1 -1
View File
@@ -49,7 +49,7 @@ class APRSIS(SpotProvider):
if self._aprsis:
self._aprsis.close()
if self._thread:
self._thread.join(timeout=15)
self._thread.join(timeout=5)
if self._thread.is_alive():
logger.warning("APRS-IS worker thread did not exit on time and will be killed.")
+6 -1
View File
@@ -1,5 +1,6 @@
import logging
import re
import socket
from datetime import datetime
from threading import Event, Lock, Thread
@@ -52,9 +53,13 @@ class DXCluster(SpotProvider):
self._stop_event.set()
with self._telnet_lock:
if self._telnet:
try:
self._telnet.sock.shutdown(socket.SHUT_RDWR)
except (AttributeError, OSError):
pass
self._telnet.close()
if self._thread:
self._thread.join(timeout=15)
self._thread.join(timeout=5)
if self._thread.is_alive():
logger.warning(f"DX Cluster {self._hostname} worker thread did not exit on time and will be killed.")
+7 -2
View File
@@ -4,7 +4,7 @@ from threading import Event, Thread
import pytz
import requests
from requests.exceptions import ConnectionError, ConnectTimeout, ReadTimeout
from requests.exceptions import ConnectionError, ConnectTimeout, JSONDecodeError, ReadTimeout
from core.constants import HTTP_HEADERS
from providers.spot.spot_provider import SpotProvider
@@ -35,7 +35,7 @@ class HTTPSpotProvider(SpotProvider):
self._stop_event.set()
self._wakeup_event.set()
if self._thread:
self._thread.join(timeout=35)
self._thread.join(timeout=12)
if self._thread.is_alive():
logger.warning(f"{self.name} spot worker thread did not exit on time and will be killed.")
@@ -73,9 +73,14 @@ class HTTPSpotProvider(SpotProvider):
logger.warning(f"HTTP {http_response.status_code} when calling {self.name} spot API.")
except ConnectionError:
self.status = "Error"
logger.warning(f"Connection error when accessing {self.name} spots API.")
except (ConnectTimeout, ReadTimeout):
self.status = "Error"
logger.warning(f"Timeout when accessing {self.name} spots API.")
except JSONDecodeError:
self.status = "Error"
logger.warning(f"Invalid or empty JSON response from {self.name} spots API.")
except Exception:
self.status = "Error"
logger.exception(f"Exception in HTTP Spot Provider ({self.name})")
+6 -1
View File
@@ -1,5 +1,6 @@
import logging
import re
import socket
from datetime import datetime
from threading import Event, Lock, Thread
@@ -41,9 +42,13 @@ class RBN(SpotProvider):
self._stop_event.set()
with self._telnet_lock:
if self._telnet:
try:
self._telnet.sock.shutdown(socket.SHUT_RDWR)
except (AttributeError, OSError):
pass
self._telnet.close()
if self._thread:
self._thread.join(timeout=15)
self._thread.join(timeout=5)
if self._thread.is_alive():
logger.warning(f"RBN (port {self._port!s}) worker thread did not exit on time and will be killed.")
+1 -1
View File
@@ -42,7 +42,7 @@ class SSESpotProvider(SpotProvider):
logger.exception(f"Exception closing SSE connection for {self.name} during stop()")
if self._thread:
self._thread.join(timeout=15)
self._thread.join(timeout=5)
if self._thread.is_alive():
logger.warning(f"{self.name} SSE worker thread did not exit on time and will be killed.")
+1 -1
View File
@@ -34,7 +34,7 @@ class WebsocketSpotProvider(SpotProvider):
if self._ws:
self._ws.close()
if self._thread:
self._thread.join(timeout=15)
self._thread.join(timeout=5)
if self._thread.is_alive():
logger.warning(f"{self.name} websocket worker thread did not exit on time and will be killed.")
@@ -35,7 +35,7 @@ class FileDownloadStaticDataProvider(StaticDataProvider):
def stop(self):
self._stop_event.set()
if self._thread:
self._thread.join(timeout=35)
self._thread.join(timeout=12)
if self._thread.is_alive():
logger.warning(f"{self.name} static data worker thread did not exit on time and will be killed.")
+1 -1
View File
@@ -77,7 +77,7 @@
</div>
<script src="/static/js/add-spot.js?v=1789803195"></script>
<script src="/static/js/add-spot.js?v=1789826851"></script>
<script>$(document).ready(function () {
$("#nav-link-add-spot").addClass("active");
}); <!-- highlight active page in nav --></script>
+1 -1
View File
@@ -83,7 +83,7 @@
</div>
<script src="/static/js/alerts.js?v=1789803195"></script>
<script src="/static/js/alerts.js?v=1789826852"></script>
<script>$(document).ready(function () {
$("#nav-link-alerts").addClass("active");
}); <!-- highlight active page in nav --></script>
+2 -2
View File
@@ -76,8 +76,8 @@
</div>
<script src="/static/js/spotsbandsandmap.js?v=1789803195"></script>
<script src="/static/js/bands.js?v=1789803195"></script>
<script src="/static/js/spotsbandsandmap.js?v=1789826851"></script>
<script src="/static/js/bands.js?v=1789826851"></script>
<script>$(document).ready(function () {
$("#nav-link-bands").addClass("active");
}); <!-- highlight active page in nav --></script>
+5 -5
View File
@@ -1,6 +1,6 @@
{% extends "skeleton.html" %}
{% block head_extra %}
<link rel="stylesheet" href="/static/css/style.css?v=1789803195" type="text/css">
<link rel="stylesheet" href="/static/css/style.css?v=1789826851" type="text/css">
<link href="/static/vendor/css/bootstrap-5.3.8.min.css" rel="stylesheet">
<link href="/static/vendor/css/fontawesome-6.7.2.min.css" rel="stylesheet">
<link href="/static/vendor/css/solid-6.7.2.min.css" rel="stylesheet">
@@ -16,10 +16,10 @@
window.fetchEventSource = fetchEventSource;
</script>
<script src="/static/js/utils.js?v=1789803195"></script>
<script src="/static/js/ui-ham.js?v=1789803195"></script>
<script src="/static/js/geo.js?v=1789803195"></script>
<script src="/static/js/common.js?v=1789803195"></script>
<script src="/static/js/utils.js?v=1789826851"></script>
<script src="/static/js/ui-ham.js?v=1789826851"></script>
<script src="/static/js/geo.js?v=1789826851"></script>
<script src="/static/js/common.js?v=1789826851"></script>
{% end %}
{% block body %}
<div class="container">
+1 -1
View File
@@ -284,7 +284,7 @@
</div>
<script src="/static/vendor/js/chart-4.4.9.umd.min.js"></script>
<script src="/static/js/conditions.js?v=1789803195"></script>
<script src="/static/js/conditions.js?v=1789826851"></script>
<script>$(document).ready(function () {
$("#nav-link-conditions").addClass("active");
}); <!-- highlight active page in nav --></script>
+2 -2
View File
@@ -113,8 +113,8 @@
const CARTODB_API_KEY = "{{ web_ui_options.get('cartodb_api_key', '') }}";
</script>
<script src="/static/js/spotsbandsandmap.js?v=1789803195"></script>
<script src="/static/js/map.js?v=1789803195"></script>
<script src="/static/js/spotsbandsandmap.js?v=1789826852"></script>
<script src="/static/js/map.js?v=1789826852"></script>
<script>$(document).ready(function () {
$("#nav-link-map").addClass("active");
}); <!-- highlight active page in nav --></script>
+2 -2
View File
@@ -125,8 +125,8 @@
</div>
<script src="/static/js/spotsbandsandmap.js?v=1789803195"></script>
<script src="/static/js/spots.js?v=1789803195"></script>
<script src="/static/js/spotsbandsandmap.js?v=1789826851"></script>
<script src="/static/js/spots.js?v=1789826851"></script>
<script>$(document).ready(function () {
$("#nav-link-spots").addClass("active");
}); <!-- highlight active page in nav --></script>
+1 -1
View File
@@ -96,7 +96,7 @@
</div>
</div>
<script src="/static/js/status.js?v=1789803195"></script>
<script src="/static/js/status.js?v=1789826851"></script>
<script>
$(document).ready(function () {
$("#nav-link-status").addClass("active");