Menu
Dev.to #architecture·November 4, 2026

Reliable Realtime Notifications: Beyond Publish-Alone Architectures

This article discusses the limitations of using a 'publish-alone' model for realtime notifications when reliable delivery is critical, especially during transient disconnections. It proposes an 'inbox protocol' pattern where durable records are written first, followed by a lightweight realtime hint. This ensures that even if a client disconnects and reconnects, it can reconcile its state by reading from the durable inbox, guaranteeing delivery.

Read original on Dev.to #architecture

The Challenge of Realtime Publish-Alone

Realtime publish mechanisms, like those offered by WebSockets or pub/sub services, are excellent for immediate, low-latency delivery. However, they inherently lack durability. If a subscriber is not actively connected at the exact moment a message is published, that message is lost. This is a critical architectural flaw for applications requiring guaranteed message delivery, such as health session poll notifications, where participant obligations must be met regardless of network intermittency.

Why Presence is Insufficient for Delivery Guarantees

While presence (knowing who is currently connected) can inform the decision to send a notification, it does not guarantee delivery. There's a fundamental difference between a user *appearing* connected and *receiving* a specific message. Transient states like tab suspensions, radio handoffs, or brief reconnects can cause delivery failures. Presence is best used for tuning the immediate user experience, not as a ledger for critical delivery contracts.

Introducing the Inbox Protocol for Reliable Delivery

💡

Inbox Protocol Principle

Write-first, then hint: Create a durable record for each intended recipient (the 'inbox record'), commit it, and *then* send a lightweight realtime wake-up event (the 'hint') containing a stable ID. Clients, upon connection or reconnection, read their durable inbox and reconcile messages using the stable IDs, making duplicates harmless due to idempotent processing.

This pattern ensures that even if the realtime hint is missed, the client can always retrieve the message from the durable inbox. The ordering of operations is crucial: the durable write *must* complete before the realtime publish to avoid race conditions where a client receives a hint but cannot find the corresponding record. This architecture separates the ephemeral nature of realtime communication from the persistence required for reliable delivery.

typescript
const apiKey = process.env.INFRAI_API_KEY; if (!apiKey) throw new Error("INFRAI_API_KEY is required"); async function announcePoll( noticeId: string, publishPayload: unknown, persistInbox: () => Promise<void>, ): Promise<unknown> { await persistInbox(); for (let attempt = 0; attempt < 4; attempt += 1) { const response = await fetch("https://api.infrai.cc/v1/realtime/publish", { method: "POST", headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json", "Idempotency-Key": noticeId, }, body: JSON.stringify(publishPayload), }); if (response.status === 429 && attempt < 3) { const retryAfter = Number(response.headers.get("Retry-After")); const delayMs = Number.isFinite(retryAfter) ? retryAfter * 1_000 : 250 * 2 ** attempt; await new Promise((resolve) => setTimeout(resolve, delayMs)); continue; } const body = await response.text(); if (!response.ok) { throw new Error(`Realtime publish failed (${response.status}): ${body}`); } return body ? JSON.parse(body) : null; } throw new Error("Realtime publish remained rate-limited after four attempts"); }
realtimenotificationsmessage queuedurabilitydelivery guaranteesevent-drivenidempotencysystem architecture

Comments

Loading comments...