Skip to content

feat(broker): hermes-broker — route the hermes.proto surface across instances - #139

Merged
ppodolsky merged 3 commits into
mainfrom
hermes-broker
Aug 8, 2026
Merged

feat(broker): hermes-broker — route the hermes.proto surface across instances#139
ppodolsky merged 3 commits into
mainfrom
hermes-broker

Conversation

@ppodolsky

Copy link
Copy Markdown
Contributor

A stateless gRPC broker fronting many hermes-server backends behind one address, so consumers scale to multiple hermes nodes by re-pointing one endpoint.

What

  • Serves the exact hermes.proto SearchService/IndexService (drop-in for all clients) plus a broker-only hermes.broker.BrokerService admin proto, grpc.health.v1, and server reflection.
  • Discovers backends via a Kubernetes pod watch (shard identity from the hermes.spacefrontiers.org/shard-id pod label, replica role from …/role) or a static --backend list; learns index→backend routing by polling ListIndexes with a Suspect/Evicted health machine (grace period, two-probe recovery).
  • Routes every RPC for an index to the backend hosting it; writes go to the shard master only (single-master model; follower replication is a designed future phase — see docs/broker.md). CreateIndex placed by glob rules (--placement "documents*=0"), so dated index families stay on their shard; the same rules pin reads/writes when an index name transiently exists on two shards during a host-to-host migration.
  • Contract preservation: byte-faithful write responses (duplicate-pk / backpressure DocumentErrors untouched), incoming grpc-timeout propagated with no broker-imposed deadlines (24h admin merges, untimed index channels), admission mirrors --max-concurrent-searches with the server's exact RESOURCE_EXHAUSTED message, ListIndexes served from cached topology.

Tests

  • Unit: topology assembly, placement globs, master validation, ambiguity rules, health transitions, grpc-timeout parsing.
  • Integration: the real broker binary vs scripted in-process mock backends — pass-through equality, per-index routing, mid-stream re-routing of IndexDocuments, deadline header presence/absence at the backend, eviction + two-probe recovery, placement.
  • E2E (new CI step): broker in front of two real hermes-server subprocesses — placement-routed creates, batch writes + commit, duplicate-pk pass-through, search + GetDocument, cross-shard isolation.

Packaging

  • publish.yml builds and pushes ghcr.io/spacefrontiers/hermes/hermes-broker alongside the server image; both existing Dockerfiles gain the new workspace member's COPY line.
  • Design doc: docs/broker.md; metrics documented in docs/metrics.md.

…ss instances

A stateless gRPC broker that fronts many hermes-server backends behind one
address. Serves the exact hermes.proto SearchService/IndexService (clients
switch by re-pointing endpoints) plus a broker-only hermes.broker admin
proto and grpc.health.v1.

- Discovery: kubernetes pod watch (shard id + role pod labels) or static
  --backend list; index->backend routing learned via ListIndexes polling
  with a Suspect/Evicted health machine (grace, two-probe recovery).
- Routing: per-index pass-through; writes to the shard master only;
  CreateIndex placed by glob rules (dated families follow their shard);
  ambiguous multi-shard names read deterministically and refuse writes
  until a rule pins them.
- Contract: byte-faithful write responses, grpc-timeout propagated with no
  broker-imposed deadlines, admission mirrors --max-concurrent-searches
  with the server's exact RESOURCE_EXHAUSTED message, ListIndexes served
  from cache.
- Tests: unit (topology/placement/health/deadline math), integration (real
  broker binary vs scripted mock backends: pass-through equality, stream
  re-routing, eviction/recovery, deadline headers), env-gated e2e against
  real hermes-server subprocesses (CI runs it after building the server).
- CI publishes ghcr.io/spacefrontiers/hermes/hermes-broker alongside the
  server image; both Dockerfiles now copy the new workspace member.
grpcurl-driven verification (the deployment runbook's parity checks) needs
reflection: neither the broker nor hermes-server previously answered
schema-less tooling. Registers both the hermes.proto and hermes-broker.proto
descriptor sets.
kube's rustls-tls stack panics at first TLS use unless a process-level
CryptoProvider is installed (rustls 0.23); install ring explicitly. A
discovery-task failure (error or panic) now drains and exits the server via
the internal shutdown channel instead of leaving a zombie broker answering
NOT_SERVING forever.
@ppodolsky
ppodolsky merged commit fa97e03 into main Aug 8, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant