dcache

package
v0.0.0-...-8c930d8 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 11, 2026 License: Apache-2.0 Imports: 28 Imported by: 0

Documentation

Overview

Package dcache provides a replicated in-memory cache: a per-process store whose set and delete operations propagate to the store of every other instance through Kafka events. A cache that lives only in the current process is the cache package instead; this one is for state every instance must see.

There is no shared storage tier. Entries must be rebuildable from their source of truth on a miss (cache-aside), and a bounded TTL is the only backstop that reconciles instances after a lost event: do not rely on ttl == 0 entries staying consistent across instances.

Known Limitations

These are accepted trade-offs of the current design, documented so callers can judge whether they fit their workload:

  • Values travel between instances as JSON. T must round-trip through encoding/json without loss: a value whose state lives in unexported fields is stored intact locally but arrives at every peer as a hollow decoded copy, and nothing reports it. Strings inside T must be valid UTF-8 for the same reason — encoding/json silently replaces invalid bytes with U+FFFD, so the peers would decode a different value. Keys are validated up front; values are not. A marshal failure, by contrast, is returned by Set.
  • Consumers join Kafka with a fresh group id at the end of the topic. Events published while an instance is down are never replayed for it; its store misses those writes until the same key is written again or the TTL expires. Building a cache waits for the group to hand it the partitions, so events published once it is in use do reach it; the wait is bounded, and an instance built while Kafka is out of reach starts deaf to its peers and says so in the log.
  • Cross-host ordering is decided by producer UnixNano timestamps, each raised past the newest stamp its instance has already applied to that key, so an instance whose clock trails a peer's still publishes writes the peers accept. Two instances writing one key before either has seen the other's event are still separated by their clocks alone: the one running ahead wins, whichever wrote first.
  • Delivery is best-effort and bounded: a record that cannot be delivered within its budget (a few retries inside a few seconds), that exceeds the broker's message size limit, or that arrives while the publishing pool is saturated is dropped with an error log, leaving the peers behind until the key is written again or the TTL expires. When an in-flight request wedges on a dead connection, failing an attempt can additionally take roughly the produce request timeout, and the records buffered for the same partition fail together with it.
  • A peer stores an entry with the ttl the event carries, counted from the moment the event is applied, so the entry lives on the peer for the propagation delay longer than on the writer.
  • Each instance bounds its per-key timestamp table by LRU eviction; evicting a key reopens that key's out-of-order acceptance window.
  • There is no lifecycle API: producers, consumers, goroutine pools and monitor tickers live for the whole process lifetime.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Cache

func Cache[T any]() (types.Cache[T], error)

Cache returns the replicated cache of type T, creating it on first use.

Why keep one cache per type in a registry? Each type's cache owns the goroutine that watches the peer set and delete events, and they never interfere with each other. The number of types is bounded, so only a few goroutines ever listen for kafka events, and fewer listeners means better throughput.

Without the registry, every Cache call would spawn another goroutine listening for kafka events and therefore far too many kafka consumers, which is not what we want. Since this function is exported we cannot stop other developers from calling it repeatedly, so the control has to stay here.

The arithmetic:

kafka consumers on one node: number of service processes * number of
dcache instances, and normally only one service process runs.
total kafka listeners: consumers per node * number of nodes

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL
⌂ 换网址