Skip to content

fix: Carry over last change to next fragment to fix max_batch_size boundary bug#3764

Draft
alco wants to merge 6 commits intomainfrom
alco/fix-max-change-batch-size-edge-case
Draft

fix: Carry over last change to next fragment to fix max_batch_size boundary bug#3764
alco wants to merge 6 commits intomainfrom
alco/fix-max-change-batch-size-edge-case

Conversation

@alco
Copy link
Member

@alco alco commented Jan 23, 2026

Summary

Fix #3726.

Fixes the bug where transactions with a number of changes that is a multiple of max_batch_size (default: 100) had their commit fragment silently dropped by ShapeLogCollector.

Root Cause

When a transaction has exactly N * max_batch_size changes (e.g. 100, 200, 300, ...):

  1. The last change triggers maybe_flush/1, returning a fragment with last_log_offset = (final_lsn, X)
  2. maybe_flush creates a new empty fragment but did NOT reset state.last_log_offset
  3. When the Commit arrives, the final fragment gets the same last_log_offset = (final_lsn, X)
  4. ShapeLogCollector.handle_txn_fragment/2 drops it as a duplicate (offset <= last_processed_offset)

A secondary issue: when a fragment is flushed at exactly max_batch_size, the last change in that fragment is not marked with last?=true, which is incorrect since it should only be set on the very last change of the entire transaction.

Solution

When flushing a fragment at the max_batch_size boundary, retain the last change from the flushed fragment and carry it over to the next fragment. This guarantees that even if the next replication message is a Commit, the resulting fragment will have at least one change with a log_offset greater than the previous fragment's last_log_offset.

This approach also fixes the last? marking issue since the carried-over change will be correctly annotated when its fragment is finalized.

@coderabbitai
Copy link

coderabbitai bot commented Jan 23, 2026

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.


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

@netlify
Copy link

netlify bot commented Jan 23, 2026

Deploy Preview for electric-next ready!

Name Link
🔨 Latest commit 8ad626f
🔍 Latest deploy log https://app.netlify.com/projects/electric-next/deploys/69868fbabd81bb000825cbdd
😎 Deploy Preview https://deploy-preview-3764--electric-next.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@codecov
Copy link

codecov bot commented Jan 23, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 87.36%. Comparing base (586bd4c) to head (8ad626f).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #3764   +/-   ##
=======================================
  Coverage   87.36%   87.36%           
=======================================
  Files          23       23           
  Lines        2050     2050           
  Branches      543      542    -1     
=======================================
  Hits         1791     1791           
  Misses        257      257           
  Partials        2        2           
Flag Coverage Δ
packages/experimental 87.73% <ø> (ø)
packages/react-hooks 86.48% <ø> (ø)
packages/start 82.83% <ø> (ø)
packages/typescript-client 93.29% <ø> (ø)
packages/y-electric 56.05% <ø> (ø)
typescript 87.36% <ø> (ø)
unit-tests 87.36% <ø> (ø)

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.

@blacksmith-sh
Copy link
Contributor

blacksmith-sh bot commented Jan 23, 2026

Found 2 test failures on Blacksmith runners:

Failures

Test View Logs
at line 92:70 in shell electric/at line 92:70 in shell electric View Logs
at line 93 in shell electric/at line 93 in shell electric View Logs

Fix in Cursor

alco added 6 commits February 7, 2026 01:23
… one change

When a transaction has exactly max_batch_size changes, the commit
fragment was getting the same last_log_offset as the preceding data
fragment. This caused ShapeLogCollector to drop it as a duplicate. The
other undesirable side effect of allowing an empty fragment with commit
is that the last change from the previous fragment **is not** marked
with last?=true.

This commit fixes both problems by retaining the last change from the
current fragment and holding it until the next fragment. In this way,
even if the next converted message is Commit, the transaction fragment
created from it will have one change and its log offset will be greater
than previous fragment's last_log_offset.
Add end-to-end tests verifying that transactions with exactly
max_batch_size (100) changes are fully received by clients.

These tests exercise the full pipeline (ReplicationClient ->
ShapeLogCollector -> HTTP API -> Client) and confirm the fix
for the bug where commit-only fragments at exact batch boundaries
were silently dropped due to duplicate offsets.
Add ELECTRIC_EXPERIMENTAL_MAX_BATCH_SIZE env var to allow configuring
the maximum number of changes buffered before flushing a transaction
fragment. This is primarily useful for testing batch boundary behavior.

Default remains 100 to match existing behavior.
Add a new integration test that validates transactions following a
batch-boundary transaction are not incorrectly skipped by
ShapeLogCollector.
@alco alco force-pushed the alco/fix-max-change-batch-size-edge-case branch from 25e45e0 to 8ad626f Compare February 7, 2026 01:04
@alco alco changed the title fix: Use end_lsn for commit fragment offset to fix max_batch_size boundary bug fix: Carry over last change to next fragment to fix max_batch_size boundary bug Feb 7, 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.

Batch INSERT Replication Fails for Sizes Divisible by 100

1 participant