Skip to content

Server lifecycle event hooks (wsUpgrade/documentOpen/fanout/wsClose) + taskComplete outcome for observability #65

Description

@bgeils

Summary

y/hub currently emits no operational events or metrics from the server, so production triage (why did this open take 30s? which client is dropping on backpressure? which rooms drive fan-out volume?) requires fleet-wide debug logging. We patched in a server.events config and it now powers 100% of our y/hub observability — ~220M events/week feed our dashboards from it. Offering it upstream:

server: {
  events: {
    wsUpgrade?:    (e: { room?, url?, userId?, accessType?, status: 'success'|'error'|'aborted', durationMs, errorType?, error?, httpStatus? }) => void | Promise<void>,
    documentOpen?: (e: { room, userId?, clientId?, gc?, status, durationMs, readDurationMs?, stateVectorDurationMs?, docSize?, syncStep1Bytes?, syncStep2Bytes?, awarenessBytes?, bytesOut?, lastClock?, errorType?, error? }) => ...,
    fanout?:       (e: { room, userId?, operation: 'fanout_ydoc_update'|'fanout_awareness_update', status, durationMs, bytesOut?, itemCount?, errorType?, error? }) => ...,
    wsClose?:      (e: { room, userId?, clientId?, status, durationMs, closeCode, closeReason? }) => ...,
  }
}

Handlers are invoked inside try/catch (a broken handler can never affect the connection), async rejections are swallowed with a log. Two details that proved especially valuable:

  • Backpressure visibility. sendData's uWS result is surfaced: a dropped fan-out or initial-sync frame emits errorType: 'backpressure'. Today upstream drops these silently — the client desyncs with zero server-side signal.
  • taskComplete.outcome: 'persisted' | 'trim-only' | 'unknown' on the worker event, so consumers can distinguish "durable state advanced" (drive webhooks/search indexing) from a trim-only pass. In v0.7.0 that needs setting at both trim-only exits (the pre-check and the new post-merge duplicate-clock recheck) plus the persisted exit.

Sample numbers this instrumentation surfaced for us in one production week: 95k document opens averaging 2.24 MB each (213 GB of initial-sync egress), awareness at 89% of all operations, and open p95 dominated by the read+merge phase — none of which is visible without hooks. We run this via a package patch and are happy to send a PR.

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