Skip to content

fix(locator): keep users online while active bindings remain - #274

Merged
shenjinti merged 1 commit into
restsend:mainfrom
dengxiayehu:fix/issue_26090611
Sep 6, 2026
Merged

fix(locator): keep users online while active bindings remain#274
shenjinti merged 1 commit into
restsend:mainfrom
dengxiayehu:fix/issue_26090611

Conversation

@dengxiayehu

Copy link
Copy Markdown
Contributor

Problem

The SIP registrar removes a binding through several independent paths: an
explicit REGISTER with expires=0, a transport close (e.g. a browser that
closes its WebSocket without unregistering), the periodic expired-registration
sweep, and lookup-time cleanup in the DB-backed locator.

A single registered identity (username + realm) may hold multiple active
bindings at once — for example, when a client reconnects and registers a fresh
Contact before the previous Contact expires or its transport is torn down.
Removing any one binding used to publish LocatorEvent::Offline / Unregistered
for the removed bindings directly. A stale binding could therefore overwrite the
fresh registration state and mark a still-registered user as offline, corrupting
downstream presence and agent state.

Change

  • Record the authoritative registration identity on every Location
    (registered_username, registered_realm), populated on register and restored
    from the DB model, so each removal path knows which identity a binding belongs
    to.
  • Gate every binding-removal event on the final active binding: a new
    locations_without_active_bindings helper re-checks has_active_bindings per
    identity and only reports offline when no binding remains.
  • Serialize locator mutations with their local registration events via a shared
    LocatorEventLock, so a concurrent register/unregister cannot race the
    check-then-publish pattern into emitting a stale event.
  • Harden DbLocator cleanup to compare-and-delete: an expired snapshot is removed
    only when id + last_modified + expires still match, inside a transaction, with
    survivors re-verified. This keeps a concurrent REGISTER — including one from
    another process that cannot share the local mutex — from being deleted by a sweep
    after it refreshed the same row.

Tests

Regression tests cover stale/fresh, concurrent, and wildcard registration paths,
asserting that removing an expired binding while a fresher binding for the same
identity remains does not report the user offline and keeps the fresh binding
routable.

- preserve authoritative registration identity on locator results
- gate every binding-removal event on the final active binding
- serialize locator mutations with local registration events
- cover stale, fresh, concurrent, and wildcard registration paths
@shenjinti
shenjinti merged commit 867d2bb into restsend:main Sep 6, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants