HarperFast / HarperFast/harper

MQTT: implement duplicate-clientId session takeover (will-message + durable-session races, esp. multi-worker)

Open
#1,604 2 comments 0 reactions 0 assignees View on GitHub
Dominant language
JavaScript
Stars
89
Forks
10
Avg merge
2d 6h
Merged PRs (30d)
200

Description

## Background

Surfaced during review of #1603 (which makes MQTT share its port across workers via `SO_REUSEPORT` on Linux). The concern is **pre-existing** — it is not introduced by #1603 — but multi-worker MQTT makes it materially more reachable, so it's worth tracking.

## Problem

MQTT does not implement duplicate-`clientId` **session takeover**. Per MQTT 3.1.1 §3.1.4, when a client connects with a `ClientId` that already has a live connection, the server MUST disconnect the existing connection first. `getSession()` in `server/DurableSubscriptionsSession.ts` creates/loads the session from the shared `hdb_session*` tables but never disconnects a prior live connection for the same `clientId` (there's no cross-connection/cross-worker registry check).

Consequences, both racing on the shared LMDB tables when a client reconnects (without a clean DISCONNECT) and the old connection is still half-open:

1. **Will-message race** — when the old connection's keepalive finally expires, its `disconnect(false)` publishes the will found in `hdb_session_will` and deletes it. But the *new* connection wrote its own will to the same shared record, so the old worker can publish the currently-connected client's will and delete it.
2. **Durable-session overwrite** — two live `DurableSubscriptionsSession` instances (potentially on different workers) mapped to the same `hdb_session` record can overwrite each other's `startTime` / `subscriptions`.

Single-worker exhibits (1) already; port-sharing across workers widens both because a reconnect can land on a different worker than the prior connection, with no shared in-memory visibility.

## Direction (not yet designed)

Needs a cross-worker view of active `clientId`s (or a takeover signal) so a new connection can evict the prior one before taking over the durable session — likely coordinated through the shared session table + `transactionBroadcast`, rather than the per-worker in-memory `mqttSettings.sessions` set. Retained-message delivery and cross-worker propagation are already handled correctly by the DB layer.

_Filed by Claude (Opus 4.8) as a follow-up to #1603._

Contributor guide

Open the contributing guide

Research direction

Start by tracing getSession() in server/DurableSubscriptionsSession.ts, then inspect the old connection's disconnect(false) path and the shared hdb_session, hdb_session_will, and transactionBroadcast behavior. The work is complete when duplicate clientId connections coordinate across workers so the prior connection is evicted before session takeover, without will-message or durable-session overwrites.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, node.js
Domain
database, distributed-systems
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Needs clarification
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.