Skip to content

Comments

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

Merged
alco merged 9 commits intomainfrom
alco/fix-max-change-batch-size-edge-case
Feb 9, 2026
Merged

fix: Carry over last change to next fragment to fix max_batch_size boundary bug#3764
alco merged 9 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 does 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 include it in 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.41%. Comparing base (586bd4c) to head (107d616).
⚠️ Report is 3 commits behind head on main.
✅ All tests successful. No failed tests found.

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

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

This comment has been minimized.

alco added 5 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.
@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
@alco alco self-assigned this Feb 9, 2026
@alco alco force-pushed the alco/fix-max-change-batch-size-edge-case branch from 8ad626f to 4ceb616 Compare February 9, 2026 13:30
@alco alco marked this pull request as ready for review February 9, 2026 14:04
Copy link
Contributor

@robacourt robacourt left a comment

Choose a reason for hiding this comment

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

Nice solution. Great work!

@alco alco merged commit f9b25d6 into main Feb 9, 2026
46 checks passed
@alco alco deleted the alco/fix-max-change-batch-size-edge-case branch February 9, 2026 19:34
@github-actions
Copy link
Contributor

This PR has been released! 🚀

The following packages include changes from this PR:

  • @core/sync-service@1.4.3

Thanks for contributing to Electric!

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

2 participants