Skip to content

Retire requests with a final page reload - #533

Merged
linkdata merged 6 commits into
mainfrom
fix/request-reload
Oct 6, 2026
Merged

linkdata merged 6 commits into
mainfrom
fix/request-reload

Conversation

@linkdata

@linkdata linkdata commented Oct 6, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • Add Request.Reload to queue a page reload, including for pending pages. Events and callbacks run normally until disconnect, and pending pages use the usual ConnectFn setup.
  • Make Reload terminal in the WebSocket writer: flush through Reload, close the socket, then cancel the request. Later records are discarded. This also applies to session and global reloads.
  • Disable browser connection recovery when a server-requested reload starts. Request.Reload uses the existing message queue and adds no lifecycle flags or event guards.
  • Wake each Request through one private buffered channel when output is queued or dirty work is assigned. Maintenance also wakes Requests with queued output. Dirty rendering keeps its normal update interval.
  • Replace all three production wake-only Update broadcasts: Request.Reload, Session.Close, and the dirty-update ticker. Preserve explicit Update rendering and overload semantics. Remove redundant test wake-ups and document automatic delivery.

This supplies the operation linkdata/jawsauth#20 uses to reload other tabs after logout. Merge this prerequisite first.

Verification

  • Real WebSocket tests cover pending, connecting, and connected requests, repeated reload calls, request cancellation after disconnect, and clients that continue sending.
  • Writer tests cover Reload at the beginning and within batches, discarded later messages, and a blocked reader. The blocked-reader regression fails without cancellation after close and passes with it.
  • A regression test verifies event dispatch continues until disconnect. Session close and browser recovery tests cover terminal Reload behavior.
  • Idle-loop regression cases cover direct commands, delayed event callbacks, assigned dirty work, Reload, and maintenance recovery, including repeated drains. Disabling wake signaling makes the original four cases fail.
  • Full normal and race suites, Node browser tests, go vet, build, generation, golangci-lint, staticcheck, and gosec pass.
  • Separate documentation review and rendered godoc completed. Claude CLI critique checked independently.

Main versus PR benchmarks

  • Main: 21dc6e5917488a48afc2a00dea031ce948d96938.
  • PR runtime: b31c95d69c8517bd6c029bdef552534161a9fe20 (PR533); benchmark commit 0db3e57 changes only benchmark source.
  • Go 1.27.1, linux/arm64. Eight samples per case, 500 ms each, GOMAXPROCS 1 and 8. Main and PR ran sequentially to avoid competing benchmark processes.
  • Queue delivery uses RunParallel, one live Request loop per worker, and waits for each message on the outbound channel. Socket I/O is excluded. Main's copy adds its required key-targeted Update broadcast after enqueueing; PR relies on automatic notification. The eight-worker result is aggregate throughput cost (wall time divided by all operations), not individual-message latency.
  • Maintenance measures a scan over idle running Requests without competing processing goroutines. Dirty fanout assigns ten tags to 100 pending Requests; their notification remains coalesced. Request lifecycle creates, claims, starts and recycles a Request. These three benchmarks are serial even at GOMAXPROCS 8.
  • Allocations include amortized per-worker setup and teardown in the delivery benchmark; Jaws creation is outside its timer.

Run the Go command from each tree's module root, saving output to
/tmp/jaws-wake-main.txt and /tmp/jaws-wake-pr.txt, respectively.

go test -run '^$' \
  -bench 'BenchmarkRequest(QueueDelivery|Maintenance|DirtyFanout|ClaimStartFinish)$' \
  -benchmem -benchtime=500ms -count=8 -cpu=1,8 .
benchstat /tmp/jaws-wake-main.txt /tmp/jaws-wake-pr.txt

Results

Medians below. ~ means benchstat found no statistically significant difference.

Workload GOMAXPROCS Main PR Change
Queue delivery 1 986 ns/op 503 ns/op -49.0%
Queue delivery, 8 workers 8 3,557 ns/op 843 ns/op -76.3%
Request lifecycle 1 1.447 µs/op 1.599 µs/op +10.5%
Request lifecycle 8 1.560 µs/op 1.571 µs/op ~
Maintenance, 100 Requests 1 1.364 µs/pass 1.333 µs/pass ~
Maintenance, 100 Requests 8 1.285 µs/pass 1.334 µs/pass ~
Maintenance, 1,000 Requests 1 14.12 µs/pass 16.11 µs/pass +14.2%
Maintenance, 1,000 Requests 8 14.60 µs/pass 18.85 µs/pass +29.1%
Dirty fanout, 100 Requests 1 6.390 µs/op 6.769 µs/op ~
Dirty fanout, 100 Requests 8 3.831 µs/op 3.887 µs/op ~

Queue delivery drops from 40 to 32 B/op and from two allocations to one. Request lifecycle adds about 112 B and one allocation (14 to 15) per Request. Maintenance still allocates nothing; dirty fanout stays at 1,296 B and three allocations per operation.

The measured delivery gain includes removing the shared Serve broadcast routing. It is not a network-latency claim. The maintenance fallback adds about 2–4 µs per scan of 1,000 idle Requests in these samples. The one-CPU lifecycle slowdown is significant (p=0.015); the eight-CPU lifecycle result is inconclusive. Some lifecycle and dirty-fanout samples vary substantially, so their nonsignificant time differences should not be interpreted as regressions or improvements.

