SuperCache (SuperCache v1.3.0)

Copy Markdown View Source

High-throughput, in-memory cache backed by ETS.

Works in both local (single-node) and distributed (cluster) modes. The mode is selected at startup via the :cluster option and is transparent to callers — the same API covers both.

Setup

# Local (single-node, default)
SuperCache.start!(key_pos: 0, partition_pos: 0)

# Distributed cluster
SuperCache.start!(
  key_pos:            0,
  partition_pos:      0,
  cluster:            :distributed,
  replication_factor: 2,
  replication_mode:   :async,
  num_partition:      16
)

# Enable debug logging
# in config.exs:
# config :super_cache, debug_log: true

Options

OptionTypeDefaultDescription
:key_posinteger0Tuple index used as the ETS key
:partition_posinteger0Tuple index used to select partition
:clusteratom:local:local or :distributed
:num_partitionintegerscheduler countNumber of ETS partitions
:table_typeatom:setETS table type

Read modes (meaningful in distributed mode)

ModeConsistencyLatency
:localEventual (default)Zero extra latency
:primaryStrong per key+1 RTT if non-primary
:quorumMajority vote+1 RTT (parallel)

Replication modes (distributed mode only)

ModeGuaranteeExtra latency
:asyncEventual (default)None
:syncAt-least-once+1 RTT per write
:strongThree-phase commit+3 RTTs per write

Basic usage

SuperCache.put!({:user, 1, "Alice"})
SuperCache.get!({:user, 1, nil})
# => [{:user, 1, "Alice"}]

# Read modes (distributed)
SuperCache.get!({:user, 1, nil}, read_mode: :primary)
SuperCache.get!({:user, 1, nil}, read_mode: :quorum)

SuperCache.delete!({:user, 1, nil})
SuperCache.delete_all()

Error handling

!-suffix functions raise on error. Non-bang variants return {:error, exception} instead and never raise.

Summary

Functions

Return an aggregated cluster-wide statistics map.

Non-raising variant of delete!/1.

Delete the record matching the key in data.

Delete all records from every partition.

Delete by explicit key and partition_data.

Delete from all partitions. Equivalent to delete_by_match!(:_, pattern).

Delete records matching pattern in partition_data (or all partitions for :_).

Delete a record directly from a specific partition table.

Delete a record directly from a partition by its integer index.

Equivalent to delete_by_key_partition!(key, key) — for tables where key_pos == partition_pos.

Returns true when SuperCache is running in distributed mode.

Retrieve records. Returns [tuple] | {:error, exception} instead of raising.

Retrieve all records whose key matches the key element of data.

Retrieve records by explicit key and partition_data.

Scan all partitions. Equivalent to get_by_match!(:_, pattern, []).

Retrieve records matching an ETS match pattern (binding lists).

Scan all partitions. Equivalent to get_by_match_object!(:_, pattern, []).

Retrieve full records matching an ETS match-object pattern.

Retrieve records directly from a specific partition table.

Retrieve records directly from a partition by its integer index.

Equivalent to get_by_key_partition!(key, key, opts) — for tables where key_pos == partition_pos.

Enqueue a tuple for a buffered (lazy) write.

Store a tuple. Returns true | {:error, exception} instead of raising.

Store a tuple in the cache.

Store multiple tuples in a single batch operation.

Store a tuple directly into a specific partition table.

Store a tuple directly into a partition by its integer index.

Non-raising variant of scan!/3.

Equivalent to scan!(:_, fun, acc).

Fold over every record in a partition (or all when :_).

Start SuperCache. Returns :ok | {:error, exception} instead of raising.

Start SuperCache with opts. Returns :ok | {:error, exception} instead of raising.

Start SuperCache with default options (local, key_pos: 0, partition_pos: 0).

Start SuperCache with the given keyword options. Raises on invalid config.

Returns true when SuperCache is running and ready.

Return local ETS record counts per partition plus :total.

Stop SuperCache and free all ETS memory.

Functions

cluster_stats()

@spec cluster_stats() :: map()

Return an aggregated cluster-wide statistics map.

In local mode wraps stats/0 in the same map format for a uniform interface. In distributed mode gathers per-node counts via :erpc.

delete(data)

@spec delete(tuple()) :: :ok | {:error, Exception.t()}

Non-raising variant of delete!/1.

delete!(data)

@spec delete!(tuple()) :: :ok

Delete the record matching the key in data.

Routes to the primary in distributed mode.

delete_all()

@spec delete_all() :: :ok

Delete all records from every partition.

In distributed mode, issues one routed delete per partition to its primary.

