Skip to content

feat: LSN tracker persistence through replication slot confirmed flush position - #3462

Merged
msfstef merged 11 commits into
mainfrom
msfstef/lsn-tracking-persistence
Nov 19, 2025
Merged

msfstef merged 11 commits into
mainfrom
msfstef/lsn-tracking-persistence

Conversation

@msfstef

@msfstef msfstef commented Nov 19, 2025

Copy link
Copy Markdown
Contributor

Closes #3448

This is part of a larger piece of work described in #3442

The initial idea was to have a separate, periodic persister of the last processed LSN, but as I was working through the code I realised that we might not even need that.

Since now consumers are idempotent in the transactions they handle (see #3452), even the our global last processed LSN is slightly behind any given shape that is ok.

Instead of persisting it, we actually recover it from the replication slot's confirmed flush LSN.

  • If the slot is new, we reset the last processed LSN to the new slot's LSN since all shapes will be invalidated
  • If the slot is an existing one, we initialise it to the confirmed flush LSN
    • If the reconnection to the slot happens at runtime (e.g. because of network partition), we preserve the current last processed LSN
    • If the reconnection happens at startup, we initialise the LSN from the slot. This value might be slightly behind if shapes processed transactions that they did not manage to acknowledge back, but we can tolerate this.

This way, shape recovery does not have to read all shapes' last processed LSN during startup.

If we need stronger guarantees of the global processed lsn being the max, we can also attempt to set it everytime we lazily recover an existing shape (if the shape's max lsn is larger than the current max processed lsn). Happy to include this in this PR or a next one.

@msfstef
msfstef requested review from alco and magnetised November 19, 2025 09:16
@pkg-pr-new

pkg-pr-new Bot commented Nov 19, 2025 •

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@electric-sql/react@3462
npm i https://pkg.pr.new/@electric-sql/client@3462
npm i https://pkg.pr.new/@electric-sql/y-electric@3462

commit: 83a6984

@codecov

codecov Bot commented Nov 19, 2025 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 79.31034% with 6 lines in your changes missing coverage. Please review.
✅ Project coverage is 67.48%. Comparing base (c7adffa) to head (83a6984).
⚠️ Report is 10 commits behind head on main.

Files with missing lines Patch % Lines
packages/sync-service/lib/electric/shape_cache.ex 0.00% 4 Missing ⚠️
...ic/postgres/replication_client/connection_setup.ex 92.85% 1 Missing ⚠️
...ce/lib/electric/replication/shape_log_collector.ex 0.00% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##             main    #3462       +/-   ##
===========================================
- Coverage   87.73%   67.48%   -20.25%     
===========================================
  Files           3      182      +179     
  Lines         163     9689     +9526     
  Branches       39      360      +321     
===========================================
+ Hits          143     6539     +6396     
- Misses         19     3148     +3129     
- Partials        1        2        +1     
Flag Coverage Δ
elixir 64.07% <79.31%> (?)
elixir-client 73.94% <ø> (?)
packages/experimental 87.73% <ø> (ø)
packages/react-hooks 86.48% <ø> (?)
packages/typescript-client 93.57% <ø> (?)
packages/y-electric 55.12% <ø> (?)
postgres-140000 62.94% <79.31%> (?)
postgres-150000 62.93% <79.31%> (?)
postgres-170000 62.84% <79.31%> (?)
postgres-180000 62.84% <79.31%> (?)
sync-service 63.08% <79.31%> (?)
typescript 87.06% <ø> (-0.67%) ⬇️
unit-tests 67.48% <79.31%> (-20.25%) ⬇️

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.

@magnetised magnetised left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Love this. Great idea. Just think we should get rid of a lot of the stuff in shape cache - it's slightly performative now.

Comment thread packages/sync-service/lib/electric/shape_cache.ex Outdated
Comment thread packages/sync-service/lib/electric/shape_cache.ex Outdated
Comment thread packages/sync-service/test/electric/replication/shape_log_collector_test.exs Outdated

@alco alco left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

👏

query = """
SELECT confirmed_flush_lsn
FROM pg_replication_slots
WHERE slot_name = #{Utils.quote_string(slot_name)}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: just as a double-checking mechanism, we could add AND NOT active to the where clause here to make sure we're not reading LSN from a slot that's used by another consumer. We already ensure exclusive access with an advisory lock but I just feel like between any two independent queries unexpected stuff may happen...

So if the slot is active, we'd get back 0 rows and that should be treated as a crash of the replication client.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Or maybe I'm overthinking too much and crashing the client here would open up a way for another Electric to steal the thunder.... I don't know.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I think that we should rely on the advisory lock since we do so in various places - if we're querying this it means that we fully intend on using this replication slot as well. If it ends up being already active then the replication client will fail when it attempts to start streaming anyway

@msfstef
msfstef merged commit ff45de1 into main Nov 19, 2025
47 checks passed
@msfstef
msfstef deleted the msfstef/lsn-tracking-persistence branch November 19, 2025 13:45
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.

Separate responsibility of global LSN tracking

3 participants