mirror of
https://github.com/PerpetualSoftware/pad.git
synced 2026-10-27 06:47:07 +00:00
461c5a3e3d
The review could not converge on this diff's comments because each round of corrections re-expanded the surface it was reviewing — rounds 16 and 17 found errors inside 15 and 16's fixes. That is a production rate being measured, not a backlog being drained, so the treatment is to write fewer claims rather than review the same ones again. PRUNED, ~135 comment lines: process narration. "An earlier version said X", "found by mutation testing", "codex round N caught this", the scoreboards. Every one of those is already in a commit message, which is where the archaeology belongs; in the source they are claims a future reader has to verify, about a past that no longer exists. KEPT, because they earn it and a reader would otherwise re-derive them: metric semantics, reachability boundaries, what a test does and does not discriminate, why the obvious alternative was rejected, and the hazards that cannot be enforced in code. MOVED TO A TEST, per the rule this run earned the hard way: a comment asserting countable behaviour belongs in the suite. Two test comments in internal/watchevents relied on "this constructor waits for its SUBSCRIBE to be confirmed" — prose, and the same assumption applied to the OTHER bus (which subscribes asynchronously) is what made a namespace test flake. It is now asserted with no polling and no sleep, and the mutation that removes the wait fails it. That rule generalises and is why round 17's find mattered: "counts every unservable resume" was prose, so its falseness could hide a real metric gap. Prose is for claims that cannot be asserted. Claude-Session: https://claude.ai/code/session_01JVDBKbgn3Xt7ndW1YoYd8X
66 lines
2.5 KiB
Go
66 lines
2.5 KiB
Go
package server
|
|
|
|
import "sync"
|
|
|
|
// Presence operation labels, passed to the failure callback. Bounded by
|
|
// construction so they are safe as metric label values.
|
|
const (
|
|
// PresenceOpRegister — the initial write for a newly connected
|
|
// session. Failure leaves the session out of the picker until the
|
|
// first successful renewal.
|
|
PresenceOpRegister = "register"
|
|
// PresenceOpRenew — the keepalive write. Sustained failure lets the
|
|
// entry expire on its TTL while the connection is still live.
|
|
PresenceOpRenew = "renew"
|
|
// PresenceOpDeregister — the explicit removal on disconnect. Failure
|
|
// leaves a dead session listed until its TTL expires, so the picker
|
|
// OVER-reports and a push aimed at it reaches nobody.
|
|
PresenceOpDeregister = "deregister"
|
|
// PresenceOpList — reading a user's sessions. Failure surfaces to the
|
|
// caller as a 503 rather than as a short list.
|
|
PresenceOpList = "list"
|
|
// PresenceOpPrune — removing index members whose session key is gone.
|
|
// Failure is the most benign: the index carries a stale member that
|
|
// the next read skips and the next prune retries.
|
|
PresenceOpPrune = "prune"
|
|
)
|
|
|
|
// presenceObservable is the nil-safe holder RedisSessionPresence embeds
|
|
// for its failure callback (BUG-2727).
|
|
//
|
|
// A CALLBACK, not an interface: one condition, one consumer, and
|
|
// RedisHealth takes a plain callback for the same job.
|
|
// internal/watchevents keeps an interface because it reports five
|
|
// distinct conditions.
|
|
//
|
|
// The registry is deliberately FAIL-SOFT everywhere — a failed write
|
|
// risks leaving a live session unlisted rather than tearing down the SSE
|
|
// connection it belongs to — which is exactly why the failures need a
|
|
// counter: nothing else surfaces them to anyone.
|
|
//
|
|
// RISKS, not guarantees: a failure means the operation reported an
|
|
// error, and Redis can fail a pipeline or a script after it applied. The
|
|
// per-op consequences in internal/metrics are written the same way.
|
|
type presenceObservable struct {
|
|
mu sync.RWMutex
|
|
onFailed func(op string)
|
|
}
|
|
|
|
// SetFailureObserver attaches the callback; nil detaches. Safe to call
|
|
// after the registry is running. The callback must be safe for concurrent
|
|
// use and must not block — it is called on the request and renewal paths.
|
|
func (o *presenceObservable) SetFailureObserver(fn func(op string)) {
|
|
o.mu.Lock()
|
|
o.onFailed = fn
|
|
o.mu.Unlock()
|
|
}
|
|
|
|
func (o *presenceObservable) reportOpFailed(op string) {
|
|
o.mu.RLock()
|
|
fn := o.onFailed
|
|
o.mu.RUnlock()
|
|
if fn != nil {
|
|
fn(op)
|
|
}
|
|
}
|