delete_by_key_partition(key, partition_data)

@spec delete_by_key_partition(any(), any()) :: :ok | {:error, Exception.t()}

Non-raising variant of delete_by_key_partition!/2.

delete_by_key_partition!(key, partition_data)

@spec delete_by_key_partition!(any(), any()) :: :ok

Delete by explicit key and partition_data.

delete_by_match(partition_data, pattern)

@spec delete_by_match(any(), tuple()) :: :ok | {:error, Exception.t()}

Non-raising variant of delete_by_match!/2.

delete_by_match!(pattern)

@spec delete_by_match!(tuple()) :: :ok

Delete from all partitions. Equivalent to delete_by_match!(:_, pattern).

delete_by_match!(partition_data, pattern)

@spec delete_by_match!(any(), tuple()) :: :ok

Delete records matching pattern in partition_data (or all partitions for :_).

delete_partition!(key, partition)

@spec delete_partition!(any(), atom() | :ets.tid()) :: :ok

Delete a record directly from a specific partition table.

Bypasses all routing and partition resolution.

Examples

partition = SuperCache.Partition.get_partition(:my_key)
SuperCache.delete_partition!(:user, partition)
# => :ok

delete_partition_by_idx!(key, partition_idx)

@spec delete_partition_by_idx!(any(), non_neg_integer()) :: :ok

Delete a record directly from a partition by its integer index.

This is the fastest delete path — bypasses routing and partition resolution.

Examples

idx = SuperCache.Partition.get_partition_order(:my_key)
SuperCache.delete_partition_by_idx!(:user, idx)
# => :ok

delete_same_key_partition(key)

@spec delete_same_key_partition(any()) :: :ok | {:error, Exception.t()}

Non-raising variant of delete_same_key_partition!/1.

delete_same_key_partition!(key)

@spec delete_same_key_partition!(any()) :: :ok

Equivalent to delete_by_key_partition!(key, key) — for tables where key_pos == partition_pos.

distributed?()

@spec distributed?() :: boolean()

Returns true when SuperCache is running in distributed mode.

Delegates to Config.distributed?/0 for zero-cost persistent_term reads.

get(data, opts \\ [])

@spec get(
  tuple(),
  keyword()
) :: [tuple()] | {:error, Exception.t()}

Retrieve records. Returns [tuple] | {:error, exception} instead of raising.

get!(data, opts \\ [])

@spec get!(
  tuple(),
  keyword()
) :: [tuple()]

Retrieve all records whose key matches the key element of data.

Options

  • :read_mode:local (default), :primary, or :quorum.

Examples

SuperCache.get!({:user, 1, nil})
SuperCache.get!({:user, 1, nil}, read_mode: :primary)
SuperCache.get!({:user, 1, nil}, read_mode: :quorum)

get_by_key_partition(key, partition_data, opts \\ [])

@spec get_by_key_partition(any(), any(), keyword()) ::
  [tuple()] | {:error, Exception.t()}

Non-raising variant of get_by_key_partition!/3.

get_by_key_partition!(key, partition_data, opts \\ [])

@spec get_by_key_partition!(any(), any(), keyword()) :: [tuple()]

Retrieve records by explicit key and partition_data.

Options

  • :read_mode:local (default), :primary, or :quorum.

get_by_match(partition_data, pattern, opts \\ [])

@spec get_by_match(any(), tuple(), keyword()) :: [[any()]] | {:error, Exception.t()}

Non-raising variant of get_by_match!/2.

get_by_match!(pattern)

@spec get_by_match!(tuple()) :: [[any()]]

Scan all partitions. Equivalent to get_by_match!(:_, pattern, []).

get_by_match!(partition_data, pattern, opts \\ [])

@spec get_by_match!(any(), tuple(), keyword()) :: [[any()]]

Retrieve records matching an ETS match pattern (binding lists).

Pass :_ as partition_data to scan all partitions.

Options

  • :read_mode:local (default), :primary, or :quorum.

get_by_match_object(partition_data, pattern, opts \\ [])

@spec get_by_match_object(any(), tuple(), keyword()) ::
  [tuple()] | {:error, Exception.t()}

Non-raising variant of get_by_match_object!/2.

get_by_match_object!(pattern)

@spec get_by_match_object!(tuple()) :: [tuple()]

Scan all partitions. Equivalent to get_by_match_object!(:_, pattern, []).

get_by_match_object!(partition_data, pattern, opts \\ [])

@spec get_by_match_object!(any(), tuple(), keyword()) :: [tuple()]

Retrieve full records matching an ETS match-object pattern.

Pass :_ as partition_data to scan all partitions.

