fix(19.7): RA-IN-04 the non-reserving store fallback writes the record first

For a store without Reserve (only a test wrapper reaches this; both shipped
stores reserve), serveNew now calls Put right after the Count check and
before the server is built, and a failed connect or initialize deletes the
record again. The session is therefore never in the local map without its
record, so a reaper pass in between cannot close it as expired. The cap of
this fallback stays check-then-act, as its comment now says.
This commit is contained in:
2026-10-23 20:22:58 +02:00
parent c0ce07ecb7
commit 034aa19904

View File

@@ -146,7 +146,11 @@ func (sr *sessionRouter) serveNew(w http.ResponseWriter, r *http.Request, id Ide
sid := rand.Text()
rec := Record{Binding: id.Binding, Created: time.Now().UTC()}
// The slot is reserved atomically before the server is built
// (A-WR-03); a store without Reserve falls back to Count then Put.
// (A-WR-03). A store without Reserve (only a test wrapper; both shipped
// stores reserve) falls back to Count then Put, still before the server
// is built, so the session is never in sr.local without its record (a
// reaper pass would close it as expired); its cap stays check-then-act
// (RA-IN-04).
res, reserves := sr.p.store.(reserver)
if reserves {
ok, err := res.Reserve(ctx, id.Binding, sid, rec, sr.p.cfg.MaxPerBinding, sr.p.cfg.IdleTTL)
@@ -168,6 +172,10 @@ func (sr *sessionRouter) serveNew(w http.ResponseWriter, r *http.Request, id Ide
sr.tooMany(w)
return
}
if err := sr.p.store.Put(ctx, sid, rec, sr.p.cfg.IdleTTL); err != nil {
sr.p.storeUnavailable(w, err)
return
}
}
release := func() {
if reserves {
@@ -189,13 +197,6 @@ func (sr *sessionRouter) serveNew(w http.ResponseWriter, r *http.Request, id Ide
sr.mu.Lock()
sr.local[sid] = ls
sr.mu.Unlock()
if !reserves {
if err := sr.p.store.Put(ctx, sid, rec, sr.p.cfg.IdleTTL); err != nil {
sr.closeLocal(sid)
sr.p.storeUnavailable(w, err)
return
}
}
logSession(logger(), "open", sid, id.Binding)
ls.transport.ServeHTTP(w, r)
if ls.ss.InitializeParams() == nil {