Skip to content

Commit 7840cd3

Browse files
committed
Address PR feedback for websocket reliability changes
1 parent 0e74740 commit 7840cd3

7 files changed

Lines changed: 159 additions & 14 deletions

File tree

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -584,11 +584,11 @@ All channel classes accept the following optional keyword arguments:
584584
| Parameter | Type | Default | Description |
585585
|-----------|------|---------|-------------|
586586
| `ping_interval` | `int` | `60` | Seconds between automatic ping frames. Set to `0` to disable pings. |
587-
| `ping_timeout` | `int` | `45` | Seconds to wait for a pong response before treating the connection as dead. Must be lower than `ping_interval`. |
587+
| `ping_timeout` | `int` | `45` | Seconds to wait for a pong response before treating the connection as dead. When `ping_interval > 0`, this must be lower than `ping_interval`. |
588588
| `auto_reconnect` | `bool` | `False` | Automatically reconnect on transient failures using exponential backoff. |
589589
| `max_reconnect_attempts` | `int` | `5` | Maximum number of reconnect attempts before giving up. |
590590
| `reconnect_backoff` | `float` | `2.0` | Base backoff delay in seconds. Doubles after each failed attempt (2s, 4s, 8s, ...). Resets on successful reconnection. |
591-
| `queue_maxsize` | `int` | `0` | Maximum messages buffered in the internal queue for `receive()`. `0` means unbounded. When set, incoming messages are dropped with a warning when the queue is full, preventing memory growth on high-frequency streams. |
591+
| `queue_maxsize` | `int` | `0` | Maximum messages buffered in the internal queues used for both `receive()` and callback delivery. `0` means unbounded. When set, incoming messages are dropped with a warning when either queue is full, preventing memory growth on high-frequency streams. |
592592
| `subscription_watchdog_timeout` | `float` | `10.0` | Maximum time to wait for all `channel_subscribed` acknowledgements after connect. On timeout, the connection is closed to trigger a clean reconnect. |
593593
| `rate_limit_backoff` | `float` | `30.0` | Minimum reconnect delay after a 429 rate-limit response. |
594594
| `throughput_log_interval` | `int` | `100` | Logs queue depth and processed counts every N messages. Set to `0` to disable periodic throughput logs. |

src/mistapi/websockets/__ws_client.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,8 @@ def _handle_message(self, ws: websocket.WebSocketApp, message: str | bytes) -> N
460460
data = json.loads(message)
461461
except (json.JSONDecodeError, TypeError):
462462
data = {"raw": message}
463+
if not isinstance(data, dict):
464+
data = {"data": data}
463465

464466
if isinstance(data, dict):
465467
self._process_subscription_event(ws, data)
@@ -473,8 +475,7 @@ def _handle_message(self, ws: websocket.WebSocketApp, message: str | bytes) -> N
473475

474476
def _handle_error(self, _ws: websocket.WebSocketApp, error: Exception) -> None:
475477
status_code = self._extract_status_code(error)
476-
if status_code is not None:
477-
self._last_http_status = status_code
478+
self._last_http_status = status_code
478479
if status_code == 429:
479480
logger.warning(
480481
"WebSocket received HTTP 429 (rate limit). "
@@ -517,12 +518,14 @@ def _handle_close(
517518
) -> None:
518519
self._connected.clear()
519520
self._cancel_subscription_watchdog()
520-
self._last_close_code = close_status_code
521-
self._last_close_msg = close_msg
521+
if close_status_code is not None:
522+
self._last_close_code = close_status_code
523+
if close_msg not in (None, ""):
524+
self._last_close_msg = close_msg
522525
logger.info(
523526
"WebSocket closed. code=%s message=%s",
524-
close_status_code,
525-
close_msg,
527+
self._last_close_code,
528+
self._last_close_msg,
526529
)
527530

528531
# ------------------------------------------------------------------

src/mistapi/websockets/location.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@ def __init__(
8484
reconnect_backoff: float = 2.0,
8585
max_reconnect_backoff: float | None = None,
8686
queue_maxsize: int = 0,
87+
subscription_watchdog_timeout: float = 10.0,
88+
rate_limit_backoff: float = 30.0,
89+
throughput_log_interval: int = 100,
8790
) -> None:
8891
channels = [f"/sites/{site_id}/stats/maps/{mid}/assets" for mid in map_ids]
8992
super().__init__(
@@ -96,6 +99,9 @@ def __init__(
9699
reconnect_backoff=reconnect_backoff,
97100
max_reconnect_backoff=max_reconnect_backoff,
98101
queue_maxsize=queue_maxsize,
102+
subscription_watchdog_timeout=subscription_watchdog_timeout,
103+
rate_limit_backoff=rate_limit_backoff,
104+
throughput_log_interval=throughput_log_interval,
99105
)
100106

101107

@@ -168,6 +174,9 @@ def __init__(
168174
reconnect_backoff: float = 2.0,
169175
max_reconnect_backoff: float | None = None,
170176
queue_maxsize: int = 0,
177+
subscription_watchdog_timeout: float = 10.0,
178+
rate_limit_backoff: float = 30.0,
179+
throughput_log_interval: int = 100,
171180
) -> None:
172181
channels = [f"/sites/{site_id}/stats/maps/{mid}/clients" for mid in map_ids]
173182
super().__init__(
@@ -180,6 +189,9 @@ def __init__(
180189
reconnect_backoff=reconnect_backoff,
181190
max_reconnect_backoff=max_reconnect_backoff,
182191
queue_maxsize=queue_maxsize,
192+
subscription_watchdog_timeout=subscription_watchdog_timeout,
193+
rate_limit_backoff=rate_limit_backoff,
194+
throughput_log_interval=throughput_log_interval,
183195
)
184196

185197

@@ -252,6 +264,9 @@ def __init__(
252264
reconnect_backoff: float = 2.0,
253265
max_reconnect_backoff: float | None = None,
254266
queue_maxsize: int = 0,
267+
subscription_watchdog_timeout: float = 10.0,
268+
rate_limit_backoff: float = 30.0,
269+
throughput_log_interval: int = 100,
255270
) -> None:
256271
channels = [f"/sites/{site_id}/stats/maps/{mid}/sdkclients" for mid in map_ids]
257272
super().__init__(
@@ -264,6 +279,9 @@ def __init__(
264279
reconnect_backoff=reconnect_backoff,
265280
max_reconnect_backoff=max_reconnect_backoff,
266281
queue_maxsize=queue_maxsize,
282+
subscription_watchdog_timeout=subscription_watchdog_timeout,
283+
rate_limit_backoff=rate_limit_backoff,
284+
throughput_log_interval=throughput_log_interval,
267285
)
268286

269287

@@ -336,6 +354,9 @@ def __init__(
336354
reconnect_backoff: float = 2.0,
337355
max_reconnect_backoff: float | None = None,
338356
queue_maxsize: int = 0,
357+
subscription_watchdog_timeout: float = 10.0,
358+
rate_limit_backoff: float = 30.0,
359+
throughput_log_interval: int = 100,
339360
) -> None:
340361
channels = [
341362
f"/sites/{site_id}/stats/maps/{mid}/unconnected_clients" for mid in map_ids
@@ -350,6 +371,9 @@ def __init__(
350371
reconnect_backoff=reconnect_backoff,
351372
max_reconnect_backoff=max_reconnect_backoff,
352373
queue_maxsize=queue_maxsize,
374+
subscription_watchdog_timeout=subscription_watchdog_timeout,
375+
rate_limit_backoff=rate_limit_backoff,
376+
throughput_log_interval=throughput_log_interval,
353377
)
354378

355379

@@ -422,6 +446,9 @@ def __init__(
422446
reconnect_backoff: float = 2.0,
423447
max_reconnect_backoff: float | None = None,
424448
queue_maxsize: int = 0,
449+
subscription_watchdog_timeout: float = 10.0,
450+
rate_limit_backoff: float = 30.0,
451+
throughput_log_interval: int = 100,
425452
) -> None:
426453
channels = [
427454
f"/sites/{site_id}/stats/maps/{mid}/discovered_assets" for mid in map_ids
@@ -436,4 +463,7 @@ def __init__(
436463
reconnect_backoff=reconnect_backoff,
437464
max_reconnect_backoff=max_reconnect_backoff,
438465
queue_maxsize=queue_maxsize,
466+
subscription_watchdog_timeout=subscription_watchdog_timeout,
467+
rate_limit_backoff=rate_limit_backoff,
468+
throughput_log_interval=throughput_log_interval,
439469
)

src/mistapi/websockets/orgs.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ def __init__(
8181
reconnect_backoff: float = 2.0,
8282
max_reconnect_backoff: float | None = None,
8383
queue_maxsize: int = 0,
84+
subscription_watchdog_timeout: float = 10.0,
85+
rate_limit_backoff: float = 30.0,
86+
throughput_log_interval: int = 100,
8487
) -> None:
8588
super().__init__(
8689
mist_session,
@@ -92,6 +95,9 @@ def __init__(
9295
reconnect_backoff=reconnect_backoff,
9396
max_reconnect_backoff=max_reconnect_backoff,
9497
queue_maxsize=queue_maxsize,
98+
subscription_watchdog_timeout=subscription_watchdog_timeout,
99+
rate_limit_backoff=rate_limit_backoff,
100+
throughput_log_interval=throughput_log_interval,
95101
)
96102

97103

@@ -161,6 +167,9 @@ def __init__(
161167
reconnect_backoff: float = 2.0,
162168
max_reconnect_backoff: float | None = None,
163169
queue_maxsize: int = 0,
170+
subscription_watchdog_timeout: float = 10.0,
171+
rate_limit_backoff: float = 30.0,
172+
throughput_log_interval: int = 100,
164173
) -> None:
165174
super().__init__(
166175
mist_session,
@@ -172,6 +181,9 @@ def __init__(
172181
reconnect_backoff=reconnect_backoff,
173182
max_reconnect_backoff=max_reconnect_backoff,
174183
queue_maxsize=queue_maxsize,
184+
subscription_watchdog_timeout=subscription_watchdog_timeout,
185+
rate_limit_backoff=rate_limit_backoff,
186+
throughput_log_interval=throughput_log_interval,
175187
)
176188

177189

@@ -241,6 +253,9 @@ def __init__(
241253
reconnect_backoff: float = 2.0,
242254
max_reconnect_backoff: float | None = None,
243255
queue_maxsize: int = 0,
256+
subscription_watchdog_timeout: float = 10.0,
257+
rate_limit_backoff: float = 30.0,
258+
throughput_log_interval: int = 100,
244259
) -> None:
245260
super().__init__(
246261
mist_session,
@@ -252,4 +267,7 @@ def __init__(
252267
reconnect_backoff=reconnect_backoff,
253268
max_reconnect_backoff=max_reconnect_backoff,
254269
queue_maxsize=queue_maxsize,
270+
subscription_watchdog_timeout=subscription_watchdog_timeout,
271+
rate_limit_backoff=rate_limit_backoff,
272+
throughput_log_interval=throughput_log_interval,
255273
)

src/mistapi/websockets/session.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,9 @@ def __init__(
9090
reconnect_backoff: float = 2.0,
9191
max_reconnect_backoff: float | None = None,
9292
queue_maxsize: int = 0,
93+
subscription_watchdog_timeout: float = 10.0,
94+
rate_limit_backoff: float = 30.0,
95+
throughput_log_interval: int = 100,
9396
) -> None:
9497
parsed = urlparse(url)
9598
if parsed.scheme.lower() != "wss" or not parsed.netloc:
@@ -105,6 +108,9 @@ def __init__(
105108
reconnect_backoff=reconnect_backoff,
106109
max_reconnect_backoff=max_reconnect_backoff,
107110
queue_maxsize=queue_maxsize,
111+
subscription_watchdog_timeout=subscription_watchdog_timeout,
112+
rate_limit_backoff=rate_limit_backoff,
113+
throughput_log_interval=throughput_log_interval,
108114
)
109115

110116
def _build_ws_url(self) -> str:

src/mistapi/websockets/sites.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ def __init__(
8181
reconnect_backoff: float = 2.0,
8282
max_reconnect_backoff: float | None = None,
8383
queue_maxsize: int = 0,
84+
subscription_watchdog_timeout: float = 10.0,
85+
rate_limit_backoff: float = 30.0,
86+
throughput_log_interval: int = 100,
8487
) -> None:
8588
channels = [f"/sites/{site_id}/stats/clients" for site_id in site_ids]
8689
super().__init__(
@@ -93,6 +96,9 @@ def __init__(
9396
reconnect_backoff=reconnect_backoff,
9497
max_reconnect_backoff=max_reconnect_backoff,
9598
queue_maxsize=queue_maxsize,
99+
subscription_watchdog_timeout=subscription_watchdog_timeout,
100+
rate_limit_backoff=rate_limit_backoff,
101+
throughput_log_interval=throughput_log_interval,
96102
)
97103

98104

@@ -171,6 +177,9 @@ def __init__(
171177
reconnect_backoff: float = 2.0,
172178
max_reconnect_backoff: float | None = None,
173179
queue_maxsize: int = 0,
180+
subscription_watchdog_timeout: float = 10.0,
181+
rate_limit_backoff: float = 30.0,
182+
throughput_log_interval: int = 100,
174183
) -> None:
175184
channels = [
176185
f"/sites/{site_id}/devices/{device_id}/cmd" for device_id in device_ids
@@ -185,6 +194,9 @@ def __init__(
185194
reconnect_backoff=reconnect_backoff,
186195
max_reconnect_backoff=max_reconnect_backoff,
187196
queue_maxsize=queue_maxsize,
197+
subscription_watchdog_timeout=subscription_watchdog_timeout,
198+
rate_limit_backoff=rate_limit_backoff,
199+
throughput_log_interval=throughput_log_interval,
188200
)
189201

190202

@@ -254,6 +266,9 @@ def __init__(
254266
reconnect_backoff: float = 2.0,
255267
max_reconnect_backoff: float | None = None,
256268
queue_maxsize: int = 0,
269+
subscription_watchdog_timeout: float = 10.0,
270+
rate_limit_backoff: float = 30.0,
271+
throughput_log_interval: int = 100,
257272
) -> None:
258273
channels = [f"/sites/{site_id}/stats/devices" for site_id in site_ids]
259274
super().__init__(
@@ -266,6 +281,9 @@ def __init__(
266281
reconnect_backoff=reconnect_backoff,
267282
max_reconnect_backoff=max_reconnect_backoff,
268283
queue_maxsize=queue_maxsize,
284+
subscription_watchdog_timeout=subscription_watchdog_timeout,
285+
rate_limit_backoff=rate_limit_backoff,
286+
throughput_log_interval=throughput_log_interval,
269287
)
270288

271289

@@ -335,6 +353,9 @@ def __init__(
335353
reconnect_backoff: float = 2.0,
336354
max_reconnect_backoff: float | None = None,
337355
queue_maxsize: int = 0,
356+
subscription_watchdog_timeout: float = 10.0,
357+
rate_limit_backoff: float = 30.0,
358+
throughput_log_interval: int = 100,
338359
) -> None:
339360
channels = [f"/sites/{site_id}/devices" for site_id in site_ids]
340361
super().__init__(
@@ -347,6 +368,9 @@ def __init__(
347368
reconnect_backoff=reconnect_backoff,
348369
max_reconnect_backoff=max_reconnect_backoff,
349370
queue_maxsize=queue_maxsize,
371+
subscription_watchdog_timeout=subscription_watchdog_timeout,
372+
rate_limit_backoff=rate_limit_backoff,
373+
throughput_log_interval=throughput_log_interval,
350374
)
351375

352376

@@ -416,6 +440,9 @@ def __init__(
416440
reconnect_backoff: float = 2.0,
417441
max_reconnect_backoff: float | None = None,
418442
queue_maxsize: int = 0,
443+
subscription_watchdog_timeout: float = 10.0,
444+
rate_limit_backoff: float = 30.0,
445+
throughput_log_interval: int = 100,
419446
) -> None:
420447
channels = [f"/sites/{site_id}/stats/mxedges" for site_id in site_ids]
421448
super().__init__(
@@ -428,6 +455,9 @@ def __init__(
428455
reconnect_backoff=reconnect_backoff,
429456
max_reconnect_backoff=max_reconnect_backoff,
430457
queue_maxsize=queue_maxsize,
458+
subscription_watchdog_timeout=subscription_watchdog_timeout,
459+
rate_limit_backoff=rate_limit_backoff,
460+
throughput_log_interval=throughput_log_interval,
431461
)
432462

433463

@@ -497,6 +527,9 @@ def __init__(
497527
reconnect_backoff: float = 2.0,
498528
max_reconnect_backoff: float | None = None,
499529
queue_maxsize: int = 0,
530+
subscription_watchdog_timeout: float = 10.0,
531+
rate_limit_backoff: float = 30.0,
532+
throughput_log_interval: int = 100,
500533
) -> None:
501534
channels = [f"/sites/{site_id}/mxedges" for site_id in site_ids]
502535
super().__init__(
@@ -509,6 +542,9 @@ def __init__(
509542
reconnect_backoff=reconnect_backoff,
510543
max_reconnect_backoff=max_reconnect_backoff,
511544
queue_maxsize=queue_maxsize,
545+
subscription_watchdog_timeout=subscription_watchdog_timeout,
546+
rate_limit_backoff=rate_limit_backoff,
547+
throughput_log_interval=throughput_log_interval,
512548
)
513549

514550

@@ -578,6 +614,9 @@ def __init__(
578614
reconnect_backoff: float = 2.0,
579615
max_reconnect_backoff: float | None = None,
580616
queue_maxsize: int = 0,
617+
subscription_watchdog_timeout: float = 10.0,
618+
rate_limit_backoff: float = 30.0,
619+
throughput_log_interval: int = 100,
581620
) -> None:
582621
channels = [f"/sites/{site_id}/pcaps"]
583622
super().__init__(
@@ -590,4 +629,7 @@ def __init__(
590629
reconnect_backoff=reconnect_backoff,
591630
max_reconnect_backoff=max_reconnect_backoff,
592631
queue_maxsize=queue_maxsize,
632+
subscription_watchdog_timeout=subscription_watchdog_timeout,
633+
rate_limit_backoff=rate_limit_backoff,
634+
throughput_log_interval=throughput_log_interval,
593635
)

0 commit comments

Comments
 (0)