swim#
An OCaml 5 implementation of the SWIM (Scalable Weakly-consistent Infection-style Process Group Membership) protocol for cluster membership and failure detection.
Overview#
This library provides:
- Membership Management: Automatic discovery and tracking of cluster nodes
- Failure Detection: Identifies unreachable nodes using periodic probes and indirect checks
- Gossip Protocol: Propagates state changes (Alive/Suspect/Dead) across the cluster
- Messaging: Cluster-wide broadcast (gossip-based) and direct point-to-point UDP messaging
- Encryption: Optional AES-256-GCM encryption for all network traffic
Built on Eio for effect-based concurrency and Kcas for lock-free shared state.
Requirements#
- OCaml >= 5.1
- Dune >= 3.20
Installation#
opam install .
Or add to your dune-project:
(depends (swim (>= 0.1.0)))
Usage#
Basic Example#
open Swim.Types
let config = {
default_config with
bind_port = 7946;
node_name = Some "node-1";
secret_key = "your-32-byte-secret-key-here!!!"; (* 32 bytes for AES-256 *)
encryption_enabled = true;
}
let () =
Eio_main.run @@ fun env ->
Eio.Switch.run @@ fun sw ->
let env_wrap = { stdenv = env; sw } in
match Swim.Cluster.create ~sw ~env:env_wrap ~config with
| Error `Invalid_key -> failwith "Invalid secret key"
| Ok cluster ->
Swim.Cluster.start cluster;
(* Join an existing cluster *)
let seed_nodes = ["192.168.1.10:7946"] in
(match Swim.Cluster.join cluster ~seed_nodes with
| Ok () -> Printf.printf "Joined cluster\n"
| Error `No_seeds_reachable -> Printf.printf "Failed to join\n");
(* Send a broadcast message to all nodes *)
Swim.Cluster.broadcast cluster ~topic:"config" ~payload:"v2";
(* Send a direct message to a specific node *)
let target = node_id_of_string "node-2" in
Swim.Cluster.send cluster ~target ~topic:"ping" ~payload:"hello";
(* Handle incoming messages *)
Swim.Cluster.on_message cluster (fun sender topic payload ->
Printf.printf "From %s: [%s] %s\n"
(node_id_to_string sender.id) topic payload);
(* Listen for membership events *)
Eio.Fiber.fork ~sw (fun () ->
let stream = Swim.Cluster.events cluster in
while true do
match Eio.Stream.take stream with
| Join node -> Printf.printf "Joined: %s\n" (node_id_to_string node.id)
| Leave node -> Printf.printf "Left: %s\n" (node_id_to_string node.id)
| Suspect_event node -> Printf.printf "Suspect: %s\n" (node_id_to_string node.id)
| Alive_event node -> Printf.printf "Alive: %s\n" (node_id_to_string node.id)
| Update _ -> ()
done);
Eio.Fiber.await_cancel ()
Configuration Options#
| Field | Default | Description |
|---|---|---|
bind_addr |
"0.0.0.0" | Interface to bind listeners |
bind_port |
7946 | Port for SWIM protocol |
protocol_interval |
1.0 | Seconds between probe rounds |
probe_timeout |
0.5 | Seconds to wait for Ack |
indirect_checks |
3 | Peers to ask for indirect probes |
secret_key |
(zeros) | 32-byte key for AES-256-GCM |
encryption_enabled |
false | Enable encryption |
Interoperability Testing#
The library includes interoperability tests with HashiCorp's memberlist (Go). This verifies protocol compatibility with the reference implementation.
Prerequisites#
- Go >= 1.19
- OCaml environment with dune
Running Interop Tests#
The interop test suite starts a Go memberlist node and an OCaml node, then verifies they can discover each other and exchange messages.
# Build the OCaml project
dune build
# Build the Go memberlist server
cd interop && go build -o memberlist-server main.go && cd ..
# Run the interop test
bash test/scripts/test_interop.sh
# Run with encryption enabled
bash test/scripts/test_interop_encrypted.sh
Manual Interop Testing#
Start the Go node:
cd interop
go run main.go -name go-node -bind 127.0.0.1 -port 7946
In another terminal, start the OCaml node:
dune exec swim-interop-test
The OCaml node will connect to the Go node and print membership statistics for 30 seconds.
Available Test Scripts#
| Script | Description |
|---|---|
test/scripts/test_interop.sh |
Basic interop test |
test/scripts/test_interop_encrypted.sh |
Interop with AES encryption |
test/scripts/test_interop_udp_only.sh |
UDP-only communication test |
test/scripts/test_interop_go_joins.sh |
Go node joining OCaml cluster |
Debug Utilities#
# Test packet encoding/decoding
dune exec swim-debug-codec
# Receive and display incoming SWIM packets
dune exec swim-debug-recv
# Send manual ping to a target node
dune exec swim-debug-ping
Running Tests#
dune runtest
License#
ISC License. See LICENSE for details.