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 } // Update rewrites an existing record; a missing or expired one stays gone. func (m *memoryStore) Update(_ context.Context, id string, rec Record, ttl time.Duration) (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 false, nil } m.records[id] = memoryRecord{rec: rec, expires: m.now().Add(ttl)} return 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 } // Reserve counts the binding's live records and writes rec under one lock. func (m *memoryStore) Reserve(_ context.Context, binding, id string, rec Record, max int, ttl time.Duration) (bool, error) { m.mu.Lock() defer m.mu.Unlock() m.sweepLocked() n := 0 for _, r := range m.records { if r.rec.Binding == binding { n++ } } if n >= max { return false, nil } m.records[id] = memoryRecord{rec: rec, expires: m.now().Add(ttl)} return true, nil } // Release drops a reservation. func (m *memoryStore) Release(_ context.Context, _ string, id string) error { m.mu.Lock() defer m.mu.Unlock() delete(m.records, id) return 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 }