feat(19.7-02): pier MCP module tracer with in-process dispatch and sessions
- modules/pier: Host contract, mcp.* config, catalog compiled from pact.HasMCP (prefixing, route binding, guard resolution, schema reflection, annotations), per-request identity through the published verifier and resolvers, built-in backend guard provider, in-process dispatch with the bouncer marker, per-identity SDK server with the per-request re-filter, stateful session router with rehydration over a memory store, audit lines - modules/surf/pier_on.go: the summercms_mcp hook adapting the router to pier.Host; the untagged hello binary carries no SDK - go.mod: go-sdk v1.8.0 and jsonschema-go v0.4.3 (decision note committed before this change) - TestPierTracerSmoke: SDK client lists and calls acme_echo end to end through surf's compiled guard chain; README and root table row
This commit is contained in:
103
modules/pier/store_memory.go
Normal file
103
modules/pier/store_memory.go
Normal file
@@ -0,0 +1,103 @@
|
||||
package pier
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// memoryStore keeps records in the process with lazy expiry and fans events
|
||||
// out to in-process subscribers; the default store, for one instance.
|
||||
type memoryStore struct {
|
||||
mu sync.Mutex
|
||||
records map[string]memoryRecord
|
||||
subs []func(Event)
|
||||
now func() time.Time
|
||||
}
|
||||
|
||||
type memoryRecord struct {
|
||||
rec Record
|
||||
expires time.Time
|
||||
}
|
||||
|
||||
func newMemoryStore() *memoryStore {
|
||||
return &memoryStore{records: map[string]memoryRecord{}, now: time.Now}
|
||||
}
|
||||
|
||||
func (m *memoryStore) sweepLocked() {
|
||||
now := m.now()
|
||||
for id, r := range m.records {
|
||||
if !r.expires.After(now) {
|
||||
delete(m.records, id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *memoryStore) Put(_ context.Context, id string, rec Record, ttl time.Duration) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
m.sweepLocked()
|
||||
m.records[id] = memoryRecord{rec: rec, expires: m.now().Add(ttl)}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *memoryStore) Get(_ context.Context, id string) (Record, bool, error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
r, ok := m.records[id]
|
||||
if !ok || !r.expires.After(m.now()) {
|
||||
delete(m.records, id)
|
||||
return Record{}, false, nil
|
||||
}
|
||||
return r.rec, true, nil
|
||||
}
|
||||
|
||||
func (m *memoryStore) Touch(_ context.Context, id string, ttl time.Duration) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
r, ok := m.records[id]
|
||||
if !ok || !r.expires.After(m.now()) {
|
||||
delete(m.records, id)
|
||||
return nil
|
||||
}
|
||||
r.expires = m.now().Add(ttl)
|
||||
m.records[id] = r
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *memoryStore) Delete(_ context.Context, id string) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
delete(m.records, id)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *memoryStore) Count(_ context.Context, binding string) (int, error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
m.sweepLocked()
|
||||
n := 0
|
||||
for _, r := range m.records {
|
||||
if r.rec.Binding == binding {
|
||||
n++
|
||||
}
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (m *memoryStore) Publish(_ context.Context, ev Event) error {
|
||||
m.mu.Lock()
|
||||
subs := append([]func(Event){}, m.subs...)
|
||||
m.mu.Unlock()
|
||||
for _, fn := range subs {
|
||||
go fn(ev)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *memoryStore) Subscribe(_ context.Context, fn func(Event)) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
m.subs = append(m.subs, fn)
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user