fix: reconciler must repair index in both directions - #122
Conversation
The reconciler only computed `stale = current ∖ fresh` and deindexed those entries. The other direction — `missing = fresh ∖ current` — was silently ignored, so a lost SSE `add_system` event left the reverse index missing that entry permanently. The WebSocket subscription would never include the new system and killmails for it would be silently dropped until restart. Observed in prod: subscribed_systems stayed at a stale value for 5+ hours across multiple reconciler ticks while real kills happened on systems that should have been re-added. Restart was the only recovery. Make reconciliation symmetric — deindex stale AND index missing — so the hourly safety net actually does what its docstring claims. Also reduce the interval from 1 hour to 15 minutes; with two-way repair the worst-case recovery time is now bounded by the interval rather than the next restart. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (1)
WalkthroughReconciler now performs two-way repair: deindexes local-only systems and indexes upstream-only systems. Reconcile interval changed from hourly to every 15 minutes. Malformed upstream responses that normalize to no ids return ChangesBidirectional Map Reconciliation
Sequence Diagram(s)sequenceDiagram
autonumber
participant Reconciler as Reconciler
participant Upstream as UpstreamAPI
participant Local as LocalReverseIndex
participant Registry as Registry
Reconciler->>Upstream: fetch systems for map
Upstream-->>Reconciler: list of system entries
Reconciler->>Reconciler: normalize upstream ids
alt normalized ids empty (malformed)
Reconciler-->>Local: preserve state (skip)
else normalized ids present
Reconciler->>Local: fetch local indexed ids for map
Reconciler->>Reconciler: compute stale_ids, missing_ids
alt stale_ids non-empty
loop per stale_id
Reconciler->>Registry: deindex_system(stale_id)
Registry-->>Reconciler: ok / error
end
end
alt missing_ids non-empty
loop per missing_id
Reconciler->>Registry: index_system(missing_id)
Registry-->>Reconciler: ok / error
end
end
Reconciler-->>Local: record reconciled state/logs
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@lib/wanderer_notifier/map/reconciler.ex`:
- Around line 42-44: The public contract of reconcile_map/2 is out of sync: the
implementation can return {:ok, :skipped_malformed} but the spec/docs only list
:reconciled | :skipped_empty; update the function spec/type and any `@doc/README`
mentions for reconcile_map/2 to include the new return {:ok, :skipped_malformed}
(or alternatively change the implementation to return one of the documented
atoms). Specifically, edit the `@spec` for reconcile_map/2 and its `@doc` examples
to declare returning {:ok, :reconciled} | {:ok, :skipped_empty} | {:ok,
:skipped_malformed} so Dialyzer is satisfied and documentation matches the code.
- Around line 278-286: The function prune_stale_systems/3 now performs two-way
reconciliation (deindexing stale IDs and indexing missing IDs) so rename it to a
name that reflects bidirectional behavior (e.g., reconcile_systems/3 or
sync_system_index/3) and update its private definition (defp prune_stale_systems
-> defp reconcile_systems) and all internal calls/usages in this module
(including the related logic around the block currently at 289-305) to the new
name to keep naming consistent and clear for future readers.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 7cbd065b-e93b-4a7c-8583-138ba70f2be2
📒 Files selected for processing (2)
lib/wanderer_notifier/map/reconciler.extest/wanderer_notifier/map/reconciler_test.exs
…ystems - reconcile_map/2 spec and @doc now declare {:ok, :skipped_malformed} which the implementation already returns; resolves the doc/spec drift surfaced in PR review. - prune_stale_systems/3 -> reconcile_systems/3: the function performs bidirectional repair (deindex stale + index missing), so the old name was misleading. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The reconciler only computed
stale = current ∖ freshand deindexed those entries. The other direction —missing = fresh ∖ current— was silently ignored, so a lost SSEadd_systemevent left the reverse index missing that entry permanently. The WebSocket subscription would never include the new system and killmails for it would be silently dropped until restart.Observed in prod: subscribed_systems stayed at a stale value for 5+ hours across multiple reconciler ticks while real kills happened on systems that should have been re-added. Restart was the only recovery.
Make reconciliation symmetric — deindex stale AND index missing — so the hourly safety net actually does what its docstring claims. Also reduce the interval from 1 hour to 15 minutes; with two-way repair the worst-case recovery time is now bounded by the interval rather than the next restart.
Summary by CodeRabbit
New Features
Bug Fixes
Tests
Documentation
Chores