mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-10-21 10:15:04 +02:00
Fix: Add Printer no longer hangs the container on P1S (#1445)
The pre-insert MQTT probe added in 0.2.4.2 (b51598ea) had two bugs that
compounded on P1S firmware specifically:
1. Fixed 2-second sleep was too short. P1S broker + TLS handshake
routinely needs 3-5s to surface CONNACK on a cold MQTT session (same
firmware family with the documented "broker stops publishing but TCP
stays alive" quirk at bambu_mqtt.py:3181), so the probe falsely rejected
a printer that would have connected fine. H2C's broker is snappier and
cleared the 2s window without trouble — which is why the reporter's
H2C added without issue and only the P1S misbehaved.
2. client.disconnect() ran synchronously on the asyncio thread.
BambuMQTTClient.disconnect() ends in paho's loop_stop() which joins
the network thread; if that thread was still mid-TLS-handshake to the
slow P1S socket when teardown ran, the join blocked the asyncio thread
for as long as the handshake took to complete or fail. POST /printers
wedged, every other HTTP request queued behind it, Docker healthcheck
timed out — user-visible symptom: "the container hangs."
Fix:
- Replace the fixed sleep with a polling loop (8s budget, 200ms tick,
early-returns the moment state.connected flips True). Slow brokers
get the headroom they need; happy-path connects still finish in
~1-2s. Constants exposed as PROBE_TIMEOUT_SECONDS / PROBE_POLL_
INTERVAL_SECONDS class attributes so tests can dial them down.
- Move client.disconnect() to await asyncio.to_thread(...) so paho's
thread-join can never block the event loop.
The empty-card-report-prevention goal of the original probe stays
intact: a genuinely wrong access code still results in connected=False
after the 8s budget, the 400 with code=printer_connection_failed
still fires, the row is still never persisted.
This commit is contained in:
@@ -616,13 +616,31 @@ class PrinterManager:
|
||||
return self._clients[printer_id].request_status_update()
|
||||
return False
|
||||
|
||||
# Probe budget for test_connection (#1445). Was a fixed 2s sleep, which was
|
||||
# too short for P1S firmware whose broker / TLS handshake routinely takes
|
||||
# 3–5s to surface a CONNACK on a cold MQTT session. We now poll up to
|
||||
# PROBE_TIMEOUT_SECONDS and early-return the moment we see connected=True,
|
||||
# so happy-path connections still finish in ~1–2s and slow brokers get the
|
||||
# headroom they need instead of getting falsely rejected.
|
||||
PROBE_TIMEOUT_SECONDS = 8.0
|
||||
PROBE_POLL_INTERVAL_SECONDS = 0.2
|
||||
|
||||
async def test_connection(
|
||||
self,
|
||||
ip_address: str,
|
||||
serial_number: str,
|
||||
access_code: str,
|
||||
) -> dict:
|
||||
"""Test connection to a printer without persisting."""
|
||||
"""Test connection to a printer without persisting.
|
||||
|
||||
Polls for up to PROBE_TIMEOUT_SECONDS and tears the probe client down
|
||||
off-loop. The teardown matters: `client.disconnect()` ends in paho's
|
||||
`loop_stop()` which `join()`s the network thread — if the thread is
|
||||
still mid-TLS-handshake to a slow printer, that join blocks the
|
||||
asyncio event loop and every other HTTP request queues behind it. The
|
||||
original synchronous teardown produced the #1445 "Docker container
|
||||
hangs" symptom on P1S when called from POST /printers/.
|
||||
"""
|
||||
client = BambuMQTTClient(
|
||||
ip_address=ip_address,
|
||||
serial_number=serial_number,
|
||||
@@ -631,7 +649,9 @@ class PrinterManager:
|
||||
|
||||
try:
|
||||
client.connect()
|
||||
await asyncio.sleep(2)
|
||||
deadline = asyncio.get_running_loop().time() + self.PROBE_TIMEOUT_SECONDS
|
||||
while not client.state.connected and asyncio.get_running_loop().time() < deadline:
|
||||
await asyncio.sleep(self.PROBE_POLL_INTERVAL_SECONDS)
|
||||
|
||||
result = {
|
||||
"success": client.state.connected,
|
||||
@@ -639,7 +659,9 @@ class PrinterManager:
|
||||
"model": client.state.raw_data.get("device_model"),
|
||||
}
|
||||
finally:
|
||||
client.disconnect()
|
||||
# Off-loop teardown — see docstring. paho's loop_stop() joins the
|
||||
# network thread which may still be in a slow TLS handshake.
|
||||
await asyncio.to_thread(client.disconnect)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
Reference in New Issue
Block a user