Tutorial

Getting Started with Aeron Cache

Install the local monolith and write your first key, value, counter, and TTL entry in minutes.

Why start here

Aeron Cache is a low-latency, highly-available key-value and counter store built on Aeron, Agrona, and SBE. In production it runs as a RAFT-clustered replicated state machine; on your laptop it runs as a single-process monolith that bundles a cluster node, the HTTP/WebSocket/SSE interfaces, and a web UI. That monolith is all you need to go from nothing to a working cache in a few minutes — and everything you learn here maps one-to-one onto the clustered deployment.

By the end of this tutorial you will have the backend running locally, created your first cache, and written and read an item three ways — through the Rust CLI, raw HTTP, and a client library — plus touched counters and per-item TTL.

Prerequisites

  • macOS or Linux with Homebrew.
  • A terminal. For the HTTP walkthrough, curl. For the client-library step, either a JDK 21+ (Java) or Node.js 18+ (TypeScript).
  • Ports 3000 (UI), 7070 (HTTP), and 7071 (WebSocket) free on localhost.

1. Install and run the monolith

Tap the formula and install. Running aeron-cache launches the monolith plus the UI.

brew tap bhf/aeron-cache
brew install aeron-cache
aeron-cache

On a successful start you will see the banner:

Initializing Aeron Cache...

    ___    __________  ____  _   __   _________   ________  _________
   /   |  / ____/ __ \/ __ \/ | / /  / ____/   | / ____/ / / / ____/
  / /| | / __/ / /_/ / / / /  |/ /  / /   / /| |/ /   / /_/ / __/   
 / ___ |/ /___/ _, _/ /_/ / /|  /  / /___/ ___ / /___/ __  / /___   
/_/  |_/_____/_/ |_|\____/_/ |_/   \____/_/  |_\____/_/ /_/_____/   

              🌲 https://github.com/bhf/aeron-cache 🌲

✅ Aeron Cache is running!
   UI      : http://localhost:3000
   Backend : PID 6221
   Config  : /home/user1/.aeron-cache
   Log     : /home/user1/.aeron-cache/aeron-cache.log

Press Ctrl+C to stop.

Verify it worked: open http://localhost:3000 — the web UI loads and connects to the backend.

Configuration

On first run, a default config is written to ~/.aeron-cache/backend.env. The HTTP REST interface is always on; the streaming gateways are toggled with environment variables:

InterfaceVariableDefault
WebSocket gatewayWEBSOCKET_GATEWAY_ENABLEDtrue
SSE gatewaySSE_GATEWAY_ENABLEDtrue
Aeron (SBE) gatewayAERON_GATEWAY_ENABLEDfalse

A disabled gateway is never started, so the matching UI streaming features will not connect. To enable the native Aeron gateway over IPC and turn SSE off, for example, add to ~/.aeron-cache/backend.env and restart:

AERON_GATEWAY_ENABLED=true
GATEWAY_TRANSPORT_MEDIA=ipc
SSE_GATEWAY_ENABLED=false

Other useful knobs in the same file: MONOLITH_EMBEDDED_DRIVER (default true; set false to attach an external aeronmd), DYNAMIC_CACHE_CREATION (default false), and CACHE_MODE (default RAFT).

2. Create a cache and put/get an item — three ways

Caches are created explicitly. We’ll make one called quickstart, write the key greeting, and read it back.

(a) The Rust CLI

Install the CLI from its own tap (full details on CLI guide):

brew tap bhf/aeron-cache-cli
brew install aeron-cache-cli
aeron-cache create quickstart
aeron-cache insert quickstart greeting "hello aeron"
aeron-cache get quickstart greeting

Expected output from the final command:

greeting = hello aeron

Verify it worked: aeron-cache list-caches shows quickstart with an item count of at least 1.

(b) Raw HTTP with curl

The REST API lives under /api/v1 on port 7070. Create, put, then get:

curl -s -X POST http://localhost:7070/api/v1/cache \
  -H 'Content-Type: application/json' \
  -d '{"cacheId":"quickstart-http"}'

curl -s -X POST http://localhost:7070/api/v1/cache/quickstart-http \
  -H 'Content-Type: application/json' \
  -d '{"key":"greeting","value":"hello over http"}'

curl -s http://localhost:7070/api/v1/cache/quickstart-http/greeting

The final call returns:

{"cacheId":"quickstart-http","key":"greeting","value":"hello over http","operationStatus":"SUCCESS"}

Verify it worked: the response carries "operationStatus":"SUCCESS" and the value you wrote. Note that business outcomes ride in operationStatus (SUCCESS, UNKNOWN_CACHE, UNKNOWN_KEY, CACHE_EXISTS, …) rather than always surfacing as HTTP error codes.

(c) A client library

The embedded clients give you the same surface in Java, TypeScript, Python, and Rust — plus a local mirror for network-free reads (see embedded clients). Here it is in Java and TypeScript.

var baseUrl = "http://localhost:7070";
var wsUrl = "ws://localhost:7071";
AeronCacheClient client = new AeronCacheClient(baseUrl, wsUrl);
client.createCache("quickstart-java");

EmbeddedAeronCache cache = client.getCache("quickstart-java");
cache.subscribe(event -> {}, true);              // hydrate a live local mirror
cache.put("greeting", "hello from java");
System.out.println(cache.getLocal("greeting"));  // -> hello from java
const baseUrl = "http://localhost:7070";
const wsUrl = "ws://localhost:7071";
const client = new AeronCacheClient(baseUrl, wsUrl);

await client.createCache("quickstart-ts");
const cache = new EmbeddedAeronCache(client, "quickstart-ts");
cache.subscribe(() => {}, undefined, undefined, true); // hydrate a live local mirror
await cache.put("greeting", "hello from ts");
console.log(cache.getLocal("greeting")); // -> hello from ts (sync local read)

Verify it worked: getLocal returns the value you just wrote — served from the client’s in-memory mirror, kept in sync over the WebSocket stream.

3. Counters

Alongside string caches, Aeron Cache has dedicated counter caches whose values are 64-bit integers, with atomic increment / decrement / set. With the CLI:

aeron-cache create-counter-cache metrics
aeron-cache put-counter metrics requests 10
aeron-cache increment-counter metrics requests 5
aeron-cache get-counter metrics requests

Expected output from get-counter:

requests = 15

Verify it worked: the value is 15 (10 + 5). Omit the amount on increment-counter to step by 1.

4. Per-item TTL (and cancelling it)

Items can carry a time-to-live in milliseconds. The TTL schedules a removal timer that you can cancel to keep the entry alive — the basis for session/presence stores that extend on activity.

aeron-cache insert-timed sessions user:42 active 60000
aeron-cache cancel-removal sessions user:42
aeron-cache get sessions user:42

Verify it worked: after cancel-removal, get still returns active once the 60-second window has passed, because the scheduled removal was cancelled. You can inspect pending timers across all caches with aeron-cache stats and the library getTimers call.

Where to next

  • embedded clients — the polyglot embedded clients: local mirrors, bulk ops, JSON merge patch, bidirectional WebSocket, and the native Aeron gateway.
  • CLI guide — the full Rust CLI reference.
  • streaming subscriptions — keyed and patch-mode subscriptions, and how the subscribe ack acts as a barrier.
  • comparison — how Aeron Cache’s model lines up against other caches.

Takeaway: one brew install gives you the same API — HTTP, WebSocket, CLI, and libraries — that you’ll run in a RAFT cluster later. Nothing you built here has to change when you scale out.