ObserverRegistrations keys registrations by webview label
(src/subscriptions.rs:257), so two independent frontend modules in the same
window that each call observe() collapse into a single registration. The first
one to call unobserve() hits remaining == 0 and triggers full teardown
(src/commands.rs:900-904), aborting the other module's subscriptions — #54's
symptom, in-window.
PR #55 documents this as a caller obligation (a window needs a single owner of the
observe/unobserve pair) and #55's subscribe() registration check does not help:
both modules share the label, so both pass it.
Note on the obvious fix: adding webview_label to ActiveSubscription and
scoping teardown by it does not fix this case. Both modules share the label, so
a label-scoped remove_for_db aborts the identical set — and teardown also calls
disable_observation(), which sets observer = None
(crates/sqlx-sqlite-toolkit/src/wrapper.rs:449-451) and drops the broker, so
every stream ends regardless. Scoping would leave the second module's dead entry
behind until self-reap, i.e. strictly less cleanup for no benefit.
Possible approaches
- A per-caller registration token returned by
observe() and passed to
unobserve(), replacing label-keyed refcounting.
- A JS-side refcount in
guest-js. Needs a module-level registry, because
Database.get() returns a fresh instance per call
(guest-js/index.ts:861-862), so per-instance state would not be shared.
Both are public-API changes, hence out of scope for #55.
Split out of the code review on #55, where it was validated as real but out of scope for that PR. See the review threads there for the supporting analysis.
Issue filed by AI model Claude
ObserverRegistrationskeys registrations by webview label(
src/subscriptions.rs:257), so two independent frontend modules in the samewindow that each call
observe()collapse into a single registration. The firstone to call
unobserve()hitsremaining == 0and triggers full teardown(
src/commands.rs:900-904), aborting the other module's subscriptions — #54'ssymptom, in-window.
PR #55 documents this as a caller obligation (a window needs a single owner of the
observe/unobserve pair) and #55's
subscribe()registration check does not help:both modules share the label, so both pass it.
Note on the obvious fix: adding
webview_labeltoActiveSubscriptionandscoping teardown by it does not fix this case. Both modules share the label, so
a label-scoped
remove_for_dbaborts the identical set — and teardown also callsdisable_observation(), which setsobserver = None(
crates/sqlx-sqlite-toolkit/src/wrapper.rs:449-451) and drops the broker, soevery stream ends regardless. Scoping would leave the second module's dead entry
behind until self-reap, i.e. strictly less cleanup for no benefit.
Possible approaches
observe()and passed tounobserve(), replacing label-keyed refcounting.guest-js. Needs a module-level registry, becauseDatabase.get()returns a fresh instance per call(
guest-js/index.ts:861-862), so per-instance state would not be shared.Both are public-API changes, hence out of scope for #55.
Split out of the code review on #55, where it was validated as real but out of scope for that PR. See the review threads there for the supporting analysis.
Issue filed by AI model Claude