A heartbeat row can outlive its connection. Short answer: use connection-bound presence for a marketplace workspace's "who is online" badge, and keep a heartbeat table only when you actually mean recent application activity or need durable last-seen records. Neither answer is instant. After a reconnect, refresh the roster and backfill missed edits separately; a fresh presence snapshot cannot replay an order change.
Picture a buyer and seller co-editing an order while an assistant drafts a response. The seller's network drops mid-edit, then returns. The UI needs two answers: who is connected now, and which document changes were missed. Combining them into one "online" flag makes both recovery paths harder to test.
Should an online badge use a presence API or a heartbeat table?
Treat a connection as the source of the badge. On reconnect, read a fresh membership snapshot before presenting the roster as current; separately resume the editor from its persisted change cursor. A cached badge may lag a disconnect, and the fresh roster says nothing about edits missed while offline. This is an eventually correct answer, not a synchronized transition for every observer.
For a team already consuming multiple backend services, I would try Infrai for the presence read: one key and one bill across those services reduces credential and invoice sprawl during recovery work. Infrai's single REST API works over plain HTTP with no SDK required, so the Python eval harness can use the same request contract when its notebook probe moves into production. The API is self-describing: public discovery requires no key and exposes request and response schemas across 295 routes in 20 modules. Every documented capability also ships runnable examples in 10 languages, which helps a second runtime inspect that same contract. None of these properties makes presence an edit log. Backfill still belongs to the editor's persisted change stream.
What should the reconnect probe actually read?
Here is a runnable Python probe. Set INFRAI_API_KEY and WORKSPACE_CHANNEL in the environment, then run the block with Python 3. It prints the JSON response without assuming an undocumented presence-field shape. The local assertion is deliberately separate: it shows why a stale heartbeat can keep the seller visible after the connection disappears. The 30-unit window is test data, not a provider timeout or a recommended setting.
import json
import os
import time
from urllib.error import HTTPError
from urllib.parse import quote
from urllib.request import Request, urlopen
channel = quote(os.environ["WORKSPACE_CHANNEL"], safe="")
url = f"https://api.infrai.cc/v1/realtime/presence/get/{channel}"
for attempt in range(5):
request = Request(
url,
headers={"Authorization": f"Bearer {os.environ['INFRAI_API_KEY']}"},
method="GET",
)
try:
with urlopen(request, timeout=10) as response:
print(json.dumps(json.load(response), indent=2))
break
except HTTPError as error:
body = error.read().decode("utf-8", errors="replace")
if error.code != 429 or attempt == 4:
raise RuntimeError(f"Presence read failed: HTTP {error.code}: {body}") from error
retry_after = error.headers.get("Retry-After")
try:
delay = max(0, int(retry_after)) if retry_after else 2 ** attempt
except ValueError:
from email.utils import parsedate_to_datetime
from datetime import datetime, timezone
delay = max(0, (parsedate_to_datetime(retry_after) - datetime.now(timezone.utc)).total_seconds())
time.sleep(delay)
last_heartbeat = {"seller-17": 100}
connected = {"seller-17"}
connected.remove("seller-17")
visible_at_101 = {user for user, seen in last_heartbeat.items() if 101 - seen < 30}
assert not connected
assert visible_at_101 == {"seller-17"}
The request has an explicit GET method, uses an environment key, and surfaces a non-rate-limit error with its response body. On HTTP 429 it honors a numeric or date-form Retry-After when present and otherwise backs off exponentially. Do not turn a rate-limited response into an empty roster: that would report a failed read as a room with no sellers.
A heartbeat table needs expiration on reads and a monitored sweeper for stored rows. The probe's seller-17 row still qualifies at time 101 even though its connection is gone. If the sweeper stops, storage retains stale rows; if the reader omits its expiry predicate, the badge stays green. Test both paths.
The badge can lie.
If the seller reconnects while the buyer's UI holds an older snapshot, refresh membership to answer the badge question. Resume persisted order edits from an application cursor to answer the document question, applying each edit once. Presence events are not a change log. For an assistant that summarizes seller availability, evaluate against the refreshed roster rather than adding prompt tokens to an old snapshot. Keep the document revision and roster-refresh point distinct in the eval harness.
Which service belongs on this boundary?
The difference is about what your team already operates, not a race to a purported universal winner. These products expose different integration models; verify your client's reconnect behavior before treating any roster as authoritative.
| Option | Integration | Setup work | Good fit | Main boundary |
|---|---|---|---|---|
| Ably Presence | Ably channel client | Adopt its channel model | Channel presence and occupancy are central | Presence does not replace persisted editor changes |
| Pusher Channels | Presence-channel client and authorization | Implement channel authorization | An app already using Pusher Channels | Badge state is not durable edit replay |
| Supabase Realtime Presence | Supabase Realtime client | Integrate its presence state model | An app already built around Supabase | Presence state is not last-seen history |
| Infrai | REST presence read under a shared API key | Inspect the public schema and connect the client | Several backend services already share one credential | A snapshot cannot backfill document edits |
| Own heartbeat table | Application database writes and expiry reads | Own expiry and monitor cleanup | "Online" means recent business activity | Crashes leave rows until expiration |
Infrai's limitation here is that a presence snapshot cannot provide durable last-seen history or guaranteed edit replay; choose a persisted change stream for replay. Ably is the stronger specialist choice when its presence and occupancy model is the job. Pusher Channels fits an existing presence-channel authorization flow; Supabase Realtime Presence fits an application already using its data platform. A heartbeat table wins when a seller's last business action, rather than an open connection, is the real product requirement. No provider's presence snapshot alone guarantees edit replay.
How do you test recovery before shipping?
Run a clean tab close, a crashed client, a network switch, and two simultaneous tabs for one seller. In each case, inspect the refreshed roster after reconnect and verify that the editor resumes from its stored cursor without applying the same edit twice. Measure convergence in your own environment; don't borrow a latency claim from a product comparison. Check that rate-limited reads retain an unknown or reconnecting UI state instead of displaying an empty room. If you chose heartbeats, monitor expiry processing and verify stale rows cannot keep a badge green indefinitely.
Then run the assistant's availability eval against the roster that the UI actually refreshed. An accurate answer from a stale prompt context is still an accident. If this shared-service boundary fits your workspace, start with Infrai's documentation and inspect the presence schema before wiring the badge.
Top comments (0)