The service runtime is the application-facing side of RingLoom. A service imports ringloom_service, starts a RingLoomEngine, registers message handlers, creates clients for target service names, and sends messages without needing to know whether the selected target is local or remote.

Main API surface

Type Purpose
RingLoomEngine Starts and owns the service runtime: metadata, registration, control agent, message consumer, clients, counters, and shutdown.
ServiceConfig Runtime configuration for storage path, group, service name, broker node ID, buffer sizes, leader election, blocking mode, and idle strategy.
ServiceClient Client proxy for a discovered target service name. Handles local producers, remote broker routing, load balancing, leader sends, and zero-copy claims.
MessageConsumer Reads inbound application messages from the service message ring and dispatches to the registered handler.
ControlAgent Processes service control messages such as registration responses, service-instance snapshots, and leader changes.
IpcProducer / IpcConsumer Low-level shared-memory producer/consumer wrappers over RingLoom’s MPSC ring buffer.

Service startup

RingLoomEngine.start performs the service-side bootstrap:

  1. opens the local broker metadata file;
  2. allocates a unique service ID from the broker metadata;
  3. creates this service’s metadata file;
  4. writes a RegisterService message to the broker control ring;
  5. waits for RegistrationResponse on the service control ring;
  6. writes an initial heartbeat;
  7. initializes the service client registry;
  8. starts the message consumer thread;
  9. starts the control agent thread.

The service metadata file is created under:

<storage_path>/<group>/services/<service_name>_node<node_id>_<service_id>.dat

Shared-memory channels

A running service participates in four shared-memory channels:

Channel Backing file Direction Purpose
Broker control ring broker metadata service → broker Register, subscribe, unregister, and related control messages.
Service control ring service metadata broker → service Registration response, discovery snapshots, and leader updates.
Broker send ring broker metadata service → broker Outbound application messages whose target is on a remote node.
Service message ring service metadata producers → service Inbound application messages from local producers or the local broker receiver.

The application handler only sees delivered application messages. Control-plane messages are consumed by the service runtime.

Same-host direct path

When a ServiceClient target is on the same node, the client writes directly into the target service’s message ring. The broker is not involved in the data path.

service A ServiceClient -> target service message ring -> target MessageConsumer

The fastest version of this path is the claim/commit API. The caller claims writable bytes in the target ring, writes the payload directly into mapped memory, and commits. No intermediate payload allocation or copy is required by RingLoom.

Cross-host routed path

When a target service instance is on another node, the same ServiceClient writes to the local broker send ring with remote routing identifiers in the message header:

service -> local broker send ring -> broker sender loop -> TCP -> remote broker receiver loop -> target service ring

The application uses the same service-name client abstraction. Discovery tells the client which node and service ID each target instance currently has.

Service discovery

RingLoomEngine.createClient("pricing") returns a ServiceClient and sends a subscription request to the broker. Whenever matching instances appear, disappear, or change leader status, the broker sends a complete ServiceInstances snapshot.

The client maintains a target list containing:

  • target node ID;
  • target service ID;
  • leader flag;
  • local producer if the target is same-host;
  • broker-routed producer metadata if the target is remote.

The default selection strategy is round robin across available instances. Callers can also send to an explicit (target_node_id, target_service_id) or to the current leader.

Message receiving

The service message consumer reads records from the service message ring. Each delivered message contains transport metadata such as source node, source service, target node, target service, template ID, flags, correlation ID, and payload bytes.

A Zig service registers a handler with:

engine.setMessageHandler(&messageHandler);

Handlers should stay allocation-free on the hot path: decode fixed layouts, update preallocated state, send using a reused client or claim, and avoid per-message logging.

Send APIs

ServiceClient supports several send styles:

API style Use case
send / sendMessage Copy-based convenience send to a load-balanced instance.
sendTo / sendToMessage Send to a specific target node and service ID.
sendToLeader / sendToLeaderMessage Send to the discovered leader instance.
tryClaim Claim payload memory for zero-copy load-balanced sends.
tryClaimTo Claim payload memory for a specific target.
tryClaimToLeader Claim payload memory for the current leader.

Request variants accept an explicit correlation ID so applications can link replies, timeouts, or tracing records.

Back-pressure contract

The service runtime reports expected runtime outcomes as typed send errors or status codes in bindings. Common outcomes include:

  • no available instance;
  • no leader available;
  • target ring full;
  • broker send ring full;
  • flow-control back-pressure;
  • peer congestion;
  • peer disconnected.

Applications should count and handle these outcomes according to their domain policy. They are not treated as crashes by the runtime.

Service-side observability

Service metadata includes counters, heartbeat timestamps, ring positions, and error state. Native services can register custom counters and gauges through the C ABI, and external tools can inspect service metadata through ringloom-stat or the Prometheus exporter.