Skip to content

Conversation

@robacourt
Copy link
Contributor

@robacourt robacourt commented Dec 2, 2025

Fix for #3414

This PR reduces the memory footprint of a consumer by only sending it changes that affect it. It does this by sending TransactionFragments to the Consumer, which also lays the groundwork to reduce the memory footprint even further (e.g. #3415 )

Performance is comparable to current (but there's a fair amount of variation in the benchmarks as shown by these two different runs!)
Screenshot 2025-12-04 at 13 06 11
Screenshot 2025-12-04 at 13 21 16

@codecov
Copy link

codecov bot commented Dec 2, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 75.36%. Comparing base (96cacdc) to head (14725fb).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3541      +/-   ##
==========================================
+ Coverage   75.21%   75.36%   +0.14%     
==========================================
  Files          51       51              
  Lines        2744     2744              
  Branches      409      405       -4     
==========================================
+ Hits         2064     2068       +4     
+ Misses        678      674       -4     
  Partials        2        2              
Flag Coverage Δ
electric-telemetry 22.71% <ø> (ø)
elixir 57.74% <ø> (+0.35%) ⬆️
elixir-client 74.47% <ø> (+0.52%) ⬆️
packages/experimental 87.73% <ø> (ø)
packages/react-hooks 86.48% <ø> (ø)
packages/typescript-client 93.08% <ø> (ø)
packages/y-electric 55.12% <ø> (ø)
typescript 87.46% <ø> (ø)
unit-tests 75.36% <ø> (+0.14%) ⬆️

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.

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.

Very nice work! Would love to see benchmarks as well!

Received TransactionFragment that has already been partially processed.
This scenario is not currently supported. It could occur if the
batch size was changed while restarting the replication client.
Copy link
Contributor

Choose a reason for hiding this comment

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

If the consumer (I suppose in the TransactionBuilder) simply discards parts of the transaction fragment it receives that it has already seen based on the changes' log offsets, wouldn't this not be a problem anymore?

A little while back I had modified the consumer to idempotently handle transactions so the collector can safely replay them and know that consumers can discard them - shouldn't consumers also idempotently handle transaction fragments regardless of how they are batched?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, this PR was dragging on so this was one of the things I deferred.

defp add_changes(%{transaction: txn} = state, %TransactionFragment{} = fragment) do
txn = %{
txn
| changes: Enum.reverse(fragment.changes) ++ txn.changes,
Copy link
Contributor

Choose a reason for hiding this comment

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

adding from above - couldn't we keep track of the last collected change log offset and only append changes that are larger than that to make the transaction building resilient to transactions being fragmented in different ways?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's a bit late to do it here, you need to guard against it at the beginning of ShapeLogCollector so that the EventRouter doesn't get messed up, and at the beginning of Consumer (potentially in the builder but the idea is we remove the builder wherever possible so it would be better to be decoupled)

@robacourt robacourt force-pushed the rob/ops-to-consumers branch from 3a7cff2 to 15b6b00 Compare December 3, 2025 17:49
@robacourt
Copy link
Contributor Author

benchmark this

@robacourt robacourt marked this pull request as ready for review December 4, 2025 13:13
@robacourt robacourt force-pushed the rob/ops-to-consumers branch from f19600e to 007acb3 Compare December 4, 2025 13:23
@robacourt robacourt merged commit fb24539 into main Dec 4, 2025
47 checks passed
@robacourt robacourt deleted the rob/ops-to-consumers branch December 4, 2025 14:10
@github-actions
Copy link
Contributor

github-actions bot commented Dec 4, 2025

Benchmark results, triggered for fe7c3

  • write fanout completed

write fanout results

  • unrelated shapes one client latency completed

unrelated shapes one client latency results

  • diverse shape fanout completed

diverse shape fanout results

  • many shapes one client latency completed

many shapes one client latency results

  • concurrent shape creation completed

concurrent shape creation results

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