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
@@ -87,6 +87,9 @@ async def _push_poll_loop() -> None:
|
||||
send_timeout + 5.0,
|
||||
float(os.getenv("STACKCHAIN_PUSH_LEASE_SECONDS", "60")),
|
||||
)
|
||||
max_concurrency = max(
|
||||
1, int(os.getenv("STACKCHAIN_PUSH_MAX_CONCURRENCY", "8"))
|
||||
)
|
||||
while True:
|
||||
await asyncio.sleep(interval)
|
||||
try:
|
||||
@@ -97,6 +100,7 @@ async def _push_poll_loop() -> None:
|
||||
session_active=dashboard_auth.managed_session_active,
|
||||
lease_seconds=lease_seconds,
|
||||
send_timeout_seconds=send_timeout,
|
||||
max_concurrency=max_concurrency,
|
||||
)
|
||||
except asyncio.CancelledError:
|
||||
raise
|
||||
|
||||
+56
-39
@@ -43,6 +43,7 @@ async def dispatch_unread_updates(
|
||||
session_active: Callable[[str], Awaitable[bool]] | None = None,
|
||||
lease_seconds: float = 60.0,
|
||||
send_timeout_seconds: float = 10.0,
|
||||
max_concurrency: int = 8,
|
||||
) -> int:
|
||||
if not configuration.enabled:
|
||||
return 0
|
||||
@@ -78,46 +79,62 @@ async def dispatch_unread_updates(
|
||||
for delivery, active in zip(deliveries, authorized)
|
||||
if active
|
||||
]
|
||||
count = 0
|
||||
for delivery in deliveries:
|
||||
for thread_id in delivery.thread_ids:
|
||||
still_owner = await asyncio.to_thread(
|
||||
store.acquire_dispatch_lease,
|
||||
owner,
|
||||
now=time.time(),
|
||||
lease_seconds=lease_seconds,
|
||||
)
|
||||
if not still_owner:
|
||||
return count
|
||||
payload = json.dumps(
|
||||
{
|
||||
"title": "New work update",
|
||||
"body": "Tap to review it in Stackchain.",
|
||||
"route": f"#/my-work/update/{thread_id}",
|
||||
"tag": f"stackchain-update-{thread_id}",
|
||||
},
|
||||
separators=(",", ":"),
|
||||
)
|
||||
try:
|
||||
if send is None:
|
||||
operation = send_web_push(
|
||||
delivery.subscription, payload, configuration
|
||||
semaphore = asyncio.Semaphore(max(1, max_concurrency))
|
||||
ownership_lost = asyncio.Event()
|
||||
|
||||
async def dispatch_device(delivery) -> int:
|
||||
async with semaphore:
|
||||
if ownership_lost.is_set():
|
||||
return 0
|
||||
count = 0
|
||||
for thread_id in delivery.thread_ids:
|
||||
still_owner = await asyncio.to_thread(
|
||||
store.acquire_dispatch_lease,
|
||||
owner,
|
||||
now=time.time(),
|
||||
lease_seconds=lease_seconds,
|
||||
)
|
||||
if not still_owner:
|
||||
ownership_lost.set()
|
||||
return count
|
||||
payload = json.dumps(
|
||||
{
|
||||
"title": "New work update",
|
||||
"body": "Tap to review it in Stackchain.",
|
||||
"route": f"#/my-work/update/{thread_id}",
|
||||
"tag": f"stackchain-update-{thread_id}",
|
||||
},
|
||||
separators=(",", ":"),
|
||||
)
|
||||
try:
|
||||
if send is None:
|
||||
operation = send_web_push(
|
||||
delivery.subscription, payload, configuration
|
||||
)
|
||||
else:
|
||||
operation = send(delivery.subscription, payload)
|
||||
await asyncio.wait_for(operation, timeout=send_timeout_seconds)
|
||||
except Exception as error:
|
||||
status = getattr(
|
||||
getattr(error, "response", None), "status_code", None
|
||||
)
|
||||
else:
|
||||
operation = send(delivery.subscription, payload)
|
||||
await asyncio.wait_for(operation, timeout=send_timeout_seconds)
|
||||
except Exception as error:
|
||||
status = getattr(getattr(error, "response", None), "status_code", None)
|
||||
if status in {404, 410}:
|
||||
await asyncio.to_thread(store.delete_session, delivery.session_id)
|
||||
if status in {404, 410}:
|
||||
await asyncio.to_thread(
|
||||
store.delete_session, delivery.session_id
|
||||
)
|
||||
break
|
||||
# Leave this device's transient failures unseen for a later
|
||||
# poll instead of paying the endpoint deadline repeatedly.
|
||||
break
|
||||
# A transient provider failure belongs to this endpoint;
|
||||
# leave it unseen for a later poll and continue fan-out.
|
||||
continue
|
||||
await asyncio.to_thread(
|
||||
store.mark_delivered, delivery.session_id, (thread_id,)
|
||||
)
|
||||
count += 1
|
||||
return count
|
||||
await asyncio.to_thread(
|
||||
store.mark_delivered, delivery.session_id, (thread_id,)
|
||||
)
|
||||
count += 1
|
||||
return count
|
||||
|
||||
counts = await asyncio.gather(
|
||||
*(dispatch_device(delivery) for delivery in deliveries)
|
||||
)
|
||||
return sum(counts)
|
||||
finally:
|
||||
await asyncio.to_thread(store.release_dispatch_lease, owner)
|
||||
Reference in new issue
Block a user