A distributed cache built on Akka Cluster Sharding and Akka HTTP. One entity actor per cache key, automatic shard rebalancing across a 3-node cluster, JSON HTTP API on every node.
Learning project β not production-ready. See Limits before using.
ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ
HTTP client ββββΆ β Node 1 β β Node 2 β β Node 3 β
(any node) β :8080 HTTP β β :8081 HTTP β β :8082 HTTP β
β :2551 Akka βββΆβ :2552 Akka βββΆβ :2553 Akka β
ββββββββ¬ββββββββ ββββββββ¬ββββββββ ββββββββ¬ββββββββ
β β β
βΌ βΌ βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββ
β Akka Cluster Sharding (10 shards) β
β shard = hash(key) % 10 β
β each shard owns N entity actors β
β entity = single CacheActor instance per key β
βββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
ββββββββββββββββββββ
β Distributed Data β shard β node assignment
β (CRDT, ddata) β gossiped via Akka Cluster
ββββββββββββββββββββ
Request lifecycle (PUT /cache/foo):
- HTTP request hits any node (e.g. node 2).
CacheRoutesparses it. CacheProxyresolves the entity ref viaClusterSharding.entityRefFor("CacheActor", "foo").- Akka routes the message to the node currently hosting shard
hash("foo") % 10. Shard rebalances move entities transparently. - The single
CacheActorfor keyfooupdates its in-memory map and replies. - Response returns through the proxy to the originating HTTP client.
Reads, writes, deletes follow the same path. Single-writer per key β no concurrency on the hot value.
| Choice | Why |
|---|---|
| Akka Cluster Sharding (not consistent hashing) | Built-in shard rebalance during membership change. No manual ring management. |
| ddata for shard coordination | CRDT-based, no external coordinator (no etcd / ZooKeeper). |
| One entity actor per key | Eliminates concurrency control on the value. Akka's mailbox is the lock. |
| In-memory state | Cache, not store. Persistence is a different problem. |
Honest list of what this does not do:
- No replication. A node dying loses all entities it owned until they're re-created on writes elsewhere. Reads fail until then.
- No persistence beyond process lifetime.
state-store-mode = ddatacovers shard assignments, not entity state.passivate-idle-entity-after = 10mevicts cold keys. - No quorum reads/writes. Single-writer-per-key gives strong consistency on hot path; failure semantics are last-writer-wins on entity recreation.
- Java serialization. Faster to set up, slow + fragile in production. Switch to Jackson or protobuf for real use.
- No metrics endpoint.
/admin/statusexposes node info; no Prometheus, no histograms. - In-memory journal. Persistence module is wired but uses
inmemjournal. Not crash-safe. - 3-node cluster fixed in scripts. Number of shards (10) is fine for ~3 nodes; needs raising for larger deployments.
- Java 11+: Required for Akka and modern Java features
- Maven 3.6+: For dependency management and building
- curl: For testing HTTP endpoints (usually pre-installed)
java -version # Should show Java 11+
mvn -version # Should show Maven 3.6+
curl --version # Should show curl info# Clone or download the project
git clone <your-repo-url>
cd akka-distributed-cache
# Run automated project setup
chmod +x scripts/setup-project.sh
./scripts/setup-project.sh# Start a single cache node for development/testing
./scripts/start-single.sh
# The server will start on:
# - HTTP API: http://localhost:8080
# - Akka Cluster: localhost:2551# Store a value (JSON format)
curl -X PUT http://localhost:8080/cache/hello \
-H 'Content-Type: application/json' \
-d '{"value":"world"}'
# Retrieve the value (returns JSON)
curl http://localhost:8080/cache/hello
# Response: {"value":"world"}
# Check node status
curl http://localhost:8080/admin/status
# View API documentation
curl http://localhost:8080/api# Start 3-node cluster
./scripts/cluster.sh start
# Check cluster status
./scripts/cluster.sh status
# Run comprehensive tests
./scripts/test-operations.sh| Method | Endpoint | Description | Request Body | Response |
|---|---|---|---|---|
PUT |
/cache/{key} |
Store a value | {"value":"data"} |
Put successful |
GET |
/cache/{key} |
Retrieve a value | None | {"value":"data"} |
DELETE |
/cache/{key} |
Remove a value | None | Delete successful |
# Store data with JSON format
curl -X PUT http://localhost:8080/cache/user123 \
-H 'Content-Type: application/json' \
-d '{"value":"john_doe"}'
# Retrieve data (returns JSON)
curl http://localhost:8080/cache/user123
# Response: {"value":"john_doe"}
# Delete data
curl -X DELETE http://localhost:8080/cache/user123
# Response: Delete successful# Test data distribution across nodes (JSON format)
curl -X PUT http://localhost:8080/cache/key1 \
-H 'Content-Type: application/json' \
-d '{"value":"node1_data"}'
curl -X PUT http://localhost:8081/cache/key2 \
-H 'Content-Type: application/json' \
-d '{"value":"node2_data"}'
# Access data from any node (automatic routing)
curl http://localhost:8082/cache/key1 # Returns: {"value":"node1_data"}
curl http://localhost:8080/cache/key2 # Returns: {"value":"node2_data"}| Method | Endpoint | Description | Response |
|---|---|---|---|
GET |
/admin/status |
Detailed node status | Node info, timestamps, sharding details |
GET |
/admin/health |
Simple health check | OK |
GET |
/ |
Root health check | Node online confirmation |
GET |
/api |
API documentation | Complete API reference |
- 200 OK: Operation successful
- 404 Not Found: Key doesn't exist or invalid endpoint
- 500 Internal Server Error: Server-side error
Purpose: Start single node for development
# Default ports (2551 for Akka, 8080 for HTTP)
./scripts/start-single.sh
# Custom ports
./scripts/start-single.sh 2552 8081Features:
- Quick startup for development
- Port conflict detection
- Direct console output
- JSON API ready
- Immediate error feedback
Purpose: Quick endpoint verification with JSON API
# Test default local instance
./scripts/verify-endpoints.sh
# Test custom URL
./scripts/verify-endpoints.sh http://localhost:8081Features:
- Tests all JSON API endpoints with correct format
- Validates response codes and JSON responses
- Shows working examples with proper Content-Type headers
- Quick troubleshooting
Purpose: Comprehensive functionality testing
./scripts/test-operations.shTest Coverage:
- JSON API operations (PUT/GET/DELETE) with proper headers
- Multi-key operations across cluster
- Cross-node data access and routing
- Entity distribution verification
- Admin endpoint validation
- Error case handling
- Cluster consistency testing
Purpose: Production cluster management with sharding
# Start 3-node cluster with sharding
./scripts/cluster.sh start
# Check detailed cluster status
./scripts/cluster.sh status
# Stop cluster gracefully
./scripts/cluster.sh stop
# Restart cluster
./scripts/cluster.sh restart
# Clean logs and stop
./scripts/cluster.sh clean
# Run comprehensive tests
./scripts/cluster.sh testFeatures:
- Manages 3-node cluster (ports 8080, 8081, 8082)
- Background process management with CountDownLatch blocking
- Cluster sharding with entity distribution
- Health verification with JSON API testing
- Detailed status reporting
- Graceful shutdown with coordinated shutdown
akka-distributed-cache/
βββ src/
β βββ main/
β β βββ java/ai/akka/cache/
β β β βββ CacheActor.java # Entity actors with sharding
β β β βββ CacheRoutes.java # HTTP JSON API routes
β β β βββ DistributedCacheApplication.java # Main app with sharding
β β βββ resources/
β β βββ application.conf # Cluster sharding configuration
β β βββ logback.xml # Logging configuration
β βββ test/java/ # Test files (future)
βββ scripts/
β βββ setup-project.sh # Project initialization
β βββ start-single.sh # Single node startup
β βββ cluster.sh # Cluster management with sharding
β βββ test-operations.sh # Comprehensive testing
β βββ verify-endpoints.sh # Quick endpoint verification
βββ logs/ # Runtime logs (node1.log, node2.log, node3.log)
βββ pids/ # Process ID files for cluster nodes
βββ pom.xml # Maven configuration
βββ README.md # This file
| Node | HTTP Port | Akka Port | Usage |
|---|---|---|---|
| Node 1 | 8080 | 2551 | Primary/Development |
| Node 2 | 8081 | 2552 | Cluster member |
| Node 3 | 8082 | 2553 | Cluster member |
- Entity Distribution: Keys are automatically distributed across nodes based on hash
- Number of Shards: 10 (configurable in application.conf)
- State Store: Distributed Data (ddata) for cluster coordination
- Rebalancing: Automatic shard rebalancing as nodes join/leave
application.conf: Akka cluster settings, sharding configuration, timeoutslogback.xml: Logging configuration for console and file outputpom.xml: Maven dependencies and build configuration
1. Port Already in Use
# Check what's using the port
lsof -i :8080
# Use different port
./scripts/start-single.sh 2551 80852. Wrong API Format The API requires JSON format for PUT operations:
# β Wrong - sending plain text
curl -X PUT http://localhost:8080/cache/hello -d 'world'
# β
Correct - sending JSON
curl -X PUT http://localhost:8080/cache/hello \
-H 'Content-Type: application/json' \
-d '{"value":"world"}'3. Compilation Errors
# Clean and rebuild
mvn clean compile
# Check Java version
java -version # Ensure Java 11+4. Missing Dependencies
# Download dependencies
mvn dependency:resolve
# Verify classpath
mvn dependency:build-classpathPUT Operations must include:
Content-Type: application/jsonheader- JSON body with
valuefield:{"value":"your-data"}
GET Operations return:
- JSON response:
{"value":"your-data"} - 404 status for missing keys
DELETE Operations:
- No body required
- Returns success message
# Store JSON data
curl -X PUT http://localhost:8080/cache/session123 \
-H 'Content-Type: application/json' \
-d '{"value":"user_data_here"}'
# Retrieve JSON data
curl http://localhost:8080/cache/session123
# Returns: {"value":"user_data_here"}
# Delete data
curl -X DELETE http://localhost:8080/cache/session123
# Returns: Delete successful# Missing key
curl http://localhost:8080/cache/nonexistent
# Returns: 404 Not Found - "Key not found"
# Invalid JSON format
curl -X PUT http://localhost:8080/cache/test -d 'plain-text'
# Returns: 400 Bad Request - Invalid JSON# Store complex JSON values
curl -X PUT http://localhost:8080/cache/user123 \
-H 'Content-Type: application/json' \
-d '{"value":"{\"name\":\"john\",\"age\":30}"}'
# Store simple strings
curl -X PUT http://localhost:8080/cache/message \
-H 'Content-Type: application/json' \
-d '{"value":"Hello World"}'# Test cross-node data access
curl -X PUT http://localhost:8080/cache/test1 \
-H 'Content-Type: application/json' \
-d '{"value":"from-node-1"}'
# Access from different node
curl http://localhost:8081/cache/test1
# Should return: {"value":"from-node-1"}