Skip to content

Conversation

@robacourt
Copy link
Contributor

@robacourt robacourt commented Jan 21, 2026

Summary

  • Fix a bug where the RelationTracker didn't notify the Configurator after restart, causing subsequent shape removals to not update the publication
  • Fix the fundamentally broken "handles relation tracker restart" test. You can see it flaking here

Problem

Production bug: When the RelationTracker restarts while the Configurator is still running (:one_for_one supervision), the restored filters were not being synced to the Configurator. This caused the internal state to become inconsistent:

  • prepared_relation_filters was restored from ShapeStatus
  • submitted_relation_filters remained empty (from init)

After a shape removal, both would be empty, so update_needed? returned false and no publication update was triggered. The system would eventually self-heal via the 60-second periodic refresh, but this is too slow for correct behavior.

Test bug: The original test expected assert_raise RuntimeError but the actual behavior when a GenServer dies during a call is an EXIT, not a RuntimeError. The async task wasn't being awaited, so the assertion failure was being silently ignored - the test was "passing" based on other assertions.

Solution

  1. Added update_publication_if_necessary(state) after restoring filters in handle_continue(:restore_relations, ...) to immediately sync with the Configurator.

  2. Rewrote the test to correctly verify the restart-and-recovery scenario without the flawed async/RuntimeError expectations.

Test plan

  • Verified the production fix is needed: without it, the test fails (publication not updated within 2s timeout)
  • Ran the test 5+ times with different seeds - all pass
  • Ran full publication_manager_test.exs - all 22 tests pass

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Fixed an issue where filter state became inconsistent when internal services restarted while running. This prevented data shape removals from properly updating the database publication. The system now correctly restores and communicates filter information during restart, ensuring consistent data synchronization behavior.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Jan 21, 2026

📝 Walkthrough

Walkthrough

RelationTracker now triggers an immediate publication update after rebuilding/restoring filters on restart, so the PublicationManager (and thus the Configurator) is explicitly notified of restored filters instead of relying on the Configurator to pull them later.

Changes

Cohort / File(s) Summary
Changeset Documentation
.changeset/fix-relation-tracker-restart.md
Adds patch-level changeset documenting the RelationTracker restart notification fix for @core/sync-service
RelationTracker Implementation
packages/sync-service/lib/electric/replication/publication_manager/relation_tracker.ex
After restoring initial publication filters, calls update_publication_if_necessary(state) instead of returning unchanged state to trigger publication/configurator notification
Publication Manager Tests
packages/sync-service/test/electric/replication/publication_manager_test.exs
Adjusts restart test: use ShapeStatus restoration, remove initial notify_alter_queries() call, stop RelationTracker directly to trigger supervisor restart, and update assertions/timeouts to verify restoration and subsequent removal

Sequence Diagram(s)

sequenceDiagram
    participant RT as RelationTracker
    participant PUB as PublicationManager
    participant CFG as Configurator

    rect rgba(200,150,255,0.5)
    Note over RT,CFG: RelationTracker restart with Configurator running
    end

    RT->>RT: Restart
    RT->>PUB: Rebuild initial filters
    RT->>PUB: restore_relations()
    RT->>PUB: update_publication_if_necessary()
    PUB->>CFG: Notify restored filters
    CFG->>PUB: Acknowledge / apply updates
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • magnetised
  • icehaunter

Poem

🐰 I hopped through filters, tidy and spry,

Restarted my tracker and gave a loud cry,
"Filters restored!" I told Publication true,
Configurator listened and fixed up the view,
Now the sync hums steady — a carrot for you! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main fix: RelationTracker not syncing with Configurator after restart. It clearly reflects the primary change across all modified files (sync-service, relation_tracker, and tests).

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dd73c4b and b602ce8.

📒 Files selected for processing (3)
  • .changeset/fix-relation-tracker-restart.md
  • packages/sync-service/lib/electric/replication/publication_manager/relation_tracker.ex
  • packages/sync-service/test/electric/replication/publication_manager_test.exs
🚧 Files skipped from review as they are similar to previous changes (1)
  • .changeset/fix-relation-tracker-restart.md
🧰 Additional context used
🧬 Code graph analysis (1)
packages/sync-service/test/electric/replication/publication_manager_test.exs (3)
packages/sync-service/lib/electric/replication/publication_manager/relation_tracker.ex (2)
  • add_shape (62-69)
  • name (57-59)
packages/sync-service/lib/electric/shape_cache/shape_status/shape_db/query.ex (1)
  • add_shape (106-137)
packages/sync-service/test/support/component_setup.ex (1)
  • add_shape (43-48)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: ensure_sync_service_image / build_image
🔇 Additional comments (2)
packages/sync-service/lib/electric/replication/publication_manager/relation_tracker.ex (1)

183-188: LGTM! This correctly fixes the synchronization gap after restart.

The fix addresses the root cause: after restart, prepared_relation_filters gets populated from ShapeStatus while submitted_relation_filters remains empty (from init). Calling update_publication_if_necessary ensures the Configurator is notified immediately rather than waiting for the 60-second periodic refresh.

packages/sync-service/test/electric/replication/publication_manager_test.exs (1)

503-524: LGTM! Test correctly validates the restart-and-recovery behavior.

The rewrite properly:

  1. Establishes the restoration precondition by adding to ShapeStatus first
  2. Uses deterministic GenServer.stop instead of timing-dependent assertions
  3. Validates that the production fix works (publication still has the relation after restart)
  4. Confirms removal still functions correctly post-restart

The 2000ms timeout is reasonable given the restart → restore → Configurator sync sequence.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link

codecov bot commented Jan 21, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 87.36%. Comparing base (fa82e33) to head (b602ce8).
⚠️ Report is 2 commits behind head on main.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3753      +/-   ##
==========================================
+ Coverage   84.01%   87.36%   +3.35%     
==========================================
  Files          44       23      -21     
  Lines        2834     2011     -823     
  Branches      528      529       +1     
==========================================
- Hits         2381     1757     -624     
+ Misses        451      252     -199     
  Partials        2        2              
Flag Coverage Δ
elixir ?
elixir-client ?
packages/experimental 87.73% <ø> (ø)
packages/react-hooks 86.48% <ø> (ø)
packages/start 82.83% <ø> (ø)
packages/typescript-client 93.47% <ø> (ø)
packages/y-electric 56.05% <ø> (ø)
typescript 87.36% <ø> (ø)
unit-tests 87.36% <ø> (+3.35%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@robacourt robacourt marked this pull request as draft January 21, 2026 14:01
…after restart

When the RelationTracker restarts while the Configurator is still running,
it now properly notifies the Configurator of the restored filters. Previously,
after a RelationTracker restart, subsequent shape removals would not update
the publication because the internal filter state was inconsistent:
- prepared_relation_filters was restored from ShapeStatus
- submitted_relation_filters remained empty
- update_needed? returned false, skipping publication update

The system would eventually self-heal via the 60-second periodic refresh,
but this fix makes it immediate.

Also fixed the "handles relation tracker restart" test which was fundamentally
broken - it expected a RuntimeError but GenServer.call to a stopped server
produces an EXIT. The async task wasn't being awaited, so the assertion
failure was being silently ignored.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@robacourt robacourt marked this pull request as ready for review January 21, 2026 14:28
@robacourt robacourt requested a review from msfstef January 21, 2026 14:37
Copy link
Contributor

@msfstef msfstef left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense!

@robacourt robacourt self-assigned this Jan 22, 2026
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.

3 participants