We’re trying something new: inspired by the ink slingers at Fly.io, we’re experimenting with mostly text-based product newsletters. If you can get through this issue before your morning coffee hits the halfway mark, we’re doing it right.
Here’s what we’ve been up to:
Product updates
- Native Rust sync implementation: We recently started moving sync logic from client SDKs to our core SQLite extension, built in Rust: %%powersync-sqlite-core%%. In this setup (currently opt-in), client SDKs still handle network connections and streaming, but the SQLite extension now manages sync state and processing. The result is significantly improved performance. More about this in the feature story section below, or see the release announcement.
- We shipped multiple improvements to the PowerSync Service, the server-side component of the PowerSync system:
- Resumable initial replication: When Sync Rules are updated, we perform initial replication from the source database using the new rules. This starts with a snapshot, followed by incremental changes. If the snapshot replication is interrupted, it previously had to restart from scratch. Now, it resumes from where it left off, resulting in better efficiency and reliability. Sources currently supported: Postgres and MongoDB.
- Postgres RLS checks: For Postgres, RLS is common and recommended on Supabase. Originally, Supabase only supported logical replication on the built-in %%postgres%% role. They’ve since added support for custom roles with the %%REPLICATION%% privilege. The problem was that switching to a custom role would silently break access to tables when RLS is in use: no error is shown, just empty results. PowerSync now checks whether RLS is enabled for every replicated table, and then logs a warning if you’re not using either a superuser or %%BYPASSRLS%%.
- Replication lag monitoring: It’s important to know if replicating changes from the source database to PowerSync becomes backlogged. This can happen for example if the rate of changes in the source database is very high. We now record the replication lag as a metric and output it in the logs, and it’s soon coming to the PowerSync Dashboard too.
- MySQL binlog backpressure handling: For MySQL, large bursts of binlog events could lead to out-of-memory errors due to unbounded size of the processing queue. We now cap queue memory use by pausing the binlog listener once a limit is hit, resuming after the queue drains or a certain amount of time has passed.
- .NET MAUI support: We’ve added a PowerSync.MAUI package (and demo app) to extend our .NET SDK to iOS, Android, and Windows via .NET MAUI. It builds on %%PowerSync.Common%% and ensures the right platform-specific extension is loaded at runtime.
Inside Syncball
(apologies to the folks at Fly.io for this one)
- We’re hiring! In addition to other roles open on our Join Us page, we’ve recently started looking for a Design Lead that comes with a wide range of responsibilities and impact.
- We’ve been impressed by this month’s community contributions: shoutout to David Martos, fethij and Yahya Jirari!
- We’ve been writing developer documentation for over a decade but only just documented our journey (Readme.io → custom framework → GitBook → Mintlify).
- Simon Binder (a.k.a. the Drift guy) was on stage at Fluttercon USA, talking about how Dart’s upcoming code assets functionality is a major improvement for building cross-platform FFI packages.
Feature story: Moving sync logic to our core Rust-based SQLite extension
We used to handle a significant amount of sync logic (processing and state management) in our client SDKs. It worked, but the performance was not as good as it could be, and it also meant that every new feature had to be re-implemented N times, once for each SDK platform.
So we started wondering, what if our native SQLite extension just did most of the work? We figured it would probably improve performance and reduce duplication of code. We’d been kicking the idea around for a while, but things got real when a customer syncing 100k+ rows started seeing brutal UI lockups on React Native. No amount of fine-tuning could fix it. That pushed us over the edge.
So we rewrote the sync client inside the core extension. Performance jumped way beyond expectations, and feature parity got a whole lot simpler.
Here’s what initial data shows:
- React Native: ~35% faster sync, with significantly better UI responsiveness
- Kotlin (1M rows): ~25% faster sync
We invite all users to try it out. See our release notes for instructions.
That’s it for this issue. Did you like it? Do you still have half a cup of coffee on your mug warmer? Let us know what you think.