perf: bound Web Push device fan-out (Closes #557)
This commit is contained in:
1 parent
cdf6ecdcb8
commit
79309196b8
4 files changed
+137
-41
No files matched your search
@@ -83,6 +83,7 @@ async def test_push_poll_authorizes_delivery_against_managed_session(monkeypatch
|
||||
captured.update(kwargs)
|
||||
raise asyncio.CancelledError
|
||||
|
||||
monkeypatch.setenv("STACKCHAIN_PUSH_MAX_CONCURRENCY", "3")
|
||||
monkeypatch.setattr(main.asyncio, "sleep", no_wait)
|
||||
monkeypatch.setattr(main, "dispatch_unread_updates", stop_after_capture)
|
||||
|
||||
@@ -90,6 +91,7 @@ async def test_push_poll_authorizes_delivery_against_managed_session(monkeypatch
|
||||
await main._push_poll_loop()
|
||||
|
||||
assert captured["session_active"] is dashboard_auth.managed_session_active
|
||||
assert captured["max_concurrency"] == 3
|
||||
|
||||
|
||||
def test_replacing_subscription_resets_delivery_cursor_and_revocation_removes_device(tmp_path):
|
||||
@@ -224,6 +226,38 @@ async def test_transient_endpoint_failure_does_not_block_healthy_devices(tmp_pat
|
||||
assert store.claim_unseen({8})[0].session_id == "session-a"
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_transient_failure_stops_that_device_until_the_next_poll(tmp_path):
|
||||
store = PushSubscriptionStore(tmp_path / "push.sqlite3")
|
||||
for session_id in ("failing-device", "healthy-device"):
|
||||
store.upsert(session_id, {
|
||||
"endpoint": f"https://push.example/{session_id}",
|
||||
"keys": {"p256dh": "public-key", "auth": "auth-secret"},
|
||||
})
|
||||
attempts = []
|
||||
|
||||
async def unread():
|
||||
return {"items": [
|
||||
{"notification_id": 8},
|
||||
{"notification_id": 9},
|
||||
]}
|
||||
|
||||
async def send(subscription, payload):
|
||||
endpoint = subscription["endpoint"]
|
||||
thread_id = int(json.loads(payload)["route"].rsplit("/", 1)[1])
|
||||
attempts.append((endpoint, thread_id))
|
||||
if endpoint.endswith("failing-device"):
|
||||
raise RuntimeError("provider unavailable")
|
||||
|
||||
config = PushConfiguration("public", "private", "mailto:ops@example.com")
|
||||
assert await dispatch_unread_updates(store, config, unread, send) == 2
|
||||
assert [item for item in attempts if item[0].endswith("failing-device")] == [
|
||||
("https://push.example/failing-device", 8)
|
||||
]
|
||||
remaining = {item.session_id: item.thread_ids for item in store.claim_unseen({8, 9})}
|
||||
assert remaining == {"failing-device": (8, 9)}
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_timed_out_endpoint_does_not_stall_push_fanout(tmp_path):
|
||||
store = PushSubscriptionStore(tmp_path / "push.sqlite3")
|
||||
@@ -254,6 +288,45 @@ async def test_timed_out_endpoint_does_not_stall_push_fanout(tmp_path):
|
||||
assert sent == ["https://push.example/session-b"]
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_dispatches_devices_concurrently_with_a_strict_bound(tmp_path):
|
||||
store = PushSubscriptionStore(tmp_path / "push.sqlite3")
|
||||
for index in range(4):
|
||||
store.upsert(f"session-{index}", {
|
||||
"endpoint": f"https://push.example/session-{index}",
|
||||
"keys": {"p256dh": "public-key", "auth": "auth-secret"},
|
||||
})
|
||||
active = 0
|
||||
peak = 0
|
||||
bound_reached = asyncio.Event()
|
||||
release = asyncio.Event()
|
||||
|
||||
async def unread():
|
||||
return {"items": [{"notification_id": 13}]}
|
||||
|
||||
async def send(_subscription, _payload):
|
||||
nonlocal active, peak
|
||||
active += 1
|
||||
peak = max(peak, active)
|
||||
if active == 2:
|
||||
bound_reached.set()
|
||||
await release.wait()
|
||||
active -= 1
|
||||
|
||||
config = PushConfiguration("public", "private", "mailto:ops@example.com")
|
||||
dispatch = asyncio.create_task(dispatch_unread_updates(
|
||||
store, config, unread, send, max_concurrency=2
|
||||
))
|
||||
await asyncio.wait_for(bound_reached.wait(), timeout=0.2)
|
||||
|
||||
assert peak == 2
|
||||
assert dispatch.done() is False
|
||||
|
||||
release.set()
|
||||
assert await dispatch == 4
|
||||
assert peak == 2
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_dispatch_removes_inactive_sessions_without_blocking_active_devices(tmp_path):
|
||||
store = PushSubscriptionStore(tmp_path / "push.sqlite3")
|
||||
|
||||
Reference in new issue
Block a user