AshScylla.PreparedStatementCache (AshScylla v1.5.3)

Copy Markdown View Source

ETS-based prepared statement cache for ScyllaDB/Cassandra queries.

Caches prepared statements keyed by {repo, cql, keyspace, opts} to eliminate repeated query parsing overhead on ScyllaDB. This is especially impactful for high-throughput workloads where the same queries are executed repeatedly.

All ETS operations are routed through the GenServer to avoid race conditions when multiple processes access the cache concurrently.

Usage

AshScylla.PreparedStatementCache.prepare(repo, "SELECT * FROM users WHERE id = ?")

Starting the Cache

Add to your supervision tree:

children = [
  AshScylla.PreparedStatementCache,
  # ... other children
]

Or start manually:

AshScylla.PreparedStatementCache.start_link([])

Limits

  • Max cache size: 10,000 entries
  • Cleanup interval: 5 minutes
  • Registered locally as __MODULE__ by default

Summary

Functions

Returns a specification to start this module under a supervisor.

Clears all cached prepared statements.

Evicts the oldest count entries from the cache.

Invalidates a specific cached statement by CQL string.

Returns the configured maximum cache size.

Prepares a CQL statement, using the cache if available.

Returns the number of cached statements.

Starts the prepared statement cache.

Returns the ETS table tid for inspection/testing.

Types

cache_entry()

@type cache_entry() :: {term(), term()}

cache_key()

@type cache_key() :: {module(), String.t(), String.t(), keyword()}

Functions

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

clear()

@spec clear() :: :ok

Clears all cached prepared statements.

evict_oldest(tid, count)

@spec evict_oldest(:ets.tid(), non_neg_integer()) :: :ok

Evicts the oldest count entries from the cache.

Exposed for testing the eviction logic.

invalidate(cql)

@spec invalidate(String.t()) :: :ok

Invalidates a specific cached statement by CQL string.

max_cache_size()

@spec max_cache_size() :: non_neg_integer()

Returns the configured maximum cache size.

Exposed for testing.

prepare(repo, cql, opts \\ [])

@spec prepare(module(), String.t(), keyword()) :: {:ok, term()} | {:error, term()}

Prepares a CQL statement, using the cache if available.

Returns {:ok, stmt} on success or {:error, reason} on failure.

size()

@spec size() :: non_neg_integer()

Returns the number of cached statements.

start_link(opts \\ [])

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

Starts the prepared statement cache.

When no :name option is given, the GenServer is registered locally as __MODULE__ so that each node runs its own per-node cache. This is required in a clustered deployment (libcluster + K8s/gossip), where a global name would be contested by every node and cause repeated :reached_max_restart_intensity crashes.

Pass a {:global, name} tuple to register globally, or a custom local name to register under a different local name.

table()

@spec table() :: :ets.tid() | nil

Returns the ETS table tid for inspection/testing.