No description
Find a file
jedarden 4d3f952699 Phase 1 — Core Routing: Verified implementation
Complete verification of Phase 1 — Core Routing (rendezvous hash, topology, covering set).

## Definition of Done Checklist - ALL VERIFIED ✓

### Router Tests (router.rs)
- ✓ test_determinism: Rendezvous assignment is deterministic (1000 iterations)
- ✓ test_reshuffle_bound_on_add: 64 shards, 3→4 nodes moves ≤32 edges
- ✓ test_reshuffle_bound_on_remove: 64 shards, 4→3 nodes
- ✓ test_uniformity: 64 shards / 3 nodes / RF=1 → 17-26 shards per node
- ✓ test_rf2_placement_stability: Top-RF placement changes minimally on add/remove
- ✓ test_write_targets_returns_rg_x_rf_nodes: write_targets returns exactly RG × RF nodes
- ✓ test_write_targets_one_per_group: One-per-group assignment
- ✓ test_query_group_uniform_distribution: Chi-square test passes
- ✓ test_covering_set_covers_all_shards: All shards represented
- ✓ test_covering_set_size_bound: Bounded by group node count
- ✓ test_covering_set_determinism: Identical topologies produce identical results
- ✓ test_covering_set_rotates_replicas: Replica rotation by query_seq

