Push last snapshot immediately to newly connected clients

The server keeps the most recent snapshot in memory (under the same
lock as the client list). When a client connects it receives that
snapshot right away, then continues to receive live updates on each
collection cycle.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-25 19:57:36 +02:00
co-authored by Claude Sonnet 4.6
parent 53dcaf537e
commit 3ab7795248
2 changed files with 20 additions and 5 deletions
+2 -2
View File
@@ -95,7 +95,7 @@ def run(args: argparse.Namespace) -> None:
logs_dir.mkdir(exist_ok=True) logs_dir.mkdir(exist_ok=True)
out = auto_out_path(logs_dir, vin) out = auto_out_path(logs_dir, vin)
push_clients, push_lock = network.start_push_server(args.host, args.port) push_clients, push_lock, push_last = network.start_push_server(args.host, args.port)
current_day = datetime.now(timezone.utc).date() current_day = datetime.now(timezone.utc).date()
store = load_store(out, vin, args.interval) store = load_store(out, vin, args.interval)
@@ -121,7 +121,7 @@ def run(args: argparse.Namespace) -> None:
snapshot = collect_snapshot(vehicle, domains) snapshot = collect_snapshot(vehicle, domains)
store["records"].append(snapshot) store["records"].append(snapshot)
save_store(out, store, args.max_records) save_store(out, store, args.max_records)
network.broadcast(snapshot, push_clients, push_lock) network.broadcast(snapshot, push_clients, push_lock, push_last)
log.info("Record #%d saved at %s", len(store["records"]), snapshot["ts"]) log.info("Record #%d saved at %s", len(store["records"]), snapshot["ts"])
except KeyboardInterrupt: except KeyboardInterrupt:
raise raise
+18 -3
View File
@@ -10,20 +10,30 @@ def _accept_loop(
server_sock: socket.socket, server_sock: socket.socket,
clients: list, clients: list,
lock: threading.Lock, lock: threading.Lock,
last_snapshot: dict,
) -> None: ) -> None:
while True: while True:
try: try:
conn, addr = server_sock.accept() conn, addr = server_sock.accept()
log.info("Push client connected from %s", addr) log.info("Push client connected from %s", addr)
with lock: with lock:
if last_snapshot["data"] is not None:
try:
line = (json.dumps(last_snapshot["data"], default=str) + "\n").encode()
conn.sendall(line)
except OSError:
log.debug("Failed to send initial snapshot to %s", addr)
conn.close()
continue
clients.append(conn) clients.append(conn)
except OSError: except OSError:
break break
def broadcast(snapshot: dict, clients: list, lock: threading.Lock) -> None: def broadcast(snapshot: dict, clients: list, lock: threading.Lock, last_snapshot: dict) -> None:
line = (json.dumps(snapshot, default=str) + "\n").encode() line = (json.dumps(snapshot, default=str) + "\n").encode()
with lock: with lock:
last_snapshot["data"] = snapshot
dead = [] dead = []
for conn in clients: for conn in clients:
try: try:
@@ -43,7 +53,12 @@ def start_push_server(host: str, port: int) -> tuple:
server_sock.listen() server_sock.listen()
clients: list = [] clients: list = []
lock = threading.Lock() lock = threading.Lock()
t = threading.Thread(target=_accept_loop, args=(server_sock, clients, lock), daemon=True) last_snapshot: dict = {"data": None}
t = threading.Thread(
target=_accept_loop,
args=(server_sock, clients, lock, last_snapshot),
daemon=True,
)
t.start() t.start()
log.info("Push server listening on %s:%d", host, port) log.info("Push server listening on %s:%d", host, port)
return clients, lock return clients, lock, last_snapshot