• webv4/root/api/events.ssjs

    From Rob Swindell (on Debian Linux)@VERT to Git commit to main/sbbs/master on Wed Jul 29 20:43:49 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/ba71a722548d66a0e7c70388
    Modified Files:
    webv4/root/api/events.ssjs
    Log Message:
    webv4: a transient event-callback error shouldn't kill the stream

    api/events.ssjs disabled a subscribed callback the first time its
    cycle() threw, so one momentary failure stopped that browser from
    getting any further updates for the life of the connection. Errors
    caused by load - a locked user record, say - are transient by nature.

    Count consecutive failures instead, dropping the callback only after
    five and resetting the count on success. The disable is logged
    separately so it reads differently from the individual failures.

    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net
  • From Rob Swindell (on Debian Linux)@VERT to Git commit to main/sbbs/master on Wed Jul 29 20:43:50 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/7399e3a62d1acd5a679e59d7
    Modified Files:
    webv4/root/api/events.ssjs
    Log Message:
    webv4: bound the lifetime of an event stream

    api/events.ssjs looped for as long as client.socket.is_connected, which
    for a client that goes away without closing its socket is forever: the
    pings are small enough to keep being accepted, nothing forces a write
    failure, and no TCP keepalive is in play to notice the peer is gone.

    Nothing else bounded it either. A session sitting inside a long-running
    script never returns to the request read path, and MaxInactivity is
    enforced only there (in sockreadline), so the one timeout the web server
    has cannot reach it. Each stranded stream holds a client slot, a session thread, an output thread and a JS runtime until the server recycles.

    Measured on this host while its 200-client web pool was saturated and
    refusing roughly a thousand connections per ten minutes: of 195 connected clients, 172 were addresses that had opened one of these streams, the
    median connection age was 6.7 hours and the oldest 20.3 hours.

    Stop after settings.event_stream_duration seconds (default 3600; 0
    restores the old unbounded behavior). The close is graceful and
    EventSource reconnects on its own, so a reader sees at most a brief gap in
    the node list - and the reconnect lets the accept-time filters re-evaluate
    a client that has held one socket for hours.

    Related to #1154, which proposes the server-side connection-age cap that
    would bound this for any script, not just this one.

    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net