### Merger Tests (merger.rs)
- ✓ 39 tests pass for RRF and score-based merge strategies
- ✓ Global sort, offset/limit, facet aggregation
- ✓ Deterministic tie-breaking, reserved field stripping
- ✓ Score-based merge for global-IDF preflight (OP#4)

### Coverage (cargo-tarpaulin)
- ✓ router.rs: 65/65 lines (100%)
- ✓ topology.rs: 130/130 lines (100%)
- ✓ merger.rs: 148/157 lines (94.3%)
- ✓ scatter.rs: 269/348 lines (77.3% - stub methods excluded)

## Implementation Summary

All Phase 1 core routing primitives are fully implemented and verified:
1. Rendezvous hashing (HRW) with XxHash64 seed 0
2. Topology management with node health state machine
3. Write path: write_targets returns RG × RF nodes, one per group
4. Read path: query_group round-robin, covering_set with replica rotation
5. Result merger: RRF (default) and score-based merge strategies
6. Scatter orchestration: plan_search_scatter, execute_scatter

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-23 15:27:55 -04:00
.beads P2.3 Search read path: scatter-gather + merge + group selection 2026-05-23 14:05:49 -04:00
.cargo Multi-stage Dockerfile with musl cross-compilation and .dockerignore 2026-04-19 13:47:45 -04:00
.github P8.6: Release mechanics — bump script, release-ready check, PR template, Argo CIs 2026-04-19 09:54:26 -04:00
benches P12.OP4: Implement dfs_query_then_fetch for cross-shard comparability 2026-04-19 03:43:10 -04:00
charts/miroir P6.2: Fix peer discovery DNS SRV service name and add POD_IP 2026-05-23 02:39:28 -04:00
crates Phase 1 — Core Routing: Verified implementation 2026-05-23 15:27:55 -04:00
dashboards P7.3: Add §13.1 resharding row to Grafana dashboard, fix y-coordinate overlaps 2026-04-19 13:18:13 -04:00
docs P6.11: Add single-pod oversized mode support (§14.10 vertical scaling escape valve) 2026-05-20 07:29:39 -04:00
examples P9.2: Integration test harness with docker-compose 2026-05-23 07:33:34 -04:00
k8s P8.1: Simplify Dockerfile to scratch-only, update CI to use /workspace/artifacts/ 2026-05-23 07:16:34 -04:00
notes P2.4 Index lifecycle endpoints: verify implementation + minor fixes 2026-05-23 15:27:55 -04:00
scripts P8.6: Release mechanics — bump script, release-ready check, PR template, Argo CIs 2026-04-19 09:54:26 -04:00
tests P9.2: Integration test harness with docker-compose 2026-05-23 07:33:34 -04:00
.dockerignore Multi-stage Dockerfile with musl cross-compilation and .dockerignore 2026-04-19 13:47:45 -04:00
.editorconfig Add repo hygiene: LICENSE, CHANGELOG, .gitignore 2026-04-18 20:47:36 -04:00
.gitignore P8: Add optional OpenTelemetry tracing deps, fix subscriber init, clean up .gitignore 2026-04-19 13:24:24 -04:00
.needle-predispatch-sha P2.3 Search read path: scatter-gather + merge + group selection 2026-05-23 14:05:49 -04:00
.proptest P1.6: Verify property tests and benchmarks for router/merger 2026-05-23 13:03:54 -04:00
1 P7.5.a: Request ID middleware + X-Request-Id response header 2026-04-21 08:01:30 -04:00
Cargo.lock P4.1: Rebalancer background worker - verification complete 2026-05-23 08:11:31 -04:00
Cargo.toml P12.OP4: Implement dfs_query_then_fetch for cross-shard comparability 2026-04-19 03:43:10 -04:00
CHANGELOG.md P11.9 v1.0 versioning-commitments policy doc (§12) 2026-05-20 06:41:27 -04:00
clippy.toml Add repo hygiene: LICENSE, CHANGELOG, .gitignore 2026-04-18 20:47:36 -04:00
Dockerfile P8.1: Simplify Dockerfile to scratch-only, update CI to use /workspace/artifacts/ 2026-05-23 07:16:34 -04:00
LICENSE Add repo hygiene: LICENSE, CHANGELOG, .gitignore 2026-04-18 20:47:36 -04:00
miroir.yaml P2.1: Fix session_pinning blocking read and verify acceptance criteria 2026-05-23 12:19:10 -04:00
proptest.toml P1.6: Add proptest.toml for 1024 test cases 2026-05-20 08:07:00 -04:00
README.md P11.7: Add quick-start example artifacts (Docker Compose + config) 2026-05-20 06:50:43 -04:00
rust-toolchain.toml Add repo hygiene: LICENSE, CHANGELOG, .gitignore 2026-04-18 20:47:36 -04:00
rustfmt.toml Add repo hygiene: LICENSE, CHANGELOG, .gitignore 2026-04-18 20:47:36 -04:00

Miroir

Multi-node Index Replication Orchestrator, Integrated Rebalancing

Miroir is a RAID-like orchestration layer for Meilisearch. It stripes a large index across a fleet of small-RAM Meilisearch nodes with a configurable replication factor, fans out search queries across all shards, and rebalances shard assignments when nodes are added or removed — all using the Meilisearch Community Edition.

The Problem

Meilisearch loads its entire index into memory-mapped LMDB files. A large index that exceeds a single server's available RAM cannot run on that server. The Enterprise Edition's native sharding is gated behind a commercial license. Miroir solves this without it.

How It Works

Client
  │
  ▼
Miroir Orchestrator
  ├── Write path: hash(doc_id) → assign to shard → write to R replicas
  ├── Read path:  scatter query to all shards → gather → merge ranked results
  └── Rebalance: on node add/remove → recompute assignments → migrate minimum shards

Meilisearch Nodes (N instances, each holding a subset of shards)
  node-0   node-1   node-2   ...   node-N

Replication Factor

Analogous to software RAID — configurable per deployment:

RF Redundancy Node failures tolerated Capacity
1 None (stripe only) 0 100% of fleet
2 One replica 1 per shard group 50% of fleet
3 Two replicas 2 per shard group 33% of fleet

Key Components

  • Orchestrator — proxy that handles shard routing, scatter-gather, result merging, and topology management
  • Shard router — consistent hash function (Rendezvous/HRW) mapping document IDs to node assignments; minimal reshuffling on topology change
  • Rebalancer — on node add/remove, recomputes assignments and migrates only the shards that changed owners; surviving replicas serve reads during rebuild
  • Result merger — normalizes and merges ranked result sets from multiple shards into a single coherent response

Stability

Miroir is currently in development (v0.x). Starting with v1.0, the project provides backward-compatibility commitments for the Meilisearch API layer, miroir-ctl CLI, config file schema, and Helm chart values.

See docs/versioning-policy.md for the full versioning policy, including what constitutes a breaking change and the deprecation process.

Status

Design phase. See docs/ for architecture detail.

Quick Start

Get Miroir running locally in 5 minutes with Docker Compose:

# Clone the repository
git clone https://github.com/jedarden/miroir.git
cd miroir

# Start the development stack (3 Meilisearch nodes + 1 Miroir orchestrator)
docker compose -f examples/docker-compose-dev.yml up -d

# Verify health
curl http://localhost:7700/health
# Expected: {"status":"available"}

# Index documents (Meilisearch-compatible API)
curl -X POST http://localhost:7700/indexes/movies/documents \
  -H "Authorization: Bearer dev-key" \
  -H "Content-Type: application/json" \
  -d '[{"id": 1, "title": "Inception"}, {"id": 2, "title": "Interstellar"}]'

# Search
curl -X POST http://localhost:7700/indexes/movies/search \
  -H "Authorization: Bearer dev-key" \
  -H "Content-Type: application/json" \
  -d '{"q": "inception"}'

# Teardown (removes containers and volumes)
docker compose -f examples/docker-compose-dev.yml down -v

See examples/README.md for more details on the development stack, configuration options, and troubleshooting.

Production deployment

For production deployments, see the Deployment Sizing Guide to determine orchestrator pod count and task store configuration based on your corpus size and query throughput.

When to use

  • Multi-pod with Redis — Recommended for production. Horizontal scaling with 2+ orchestrator pods delivers fault tolerance (zero-downtime rollouts, pod-loss survival) and scales query throughput via HPA. See Deployment Sizing Guide.

  • Single oversized pod — Supported for dev clusters, very small deployments, or constrained environments. A single pod at 4 vCPU / 8 GB is validated but loses HA benefits (no zero-downtime rollouts, no pod-loss survival). See Single-Pod Mode.

  • Large index sharding — When a single Meilisearch node cannot fit your corpus in RAM, Miroir stripes it across multiple nodes with configurable replication factor.

Additional production resources: