Ferricstore.FetchOrCompute (ferricstore v0.3.3)

Copy Markdown View Source

GenServer managing compute locks for cache-aside with stampede protection.

When a cache miss occurs, only one client is allowed to compute the value (the "computer"). All other clients requesting the same key block as "waiters" until the computer delivers the result or an error.

Stampede protection

Without stampede protection, a cache miss on a hot key causes N concurrent clients to all independently compute the same expensive value. This module serializes computation: only the first caller gets {:compute, hint}, subsequent callers block until the result arrives.

Timeout handling

If the computer does not deliver a result within compute_timeout_ms (default 30 seconds), the next waiter in line is promoted to computer. If there are no waiters, the lock is simply cleared.

ETS table

Compute locks are stored in :ferricstore_compute_locks as:

{key, computer_pid, waiters_list, started_at_ms, compute_hint}

This table is owned by the GenServer process.

Summary

Functions

Returns a specification to start this module under a supervisor.

Attempts to fetch a key from the store, or initiates a compute lock.

Reports a compute error for a key.

Delivers the computed result for a key.

Starts the FetchOrCompute GenServer.

Functions

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

fetch_or_compute(key, ttl_ms, hint)

@spec fetch_or_compute(binary(), pos_integer(), binary()) ::
  {:hit, binary()} | {:compute, binary()} | {:ok, binary()} | {:error, binary()}

Attempts to fetch a key from the store, or initiates a compute lock.

Parameters

  • key -- the cache key to look up
  • ttl_ms -- TTL in milliseconds for the value when stored
  • hint -- an opaque string hint passed to the computer (e.g. a URL or function name to call)

Returns

  • {:hit, value} -- the key was found in the store
  • {:compute, hint} -- the caller is the computer; it must call fetch_or_compute_result/3 or fetch_or_compute_error/2
  • {:ok, value} -- the caller was a waiter and received the result
  • {:error, reason} -- the compute failed (error propagated to waiters)

fetch_or_compute_error(key, error_msg)

@spec fetch_or_compute_error(binary(), binary()) :: :ok

Reports a compute error for a key.

Wakes all waiters with {:error, error_msg} and clears the compute lock.

Parameters

  • key -- the cache key
  • error_msg -- error message to propagate to waiters

Returns

:ok

fetch_or_compute_result(key, value, ttl_ms)

@spec fetch_or_compute_result(binary(), binary(), pos_integer()) :: :ok

Delivers the computed result for a key.

Stores the value in the Router with the given TTL, wakes all waiters with {:ok, value}, and clears the compute lock.

Parameters

  • key -- the cache key
  • value -- the computed value to store
  • ttl_ms -- TTL in milliseconds

Returns

:ok

start_link(opts \\ [])

@spec start_link(keyword()) :: GenServer.on_start()

Starts the FetchOrCompute GenServer.

Options

  • :compute_timeout_ms -- timeout in milliseconds before a stalled computer is replaced (default: 30000)