Prevent shape removal during server restart#4666
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #4666 +/- ##
=======================================
Coverage 60.04% 60.05%
=======================================
Files 397 397
Lines 43766 43766
Branches 12586 12590 +4
=======================================
+ Hits 26281 26282 +1
Misses 17403 17403
+ Partials 82 81 -1
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Claude Code ReviewSummarySince my last review the PR has been narrowed to a single, root-cause fix in What's Working Well
Issues FoundCritical (Must Fix)None. Important (Should Fix)None. Suggestions (Nice to Have)1. (Carried over) The File: The four clauses cover Worth noting this window is genuinely narrow: a clean stack teardown exits the materializer with catch
:exit, reason ->
Logger.debug(fn ->
"Materializer notify call exited (#{inspect(reason)}); " <>
"deferring to monitored :DOWN for #{state.shape_handle}"
end)
:ok
endNon-blocking — Issue ConformanceNo GitHub issue is linked (only the internal Previous Review Status
Review iteration: 2 | 2026-07-16 |
4791606 to
2eb29c5
Compare
Bug 6 surfaced as a 409 (must-refetch) on a healthy subquery shape after a StackSupervisor restart, with a deterministic regression test that fails without the fix: - consumer.ex: notify_materializer_of_new_changes/3 now catches the :exit (:noproc / transient :normal/:shutdown) from its inline materializer call. Previously, when the materializer died mid-call during shutdown the consumer crashed with a non-shutdown reason, routed through handle_writer_termination, and removed the shape from disk — leaving it half-removed (the SLC was already gone) and 409ing after restart. The pending :DOWN now drives a clean stop instead. This is the part of the fix battle-tested in rob/restore-subqueries-bug-5. The earlier speculative ArgumentError fallbacks in shape_status.ex and api.ex turned out not to be needed and have been dropped. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2eb29c5 to
6aef3c7
Compare
|
This PR has been released! 🚀 The following packages include changes from this PR:
Thanks for contributing to Electric! |
Summary
Fixes bug 6 from the restart-aware oracle property-test triage (#4648 /
bugs.md): a healthy subquery shape returning a409 (must-refetch)after aStackSupervisorrestart.A single targeted fix, with a deterministic regression test that fails without the fix and passes with it. This is the part of the fix that was battle-tested in
rob/restore-subqueries-bug-5.Problem
Bug 6 is a shutdown race. During a restart's stack teardown, a dependency consumer's inline
GenServer.callinto its materializer (notify_materializer_of_new_changes/3) races the materializer's death and exits with:noproc. The consumer crashes with a non-shutdown reason, which routes throughhandle_writer_terminationand removes the shape from disk. Because theShapeLogCollectoris already gone mid-shutdown, the removal half-completes — the handle is deleted from SQLite but cleanup fails — so after the restart the handle is missing and a fresh poll on thatoptimized: trueshape gets a 409.Solution
consumer.ex—notify_materializer_of_new_changes/3catches the:exit(:noproc, and transient:normal/:shutdown) from its inline materializer call. The pending monitored:DOWNthen drives a clean stop instead of the abnormal-shutdown path that removes the shape.Test Plan
consumer_test.exs— "dependency consumer survives a :noproc from its materializer without removing the shape". Without the fix: the consumer crashes with{:noproc, …}and logs "Removing shape …".Generated with Claude Code