Documentation
¶
Overview ¶
Package memcache is filex's ONE in-process cache for small state and settings that live in the database: a bounded, expiring map (Cache), and the same map kept in front of a durable store, written through it in both directions (Through).
The owner's ruling (2026-10-06, #184): keep it in the database, and where the database is not fast enough keep a simple in-memory cache in front of it - not Redis, nothing outside the process - for this kind of state and setting, both ways.
Why one package ¶
The tree grew its process-local caches one at a time, each a map, a mutex and a hand-written expiry: the storage usage card's (api/handlers quota_storages.go, now a Cache), the S3 etag memo, the protocol sessions' grant sets, the document server's record of who opened a session. Every one of them answers the same three questions - how big may it get, how long is an entry true, how does it forget one - and every hand-written answer is a place to get one wrong (a map with no ceiling is a memory incident). New state goes here; the older ones move here when they are next touched.
The rule that keeps it honest (Through) ¶
⚠⚠ The cache is a READ ACCELERATOR, never the authority. Behind several filex instances sharing one database, another instance's write is not in this instance's cache, so an entry can be stale for up to its TTL:
- a write goes to the store FIRST and to the cache only when the store took it (Set, Add, Delete): the cache never says something the store does not;
- a read for speed may be answered from the cache (Get);
- a read that DECIDES something another instance may have changed - may this save go over the file? - is read from the store (Fresh), which also refreshes the cache. When the store cannot answer, Fresh says so (err) and the caller chooses; it does not quietly fall back to a possibly stale entry.
A nil Backing is a cache with nothing behind it: every call works on memory alone (a unit test, a Service built without a database).
Index ¶
- Constants
- type Backing
- type Cache
- type Options
- type Through
- func (t *Through[K, V]) Add(ctx context.Context, k K, v V) (V, error)
- func (t *Through[K, V]) Cache() *Cache[K, V]
- func (t *Through[K, V]) Delete(ctx context.Context, k K) error
- func (t *Through[K, V]) Fresh(ctx context.Context, k K) (V, bool, error)
- func (t *Through[K, V]) Get(ctx context.Context, k K) (V, bool, error)
- func (t *Through[K, V]) Set(ctx context.Context, k K, v V) error
Constants ¶
const DefaultMaxEntries = 4096
DefaultMaxEntries bounds a Cache whose Options leave MaxEntries at 0.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Backing ¶
type Backing[K comparable, V any] interface { // Load reads k; ok is false when there is nothing stored. Load(ctx context.Context, k K) (v V, ok bool, err error) // Store creates or overwrites k. Store(ctx context.Context, k K, v V) error // Add stores v for k unless something is stored already, and returns // what is stored now (the first writer's value). Add(ctx context.Context, k K, v V) (V, error) // Remove deletes k; nothing stored is not an error. Remove(ctx context.Context, k K) error }
Backing is the durable store a Through keeps a Cache in front of.
type Cache ¶
type Cache[K comparable, V any] struct { // contains filtered or unexported fields }
Cache is a bounded map with an expiry and a least-recently-used eviction. Safe for concurrent use; the zero value is not usable (New). Every method is nil-safe: a nil *Cache holds nothing and remembers nothing.
func (*Cache[K, V]) DeleteFunc ¶
DeleteFunc forgets every entry match says yes to (an invalidation by content: every entry of one node, say); n = entries forgotten.
func (*Cache[K, V]) Expire ¶
Expire forgets the entries past their TTL now (Get forgets them as it meets them; this is for a sweep); n = entries forgotten.
type Options ¶
type Options struct {
// MaxEntries is the most entries held; when full, the least recently
// used goes. 0 = DefaultMaxEntries.
MaxEntries int
// TTL is how long an entry is served after it was written; 0 = until it
// is evicted or deleted.
TTL time.Duration
// Now is the clock; nil = time.Now. Tests move it.
Now func() time.Time
}
Options shape a Cache.
type Through ¶
type Through[K comparable, V any] struct { // contains filtered or unexported fields }
Through is a Cache in front of a Backing, written through in both directions (see the package doc for the rule).
func NewThrough ¶
func NewThrough[K comparable, V any](back Backing[K, V], o Options) *Through[K, V]
NewThrough keeps a Cache shaped by o in front of back (nil: memory only).
func (*Through[K, V]) Add ¶
Add stores v for k unless something is stored already, and returns what is stored now; the cache holds that. Memory only: the cache's entry when it has a fresh one.
func (*Through[K, V]) Cache ¶
Cache is the memory half, for an invalidation the store does not drive.
func (*Through[K, V]) Delete ¶
Delete removes k from the store and then from the cache. A delete the store refuses still forgets the cache's entry: the next read asks the store.
func (*Through[K, V]) Fresh ¶
Fresh answers from the store, and refreshes (or clears) the cache with what it said. The read for a decision another instance's write may change. Memory only (no Backing): the cache's answer.