OrionKV is a distributed key-value store with:
- a control plane for membership, gossip, failure detection, consistent hashing, join, leave, and rebalance
- a coordination plane for quorum reads and writes, replica routing, merge logic, and repair
- a data plane for local storage, persistence, replica application, and range transfer
- a separate client-router application that acts as the client-facing HTTP entrypoint and maintains a refreshable view of alive OrionKV nodes
This repository contains both the OrionKV node runtime and the client-router runtime.
Main class:
src/main/java/com/orionkv/NodeApplication.java
Artifact:
target/orionkv-0.0.1-SNAPSHOT.jar
Responsibilities:
- gossip membership
- failure detection
- consistent hashing with virtual nodes
- join / leave / failure rebalance
- quorum read / write coordination
- local persistence and replica serving
Main class:
src/main/java/com/orionkv/clientrouter/ClientRouterApplication.java
Artifact:
target/orionkv-0.0.1-SNAPSHOT-client-router.jar
Responsibilities:
- maintain a registry of alive node gRPC addresses
- provide seed resolution for joining nodes
- refresh the node table from a seed node
- proxy client
PUT,GET, andDELETErequests to the cluster
+--------------------------------------+
| Client Router |
|--------------------------------------|
| seed resolution | registry refresh |
| routed client HTTP requests |
+-------------------+------------------+
|
v
+--------------------------------------------------+
| OrionKV Node |
+--------------------------------------------------+
| Coordination Plane | Control Plane | Data Plane |
|--------------------------------------------------|
| quorum routing | gossip | local KV |
| read/write merge | FD | WAL/persist |
| replica selection | hash ring | replica RPC |
| read repair | rebalance | range stream |
+--------------------------------------------------+
Core source:
src/main/java/com/orionkv/controlplanesrc/main/java/com/orionkv/coordinationplanesrc/main/java/com/orionkv/dataplanesrc/main/java/com/orionkv/clientroutersrc/main/java/com/orionkv/configsrc/main/proto/controlplane.protosrc/main/proto/coordination.proto
Tests:
src/test/java/com/orionkv
Operational notes:
Operational scripts:
scripts/start-cluster.shscripts/kill-all.shscripts/docker-cluster-reset.shscripts/docker-cluster-restart.shscripts/docker-cluster-smoke.shscripts/generate-docker-compose.shscripts/fair-cluster-load.shscripts/docker-balance-audit.shscripts/latency-benchmark.shscripts/render-latency-results.shscripts/render-theoretical-latency-results.shscripts/plot-latency-benchmark.pyscripts/watch-grpc-key.shscripts/ubuntu-docker-e2e.sh
Run tests:
mvn clean testPackage both applications:
mvn clean package -DskipTestsProduced artifacts:
target/orionkv-0.0.1-SNAPSHOT.jartarget/orionkv-0.0.1-SNAPSHOT-client-router.jar
mvn clean package -DskipTestsjava -jar target/orionkv-0.0.1-SNAPSHOT-client-router.jar \
--server.port=8090 \
--client.router.registry-path=data/client-router-nodes.json \
--client.router.refresh-interval-ms=5000 \
--client.router.rpc-timeout-ms=3000./scripts/start-cluster.shThis starts a local 6-node cluster and configures each node to use the client router base URL.
./scripts/kill-all.shThe client router is part of the normal cluster lifecycle, not just an optional client proxy.
curl -X POST http://127.0.0.1:8090/client/nodes/seed \
-H 'Content-Type: application/json' \
-d '{"nodeId":"node-1","grpcAddress":"10.0.0.11:9091"}'If the router registry is empty, the response points the node back to itself as seed and stores it.
curl -X POST http://127.0.0.1:8090/client/nodes/seed \
-H 'Content-Type: application/json' \
-d '{"nodeId":"node-2","grpcAddress":"10.0.0.12:9092"}'The router returns an already known alive node as the seed target.
After the node has joined and bootstrap has completed:
curl -X POST http://127.0.0.1:8090/client/nodes/confirm \
-H 'Content-Type: application/json' \
-d '{"joiningNodeId":"node-2","seedGrpcAddress":"10.0.0.11:9091"}'curl -X POST http://127.0.0.1:8090/client/nodes/refresh \
-H 'Content-Type: application/json' \
-d '{"seedGrpcAddress":"10.0.0.11:9091"}'curl http://127.0.0.1:8090/client/nodescurl -X POST http://127.0.0.1:8090/client/nodes/register \
-H 'Content-Type: application/json' \
-d '{"nodeId":"node-9","grpcAddress":"10.0.0.19:9099"}'Put:
curl -X PUT http://127.0.0.1:8090/client/kv/user-1 \
-H 'Content-Type: application/json' \
-d '{"value":"alice"}'Get:
curl http://127.0.0.1:8090/client/kv/user-1Delete:
curl -X DELETE http://127.0.0.1:8090/client/kv/user-1 \
-H 'Content-Type: application/json' \
-d '{"timestamp":2000}'Check membership across local nodes:
for p in 9091 9092 9093 9094 9095 9096; do
echo "=== $p ==="
grpcurl -plaintext -d '{}' -proto src/main/proto/controlplane.proto \
127.0.0.1:$p orionkv.node.ClusterRpc/GetMembership
doneDirect coordination RPC write:
grpcurl -plaintext -d '{"requestId":"r1","key":"alpha","value":"v1","timestamp":"0"}' \
-proto src/main/proto/coordination.proto \
127.0.0.1:9091 orionkv.node.CoordinationRpc/PutDirect coordination RPC read:
grpcurl -plaintext -d '{"requestId":"r2","key":"alpha"}' \
-proto src/main/proto/coordination.proto \
127.0.0.1:9092 orionkv.node.CoordinationRpc/GetDirect replica read:
grpcurl -plaintext -d '{"requestId":"r3","key":"alpha"}' \
-proto src/main/proto/coordination.proto \
127.0.0.1:9093 orionkv.node.ReplicaDataRpc/GetReplicaCluster summary over HTTP:
curl http://127.0.0.1:8081/internal/cluster/summaryThe cluster summary endpoint returns an aggregated cluster view built from alive nodes, including:
- quick facts such as active node count, responding node count, total records, and average records per node
- balance information such as minimum / maximum records and spread across nodes
- node-by-node distribution data for replica storage counts
Build the image:
docker build -t orionkv:local .Restart a generated Docker cluster:
HOST_IP=127.0.0.1 \
SEED_HOST_IP=127.0.0.1 \
./scripts/docker-cluster-restart.shReset Docker cluster state:
./scripts/docker-cluster-reset.shSmoke-test the Docker cluster:
./scripts/docker-cluster-smoke.shFor Ubuntu / VCL flows, use:
scripts/setup-ubuntu-host.shscripts/ubuntu-docker-e2e.sh
Run a benchmark:
bash scripts/latency-benchmark.shGenerate an HTML report from an existing raw or summary CSV:
bash scripts/render-latency-results.sh results/<run>.csvGenerate a theoretical/modelled report from the same measured run:
bash scripts/render-theoretical-latency-results.sh results/<run>.csvArtifacts:
- raw CSV
- summary CSV
- HTML report
node.node-idnode.addressnode.bind-portnode.seed-addressnode.client-router-base-urlnode.gossip-interval-msdefault5000node.self-heartbeat-interval-msdefault1000node.failure-detection-interval-msdefault2000node.suspect-timeout-msdefault10000node.dead-timeout-msdefault30000node.virtual-node-countdefault512node.replication-factordefault3node.write-quorumdefault2node.read-quorumdefault2
client.router.registry-pathdefaultdata/client-router-nodes.jsonclient.router.refresh-interval-msdefault5000client.router.rpc-timeout-msdefault3000client.router.join-confirmation-attemptsdefault20client.router.join-confirmation-delay-msdefault1000
dataplane.storage.log-pathdefaultdata/wal.log
GossipRpc.Gossip(GossipPayload) -> MembershipStateClusterRpc.Join(JoinNodeRequest) -> MembershipStateClusterRpc.GetMembership(google.protobuf.Empty) -> MembershipState
CoordinationRpc.Put(ClientPutRequest) -> ClientPutResponseCoordinationRpc.Get(ClientGetRequest) -> ClientGetResponseReplicaDataRpc.PutReplica(ReplicaPutRequest) -> ReplicaPutResponseReplicaDataRpc.GetReplica(ReplicaGetRequest) -> ReplicaGetResponse
POST /client/nodes/registerPOST /client/nodes/seedPOST /client/nodes/confirmPOST /client/nodes/refreshGET /client/nodesPUT /client/kv/{key}GET /client/kv/{key}DELETE /client/kv/{key}
PUT /api/kv/{key}GET /api/kv/{key}DELETE /api/kv/{key}?timestamp=...GET /internal/cluster/summaryGET /internal/storage/range?startToken=...&endToken=...POST /internal/replica/putPOST /internal/replica/apply-batchGET /internal/replica/stream?startToken=...&endToken=...
- membership, gossip, failure detection, ring convergence, quorum operations, and rebalance flows are implemented
- client-router integration is part of the main operational path
- Docker-based multi-node deployment and audit tooling are present
- latency benchmarking and HTML reporting flows are included in the repo