Options

  • :read_mode:local (default), :primary, or :quorum.

get_partition!(key, partition)

@spec get_partition!(any(), atom() | :ets.tid()) :: [tuple()]

Retrieve records directly from a specific partition table.

Bypasses all routing and partition resolution. The key is looked up directly in the given partition table.

Examples

partition = SuperCache.Partition.get_partition(:my_key)
SuperCache.get_partition!(:user, partition)
# => [{:user, 1, "Alice"}]

get_partition_by_idx!(key, partition_idx)

@spec get_partition_by_idx!(any(), non_neg_integer()) :: [tuple()]

Retrieve records directly from a partition by its integer index.

This is the fastest read path — bypasses routing, partition resolution, and partition table name lookup.

Examples

idx = SuperCache.Partition.get_partition_order(:my_key)
SuperCache.get_partition_by_idx!(:user, idx)
# => [{:user, 1, "Alice"}]

get_same_key_partition(key, opts \\ [])

@spec get_same_key_partition(
  any(),
  keyword()
) :: [tuple()] | {:error, Exception.t()}

Non-raising variant of get_same_key_partition!/2.

get_same_key_partition!(key, opts \\ [])

@spec get_same_key_partition!(
  any(),
  keyword()
) :: [tuple()]

Equivalent to get_by_key_partition!(key, key, opts) — for tables where key_pos == partition_pos.

lazy_put(data)

@spec lazy_put(tuple()) :: :ok

Enqueue a tuple for a buffered (lazy) write.

Falls back to put!/1 with a warning when :strong replication is active.

put(data)

@spec put(tuple()) :: true | {:error, Exception.t()}

Store a tuple. Returns true | {:error, exception} instead of raising.

put!(data)

@spec put!(tuple()) :: true

Store a tuple in the cache.

In distributed mode, routes the write to the partition's primary node and replicates according to :replication_mode. Raises on error.

put_batch!(data_list)

@spec put_batch!([tuple()]) :: :ok

Store multiple tuples in a single batch operation.

In distributed mode, groups data by partition and sends each group in a single :erpc call, dramatically reducing network overhead.

Example

SuperCache.put_batch!([
  {:user, 1, "Alice"},
  {:user, 2, "Bob"},
  {:session, "tok1", :active}
])

put_partition!(data, partition)

@spec put_partition!(tuple(), atom() | :ets.tid()) :: true

Store a tuple directly into a specific partition table.

Bypasses all routing and partition resolution. Use when you already know the target partition table atom (e.g., from a previous get_partition/1 call or from Partition.get_all_partition/0).

Examples

partition = SuperCache.Partition.get_partition(:my_key)
SuperCache.put_partition!({:user, 1, "Alice"}, partition)

put_partition_by_idx!(data, partition_idx)

@spec put_partition_by_idx!(tuple(), non_neg_integer()) :: true

Store a tuple directly into a partition by its integer index.

This is the fastest write path — bypasses routing, partition resolution, and even the partition table name lookup. The index is used directly to fetch the cached partition table atom from persistent_term.

Examples

# Get partition index once
idx = SuperCache.Partition.get_partition_order(:my_key)

# Use it for fast repeated access
SuperCache.put_partition_by_idx!({:user, 1, "Alice"}, idx)

scan(partition_data, fun, acc)

@spec scan(any(), (any(), any() -> any()), any()) :: any() | {:error, Exception.t()}

Non-raising variant of scan!/3.

scan!(fun, acc)

@spec scan!((any(), any() -> any()), any()) :: any()

Equivalent to scan!(:_, fun, acc).

scan!(partition_data, fun, acc)

@spec scan!(any(), (any(), any() -> any()), any()) :: any()

Fold over every record in a partition (or all when :_).

Always reads from the local ETS table regardless of replication mode.

start()

@spec start() :: :ok | {:error, Exception.t()}

Start SuperCache. Returns :ok | {:error, exception} instead of raising.

start(opts)

@spec start(keyword()) :: :ok | {:error, Exception.t()}

Start SuperCache with opts. Returns :ok | {:error, exception} instead of raising.

start!()

@spec start!() :: :ok

Start SuperCache with default options (local, key_pos: 0, partition_pos: 0).

start!(opts)

@spec start!(keyword()) :: :ok

Start SuperCache with the given keyword options. Raises on invalid config.

started?()

@spec started?() :: boolean()

Returns true when SuperCache is running and ready.

stats()

@spec stats() :: keyword()

Return local ETS record counts per partition plus :total.

stop()

@spec stop() :: :ok

Stop SuperCache and free all ETS memory.