Cluster Quick Start (Single Node)
Before adding multiple nodes, read Distributed Systems. Multi-node clusters (
raft_cluster_size > 1) fail closed at startup (error.RaftTransportUnavailable) unless you supply a real.transport(or explicitly set.allow_stub_raft_transport = truefor membership-only operation) and configure authentication credentials (own_key/peer_keysorcluster_secret).
1. Bootstrapping a Single Node
const std = @import("std");
const zmodu = @import("zmodu");
var cluster: *zmodu.ClusterBootstrap = undefined;
pub fn main(init: std.process.Init) !void {
const allocator = init.gpa;
const io = init.io;
var boot = try zmodu.ClusterBootstrap.init(allocator, io, .{
.node_id = "node-1",
.port = 9001,
.peers = &.{},
.raft_cluster_size = 1,
});
defer boot.deinit();
try boot.start();
cluster = &boot;
// `tick()` runs one gossip/health pass, republishes the read-side ClusterView,
// and advances `raft.tick()`. `error.ReadersBusy` means a request is reading
// the previous snapshot slot and is safe to ignore until the next tick.
while (true) {
boot.tick() catch |err| switch (err) {
error.ReadersBusy => {},
else => return err,
};
try std.Io.sleep(io, std.Io.Duration.fromMilliseconds(1000), .awake);
}
}
Request handlers must read cluster membership through the lock-free read-side snapshot (cluster.pick(key) or cluster.getView().acquire()), never by locking the mutable membership map.
2. Mounting Health and Metrics Endpoints
fn clusterHealth(ctx: *zmodu.http.Context) anyerror!void {
const json = try zmodu.clusterHealthJson(ctx.allocator, cluster);
defer ctx.allocator.free(json);
try ctx.json(200, json);
}
fn clusterMetrics(ctx: *zmodu.http.Context) anyerror!void {
const text = try cluster.getMetrics().toPrometheus(ctx.allocator);
defer ctx.allocator.free(text);
try ctx.text(200, text);
}
// Register routes on your HTTP server:
var server = zmodu.http.Server.init(io, allocator, 8080);
defer server.deinit();
try server.addRoute(.{ .method = .GET, .path = "/cluster/health", .handler = clusterHealth });
try server.addRoute(.{ .method = .GET, .path = "/cluster/metrics", .handler = clusterMetrics });
try server.start();
Verify the endpoints:
curl -s http://127.0.0.1:8080/cluster/health
# {"status":"UP","node_id":"node-id","cluster":{"nodes_active":1,"raft_term":0,"raft_state":"follower"}}
curl -s http://127.0.0.1:8080/cluster/metrics
# zigmodu_cluster_nodes_active 1
# zigmodu_cluster_leader_epoch 0
# zigmodu_cluster_messages_sent_total 0