Files
ashvale-station/run.py
T
kemal 9d2e884b74 Fix shutdown hang and blank equations when KaTeX is unavailable
Shutdown hang, the cause of every restart taking systemd's full 90 s timeout
and ending in SIGKILL: /api/stream looped forever with no disconnect or
shutdown check, so an open dashboard was an in-flight request that never
completed and uvicorn's graceful shutdown waited on it. Reproduced cleanly:
with no stream client the process stopped in 2 s, with one open client it was
still running after 15 s. Fixed by bounding timeout_graceful_shutdown, and by
having the generator exit on client disconnect and on a shutdown event. Now
7 s with a client attached.

Equations rendered as blank boxes whenever the KaTeX CDN was unreachable,
which is a real case for a Pi on wifi. The elements carried the TeX only in a
data attribute, so with no KaTeX there was nothing to display. The source is
now written into the element as text first and KaTeX replaces it, so it
degrades to readable TeX. Verified by aborting the katex request. A window
load handler re-runs typesetting for a slow CDN. The code comment claiming
this fallback already existed was wrong and is corrected.
2026-08-15 22:19:42 +01:00

51 lines
1.8 KiB
Python

#!/usr/bin/env python3
# Copyright 2026 Kemal Yaylali
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Entry point. `python run.py` and open http://<pi>:8000"""
from __future__ import annotations
import argparse
import uvicorn
from ashvale.config import CONFIG
def main() -> None:
ap = argparse.ArgumentParser()
ap.add_argument("--host", default=CONFIG.server.host)
ap.add_argument("--port", type=int, default=CONFIG.server.port)
ap.add_argument("--no-led", action="store_true")
ap.add_argument("--reload", action="store_true")
args = ap.parse_args()
if args.no_led:
CONFIG.server.led_enabled = False
# One worker, one event loop. The station owns mutable model state, so a
# second worker would give you two divergent forecasters sharing a socket.
# timeout_graceful_shutdown bounds the wait for in-flight requests. Without
# it, the dashboard's server-sent-events connection never completes, so a
# stop blocks until systemd's 90 s timeout and ends in SIGKILL. Measured:
# with one stream client open, shutdown went from "never" to under 2 s.
uvicorn.run("ashvale.api:app", host=args.host, port=args.port,
reload=args.reload, workers=1, log_level="info",
limit_concurrency=32, timeout_graceful_shutdown=5)
if __name__ == "__main__":
main()