Skip to content

Commit 2262fc6

Browse files
robacourtclaude
andcommitted
Drop redundant materializer replay-skip guard
The materializer had a `new_changes` clause that skipped ranges already applied (<= applied_offset), guarding against the source consumer re-notifying it with transactions the persistent slot replays after a restart. That dedup now happens at the source: the consumer skips already-applied transactions (at/below its restored latest_offset) before `apply_event`, so it never re-notifies the materializer for them — the guard is unreachable. Remove it and rely on the single source-level dedup. Confirmed by the optimized_refetch restart repro (which previously triggered the materializer's "Key already exists" crash) staying green across repeated runs, plus the materializer/consumer/subquery suites and the other restore tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 24cdcdc commit 2262fc6

2 files changed

Lines changed: 4 additions & 18 deletions

File tree

packages/sync-service/lib/electric/shapes/consumer/materializer.ex

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -493,20 +493,6 @@ defmodule Electric.Shapes.Consumer.Materializer do
493493
{:reply, :ok, state}
494494
end
495495

496-
def handle_call(
497-
{:new_changes, {_range_start, range_end}, _xid, _commit?},
498-
_from,
499-
%{applied_offset: applied_offset} = state
500-
)
501-
when is_log_offset_lte(range_end, applied_offset) do
502-
# This range has already been applied — either during the startup history
503-
# replay (`read_history_up_to_subscribed`) or a previous `new_changes` call.
504-
# This happens on restart when the persistent replication slot re-delivers
505-
# already-persisted transactions. Re-applying them would raise
506-
# "Key already exists" in `apply_changes/2`, so skip the range entirely.
507-
{:reply, :ok, state}
508-
end
509-
510496
def handle_call({:new_changes, {range_start, range_end}, xid, commit?}, _from, state) do
511497
stack_storage = Storage.for_stack(state.stack_id)
512498
storage = Storage.for_shape(state.shape_handle, stack_storage)

packages/sync-service/test/integration/oracle_restore_test.exs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -138,10 +138,10 @@ defmodule Electric.Integration.OracleRestoreTest do
138138
test "optimized subquery shapes stay consistent when the slot replays a backlog after restart",
139139
ctx do
140140
# Two `optimized: true` subquery shapes over the same `projects` source. After
141-
# the restart the persistent slot re-delivers batch_1's already-applied changes
142-
# to the subquery materializer; the materializer must ignore ranges it already
143-
# applied during its startup history replay so the shapes stay consistent and
144-
# the polling client is not sent a 409 must-refetch.
141+
# the restart the persistent slot replays batch_1's already-applied
142+
# transactions; the source consumer skips those (at/below its restored
143+
# `latest_offset`) before re-notifying the subquery materializer, so the shapes
144+
# stay consistent and the polling client is not sent a 409 must-refetch.
145145
shapes = [
146146
%{
147147
name: "issues_of_active_projects",

0 commit comments

Comments
 (0)