SchemaCache
An Ecto-aware caching library providing cache-aside and write-through abstractions with automatic invalidation.
SchemaCache understands the relationship between your Ecto schemas and cached query results. When a schema is mutated, it knows exactly which cached values are affected and can evict or update them automatically.
Installation
Add schema_cache to your list of dependencies in mix.exs:
def deps do
[
{:schema_cache, "~> 0.1.0"}
]
end
Add SchemaCache.Supervisor to your application supervision tree:
children = [
{SchemaCache.Supervisor, adapter: SchemaCache.Adapters.ETS},
# ... other children
]Quick Start
SchemaCache provides four core operations: read, create, update, and delete.
Cache-Aside
On a cache miss, SchemaCache invokes your callback to fetch from the source, caches the result, and records which schemas appear in it for later eviction.
# Cache a single record
{:ok, user} =
SchemaCache.read("find_user", %{id: 5}, :timer.minutes(15), fn ->
MyApp.Users.find(%{id: 5})
end)
# Cache a collection
users =
SchemaCache.read("users", %{active: true}, :timer.minutes(5), fn ->
MyApp.Users.all(%{active: true})
end)Create
After inserting a new record, evict all cached collections for that schema type so they refresh on the next read and include the new record.
{:ok, new_user} =
SchemaCache.create(fn ->
%User{}
|> User.changeset(%{name: "Bob", email: "bob@example.com"})
|> Repo.insert()
end)Update with Eviction (default)
After an update, evict all cache keys that reference the mutated schema instance. The next read will fetch fresh data from the source.
{:ok, updated_user} =
SchemaCache.update(fn ->
MyApp.Users.update_user(user, %{name: "Alice"})
end)Update with Write-Through
Update all cached values referencing the schema in place, avoiding cache misses entirely. Use when your application can't serve stale results after a write.
{:ok, updated_user} =
SchemaCache.update(
fn -> MyApp.Users.update_user(user, %{name: "Alice"}) end,
strategy: :write_through
)Delete
After a deletion, evict all cache keys that reference the deleted schema instance.
{:ok, deleted_user} =
SchemaCache.delete(fn -> Repo.delete(user) end)How It Works
SchemaCache maintains a reverse index from Ecto schemas to cache keys (SMKES). When a query result is cached, it records which schemas appear in that result. On mutation, it looks up all affected cache keys and evicts or updates them.
For a detailed explanation, see the Architecture Guide.
Using with ElixirCache
ElixirCache modules work out of the box. No wrapper module needed. SchemaCache auto-detects ElixirCache modules and translates API signatures automatically:
children = [
MyApp.Cache,
{SchemaCache.Supervisor, adapter: MyApp.Cache}
]
For Redis-backed ElixirCache modules, SchemaCache also auto-detects native set
operations via command/1 with zero configuration. See the
ElixirCache Integration Guide
for details.
Documentation
- Introduction
- Tutorials: Installation | Basic Operations
- How-to: Writing Adapters | Using with ElixirCache
- Explanation: Architecture (SMKES)
- HexDocs
Contributing
Prerequisites
- Elixir ~> 1.14
- PostgreSQL (for integration tests)
- Redis (optional, for Redis adapter tests)
- Docker (optional, for running services via docker-compose)
Local Setup
# Clone the repository
git clone https://github.com/BobbieBarker/schema_cache.git
cd schema_cache
# Install dependencies
mix deps.get
# Create and migrate the test database
MIX_ENV=test mix ecto.setup
# Run the test suite
mix testRunning Redis Tests
Redis adapter tests require a running Redis instance. You can start one with docker-compose:
docker-compose up -d redis
mix testTests that require Redis will automatically skip if Redis is not available.
Code Quality
# Linting
mix credo
# Type checking
mix dialyzer
# Test coverage
mix coverallsPull Requests
-
Fork the repository and create your branch from
main. - Write tests for any new functionality.
-
Ensure
mix test,mix credo, andmix dialyzerpass. - Open a pull request with a clear description of the change.
License
MIT License. See LICENSE for details.