diff --git a/.changeset/storage-and-codegen-notes.md b/.changeset/storage-and-codegen-notes.md new file mode 100644 index 0000000000..4b9ff11045 --- /dev/null +++ b/.changeset/storage-and-codegen-notes.md @@ -0,0 +1,5 @@ +--- +'zero-swift': patch +--- + +Documents that the main thread waits up to about 5 s when it opens a database another program holds locked, and up to about 10 s in `flush()` or `close()` on a busy one; that two generated schemas with a table of the same name need modules of their own; and that a flipped `whereExists` reaching a `limit` early, where JS asserts "Unexpected early return prevented full hydration", keeps working in Swift. diff --git a/packages/zero-swift-cli/README.md b/packages/zero-swift-cli/README.md index 8206c21c1f..a9f25da0e3 100644 --- a/packages/zero-swift-cli/README.md +++ b/packages/zero-swift-cli/README.md @@ -56,6 +56,11 @@ Rename a row struct whose name clashes with a type you use. A table named `list` generates `struct List`, which hides SwiftUI's `List`; set `tables.list.type` to `NoteList`, or set a `typePrefix`. +Two generated schemas with a table of the same name can't share a module: +both declare `tx.mutate.`. Generate each into a module of its own. +Code that imports both names the row type, as in +`TypedTableMutator(tx.mutate).insert(…)`. + ### Names Tables, columns, relationships, JSON fields and enumeration values keep diff --git a/swift/DESIGN.md b/swift/DESIGN.md index 41a12e1892..def08aa1f8 100644 --- a/swift/DESIGN.md +++ b/swift/DESIGN.md @@ -219,10 +219,18 @@ Rationale: - If the store can't be opened, the client runs on memory and calls `onStorageFailure`, as JS does. A failed write stops writing and calls it too. A write that finds the database busy is kept and written with the - next one, or by `flush()` and `close()`, which try twice more before they + next one, or by `flush()` and `close()`, which try once more before they call it a failure. Stored data that doesn't decode is lost client state, as a corrupt ref count is in JS: the client deletes the database, starts a new one, and calls `onClientStateNotFound` if the app set it. +- These waits are on the main thread, each up to SQLite's 5 s busy + timeout. Opening a database another connection holds locked blocks + `Zero.init` for about 5 s before the client falls back to memory, and + `flush()` or `close()` on a busy database blocks for up to about 10 s: + the queued write, then the retry. With one process per store, only + another program with the file open, such as a SQLite browser, holds + such a lock. Opening and closing the store off the main actor would + avoid the waits; that is deferred. - A client writes a poke's snapshot only when its cookie is newer than the stored one, as Replicache persists, so a client of the group that is behind another doesn't overwrite it. diff --git a/swift/Sources/Zero/Zero.docc/JSParity.md b/swift/Sources/Zero/Zero.docc/JSParity.md index 86c3524a43..0873affe40 100644 --- a/swift/Sources/Zero/Zero.docc/JSParity.md +++ b/swift/Sources/Zero/Zero.docc/JSParity.md @@ -163,6 +163,13 @@ is written or sent. connection in the `error` state, a mutation that fails is committed and its `client` outcome is the error, and later mutations, `materialize`, and `run` fail with the same message. +- **A flipped `whereExists` stopping a `limit` early.** To learn whether a + row a change brings has a match, a flipped `whereExists` reads its + subquery's first row only. When that read goes through a `limit` whose + rows for this row weren't read before, JS asserts ("Unexpected early + return prevented full hydration") and the change fails as above. Swift + keeps working and shows the rows JS shows once its `Take` reads the rest + of such a fetch, as the comment by that assertion proposes. - **Nulls in columns that can't be null.** When the server sends null for a column the schema says isn't optional, a `String`, `Double`, `Int`, or `Bool` property reads as `""`, `0`, or `false`. JS hands the null