fix(pier): widen returning identities, warm the Redis store, end stateless listen streams, refuse personal tokens without a TokenID
- Identity.fingerprint covers scopes, both subjects, the ceiling and the token kind; a stateful lookup or stateless hit whose fingerprint changed re-filters the server with the event diff under its own mutex (A-WR-04) - Mount warms a Redis store in the background with a 1 s to 30 s backoff and every request kicks a pending open without waiting, so stateless-only instances subscribe to fan-out (A-WR-05) - a stateless subscriptions/listen POST re-verifies its bearer every recheck interval; an LRU-evicted stateless server closes its sessions (A-WR-06) - resolvePersonalToken refuses a subject with TokenID 0, and the pact.MCPPersonalTokenResolver contract says one resolver per application, false means unauthorized and TokenID keys the binding (A-WR-09, A-IN-03)
This commit is contained in:
@@ -58,10 +58,10 @@ type Pier struct {
|
||||
version string
|
||||
instance string
|
||||
|
||||
// stopReap ends the session reaper Mount starts. backpack.App has no
|
||||
// shutdown hook yet, so in production the reaper runs for the life of
|
||||
// the process, like the store subscription; when backpack gains one,
|
||||
// Mount registers stopReap there.
|
||||
// stopReap ends the session reaper (and a Redis store's warm-up) Mount
|
||||
// starts. backpack.App has no shutdown hook yet, so in production the
|
||||
// reaper runs for the life of the process, like the store subscription;
|
||||
// when backpack gains one, Mount registers stopReap there.
|
||||
stopReap context.CancelFunc
|
||||
}
|
||||
|
||||
@@ -72,8 +72,10 @@ type Pier struct {
|
||||
// behind the inline throttle. Nothing is mounted when the flag is off. A
|
||||
// bad binding, a duplicate name, an unknown guard or a route already
|
||||
// registered at mcp.path fails with an error naming the plugin, so boot
|
||||
// (and route:list) fail. Nothing dials Redis here. The mounted *Pier is
|
||||
// published on the application.
|
||||
// (and route:list) fail. Mount never waits for Redis: a Redis store is
|
||||
// opened by a background warm-up with a backoff, so the event subscription
|
||||
// exists before traffic arrives. Mount also starts the session reaper. The
|
||||
// mounted *Pier is published on the application.
|
||||
func Mount(host Host, app *backpack.App, plugins []party.Plugin) error {
|
||||
if host == nil || app == nil {
|
||||
return errors.New("pier: a host and an application are required")
|
||||
@@ -139,13 +141,17 @@ func Mount(host Host, app *backpack.App, plugins []party.Plugin) error {
|
||||
reapCtx, stop := context.WithCancel(context.Background())
|
||||
p.stopReap = stop
|
||||
go p.sessions.reap(reapCtx, reapInterval(cfg.IdleTTL))
|
||||
if ls, ok := p.store.(*lazyStore); ok {
|
||||
go ls.warm(reapCtx)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// newStore builds the session store mcp.sessions.store selects: the memory
|
||||
// store, subscribed to its own events at once, or a Redis store opened on
|
||||
// first use from redis.url under cooler's key prefix (nothing dials at
|
||||
// mount; a missing URL is a 503 on the first request, logged once).
|
||||
// mount itself; a background warm-up opens it with a backoff, and a missing
|
||||
// URL is a 503 on stateful requests, logged).
|
||||
func (p *Pier) newStore() Store {
|
||||
if p.cfg.SessionStore != StoreRedis {
|
||||
ms := newMemoryStore()
|
||||
|
||||
Reference in New Issue
Block a user