-
Notifications
You must be signed in to change notification settings - Fork 55
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
base: main
Are you sure you want to change the base?
Sync streams #707
Conversation
|
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 :)
|
||
// Request a random lock until this client is disposed. The name of the lock is sent to the shared worker, which | ||
// will also attempt to acquire it. Since the lock is returned when the tab is closed, this allows the share worker | ||
// to free resources associated with this tab. |
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.
This is very nice
54cdd06
to
4f4678d
Compare
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.