Examples
Walkthroughs for common RingLoom service communication patterns.
The repository includes two kinds of examples:
- small test services used by the end-to-end and performance harnesses;
- the order-management sample, which is a complete two-node application.
Use the small services when you want to validate one behavior quickly. Use the order-management sample when you want to see how multiple services fit together.
Echo and ping: simplest local IPC
The quickstart uses ringloom-test-echo-service and ringloom-test-ping-service with one broker. The flow is:
ping service -> echo service
Because both services register on the same broker node, the ping client’s discovered target is local. Sends go directly into the echo service’s message ring through shared memory.
Useful options:
| Service | Option | Purpose |
|---|---|---|
| echo | --max-messages N |
Stop after receiving N messages. |
| echo | --reply-delay-ms N |
Simulate a slow handler. |
| ping | --message-count N |
Number of messages to send. |
| ping | --message-size N |
Payload size in bytes. |
| ping | --send-interval-ns N |
Pace sends for unloaded latency measurement. |
| ping | --spin-timeout-ms N |
Retry briefly when back-pressure occurs. |
| both | --storage-path, --group, --broker-node-id |
Match the local broker config. |
Forwarder chain
ringloom-test-forwarder-service is used by the harness to validate multi-hop service routing. The pattern is:
producer -> forwarder -> echo
This demonstrates that a service can receive a message through its message handler and then use a ServiceClient to send the next message. Keep forwarding handlers small: decode fixed fields, update counters, claim the outbound buffer, write the next payload, and commit.
Slow consumer and back-pressure
ringloom-test-slow-consumer-service and echo’s --reply-delay-ms option are useful for seeing how the system behaves when a target service cannot drain its message ring as fast as producers fill it.
Expected behavior under pressure:
- producers may see
SendBufferFullor flow-control errors; - ring usage reported by
ringloom-statrises; - peer send pending counters rise for remote targets when enabled;
- the system remains stable instead of allocating unbounded queues.
Back-pressure is not an exceptional condition. Production services should count it, decide whether to retry, shed, buffer outside the hot path, or report overload to upstream callers.
Leader-aware services
ringloom-test-leader-service exercises per-service leader election. Multiple instances register with the same service name and leader election enabled. The broker designates one leader and sends leader-change updates to subscribers.
A caller that needs active-instance semantics should use leader-aware send paths rather than implementing its own coordination:
- send to any instance for load-balanced work;
- send to the leader for ordered or stateful work;
- subscribe to lifecycle updates if you need logs or cold-path reconfiguration when the leader changes.
Crash and restart scenarios
ringloom-test-crashy-service exists for recovery tests. It helps validate that broker heartbeat checks remove dead services, notify discovery subscribers, and allow replacement services to register cleanly.
For local debugging, inspect the preserved e2e temp directory after a failing test. The harness keeps generated config, process logs, result JSON, and storage metadata under /tmp/ringloom-e2e-<scenario>-<seq>/ when a scenario fails.
Order-management sample
The sample under samples/order-management starts a two-node application with six service types:
order-simulator -> order-gateway -> risk-service -> matching-engine -> execution-service -> portfolio-service
Default placement:
| Node | Services |
|---|---|
| node 1 | order-simulator, order-gateway, risk-service, portfolio-service |
| node 2 | matching-engine, execution-service |
The normal message path intentionally crosses broker nodes twice:
- simulator to gateway — local shared-memory IPC;
- gateway to risk — local shared-memory IPC;
- risk to matching — cross-broker TCP route;
- matching to execution — local shared-memory IPC on node 2;
- execution to portfolio — cross-broker TCP route back to node 1.
Run the default profile:
zig build sample-order-management
zig build run-sample-order-management -- --profile default
Run the full profile:
zig build run-sample-order-management -- --profile full --orders 100000 --rate-per-sec 50000
The full profile adds another risk-service instance and another matching-engine instance. It demonstrates load balancing, leader election for matching, lifecycle updates, and optional process restart behavior.
What the sample demonstrates
| Feature | Where to look |
|---|---|
| Fixed wire payloads | samples/order-management/src/common/protocol.zig |
| Shared service names | samples/order-management/src/common/service_names.zig |
| Service startup wiring | samples/order-management/src/common/app.zig |
| Deterministic order generation | order_simulator.zig |
| Local forwarding | order_gateway.zig and risk_service.zig |
| Cross-node routing | risk_service.zig to matching_engine.zig, then execution_service.zig to portfolio_service.zig |
| Leader-aware routing | matching_engine.zig in the full profile |
| Results and summaries | workspace results/ directory printed by the run script |
| Runtime inspection | ringloom-stat command printed by the run script |
Application payload pattern
The sample uses a small domain envelope followed by fixed extern struct bodies. The envelope carries correlation and timing fields. The RingLoom transport already carries routing and template metadata, so the application payload should not duplicate that information unless the domain needs it.
A typical hot-path send looks like this conceptually:
- choose a target client by service name;
- claim outbound payload memory with a template ID and payload length;
- write the fixed payload directly into the claim buffer;
- commit the claim;
- increment application counters outside the critical path when possible.
Avoid per-message heap allocation, string formatting, JSON serialization, or log output in handlers. Those are fine for startup, shutdown, and summary paths, but they defeat the predictable latency model if they run for every message.
Testing scenarios in the repository
The e2e harness covers the patterns that the examples build on:
| Scenario family | Coverage |
|---|---|
| startup | broker config, metadata creation, signal handling |
| registration | single and multiple services, service ID assignment |
| local IPC | direct same-host service-to-service communication |
| discovery | late registration and removal updates |
| cross-broker | two- and three-broker routing |
| fragmentation | large message handling |
| heartbeat timeout | dead service cleanup |
| restart | metadata reuse and cleanup |
| leader election | failover and multiple instances |
| graceful unregister | clean shutdown and discovery removal |
| backpressure | slow consumers and multiple producers |
Run them with:
zig build e2e
Use these scenarios as executable examples when you are designing a new service topology.