Documentation
¶
Overview ¶
Package modelcache provides a CachingModelStore decorator that memoizes LOCKED model descriptors. Correctness rests on the catalog invariants (commutativity + validation-monotonicity from internal/domain/model/schema) plus the validator-path refresh-on- stale in internal/domain/entity/handler.go; gossip invalidation and the TTL lease are performance/hygiene layers. See docs/superpowers/specs/2026-04-20-model-schema-extensions-design.md §4.5.
Index ¶
- func DecodeInvalidation(raw []byte) (tenantID string, ref spi.ModelRef, ok bool)
- func EncodeInvalidation(tenantID string, ref spi.ModelRef) ([]byte, error)
- type CachingModelStore
- func (c *CachingModelStore) Delete(ctx context.Context, ref spi.ModelRef) error
- func (c *CachingModelStore) EntryExpiresAt(ref spi.ModelRef) time.Time
- func (c *CachingModelStore) ExtendSchema(ctx context.Context, ref spi.ModelRef, delta spi.SchemaDelta) error
- func (c *CachingModelStore) Get(ctx context.Context, ref spi.ModelRef) (*spi.ModelDescriptor, error)
- func (c *CachingModelStore) GetAll(ctx context.Context) ([]spi.ModelRef, error)
- func (c *CachingModelStore) IsLocked(ctx context.Context, ref spi.ModelRef) (bool, error)
- func (c *CachingModelStore) Lock(ctx context.Context, ref spi.ModelRef) error
- func (c *CachingModelStore) RefreshAndGet(ctx context.Context, ref spi.ModelRef) (*spi.ModelDescriptor, error)
- func (c *CachingModelStore) Save(ctx context.Context, desc *spi.ModelDescriptor) error
- func (c *CachingModelStore) SetChangeLevel(ctx context.Context, ref spi.ModelRef, level spi.ChangeLevel) error
- func (c *CachingModelStore) SubscribeLocal(h func(tenant string, ref spi.ModelRef))
- func (c *CachingModelStore) Unlock(ctx context.Context, ref spi.ModelRef) error
- type CachingStoreFactory
- func (f *CachingStoreFactory) AsyncSearchStore(ctx context.Context) (spi.AsyncSearchStore, error)
- func (f *CachingStoreFactory) Close() error
- func (f *CachingStoreFactory) EntityStore(ctx context.Context) (spi.EntityStore, error)
- func (f *CachingStoreFactory) KeyValueStore(ctx context.Context) (spi.KeyValueStore, error)
- func (f *CachingStoreFactory) MessageStore(ctx context.Context) (spi.MessageStore, error)
- func (f *CachingStoreFactory) ModelStore(ctx context.Context) (spi.ModelStore, error)
- func (f *CachingStoreFactory) ScheduledTaskStore(ctx context.Context) (spi.ScheduledTaskStore, error)
- func (f *CachingStoreFactory) StateMachineAuditStore(ctx context.Context) (spi.StateMachineAuditStore, error)
- func (f *CachingStoreFactory) SubscribeLocal(h func(tenant string, ref spi.ModelRef))
- func (f *CachingStoreFactory) SupportsCompositeUniqueKeys() bool
- func (f *CachingStoreFactory) TransactionManager(ctx context.Context) (spi.TransactionManager, error)
- func (f *CachingStoreFactory) WorkflowStore(ctx context.Context) (spi.WorkflowStore, error)
- type Clock
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func DecodeInvalidation ¶
DecodeInvalidation is the inverse. Returns ok=false on malformed input or any blank field so the gossip handler drops it silently.
Types ¶
type CachingModelStore ¶
type CachingModelStore struct {
// contains filtered or unexported fields
}
CachingModelStore wraps an spi.ModelStore. Zero-value not safe — use New.
func New ¶
func New(inner spi.ModelStore, broadcaster spi.ClusterBroadcaster, clk Clock, lease time.Duration) *CachingModelStore
New constructs a CachingModelStore.
broadcaster may be nil for single-node deployments — invalidation then only fires locally.
clock may be nil to use the wall clock.
lease must be > 0; actual expiry is jittered ±10% to prevent the cross-node herd that a uniform lease would produce.
func (*CachingModelStore) EntryExpiresAt ¶
func (c *CachingModelStore) EntryExpiresAt(ref spi.ModelRef) time.Time
EntryExpiresAt is test-only introspection for the jitter-band check. Returns the zero time if no entry is cached for ref in any tenant.
func (*CachingModelStore) ExtendSchema ¶
func (c *CachingModelStore) ExtendSchema(ctx context.Context, ref spi.ModelRef, delta spi.SchemaDelta) error
func (*CachingModelStore) Get ¶
func (c *CachingModelStore) Get(ctx context.Context, ref spi.ModelRef) (*spi.ModelDescriptor, error)
func (*CachingModelStore) RefreshAndGet ¶
func (c *CachingModelStore) RefreshAndGet(ctx context.Context, ref spi.ModelRef) (*spi.ModelDescriptor, error)
RefreshAndGet forces a cache miss, collapses concurrent callers via singleflight, then reads from the inner store and repopulates the cache. Used by the validator refresh-on-stale path.
func (*CachingModelStore) Save ¶
func (c *CachingModelStore) Save(ctx context.Context, desc *spi.ModelDescriptor) error
func (*CachingModelStore) SetChangeLevel ¶
func (c *CachingModelStore) SetChangeLevel(ctx context.Context, ref spi.ModelRef, level spi.ChangeLevel) error
func (*CachingModelStore) SubscribeLocal ¶ added in v0.7.0
func (c *CachingModelStore) SubscribeLocal(h func(tenant string, ref spi.ModelRef))
SubscribeLocal registers an in-process invalidation handler. The handler fires for every invalidation seen by this store — originating from a local mutation (Save/Lock/Unlock/SetChangeLevel/ Delete/ExtendSchema) AND from gossip events received from peer nodes. Downstream caches keyed off the same (tenant, ref) tuple (e.g. the path-validation negative cache) use this to stay in lock step with the descriptor cache regardless of cluster topology.
Issue #174 — pre-fix the path-validation cache subscribed to the gossip broadcaster directly, so single-node deployments (where the broadcaster is nil) never received any invalidation events.
type CachingStoreFactory ¶
type CachingStoreFactory struct {
// contains filtered or unexported fields
}
CachingStoreFactory wraps an spi.StoreFactory so that ModelStore(ctx) returns a per-request store whose Get / RefreshAndGet calls route through a single shared cache. All other factory methods pass through to the inner factory unchanged.
The cache is gossip-aware: mutations on a peer node evict the local entry via the "model.invalidate" topic. The ±10% jittered TTL is the fallback when gossip drops a message.
func NewCachingStoreFactory ¶
func NewCachingStoreFactory( inner spi.StoreFactory, broadcaster spi.ClusterBroadcaster, clk Clock, lease time.Duration, ) *CachingStoreFactory
NewCachingStoreFactory wraps inner. broadcaster may be nil for single-node deployments. clk may be nil to use the wall clock. lease must be > 0.
func (*CachingStoreFactory) AsyncSearchStore ¶
func (f *CachingStoreFactory) AsyncSearchStore(ctx context.Context) (spi.AsyncSearchStore, error)
func (*CachingStoreFactory) Close ¶
func (f *CachingStoreFactory) Close() error
func (*CachingStoreFactory) EntityStore ¶
func (f *CachingStoreFactory) EntityStore(ctx context.Context) (spi.EntityStore, error)
func (*CachingStoreFactory) KeyValueStore ¶
func (f *CachingStoreFactory) KeyValueStore(ctx context.Context) (spi.KeyValueStore, error)
func (*CachingStoreFactory) MessageStore ¶
func (f *CachingStoreFactory) MessageStore(ctx context.Context) (spi.MessageStore, error)
func (*CachingStoreFactory) ModelStore ¶
func (f *CachingStoreFactory) ModelStore(ctx context.Context) (spi.ModelStore, error)
ModelStore returns a per-request store that reads through the shared cache and delegates mutations to the inner per-tenant store.
func (*CachingStoreFactory) ScheduledTaskStore ¶ added in v0.8.3
func (f *CachingStoreFactory) ScheduledTaskStore(ctx context.Context) (spi.ScheduledTaskStore, error)
func (*CachingStoreFactory) StateMachineAuditStore ¶
func (f *CachingStoreFactory) StateMachineAuditStore(ctx context.Context) (spi.StateMachineAuditStore, error)
func (*CachingStoreFactory) SubscribeLocal ¶ added in v0.7.0
func (f *CachingStoreFactory) SubscribeLocal(h func(tenant string, ref spi.ModelRef))
SubscribeLocal registers an in-process invalidation handler on the shared CachingModelStore. The handler receives (tenant, ref) for every model invalidation — local mutations and gossip-received events alike. Downstream caches use this to stay in lock step regardless of cluster topology (issue #174).
func (*CachingStoreFactory) SupportsCompositeUniqueKeys ¶ added in v0.8.2
func (f *CachingStoreFactory) SupportsCompositeUniqueKeys() bool
SupportsCompositeUniqueKeys forwards the optional capability check to the inner factory. Returns false if the inner factory does not implement spi.CompositeUniqueKeyCapable.
func (*CachingStoreFactory) TransactionManager ¶
func (f *CachingStoreFactory) TransactionManager(ctx context.Context) (spi.TransactionManager, error)
func (*CachingStoreFactory) WorkflowStore ¶
func (f *CachingStoreFactory) WorkflowStore(ctx context.Context) (spi.WorkflowStore, error)