Skip to content

Conversation

@magnetised
Copy link
Contributor

@magnetised magnetised commented Jan 7, 2026

Improves memory usage and actually greatly simplifies the ShapeStatus code as it removes the need for a "restore" path at all, let alone 2 distinct versions based on clean vs unclean shutdown.

The only metadata left in RAM is the LRU information, which is just {handle, timestamp}.

The access is via two NimblePool instances, one for reads which has a "worker" per core and a single write worker which guarantees serialized writes.

Performance locally seems reasonable, though it will be slower since - even if the data is in sqlite's page cache - we're now loading shape data from disk and deserializing using :erlang.binary_to_term/1.

My (hopefully) only controversial move is to add snapshot status information to the shape data so that we can initialize the system and remove any shapes that were left in a half-baked state without querying the storage backend.

I think this is cleaner that the current system, which AFAICT has issues if the system is terminated before a snapshot is started (since the snapshotting requires an active consumer and consumers are now lazily started, meaning that await_snapshot_start/2 will crash or hang).

The implications of this are:

  • shape creation is going to be a bit slower. the benchmarks below show that.
  • startup is a bit slower, even with 1 shape. So something like a 50ms base init time with 1 shape.
  • iterating all defined shapes is going to be slower. Locally with 150,000 shapes I'm seeing ~4 seconds to restore publication filters and ~8s to re-build the filters. This slowdown I think is purely the cost of deserialising the data from sqlite and probably the more complex memory path of disk->sqlite->nif->erlang.
  • shutdown seems more consistently quick - not having to dump the ets tables to disk may have been the problem there

I've let claude review and addressed its comments, it's conclusion:

This is a well-structured change that simplifies the codebase significantly. The removal of the separate backup/restore paths and ETS mirror tables reduces complexity. The SQLite implementation follows best practices for connection pooling and threading.

@netlify
Copy link

netlify bot commented Jan 7, 2026

Deploy Preview for electric-next ready!

Name Link
🔨 Latest commit 3653942
🔍 Latest deploy log https://app.netlify.com/projects/electric-next/deploys/695f9d5ec8e46b0008730611
😎 Deploy Preview https://deploy-preview-3682--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 7, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 87.93%. Comparing base (b11b8ea) to head (6566a32).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #3682   +/-   ##
=======================================
  Coverage   87.93%   87.93%           
=======================================
  Files          23       23           
  Lines        1898     1898           
  Branches      499      500    +1     
=======================================
  Hits         1669     1669           
  Misses        227      227           
  Partials        2        2           
Flag Coverage Δ
packages/experimental 87.73% <ø> (ø)
packages/react-hooks 86.48% <ø> (ø)
packages/start 89.58% <ø> (ø)
packages/typescript-client 93.35% <ø> (ø)
packages/y-electric 56.05% <ø> (ø)
typescript 87.93% <ø> (ø)
unit-tests 87.93% <ø> (ø)

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.

@magnetised magnetised force-pushed the magnetised/3514-sqlite branch from 7226bf9 to 891453c Compare January 8, 2026 11:29
@magnetised
Copy link
Contributor Author

benchmark this

@blacksmith-sh

This comment has been minimized.

@github-actions
Copy link
Contributor

github-actions bot commented Jan 8, 2026

Benchmark results, triggered for 89145

  • write fanout completed

write fanout results

  • unrelated shapes one client latency completed

unrelated shapes one client latency results

  • many shapes one client latency completed

many shapes one client latency results

  • concurrent shape creation completed

concurrent shape creation results

  • diverse shape fanout completed

diverse shape fanout results

@magnetised magnetised force-pushed the magnetised/3514-sqlite branch from 891453c to 3653942 Compare January 8, 2026 12:04
@blacksmith-sh

This comment has been minimized.

@magnetised magnetised force-pushed the magnetised/3514-sqlite branch 2 times, most recently from 7ecf14d to 3fa21d8 Compare January 8, 2026 12:36
@magnetised magnetised marked this pull request as ready for review January 8, 2026 12:38
@blacksmith-sh

This comment has been minimized.

Replace enormous ets tables holding shape lookup information with a
single SQLite database.

This massively reduces memory usage and removes the need to iterate the
storage backend for shape information in the case of an unclean
shutdown.
@magnetised magnetised force-pushed the magnetised/3514-sqlite branch from 3fa21d8 to 6566a32 Compare January 8, 2026 12:42
SELECT handle FROM shapes WHERE comparable = ?1 LIMIT 1
""",
shape_lookup: """
SELECT shape FROM shapes WHERE handle = ?1 LIMIT 1
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we not make handle unique? Otherwise this returns an arbitrary shape. It would also mean you could get rid of the LIMIT 1. Currently we're only unique on the composite (comparable, handle)

SELECT 1 FROM shapes WHERE handle = ?1
""",
handle_lookup: """
SELECT handle FROM shapes WHERE comparable = ?1 LIMIT 1
Copy link
Contributor

Choose a reason for hiding this comment

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

Same for comparable. This should be unique too

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