Skip to content

Presence from one client can be published out of order #768

Open

Description

@alecgibson

Agent decides which of a client's presence updates for an id is newest by pv, plus the order requests reach _broadcastPresence. Neither holds in two cases, both on main today:

  1. A presence re-created with the same id straight after its leave is lost:
    1. localPresence.destroy() (or localPresence.submit(null))
    2. Immediately presence.create(sameId).submit(value)
    3. The new presence (pv 0) reaches receivePresence before the leave, which waits a tick in _unsubscribeDocVersion. It isn't stored over the old pv 0, the leave is published last, and remote clients show nothing
  2. A leave that overtakes an older update for the same id in async receive middleware is followed by that update, so remote clients keep a ghost presence until the client unsubscribes or disconnects.

Remote clients can't untangle either: pv restarts at 0 when a presence is re-created, and a leave wipes the remote's pv history for that id.

A per-agent counter stamped on each message before receive middleware runs would give the server one order to go by: accept a presence only if it's newer than the last one accepted for that channel and id. It would also replace the pv and arrival-order rules #751 adds to drop presence that a leave has overtaken.

Activity

  1. alecgibson commented on Oct 22, 2026

    @alecgibson
    CollaboratorAuthor

    Two more cases for this, from #751. There, the Agent stores a presence in presenceRequests before transforming it, and an older update that is still transforming isn't published if a newer one has replaced it. When the newer one's transform fails, #751 hands the slot back to the request it replaced, but only one step back. That leaves two orderings where a valid older update is still dropped:

    1. An older update A and a newer update B are both transforming.
    2. A finishes first and isn't published, because B has replaced it.
    3. B then fails, and the remote never sees either.

    And:

    1. A is still transforming, while newer updates B and C are sent back to back, so C is stored before B fails.
    2. B fails, but C holds the slot, so nothing changes.
    3. C fails and hands the slot back to B, which has already failed.
    4. A finishes and isn't published, because B has replaced it.

    Both come from recording a presence as accepted before its transform succeeds. Whatever order the fix here settles on should only count a presence as accepted once its transform has succeeded, which would also let the restore-on-failure from #751 go.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions