Documentation
¶
Index ¶
- Constants
- func CacheBackendMustFromContainer(serviceContainer containercontract.Container) cachecontract.Backend
- func CacheBackendMustFromResolver(resolver containercontract.Resolver) cachecontract.Backend
- func CacheMustFromContainer(serviceContainer containercontract.Container) cachecontract.Cache
- func CacheMustFromResolver(resolver containercontract.Resolver) cachecontract.Cache
- func CacheSerializerMustFromContainer(serviceContainer containercontract.Container) cachecontract.Serializer
- func CacheSerializerMustFromResolver(resolver containercontract.Resolver) cachecontract.Serializer
- func IsDeserializationError(err error) bool
- func NewJsonSerializer() cachecontract.Serializer
- func Remember(cacheInstance cachecontract.Cache, key string, ttl time.Duration, ...) (any, error)
- type DeserializationError
- type InMemoryBackend
- func (instance *InMemoryBackend) Clear() error
- func (instance *InMemoryBackend) Close() error
- func (instance *InMemoryBackend) Decrement(key string, delta int64) (int64, error)
- func (instance *InMemoryBackend) Delete(key string) error
- func (instance *InMemoryBackend) DeleteMultiple(keys []string) error
- func (instance *InMemoryBackend) Get(key string) ([]byte, bool, error)
- func (instance *InMemoryBackend) Has(key string) (bool, error)
- func (instance *InMemoryBackend) Increment(key string, delta int64) (int64, error)
- func (instance *InMemoryBackend) Many(keys []string) (map[string][]byte, error)
- func (instance *InMemoryBackend) Set(key string, payload []byte, ttl time.Duration) error
- func (instance *InMemoryBackend) SetMultiple(items map[string][]byte, ttl time.Duration) error
- type Item
- func (instance *Item) CreatedAt() time.Time
- func (instance *Item) ExpiresAt() *time.Time
- func (instance *Item) HitCount() uint64
- func (instance *Item) Key() string
- func (instance *Item) LastAccessedAt() time.Time
- func (instance *Item) Payload() []byte
- func (instance *Item) Touch(accessTime time.Time)
- type JsonSerializer
- type Manager
- func (instance *Manager) Clear() error
- func (instance *Manager) Close() error
- func (instance *Manager) Decrement(key string, delta int64) (int64, error)
- func (instance *Manager) Delete(key string) error
- func (instance *Manager) DeleteMultiple(keys []string) error
- func (instance *Manager) Get(key string) (any, bool, error)
- func (instance *Manager) GetCounter(key string) (int64, bool, error)
- func (instance *Manager) Has(key string) (bool, error)
- func (instance *Manager) Increment(key string, delta int64) (int64, error)
- func (instance *Manager) Many(keys []string) (map[string]any, error)
- func (instance *Manager) NormalizeStoredValue(value any) (any, error)
- func (instance *Manager) Set(key string, value any, ttl time.Duration) error
- func (instance *Manager) SetMultiple(items map[string]any, ttl time.Duration) error
- type RememberOption
- func (instance *RememberOption) Context() context.Context
- func (instance *RememberOption) EnableStampedeProtection() bool
- func (instance *RememberOption) IsCancelable() bool
- func (instance *RememberOption) WaitTimeout() time.Duration
- func (instance *RememberOption) WithCancelable(isCancelable bool) *RememberOption
- func (instance *RememberOption) WithContext(callerContext context.Context) *RememberOption
- func (instance *RememberOption) WithStampedeProtectionEnabled(enableStampedeProtection bool) *RememberOption
- func (instance *RememberOption) WithWaitTimeout(waitTimeout time.Duration) *RememberOption
Constants ¶
const ( ServiceCache = "service.cache" ServiceCacheBackend = "service.cache.backend" ServiceCacheSerializer = "service.cache.serializer" )
Variables ¶
This section is empty.
Functions ¶
func CacheBackendMustFromContainer ¶
func CacheBackendMustFromContainer(serviceContainer containercontract.Container) cachecontract.Backend
func CacheBackendMustFromResolver ¶
func CacheBackendMustFromResolver(resolver containercontract.Resolver) cachecontract.Backend
func CacheMustFromContainer ¶
func CacheMustFromContainer(serviceContainer containercontract.Container) cachecontract.Cache
func CacheMustFromResolver ¶
func CacheMustFromResolver(resolver containercontract.Resolver) cachecontract.Cache
func CacheSerializerMustFromContainer ¶
func CacheSerializerMustFromContainer(serviceContainer containercontract.Container) cachecontract.Serializer
func CacheSerializerMustFromResolver ¶
func CacheSerializerMustFromResolver(resolver containercontract.Resolver) cachecontract.Serializer
func IsDeserializationError ¶ added in v1.19.0
func NewJsonSerializer ¶
func NewJsonSerializer() cachecontract.Serializer
NewJsonSerializer stores the value as a bare json document, with no envelope and no schema discriminant. The consequence belongs to the deployment, not to a call: a document written by one version of the application decodes cleanly in the next one, so a field added since is simply absent and the caller reads the zero value — an empty role list, a false flag — with no decoding error to notice and no version to compare. Nothing heals it either, since an entry cached with a ttl of zero never lapses. Where the shape of a cached value can change between releases, either carry a version inside the value or move the cache key with the shape; the session package writes the same hazard down on its own file storage.
func Remember ¶
func Remember( cacheInstance cachecontract.Cache, key string, ttl time.Duration, callback func(ctx context.Context) (any, error), option *RememberOption, ) (any, error)
Remember answers the cached value, computing it through the callback on a miss and storing it. The computed value is run through the backend's stored shape before it is returned, so the miss answers exactly what every later hit answers — a callback's int comes back a float64 and its struct a map, and this makes that true from the first call rather than only from the second. The cost is that the round-trip is uniform: with the default JSON serializer an integer beyond 2^53 comes back changed on the computing call as well, not only on the cached reads. Where a cached value carries an integer that large, carry a version inside it or key it so it never decodes through JSON. No major escapes this: every default serializer in the tree decodes through the same json.Unmarshal into any.
Types ¶
type DeserializationError ¶ added in v1.19.0
type DeserializationError struct {
// contains filtered or unexported fields
}
func NewDeserializationError ¶ added in v1.19.0
func NewDeserializationError(keys []string, causeErr error) *DeserializationError
NewDeserializationError marks a cache read that found a payload the serializer cannot decode, naming the keys it happened under. The type matters more than the message: Remember treats an error of this type as a miss and recomputes, overwriting the corrupt payload, while every other error keeps meaning the cache itself failed. A Cache implementation of your own that wraps its deserialization failures in it inherits that self-healing.
func (*DeserializationError) Error ¶ added in v1.19.0
func (instance *DeserializationError) Error() string
func (*DeserializationError) Unwrap ¶ added in v1.19.0
func (instance *DeserializationError) Unwrap() error
type InMemoryBackend ¶
type InMemoryBackend struct {
// contains filtered or unexported fields
}
func NewInMemoryBackend ¶
func NewInMemoryBackend( maxItems int, cleanupInterval time.Duration, clockInstance clockcontract.Clock, ) *InMemoryBackend
NewInMemoryBackend builds the framework's in-memory cache backend and starts its cleanup goroutine, which only Close stops — an instance abandoned without Close keeps the goroutine, its ticker and the whole entry map alive for the rest of the process; there is no finalizer fallback. maxItems bounds the entry count: zero disables the bound and a negative value panics, so a bound computed wrong cannot silently disarm eviction. cleanupInterval is how often the sweep collects lapsed entries, defaulting to a minute when non-positive; it is not a lifetime applied to anything.
func (*InMemoryBackend) Clear ¶
func (instance *InMemoryBackend) Clear() error
func (*InMemoryBackend) Close ¶
func (instance *InMemoryBackend) Close() error
func (*InMemoryBackend) Decrement ¶
func (instance *InMemoryBackend) Decrement(key string, delta int64) (int64, error)
func (*InMemoryBackend) Delete ¶
func (instance *InMemoryBackend) Delete(key string) error
func (*InMemoryBackend) DeleteMultiple ¶
func (instance *InMemoryBackend) DeleteMultiple(keys []string) error
func (*InMemoryBackend) Get ¶
func (instance *InMemoryBackend) Get(key string) ([]byte, bool, error)
func (*InMemoryBackend) Increment ¶
func (instance *InMemoryBackend) Increment(key string, delta int64) (int64, error)
func (*InMemoryBackend) Many ¶
func (instance *InMemoryBackend) Many(keys []string) (map[string][]byte, error)
func (*InMemoryBackend) SetMultiple ¶
type Item ¶
type Item struct {
// contains filtered or unexported fields
}
func (*Item) LastAccessedAt ¶
type JsonSerializer ¶
type JsonSerializer struct{}
func (*JsonSerializer) Deserialize ¶
func (instance *JsonSerializer) Deserialize(payload []byte) (any, error)
type Manager ¶
type Manager struct {
// contains filtered or unexported fields
}
func NewManager ¶
func NewManager( backend cachecontract.Backend, serializer cachecontract.Serializer, ) *Manager
NewManager takes a backend it does not own: Close leaves it open, because a backend handed in was built by someone else and is closed by whoever built it. That is what the container path needs — the backend is a registered service the container closes itself, so a manager that closed it too would close it twice, which a backend wrapping a connection typically reports as a failure on the second call and turns a clean shutdown into a reported one. Use NewManagerOwningBackend to get the cascade back.
func NewManagerOwningBackend ¶ added in v1.19.0
func NewManagerOwningBackend( backend cachecontract.Backend, serializer cachecontract.Serializer, ) *Manager
NewManagerOwningBackend takes a backend it closes when it is closed itself, for the caller that builds both by hand and wants one Close to end both. Do not use it for a backend that is also registered as a service: the container closes every service it created, so the backend would be closed once by this manager and once by the container.
func (*Manager) DeleteMultiple ¶
func (*Manager) GetCounter ¶ added in v1.19.0
GetCounter reads a key written by Increment or Decrement. Those operations are backend-native and store the count as decimal text rather than through the serializer, so Get would hand the raw payload to a deserializer that does not expect it.
func (*Manager) Increment ¶
the counter operations are backend-native so a distributed backend keeps them atomic, which means they bypass the serializer and store the count as decimal text; a counter key must therefore be read with GetCounter rather than Get, and must not be mixed with Set on the same key
func (*Manager) Many ¶
an entry whose payload does not deserialize is left out of the result the way an absent key is — Get answers the same entry with exists false — and the keys it happened under come back in a DeserializationError beside the values that did decode, so one corrupt entry no longer discards the whole answer and the error names its culprits deterministically.
func (*Manager) NormalizeStoredValue ¶ added in v1.19.0
NormalizeStoredValue answers the shape a value stored through this manager reads back as: one serializer round-trip, run locally with no backend involved. Remember consults it so the computing call and the cached calls answer one shape — without it, a callback's int came back float64 and its struct came back a map, but only from the second call on.
type RememberOption ¶
type RememberOption struct {
// contains filtered or unexported fields
}
RememberOption starts from the constructor defaults wherever it is built: the fields are unexported, so outside this package the only writes are the With setters, and a setter called on the exact zero value first reads the receiver as NewDefaultRememberOption before applying its own field. Without that reading, a zero value plus one setter carried a waitTimeout of zero — every miss answered with a timeout while the callback computed in the background — and configuring cancelability alone silently disarmed the stampede protection the caller never asked to configure.
waitTimeout is a pointer so that a deliberate zero — no-wait, spelled NewDefaultRememberOption().WithWaitTimeout(0) — is told apart from the field left unspoken: a nil answers the default wait, a set pointer answers exactly what it holds. Without the distinction, a protection-off option that also asked for no wait was the zero struct itself, which the guard below reads as the constructor defaults, so the caller who spelled "no coalescing and no wait" got protection armed with an unbounded wait — the opposite, on both fields. The guard now equals the zero struct only for a value built outside the constructor, which never sets the pointer, and that value is what should read as the defaults.
The caller's context lives here rather than in a signature of its own because it is one more optional thing about this call, and because it governs the wait together with waitTimeout and isCancelable: what the three do to each other is written once, on Context below. The zero-value guard survives the interface field — comparing against the zero struct puts a nil interface on one side, so the operands never share a dynamic type and the comparison answers false instead of panicking on an uncomparable one.
func NewDefaultRememberOption ¶
func NewDefaultRememberOption() *RememberOption
NewDefaultRememberOption arms stampede protection with an unbounded wait and no cancelation. Under exactly these defaults a callback that never returns pins its key for the life of the process: the leader cannot be told to stop (a non-cancelable flight hands it a background context), the entry is deleted only by the leader's own return, and the one path that replaces a live entry requires a canceled flight, which a non-cancelable one never becomes — so every later caller of the key parks behind it, one goroutine each. A callback that can hang wants its own deadline, WithWaitTimeout on the waiters, or WithCancelable so an abandoned flight is canceled and replaced.
func (*RememberOption) Context ¶ added in v1.19.0
func (instance *RememberOption) Context() context.Context
Context answers the context that governs THIS caller's wait, context.Background when none was given. It ends the wait and nothing else: the computation belongs to whoever leads the flight and is awaited by everybody coalesced onto it, so a client that disconnects takes its own caller out and leaves the value being computed for the others. What cancels the computation is still the last waiter leaving, and only on a cancelable option.
The three settings answer in this order: a canceled context ends the wait first, then the wait timeout, then the leader. A zero wait timeout means no waiting at all, so the context never gets to be consulted, and an unbounded wait — the shipped default — is exactly where a context matters most, since without one the waiter parks for as long as the callback takes however long ago its own request was abandoned. Where the callback is the one that should stop, hand its own deadline to the callback: the context it receives is the flight's, not this one.
func (*RememberOption) EnableStampedeProtection ¶
func (instance *RememberOption) EnableStampedeProtection() bool
func (*RememberOption) IsCancelable ¶
func (instance *RememberOption) IsCancelable() bool
func (*RememberOption) WaitTimeout ¶
func (instance *RememberOption) WaitTimeout() time.Duration
func (*RememberOption) WithCancelable ¶
func (instance *RememberOption) WithCancelable(isCancelable bool) *RememberOption
func (*RememberOption) WithContext ¶ added in v1.19.0
func (instance *RememberOption) WithContext(callerContext context.Context) *RememberOption
func (*RememberOption) WithStampedeProtectionEnabled ¶
func (instance *RememberOption) WithStampedeProtectionEnabled(enableStampedeProtection bool) *RememberOption
func (*RememberOption) WithWaitTimeout ¶
func (instance *RememberOption) WithWaitTimeout(waitTimeout time.Duration) *RememberOption