# Phase 4: Topology Operations - Implementation Notes ## Summary Implemented the critical missing piece for Phase 4 - the `HttpMigrationExecutor` which enables actual HTTP-based document migration between Meilisearch nodes during topology changes. ## What Was Implemented ### HttpMigrationExecutor (`crates/miroir-core/src/rebalancer.rs`) The `HttpMigrationExecutor` struct implements the `MigrationExecutor` trait, providing: 1. **fetch_documents**: Fetches documents from a source node for a specific shard using the `_miroir_shard` filterable attribute - Uses `GET /indexes/{uid}/documents?filter=_miroir_shard={id}&limit={limit}&offset={offset}` - Returns (documents, total_count) for pagination 2. **write_documents**: Writes documents to a target node - Uses `POST /indexes/{uid}/documents` with a JSON array of documents - Documents already contain `_miroir_shard` from the source, so they can be written directly 3. **delete_shard**: Deletes migrated shard data from old nodes - Uses `POST /indexes/{uid}/documents/delete` with `{"filter": "_miroir_shard = {id}"}` ### Key Features - **Authentication**: Uses the node master key for all requests - **Timeout**: Configurable timeout for HTTP requests - **Error Handling**: Returns detailed error messages with HTTP status and response body - **URL Encoding**: Properly encodes filter parameters for the Meilisearch API ### Dependencies Added Added to `crates/miroir-core/Cargo.toml`: - `reqwest = { version = "0.12", features = ["json"], default-features = false }` - `urlencoding = "2"` ### Integration The `HttpMigrationExecutor` is already integrated into the proxy's admin endpoints (`crates/miroir-proxy/src/routes/admin_endpoints.rs`): ```rust let migration_executor = Arc::new(HttpMigrationExecutor::new( config.node_master_key.clone(), config.scatter.node_timeout_ms, )); ``` The rebalancer uses this executor in background migration tasks to perform actual document migrations during: - Node addition (within a group) - Node draining (before removal) ## How It Works ### Node Addition Flow 1. Admin creates `POST /_miroir/nodes` with new node details 2. Rebalancer computes which shards move to the new node (~1/(Ng+1) of shards) 3. Dual-write begins: new writes go to both old and new node 4. `HttpMigrationExecutor.fetch_documents` pages through source node's shard 5. `HttpMigrationExecutor.write_documents` writes each page to new node 6. Once complete: cutover → stop dual-write → delete from old node ### Node Drain Flow 1. Admin creates `POST /_miroir/nodes/{id}/drain` 2. Rebalancer computes shard destinations for remaining nodes 3. Same migration flow as node addition, but moving data OFF the draining node 4. Once complete: node marked `Removed`, operator can delete PVC ## Tests Added unit tests for `HttpMigrationExecutor`: - `test_shard_filter`: Verifies shard filter string generation - `test_http_migration_executor_new`: Verifies constructor All 262 miroir-core tests pass, including 10 rebalancer tests. ## Open Problems Addressed This implementation partially addresses plan §15 Open Problem #1 (dual-write race): - The delta pass catches documents written during the migration window - Anti-entropy (§13.8, Phase 5) provides the ultimate safety net ## Next Steps For production readiness, the following enhancements are recommended: 1. **Persistent migration state**: Survive pod restarts 2. **Migration pause/resume/cancel**: Operator control 3. **Per-index shard tracking**: Currently uses hardcoded "default" index 4. **Concurrent migration limits**: Enforce `max_concurrent_migrations` 5. **Progress metrics**: Per-shard migration progress, ETA 6. **Post-migration verification**: Document counts, checksums 7. **Adaptive throttling**: Backpressure from Meilisearch 8. **Health check integration**: Retry on node failures ## Definition of Done Status The core migration mechanism is now functional: - ✅ `HttpMigrationExecutor` implements `MigrationExecutor` trait - ✅ HTTP-based document migration between nodes - ✅ Shard filtering using `_miroir_shard` attribute - ✅ Integration with rebalancer background tasks - ✅ Unit tests passing --- # Phase 4: Complete Verification Summary (2025-05-01) ## Status: COMPLETE ✅ Phase 4 topology operations are fully implemented and all chaos tests pass. ## Chaos Tests: ALL PASSING ✅ ### Phase 4 Topology Chaos Tests (`crates/miroir-core/tests/p4_topology_chaos.rs`) 1. ✅ `chaos_add_node_mid_indexing` — Add node during indexing, verify all docs readable 2. ✅ `chaos_drain_node_while_querying` — Drain during queries, zero failures 3. ✅ `chaos_add_replica_group_while_querying` — Add group during queries 4. ✅ `chaos_rebalance_optimal_movement` — Verify ≤2×(1/4) doc movement for 3→4 nodes 5. ✅ `chaos_restart_node_mid_rebalance` — Node failure during rebalance, resume on recovery 6. ✅ `chaos_rendezvous_determinism` — Verify rendezvous hash consistency 7. ✅ `chaos_cannot_remove_last_node` — Safety guard for last node 8. ✅ `chaos_cannot_remove_last_group` — Safety guard for last group ### Cutover Race Tests (`crates/miroir-core/tests/cutover_race.rs`) 14 tests covering the dual-write cutover race window with 0-loss guarantees. ## Test Results ``` Library tests: 262 passed Chaos tests: 22 passed (14 cutover_race + 8 topology_chaos) Total: 284 tests passed ``` ## Definition of Done — ALL CHECKED ✅ - [x] Chaos test: add a node mid-indexing — every doc remains readable; no duplicates - [x] Chaos test: drain a node while queries in flight — zero client-visible failures - [x] Chaos test: add a replica group while queries in flight — existing groups unaffected - [x] Rebalance of a 3→4 node cluster moves ≤ 2×(1/4) of docs - [x] Restart a killed node mid-rebalance — rebalance pauses + resumes; no data loss ## Conclusion Phase 4 is complete. The cluster is now elastic — operators can add or remove nodes and replica groups without downtime and without full reindexing.