Commit db86e72
Add stateless subscriptions example with listen-based e2e (#13)
Supersedes #9 (approved there; GitHub closed it when #8's base branch
was deleted, and a rebased head cannot be reopened). Identical content
rebased onto main after #8's squash-merge — the review threads and
approval live on #9.
---
Stacked on #8 (go-sdk v1.7.0 bump) — the follow-up it deferred.
Adds `examples/subscriptions/cmd/subscriptions-stateless`: the tasks
subscription wiring served with `StreamableHTTPOptions{Stateless: true,
PropagateRequestCancellation: true}` — the deployment shape for
horizontally scaled servers behind a plain round-robin load balancer,
and the only mode in which the Go SDK speaks protocol revision
2026-07-28.
## What the e2e suite pins
- **2026-07-28 negotiated over plain HTTP** via `server/discover` (the
connect helper fails if a session ever falls back below it), with normal
tool round-trips.
- **Listen-based subscription delivery**: `resources/subscribe` doesn't
exist on this path — `ClientSession.Subscribe` opens a per-URI
`subscriptions/listen` stream, the server's `SubscribeHandler` still
fires per URI (same ACL gate as the legacy path), and `ResourceUpdated`
pushes flow back over the stream. Two concurrent clients on the same URI
prove per-connection fan-out.
- **Teardown**: `Unsubscribe` ends the listen stream,
`UnsubscribeHandler` fires, and further mutations deliver nothing.
- **`PropagateRequestCancellation`**: aborting the HTTP request mid-call
cancels the in-flight handler context (proven with an instrumented
blocking tool).
- **Legacy coexistence**: the same stateless endpoint still answers a
classic `initialize` from pre-2026 clients (echoing their requested
version), and GET is rejected with 405/`Allow: POST` per the stateless
contract.
Subscription registration is asynchronous under `subscriptions/listen`
(the client dispatches without awaiting), so delivery tests mutate in a
poll loop until the notification arrives rather than mutating once — the
suite passed three consecutive `-race -count=3` runs with no flakes.
Docs: new "Serving stateless" section in
`examples/subscriptions/README.md` (why no session affinity is needed:
subscription state lives on the connection; a dropped stream is
re-issued by the client to whichever replica answers next; feed every
replica from a shared event source), plus main README examples-table and
cmd-list entries.
## Verification
- `go test -race -count=1 ./...` green (new suite: 6 tests, also run at
`-count=3` for flake detection)
- `golangci-lint run` 0 issues; `go mod tidy` no-op (no new
dependencies)
---------
Co-authored-by: Jiacheng Xu <xjcmaxwellcjx@gmail.com>1 parent c3265ab commit db86e72
5 files changed
Lines changed: 937 additions & 2 deletions
File tree
- examples/subscriptions
- cmd/subscriptions-stateless
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
52 | 52 | | |
53 | 53 | | |
54 | 54 | | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
55 | 59 | | |
56 | 60 | | |
57 | 61 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
383 | 383 | | |
384 | 384 | | |
385 | 385 | | |
386 | | - | |
| 386 | + | |
387 | 387 | | |
388 | 388 | | |
389 | 389 | | |
| |||
464 | 464 | | |
465 | 465 | | |
466 | 466 | | |
467 | | - | |
| 467 | + | |
468 | 468 | | |
469 | 469 | | |
470 | 470 | | |
| |||
477 | 477 | | |
478 | 478 | | |
479 | 479 | | |
| 480 | + | |
480 | 481 | | |
481 | 482 | | |
482 | 483 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
195 | 195 | | |
196 | 196 | | |
197 | 197 | | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
198 | 292 | | |
199 | 293 | | |
200 | 294 | | |
| |||
203 | 297 | | |
204 | 298 | | |
205 | 299 | | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
206 | 305 | | |
207 | 306 | | |
208 | 307 | | |
| |||
0 commit comments