Full benchstat output
goos: linux
goarch: arm64
pkg: github.com/linkdata/jaws
                                   │ /tmp/jaws-wake-main.txt │        /tmp/jaws-wake-pr.txt        │
                                   │         sec/op          │    sec/op     vs base               │
RequestClaimStartFinish                         1.447µ ±  6%   1.599µ ±  8%  +10.47% (p=0.015 n=8)
RequestClaimStartFinish-8                       1.560µ ± 27%   1.571µ ± 26%        ~ (p=0.592 n=8)
RequestQueueDelivery                            986.0n ±  6%   503.2n ±  3%  -48.96% (p=0.000 n=8)
RequestQueueDelivery-8                         3556.5n ± 13%   843.4n ±  5%  -76.29% (p=0.000 n=8)
RequestMaintenance/requests=100                 1.364µ ±  5%   1.333µ ± 13%        ~ (p=0.959 n=8)
RequestMaintenance/requests=100-8               1.285µ ±  7%   1.334µ ±  4%        ~ (p=0.152 n=8)
RequestMaintenance/requests=1000                14.12µ ±  9%   16.11µ ± 16%  +14.15% (p=0.003 n=8)
RequestMaintenance/requests=1000-8              14.60µ ±  5%   18.85µ ± 24%  +29.08% (p=0.000 n=8)
RequestDirtyFanout                              6.390µ ± 36%   6.769µ ± 35%        ~ (p=0.168 n=8)
RequestDirtyFanout-8                            3.831µ ±  6%   3.887µ ± 11%        ~ (p=0.382 n=8)
geomean                                         3.051µ         2.618µ        -14.19%

                                   │ /tmp/jaws-wake-main.txt │         /tmp/jaws-wake-pr.txt         │
                                   │          B/op           │     B/op      vs base                 │
RequestClaimStartFinish                         956.5 ± 0%      1069.0 ± 0%  +11.76% (p=0.000 n=8)
RequestClaimStartFinish-8                       953.0 ± 0%      1065.0 ± 0%  +11.75% (p=0.000 n=8)
RequestQueueDelivery                            40.00 ± 0%       32.00 ± 0%  -20.00% (p=0.000 n=8)
RequestQueueDelivery-8                          40.00 ± 0%       32.00 ± 0%  -20.00% (p=0.000 n=8)
RequestMaintenance/requests=100                 0.000 ± 0%       0.000 ± 0%        ~ (p=1.000 n=8) ¹
RequestMaintenance/requests=100-8               0.000 ± 0%       0.000 ± 0%        ~ (p=1.000 n=8) ¹
RequestMaintenance/requests=1000                0.000 ± 0%       0.000 ± 0%        ~ (p=1.000 n=8) ¹
RequestMaintenance/requests=1000-8              0.000 ± 0%       0.000 ± 0%        ~ (p=1.000 n=8) ¹
RequestDirtyFanout                            1.266Ki ± 0%     1.266Ki ± 0%        ~ (p=1.000 n=8) ¹
RequestDirtyFanout-8                          1.266Ki ± 0%     1.266Ki ± 0%        ~ (p=1.000 n=8) ¹
geomean                                                    ²                  -2.21%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

                                   │ /tmp/jaws-wake-main.txt │        /tmp/jaws-wake-pr.txt        │
                                   │        allocs/op        │ allocs/op   vs base                 │
RequestClaimStartFinish                         14.00 ± 0%     15.00 ± 0%   +7.14% (p=0.000 n=8)
RequestClaimStartFinish-8                       14.00 ± 0%     15.00 ± 0%   +7.14% (p=0.000 n=8)
RequestQueueDelivery                            2.000 ± 0%     1.000 ± 0%  -50.00% (p=0.000 n=8)
RequestQueueDelivery-8                          2.000 ± 0%     1.000 ± 0%  -50.00% (p=0.000 n=8)
RequestMaintenance/requests=100                 0.000 ± 0%     0.000 ± 0%        ~ (p=1.000 n=8) ¹
RequestMaintenance/requests=100-8               0.000 ± 0%     0.000 ± 0%        ~ (p=1.000 n=8) ¹
RequestMaintenance/requests=1000                0.000 ± 0%     0.000 ± 0%        ~ (p=1.000 n=8) ¹
RequestMaintenance/requests=1000-8              0.000 ± 0%     0.000 ± 0%        ~ (p=1.000 n=8) ¹
RequestDirtyFanout                              3.000 ± 0%     3.000 ± 0%        ~ (p=1.000 n=8) ¹
RequestDirtyFanout-8                            3.000 ± 0%     3.000 ± 0%        ~ (p=1.000 n=8) ¹
geomean                                                    ²               -11.74%               ²
¹ all samples are equal
² summaries must be >0 to compute geomean

@linkdata
linkdata marked this pull request as ready for review October 6, 2026 10:26
@linkdata
linkdata merged commit 7dc6b65 into main Oct 6, 2026
7 checks passed
@linkdata
linkdata deleted the fix/request-reload branch October 6, 2026 10:26
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.

1 participant