mökv is a distributed, in-memory key-value store. It utilizes Raft for consensus, serf for discvoery, gRPC for communication, and TLS for security.
Note
This is a project to learn more about distributed systems and Go.
- Distributed Architecture: Data is replicated across multiple nodes for fault tolerance.
- In-Memory Storage: Provides fast read and write operations.
RaftConsensus: Ensures data consistency across the cluster.gRPCInterface: Offers a well-definedAPIfor interacting with the store.TLSEncryption: Secures communication between nodes and clients.- Access Control: Uses
Casbinfor authorization, enabling fine-grained control over data access. - Metrics: Exposes
Prometheusmetrics for monitoring cluster health and performance. - Service Discovery: Uses
serffor automatic node discovery and membership management. - Load Balancing: Implements
gRPCclient-side load balancing, directing write operations to the leader and read operations to followers.
To run mökv:
-
Clone the repository:
git clone git@github.com:dynamic-calm/mokv.git cd mokv -
Generate TLS Certificates:
make gencert
This command uses
cfsslto generate the necessaryTLScertificates in the$HOME/.mokvdirectory. -
Compile the code:
make build
This will create an executable binary
mokvin thebin/directory.
Configuration is done through command-line flags or a configuration file. A sample configuration file (example/config.yaml) is provided. Copy certs/model.conf and certs/policy.csv to $HOME/.mokv.
Here's an example config.yaml:
data-dir: /tmp/mokv-data
node-name: node1
bind-addr: 127.0.0.1:8401
rpc-port: 8400
start-join-addrs: []
bootstrap: true
acl-model-file: $HOME/.mokv/model.conf
acl-policy-file: $HOME/.mokv/policy.csv
server-tls-cert-file: $HOME/.mokv/server.pem
server-tls-key-file: $HOME/.mokv/server-key.pem
server-tls-ca-file: $HOME/.mokv/ca.pem
peer-tls-cert-file: $HOME/.mokv/root-client.pem
peer-tls-key-file: $HOME/.mokv/root-client-key.pem
peer-tls-ca-file: $HOME/.mokv/ca.pem
metrics-port: 4000-
Start the first node:
bin/mokv --config-file example/config.yaml
-
Start additional nodes:
Modify the
example/config.yamlfile with the appropriatenode-name,bind-addr, andrpc-port. Crucially, setstart-join-addrsto the address of the first node (e.g.,127.0.0.1:8401). Also setbootstrap: falsefor the additional nodes. Then, run the command again:bin/mokv --config-file example/config2.yaml # Example config for the second nodeRefer to
example/start_nodes.shfor a convenient script to start a cluster.
mökv exposes a gRPC API defined in internal/api/kv.proto. You can use a gRPC client to interact with the store.
service KV {
rpc Get(GetRequest) returns (GetResponse) {}
rpc Set(SetRequest) returns (SetResponse) {}
rpc Delete(DeleteRequest) returns (DeleteResponse) {}
rpc List(google.protobuf.Empty) returns (stream GetResponse) {}
rpc GetServers(google.protobuf.Empty) returns (GetServersResponse){}
}mökv combines Serf for node discovery and Raft for consistent data replication. Here's how the key components interact:
-
Serf: Dynamic Membership:SerfusesUDPto monitor cluster membership. When a node joins, theserf.EventMemberJoinevent triggers theJoinfunction (internal/kv/kv.go), adding the node as aRaftvoter. This ensures theRaftcluster reflects the current active nodes. -
Raft: Consensus and the FSM:Raftguarantees data consistency. One node isLeader, handling all write operations. Write operations becomeRaftlog entries, replicated toFollowers. The Finite State Machine (FSM) is the core ofRaft'soperation:-
Applying Log Entries: When a log entry is committed (acknowledged by a quorum), the
Applymethod of theFSM(ininternal/kv/kv.go) is invoked. TheApplymethod handles different request types:- Set Request: Updates the in-memory key-value store (
kv.store) with the new key-value pair. - Delete Request: Removes the specified key from the in-memory store.
- Set Request: Updates the in-memory key-value store (
-
Data Flow for Writes:
gRPC->Raft Leader->Log Entry->Replication to Followers->FSM Apply->kv.store.
-
-
Persistence (
raft-boltdb):mökvusesraft-boltdbto persist Raft's log, stable state, and periodic snapshots to disk. This enables recovery after node failures.- Snapshotting: The
FSM'sSnapshotmethod creates a snapshot of the current in-memory state. - Restoring State: After a crash, the
FSM'sRestoremethod loads the latest snapshot and replays any subsequent log entries, reconstructing the in-memorykv.storeto a consistent state. This entire process happens automatically whensetupRaftis called during startup.
- Snapshotting: The
mökv uses gRPC for efficient communication between clients and the cluster, secured with TLS client certificates for authentication and Casbin for authorization.
-
API Definition: The core
gRPCservice,KV, is defined ininternal/api/kv.proto, exposing methods likeGet,Set,Delete,List, andGetServers. -
gRPCServer: The server implementation resides ininternal/server/server.go, handlinggRPCrequests. -
Interceptors:
gRPCInterceptors are used to handle:- Logging: Each incoming request is logged for monitoring.
- Authentication: Authenticates clients using
TLSclient certificates. The certificate's Common Name (CN) is extracted and used as the subject for authorization (see below). This is handled in theauthenticatefunction ininternal/server/server.go.
-
Authorization (
Casbin): Theinternal/auth/auth.goenforces access control. Casbin determines if the authenticated user (identified by theCN) has permission to perform an action ("produce" for writes, "consume" for reads). -
Client-Side Load Balancing (Name Resolution and Picker):
mökvuses client-side load balancing.-
Name Resolver (
internal/discovery/resolver.go): The name resolver periodically callsGetServersto discover availablemökvnodes and their roles (Leader/Follower). It updates the list of available servers with theis_leaderattribute. -
Picker (
internal/discovery/picker.go): The Picker directs requests based on the operation type and the leader status of available connections:- Writes (
Set,Delete): These are routed to the Leader node to ensure consistency. - Reads (
Get,List): These are balanced among available Follower nodes for improved read performance.
- Writes (
-