hermes-broker-server 0.4.1

Hermes gRPC broker server with fanout, queue groups, and durable delivery.
Documentation

hermes-server

gRPC server crate for the Hermes broker.

This crate provides the server runtime: publishing, fanout / queue-group subscriptions, and durable mode (with storage) when enabled.

Installation

Add the dependency:

[dependencies]
hermes-broker-server = "0.3"
tokio = { version = "1", features = ["full"] }

Quick start

1) Start with default config

use tokio::net::TcpListener;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let listener = TcpListener::bind("0.0.0.0:4222").await?;
    hermes_server::run(listener).await?;
    Ok(())
}

2) Start with custom config

use tokio::net::TcpListener;
use hermes_server::config::ServerConfig;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let listener = TcpListener::bind("127.0.0.1:4222").await?;

    let config = ServerConfig {
        subscriber_channel_capacity: 4096,
        grpc_output_buffer: 256,
        store_path: Some("hermes.redb".into()),
        redelivery_interval_secs: 3,
        max_delivery_attempts: 10,
        retention_secs: 24 * 3600,
        default_ack_timeout_secs: 30,
        default_max_in_flight: 64,
        gc_interval_secs: 60,
        redelivery_batch_size: 200,
        ..ServerConfig::default()
    };

    hermes_server::run_with_config(listener, config).await?;
    Ok(())
}

Configuration reference

All settings can be set programmatically via ServerConfig or through environment variables with ServerConfig::from_env().

Network

Env variable Config field Default Description
HERMES_LISTEN_ADDR listen_addr 0.0.0.0:4222 IP address and port the gRPC server binds to.

Buffers

Env variable Config field Default Description
HERMES_CHANNEL_CAPACITY subscriber_channel_capacity 8192 Capacity of the main buffer between publishers and subscribers (broadcast channel for fanout, mpsc channel for queue groups). When a subscriber is slow, messages accumulate here. In fanout mode, exceeding this capacity causes the subscriber to lag and miss messages.
HERMES_GRPC_OUTPUT_BUFFER grpc_output_buffer 1024 Per-subscriber buffer between the internal forwarding task and the gRPC output stream. Keep it small — back-pressure should be handled by the main channel above.

Durable storage

Env variable Config field Default Description
HERMES_STORE_PATH store_path None Path to the redb database file. When set, durable mode is enabled (persistent messages, ack/nack, redelivery). When None, the server runs in fire-and-forget mode only.

Redelivery

These settings control the background loop that re-delivers messages that were sent to a consumer but never acknowledged within the timeout window.

Env variable Config field Default Description
HERMES_REDELIVERY_INTERVAL redelivery_interval_secs 5 How often (in seconds) the redelivery loop checks for expired messages.
HERMES_MAX_DELIVERY_ATTEMPTS max_delivery_attempts 5 Maximum number of delivery attempts per message. After this many failures the message is dead-lettered.
HERMES_REDELIVERY_BATCH_SIZE redelivery_batch_size 100 Maximum number of expired messages processed per consumer per redelivery cycle. Prevents a single cycle from monopolizing the CPU when thousands of messages have expired.

Ack / in-flight control

Env variable Config field Default Description
HERMES_ACK_TIMEOUT default_ack_timeout_secs 30 Time (in seconds) a consumer has to acknowledge a message after receiving it. If the deadline passes without an ack, the message is considered expired and eligible for redelivery. Consumers can override this per-subscription in their DurableSubscribeRequest.
HERMES_MAX_IN_FLIGHT default_max_in_flight 32 Maximum number of unacknowledged messages the broker will send to a single consumer at the same time. Limits pressure on slow consumers. Consumers can override this per-subscription.

Garbage collection

Env variable Config field Default Description
HERMES_RETENTION_SECS retention_secs 3600 How long (in seconds) acknowledged messages are kept in the store before being deleted by the GC loop. Useful for short-term auditing.
HERMES_GC_INTERVAL gc_interval_secs 60 How often (in seconds) the GC loop runs to purge old acknowledged messages.

Data flow

Publisher
   │
   ▼
┌──────────────────────────────────┐
│  subscriber_channel_capacity     │  ← main buffer (default 8192)
│  (broadcast for fanout,          │
│   mpsc for queue groups)         │
└──────────────┬───────────────────┘
               │
               ▼
┌──────────────────────────────────┐
│  grpc_output_buffer              │  ← per-subscriber gRPC buffer (default 1024)
│  (mpsc → tonic stream)           │
└──────────────┬───────────────────┘
               │
               ▼
           gRPC client

Background loops (durable mode only):

┌───────────────────┐  every 5s     ┌─────────────────────────┐
│  redelivery loop  ├──────────────►│ re-sends expired msgs   │
│                   │  ≤100/consumer│ (up to max_delivery_attempts) │
└───────────────────┘               └─────────────────────────┘

┌───────────────────┐  every 60s    ┌─────────────────────────┐
│  GC loop          ├──────────────►│ deletes acked msgs      │
│                   │  retention:1h │ older than retention     │
└───────────────────┘               └─────────────────────────┘

Example

HERMES_LISTEN_ADDR=0.0.0.0:4222 \
HERMES_STORE_PATH=./hermes.redb \
HERMES_CHANNEL_CAPACITY=16384 \
HERMES_RETENTION_SECS=86400 \
HERMES_MAX_IN_FLIGHT=64 \
cargo run -p hermes-server

Notes

  • Without HERMES_STORE_PATH (or store_path = None), the server runs in fire-and-forget mode — no persistence, no redelivery, no GC.
  • With a configured store, redelivery and GC loops are automatically spawned and stopped on graceful shutdown.
  • The service exposes gRPC server reflection for API discovery (e.g. with grpcurl).