Replies: 1 comment
-
|
Hi @no33jou, I responded in your PR #455. Let's keep the discussion over there, so I will close this for now. |
Beta Was this translation helpful? Give feedback.
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Summary
A
DatabaseWriterwrapper that preserves its own object identity while swapping its underlying connection (typical "account switch" pattern in apps that keep@Dependency(\.defaultDatabase)stable across logins) silently breaks@FetchOne. Writes against the new connection never reach the property wrapper.@FetchAllis not affected — by accident, not by design.Reproduction
Minimal Swift package, runs in 5 sec:
https://github.com/no33jou/fetchone-mutable-db-repro
The two
[Identity] mutable: ObjectIdentifier(0x…)lines printed before and after the swap are identical — that is the bug'"'"'s fingerprint.Versions: sqlite-data 1.6.1 · swift-sharing 2.7.4 · GRDB 7.x · macOS 14+.
Root cause
FetchKeyID(Sources/SQLiteData/Internal/FetchKey.swift L172-188) hashesObjectIdentifier(database). swift-sharing'"'"'sPersistentReferencesdedupes bykey.id, so a wrapper that keeps its own identity but swaps inner pools hits the cached_PersistentReference. That reference'"'"'s GRDBValueObservationis still subscribed to the previous inner connection (now closed); writes to the new connection never reach the@FetchOne.@FetchAllhappens to escape becauseFetchKey<[T]>.Default(_SharedKeyDefaultwrapping) takes a different generic-cast path insidePersistentReferences.value(forKey:)and misses the cache by accident.Proposed fix
Add
FetchKeyCache.invalidate(database:). Wrappers call it after swapping the inner connection. Subscriptions registered against that wrapper'"'"'sObjectIdentifierreattach to the wrapper'"'"'s current inner connection; existing GRDB cancellables are released. The next.load()reflects the new connection'"'"'s state and pushes resume.Additive, no public API breakage. Code that doesn'"'"'t swap an inner connection is unaffected.
PR: #455
Alternatives considered
invalidateAll()/invalidate(forKey:)): wider blast radius, pollutes the cache abstraction. Rejected for round 1.$prop = FetchOne(wrappedValue: ...)): works (see repro--fix) but every user has to re-derive it. Not discoverable.Happy to discuss API shape and scope before merge.
Beta Was this translation helpful? Give feedback.
All reactions