Skip to content

Configure busy timeout on SQLite storage to prevent lock failures. - #1988

Merged
davidz25 merged 1 commit into
mainfrom
fix-1979
Sep 8, 2026
Merged

Configure busy timeout on SQLite storage to prevent lock failures.#1988
davidz25 merged 1 commit into
mainfrom
fix-1979

Conversation

@davidz25

@davidz25 davidz25 commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

On iOS, the SQLite C library defaults busy_timeout to 0 ms. When multiple connections access the same underlying database concurrently (such as an app and an app extension sharing an App Group container), any lock contention immediately throws SQLiteException: Error code: 5, message: database is locked.

Configure a default busy timeout of 5 seconds for SQLite connections on iOS:

  • In SqliteStorage, accept a busyTimeout: Duration = 5.seconds parameter, validate that it is non-negative, and execute PRAGMA busy_timeout in the init block. Provide backward-compatible constructors.
  • In IosStorage, delegate busyTimeout configuration to SqliteStorage, and provide secondary constructors with (storageFileUrl, excludeFromBackup) and (storageFileUrl, excludeFromBackup, busyTimeoutMs) overloads for Swift/Objective-C compatibility.
  • In Storage, update KDoc to document concurrency and multi-instance safety guarantees across processes.
  • In IosStorageTest, add unit test testBusyTimeout() reproducing the zero-timeout failure under lock contention, verifying that the default 5-second timeout waits and succeeds, and verifying that exceeding the timeout fails. Add testSqliteStorageBusyTimeout() verifying standalone SqliteStorage instances.
  • In AndroidStorageTest, add testBusyTimeout() verifying that Android SQLite automatically configures a busy timeout (>= 2000 ms) and handles contention without error.

Fixes #1979.

Test: Ran ./gradlew :multipaz:jvmTest
Test: Ran ./gradlew :multipaz:iosSimulatorArm64Test
Test: Ran ./gradlew :multipaz:connectedDebugAndroidTest (on Pixel 8 Pro)
Test: Ran ./gradlew detekt
Test: Ran xcodebuild -project samples/SwiftTestApp/SwiftTestApp.xcodeproj -scheme SwiftTestApp -sdk iphonesimulator build

On iOS, the SQLite C library defaults `busy_timeout` to 0 ms. When multiple
connections access the same underlying database concurrently (such as an app
and an app extension sharing an App Group container), any lock contention
immediately throws `SQLiteException: Error code: 5, message: database is
locked`.

Configure a default busy timeout of 5 seconds for SQLite connections on iOS:
- In `SqliteStorage`, accept a `busyTimeout: Duration = 5.seconds` parameter,
  validate that it is non-negative, and execute `PRAGMA busy_timeout` in the
  `init` block. Provide backward-compatible constructors.
- In `IosStorage`, delegate `busyTimeout` configuration to `SqliteStorage`,
  and provide secondary constructors with `(storageFileUrl, excludeFromBackup)`
  and `(storageFileUrl, excludeFromBackup, busyTimeoutMs)` overloads for
  Swift/Objective-C compatibility.
- In `Storage`, update KDoc to document concurrency and multi-instance
  safety guarantees across processes.
- In `IosStorageTest`, add unit test `testBusyTimeout()` reproducing the
  zero-timeout failure under lock contention, verifying that the default
  5-second timeout waits and succeeds, and verifying that exceeding the
  timeout fails. Add `testSqliteStorageBusyTimeout()` verifying standalone
  `SqliteStorage` instances.
- In `AndroidStorageTest`, add `testBusyTimeout()` verifying that Android
  SQLite automatically configures a busy timeout (>= 2000 ms) and handles
  contention without error.

Fixes #1979.

Test: Ran ./gradlew :multipaz:jvmTest
Test: Ran ./gradlew :multipaz:iosSimulatorArm64Test
Test: Ran ./gradlew :multipaz:connectedDebugAndroidTest (on Pixel 8 Pro)
Test: Ran ./gradlew detekt
Test: Ran xcodebuild -project samples/SwiftTestApp/SwiftTestApp.xcodeproj -scheme SwiftTestApp -sdk iphonesimulator build

Signed-off-by: David Zeuthen <zeuthen@gmail.com>
@davidz25
davidz25 merged commit 6dd6493 into main Sep 8, 2026
4 checks passed
@davidz25
davidz25 deleted the fix-1979 branch September 8, 2026 21:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

IosStorage sets no busy_timeout, so a second connection fails immediately with SQLITE_BUSY

1 participant