-
Notifications
You must be signed in to change notification settings - Fork 58
Sync streams #707
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Sync streams #707
Conversation
🦋 Changeset detectedLatest commit: 8046cae The changes in this PR will be included in the next version bump. This PR includes changesets to release 8 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
I still need to review properly, but this part stood out to me:
I wonder if this could break some common use cases? For example, a developer may just want to subscribe to relevant streams when loading a page: async function loadData(id) {
const subscription = db.syncStream('my_stream', {id}).subscribe();
await subscription.waitForFirstSync();
} However, the subscription is garbage collected, which would trigger the implicit unsubscribe logic. Of course, we don't do that, and the user uses a long-running SPA, the subscription could stay alive forever, long after the user navigated to a different page. So it feels like overall for an SPA, the developer will need to keep track of these subscriptions somewhere. I'm just not sure what the best behavior is if they don't - both unsubscribing automatically and not doing that has potential for issues. Maybe instead just logging a warning when a subscription is garbage collected without an explicit unsubscribe? We'll also need some guides on how to handle this with some common frameworks. |
4f4cecb
to
de04c27
Compare
That sounds good to me, I've changed the finalizer to a warning.
I assume that at least in React, we'd have a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm quite happy with the changes from my side. I could not spot any concerns or potential issues. This looks good to me :)
54cdd06
to
4f4678d
Compare
82d316d
to
1818220
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very happy with this from my side 😄
This ports sync streams (Dart PR: powersync-ja/powersync.dart#317) to the JavaScript SDK.
Subscriptions are requested by calling
db.syncStream('stream_name', params).subscribe()
. Thesubscribe()
call returns a subscription handle which can be used tounsubscribe()
the stream. As an additional measure to ensure streams don't leak, we also add a finalization registry on streams to callunsubscribe()
implicitly.When a stream is subscribed to while offline, we don't really have to do much in the SDK. Most of the logic is implemented in the core extension, which will remember the subscription (so that, if we're connecting later but within the TTL, the stream is included even if the subscription no longer exists).
Because all streams (even those that have never been resolved - relevant for introspection) are included in the sync status, we need to update the sync status for new subscriptions even while offline. The new
powersync_offline_sync_status()
funciton can be used for that, it also replaces the query onps_sync_state
used during initialization.To report the current status of stream subscriptions, we can simply take them from the JSON provided by the core extension.
While connected, the core extension needs to know all streams that have a subscription active. This information is provided in two ways:
connect()
is called, we provide a snapshot of all streams that are currently active.updateSubscriptions()
with the new set of stream subscriptions. The core extension is informed about this change, which has two effects:Within a
PowerSyncDatabase
instance, it is possible to subscribe to the same stream / params multiple times. The subscriptions we pass to the core extension is a de-duplicated set of all active subscription instances (managed with a refcount).With a shared sync worker, the same concept applies across different tabs as well: Each tabs sends its de-duplicated set of active subscriptions to the worker, which then applies its own de-duplication logic before notifying the core extension.
The shared worker also keeps track of which tabs are owning which subcriptions. That allows it to update the set when a tab is closing (in case some subscriptions were only active in a single tab).
TODO:
I will add hooks for subscriptions in a follow-up PR.