From d306593b9c44aae9f924046e8f3a4eb406c81fe2 Mon Sep 17 00:00:00 2001 From: stevensJourney Date: Mon, 10 Nov 2025 11:38:31 +0200 Subject: [PATCH 1/3] feat: ability to provide custom dispatch implementations --- CHANGELOG.md | 255 +++++++++--------- .../kotlin/com/powersync/DispatchStrategy.kt | 109 ++++++++ .../kotlin/com/powersync/PowerSyncDatabase.kt | 10 +- .../com/powersync/PowerSyncDatabaseFactory.kt | 5 +- .../com/powersync/db/PowerSyncDatabaseImpl.kt | 6 +- .../db/internal/InternalDatabaseImpl.kt | 17 +- .../pool/SwiftSQLiteConnectionPool.kt | 10 + 7 files changed, 278 insertions(+), 134 deletions(-) create mode 100644 common/src/commonMain/kotlin/com/powersync/DispatchStrategy.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index a97931b1..b3638050 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,21 @@ ## 1.9.0 (unreleased) +- Add `DispatchStrategy` API for customizing how database operations are dispatched to coroutine contexts. + By default, operations use `Dispatchers.IO`, but you can now provide a custom `CoroutineDispatcher` or + a fully custom `DispatchFunction` for complete control over the execution context. + + ```kotlin + // Use default (Dispatchers.IO) + PowerSyncDatabase(factory, schema) + + // Use a specific dispatcher + PowerSyncDatabase(factory, schema, dispatchStrategy = DispatchStrategy.Dispatcher(Dispatchers.Default)) + + // Use a custom function + PowerSyncDatabase(factory, schema, dispatchStrategy = DispatchStrategy.Custom(myCustomFunction)) + ``` + - Sync options: `newClientImplementation` is now the default. - Make `androidx.sqlite:sqlite-bundled` an API dependency of `:core` to avoid toolchain warnings. @@ -11,9 +26,9 @@ ## 1.8.0 -- Refactor SDK: `com.powersync:powersync-core` has an identical API, but now depends on +- Refactor SDK: `com.powersync:powersync-core` has an identical API, but now depends on `com.powersync:powersync-common` where most logic is implemented. - - __POTENTIALLY BREAKING CHANGE__: If you were injecting a `DatabaseDriverFactory` into Koin or Dagger, note that the + - **POTENTIALLY BREAKING CHANGE**: If you were injecting a `DatabaseDriverFactory` into Koin or Dagger, note that the `PowerSyncDatabase()` factory method now takes a more generic `PersistentConnectionFactory`. - If you're using `PowerSyncDatabase.inMemory`, you explicitly have to import `com.powersync.inMemory` now. - Update the PowerSync core extension to version 0.4.8. @@ -32,78 +47,78 @@ ## 1.6.1 -* Fix `dlopen failed: library "libpowersync.so.so" not found` errors on Android. +- Fix `dlopen failed: library "libpowersync.so.so" not found` errors on Android. ## 1.6.0 -* Remove internal SQLDelight and SQLiter dependencies. -* Add `rawConnection` getter to `ConnectionContext`, which is a `SQLiteConnection` instance from +- Remove internal SQLDelight and SQLiter dependencies. +- Add `rawConnection` getter to `ConnectionContext`, which is a `SQLiteConnection` instance from `androidx.sqlite` that can be used to step through statements in a custom way. -* Fix an issue where `watch()` would run queries more often than intended. -* Add an integration for the Room database library ([readme](integrations/room/README.md)). -* Add the `com.powersync:integration-sqldelight` module providing a SQLDelight driver based on open +- Fix an issue where `watch()` would run queries more often than intended. +- Add an integration for the Room database library ([readme](integrations/room/README.md)). +- Add the `com.powersync:integration-sqldelight` module providing a SQLDelight driver based on open PowerSync instances. See [the readme](integrations/sqldelight/README.md) for details. ## 1.5.1 -* Fix issue in legacy sync client where local writes made offline could have their upload delayed +- Fix issue in legacy sync client where local writes made offline could have their upload delayed until a keepalive event was received. This could also cause downloaded updates to be delayed even further until all uploads were completed. -* [Internal] Update core extension to 0.4.5 +- [Internal] Update core extension to 0.4.5 ## 1.5.0 -* Add `PowerSyncDatabase.getCrudTransactions()`, returning a flow of transactions. This is useful +- Add `PowerSyncDatabase.getCrudTransactions()`, returning a flow of transactions. This is useful to upload multiple transactions in a batch. -* Fix modifying severity of the global Kermit logger -* Add `PowerSync` tag for the logs -* Fix `null` values in CRUD entries being reported as `"null"` strings. -* [INTERNAL] Added helpers for Swift read and write lock exception handling. +- Fix modifying severity of the global Kermit logger +- Add `PowerSync` tag for the logs +- Fix `null` values in CRUD entries being reported as `"null"` strings. +- [INTERNAL] Added helpers for Swift read and write lock exception handling. ## 1.4.0 -* Added the ability to log PowerSync service HTTP request information via specifying a +- Added the ability to log PowerSync service HTTP request information via specifying a `SyncClientConfiguration` in the `SyncOptions.clientConfiguration` parameter used in `PowerSyncDatabase.connect()` calls. -* `CrudEntry`: Introduce `SqliteRow` interface for `opData` and `previousValues`, providing typed +- `CrudEntry`: Introduce `SqliteRow` interface for `opData` and `previousValues`, providing typed access to the underlying values. -* Update core extension to 0.4.2, fixing a bug where `hasSynced` would turn `false` when losing +- Update core extension to 0.4.2, fixing a bug where `hasSynced` would turn `false` when losing connectivity. ## 1.3.1 -* Update SQLite to 3.50.3. -* Android: Ensure JNI libraries are 16KB-aligned. -* Support receiving binary sync lines over HTTP when the Rust client is enabled. -* Remove the experimental websocket transport mode. -* Update to Kotlin 2.2.0. -* Migrate to `kotlin.time` APIs where appropriate. +- Update SQLite to 3.50.3. +- Android: Ensure JNI libraries are 16KB-aligned. +- Support receiving binary sync lines over HTTP when the Rust client is enabled. +- Remove the experimental websocket transport mode. +- Update to Kotlin 2.2.0. +- Migrate to `kotlin.time` APIs where appropriate. ## 1.3.0 -* Support tables created outside of PowerSync with the `RawTable` API. +- Support tables created outside of PowerSync with the `RawTable` API. For more information, see [the documentation](https://docs.powersync.com/usage/use-case-examples/raw-tables). -* Fix `runWrapped` catching cancellation exceptions. -* Fix errors in `PowerSyncBackendConnector.fetchCredentials()` crashing Android apps. +- Fix `runWrapped` catching cancellation exceptions. +- Fix errors in `PowerSyncBackendConnector.fetchCredentials()` crashing Android apps. ## 1.2.2 -* Supabase: Avoid creating `Json` serializers multiple times. -* Fix local writes not being uploaded correctly when using WebSockets as a transport protocol. +- Supabase: Avoid creating `Json` serializers multiple times. +- Fix local writes not being uploaded correctly when using WebSockets as a transport protocol. ## 1.2.1 -* [Supabase Connector] Fixed issue where only `400` HTTP status code errors where reported as +- [Supabase Connector] Fixed issue where only `400` HTTP status code errors where reported as connection errors. The connector now reports errors for codes `>=400`. -* Update PowerSync core extension to `0.4.1`, fixing an issue with the new Rust client. -* Rust sync client: Fix writes made while offline not being uploaded reliably. -* Add watchOS support. +- Update PowerSync core extension to `0.4.1`, fixing an issue with the new Rust client. +- Rust sync client: Fix writes made while offline not being uploaded reliably. +- Add watchOS support. ## 1.2.0 -* Add a new sync client implementation written in Rust instead of Kotlin. While this client is still +- Add a new sync client implementation written in Rust instead of Kotlin. While this client is still experimental, we intend to make it the default in the future. The main benefit of this client is faster sync performance, but upcoming features will also require this client. We encourage interested users to try it out by opting in to `ExperimentalPowerSyncAPI` and passing options when @@ -116,7 +131,7 @@ ``` Switching between the clients can be done at any time without compatibility issues. If you run into issues with the new client, please reach out to us! -* In addition to HTTP streams, the Kotlin SDK also supports fetching sync instructions from the +- In addition to HTTP streams, the Kotlin SDK also supports fetching sync instructions from the PowerSync service in a binary format. This requires the new sync client, and can then be enabled on the sync options: ```Kotlin @@ -126,79 +141,79 @@ method = ConnectionMethod.WebSocket() )) ``` -* [Android, JVM] Use version `0.4.0` of `powersync-sqlite-core`. +- [Android, JVM] Use version `0.4.0` of `powersync-sqlite-core`. ## 1.1.1 -* Fix reported progress around compactions / defrags on the sync service. -* [Android] Set `temp_store_directory`, avoiding crashes for large materialized views. +- Fix reported progress around compactions / defrags on the sync service. +- [Android] Set `temp_store_directory`, avoiding crashes for large materialized views. ## 1.1.0 -* Add `trackPreviousValues` option on `Table` which sets `CrudEntry.previousValues` to previous +- Add `trackPreviousValues` option on `Table` which sets `CrudEntry.previousValues` to previous values on updates. -* Add `trackMetadata` option on `Table` which adds a `_metadata` column that can be used for +- Add `trackMetadata` option on `Table` which adds a `_metadata` column that can be used for updates. The configured metadata is available through `CrudEntry.metadata`. -* Add `ignoreEmptyUpdates` option which skips creating CRUD entries for updates that don't change +- Add `ignoreEmptyUpdates` option which skips creating CRUD entries for updates that don't change any values. ## 1.0.1 -* [Internal] Version bump for broken Swift release pipeline +- [Internal] Version bump for broken Swift release pipeline ## 1.0.0 -* Bump SDK to V1/Stable feature status -* Fixed `CrudBatch` `hasMore` always returning false. -* Added `triggerImmediately` to `onChange` method. -* Report real-time progress information about downloads through `SyncStatus.downloadProgress`. -* Compose: Add `composeState()` extension method on `SyncStatus`. -* [Internal] Added helper method for Swift `PowerSyncException` throwing. +- Bump SDK to V1/Stable feature status +- Fixed `CrudBatch` `hasMore` always returning false. +- Added `triggerImmediately` to `onChange` method. +- Report real-time progress information about downloads through `SyncStatus.downloadProgress`. +- Compose: Add `composeState()` extension method on `SyncStatus`. +- [Internal] Added helper method for Swift `PowerSyncException` throwing. ## 1.0.0-BETA32 -* Added `onChange` method to the PowerSync client. This allows for observing table changes. -* Removed unnecessary `User-Id` header from internal PowerSync service requests. -* Fix loading native PowerSync extension for Java targets. +- Added `onChange` method to the PowerSync client. This allows for observing table changes. +- Removed unnecessary `User-Id` header from internal PowerSync service requests. +- Fix loading native PowerSync extension for Java targets. ## 1.0.0-BETA31 -* Added helpers for Attachment syncing. -* Fix `getNextCrudTransaction()` only returning a single item. +- Added helpers for Attachment syncing. +- Fix `getNextCrudTransaction()` only returning a single item. ## 1.0.0-BETA30 -* Fix a deadlock when calling `connect()` immediately after opening a database. +- Fix a deadlock when calling `connect()` immediately after opening a database. The issue has been introduced in version `1.0.0-BETA29`. ## 1.0.0-BETA29 -* Fix potential race condition between jobs in `connect()` and `disconnect()`. -* [JVM Windows] Fixed PowerSync Extension temporary file deletion error on process shutdown. -* [iOS] Fixed issue where automatic driver migrations would fail with the error: +- Fix potential race condition between jobs in `connect()` and `disconnect()`. +- [JVM Windows] Fixed PowerSync Extension temporary file deletion error on process shutdown. +- [iOS] Fixed issue where automatic driver migrations would fail with the error: ``` Sqlite operation failure database is locked attempted to run migration and failed. closing connection ``` -* Fix race condition causing data received during uploads not to be applied. +- Fix race condition causing data received during uploads not to be applied. ## 1.0.0-BETA28 -* Update PowerSync SQLite core extension to 0.3.12. -* Added queing protection and warnings when connecting multiple PowerSync clients to the same +- Update PowerSync SQLite core extension to 0.3.12. +- Added queing protection and warnings when connecting multiple PowerSync clients to the same database file. -* Improved concurrent SQLite connection support accross various platforms. All platforms now use a +- Improved concurrent SQLite connection support accross various platforms. All platforms now use a single write connection and multiple read connections for concurrent read queries. -* Added the ability to open a SQLite database given a custom `dbDirectory` path. This is currently +- Added the ability to open a SQLite database given a custom `dbDirectory` path. This is currently not supported on iOS due to internal driver restrictions. -* Internaly improved the linking of SQLite for iOS. -* Enabled Full Text Search on iOS platforms. -* Added the ability to update the schema for existing PowerSync clients. -* Fixed bug where local only, insert only and view name overrides were not applied for schema +- Internaly improved the linking of SQLite for iOS. +- Enabled Full Text Search on iOS platforms. +- Added the ability to update the schema for existing PowerSync clients. +- Fixed bug where local only, insert only and view name overrides were not applied for schema tables. -* The Android SQLite driver now uses +- The Android SQLite driver now uses the [Xerial JDBC library](https://github.com/xerial/sqlite-jdbc). This removes the requirement for users to add the jitpack Maven repository to their projects. @@ -215,52 +230,52 @@ Sqlite operation failure database is locked attempted to run migration and faile ## 1.0.0-BETA27 -* Improved watch query internals. Added the ability to throttle watched queries. -* Fixed `uploading` and `downloading` sync status indicators. +- Improved watch query internals. Added the ability to throttle watched queries. +- Fixed `uploading` and `downloading` sync status indicators. ## 1.0.0-BETA26 -* Support bucket priorities and partial syncs. -* Android: Add ProGuard rules to prevent methods called through JNI from being minified or removed. +- Support bucket priorities and partial syncs. +- Android: Add ProGuard rules to prevent methods called through JNI from being minified or removed. ## 1.0.0-BETA25 -* JVM: Lower minimum supported version from 17 to 8. +- JVM: Lower minimum supported version from 17 to 8. ## 1.0.0-BETA24 -* Improve internal handling of watch queries to avoid issues where updates are not being received +- Improve internal handling of watch queries to avoid issues where updates are not being received due to transaction commits occurring after the query is run. -* Fix issue in JVM build where `columnNames` was throwing an error due to the index of the JDBC +- Fix issue in JVM build where `columnNames` was throwing an error due to the index of the JDBC driver starting at 1 instead of 0 as in the other drivers/ -* Throw and not just catch `CancellationExceptions` in `runWrappedSuspending` +- Throw and not just catch `CancellationExceptions` in `runWrappedSuspending` ## 1.0.0-BETA23 -* Make `execute` and `PowerSyncTransaction` functions throwable for Swift +- Make `execute` and `PowerSyncTransaction` functions throwable for Swift ## 1.0.0-BETA22 -* Fix `updateHasSynced` internal null pointer exception +- Fix `updateHasSynced` internal null pointer exception ## 1.0.0-BETA21 -* Improve error handling for Swift by adding @Throws annotation so errors can be handled in Swift -* Throw PowerSync exceptions for all public facing methods +- Improve error handling for Swift by adding @Throws annotation so errors can be handled in Swift +- Throw PowerSync exceptions for all public facing methods ## 1.0.0-BETA20 -* Add cursor optional functions: `getStringOptional`, `getLongOptional`, `getDoubleOptional`, +- Add cursor optional functions: `getStringOptional`, `getLongOptional`, `getDoubleOptional`, `getBooleanOptional` and `getBytesOptional` when using the column name which allow for optional return types -* Throw errors for invalid column on all cursor functions -* `getString`, `getLong`, `getBytes`, `getDouble` and `getBoolean` used with the column name will +- Throw errors for invalid column on all cursor functions +- `getString`, `getLong`, `getBytes`, `getDouble` and `getBoolean` used with the column name will now throw an error for non-null values and expect a non optional return type ## 1.0.0-BETA19 -* Allow cursor to get values by column name e.g. `getStringOptional("id")` -* BREAKING CHANGE: If you were using `SqlCursor` from SqlDelight previously for your own custom +- Allow cursor to get values by column name e.g. `getStringOptional("id")` +- BREAKING CHANGE: If you were using `SqlCursor` from SqlDelight previously for your own custom mapper then you must now change to `SqlCursor` exported by the PowerSync module. Previously you would import it like this: @@ -277,80 +292,80 @@ Sqlite operation failure database is locked attempted to run migration and faile ## 1.0.0-BETA18 -* BREAKING CHANGE: Move from async sqldelight calls to synchronous calls. This will only affect +- BREAKING CHANGE: Move from async sqldelight calls to synchronous calls. This will only affect `readTransaction` and `writeTransaction`where the callback function is no longer asynchronous. ## 1.0.0-BETA17 -* Add fix for Windows using JVM build +- Add fix for Windows using JVM build ## 1.0.0-BETA16 -* Add `close` method to database methods -* Throw when error is a `CancellationError` and remove invalidation for all errors in +- Add `close` method to database methods +- Throw when error is a `CancellationError` and remove invalidation for all errors in `streamingSync` catch. ## 1.0.0-BETA15 -* Update powersync-sqlite-core to 0.3.8 -* Increase maximum amount of columns from 63 to 1999 +- Update powersync-sqlite-core to 0.3.8 +- Increase maximum amount of columns from 63 to 1999 ## 1.0.0-BETA14 -* Add JVM compatibility -* Revert previous iOS changes as they resulted in further issues. +- Add JVM compatibility +- Revert previous iOS changes as they resulted in further issues. ## 1.0.0-BETA13 -* Move iOS database driver to use IO dispatcher which should avoid race conditions and improve +- Move iOS database driver to use IO dispatcher which should avoid race conditions and improve performance. ## 1.0.0-BETA12 -* Use transaction context in `writeTransaction` in `BucketStorageImpl`. +- Use transaction context in `writeTransaction` in `BucketStorageImpl`. ## 1.0.0-BETA11 -* Update version to fix deployment issue of previous release +- Update version to fix deployment issue of previous release ## 1.0.0-BETA10 -* Change Swift package name from `PowerSync` to `PowerSyncKotlin` +- Change Swift package name from `PowerSync` to `PowerSyncKotlin` ## 1.0.0-BETA9 -* Re-enable SKIE `SuspendInterop` -* Move transaction functions out of `PowerSyncTransactionFactory` to avoid threading issues in Swift +- Re-enable SKIE `SuspendInterop` +- Move transaction functions out of `PowerSyncTransactionFactory` to avoid threading issues in Swift SDK ## 1.0.0-BETA8 -* Disable SKIE `SuspendInterop` plugin to fix overriding `suspend` functions in Swift +- Disable SKIE `SuspendInterop` plugin to fix overriding `suspend` functions in Swift ## 1.0.0-BETA7 -* Update supabase connector to use supabase-kt version 3 -* Handle Postgres error codes in supabase connector +- Update supabase connector to use supabase-kt version 3 +- Handle Postgres error codes in supabase connector ## 1.0.0-BETA6 -* Fix Custom Write Checkpoint application logic +- Fix Custom Write Checkpoint application logic ## 1.0.0-BETA5 -* Fix `hasSynced` not updating after `disconnectAndClear` -* Fix error being thrown in iOS app launch +- Fix `hasSynced` not updating after `disconnectAndClear` +- Fix error being thrown in iOS app launch ## 1.0.0-BETA4 -* Fix sync status being reset when `update` function is run +- Fix sync status being reset when `update` function is run ## 1.0.0-BETA3 -* Add `waitForFirstSync` function - which resolves after the initial sync is completed -* Upgrade to Kotlin 2.0.20 - should not cause any issues with users who are still on Kotlin 1.9 -* Upgrade `powersync-sqlite-core` to 0.3.0 - improves incremental sync performance -* Add client sync parameters - which allows you specify sync parameters from the +- Add `waitForFirstSync` function - which resolves after the initial sync is completed +- Upgrade to Kotlin 2.0.20 - should not cause any issues with users who are still on Kotlin 1.9 +- Upgrade `powersync-sqlite-core` to 0.3.0 - improves incremental sync performance +- Add client sync parameters - which allows you specify sync parameters from the client https://docs.powersync.com/usage/sync-rules/advanced-topics/client-parameters-beta ```kotlin @@ -368,25 +383,25 @@ params = params ) ``` -* Add schema validation when schema is generated -* Add warning message if there is a crudItem in the queue that has not yet been synced and after a +- Add schema validation when schema is generated +- Add warning message if there is a crudItem in the queue that has not yet been synced and after a delay rerun the upload ## 1.0.0-BETA2 -* Publish persistence package +- Publish persistence package ## 1.0.0-BETA1 -* Improve API by changing from Builder pattern to simply instantiating the database +- Improve API by changing from Builder pattern to simply instantiating the database `PowerSyncDatabase` E.g. `val db = PowerSyncDatabase(factory, schema)` -* Use callback context in transactions +- Use callback context in transactions E.g. `db.writeTransaction{ ctx -> ctx.execute(...) }` -* Removed unnecessary expiredAt field -* Added table max column validation as there is a hard limit of 63 columns -* Moved SQLDelight models to a separate module to reduce export size -* Replaced default Logger with [Kermit Logger](https://kermit.touchlab.co/) which allows users to +- Removed unnecessary expiredAt field +- Added table max column validation as there is a hard limit of 63 columns +- Moved SQLDelight models to a separate module to reduce export size +- Replaced default Logger with [Kermit Logger](https://kermit.touchlab.co/) which allows users to more easily use and/or change Logger settings -* Add `retryDelay` and `crudThrottle` options when setting up database connection -* Changed `_viewNameOverride` to `viewNameOverride` +- Add `retryDelay` and `crudThrottle` options when setting up database connection +- Changed `_viewNameOverride` to `viewNameOverride` diff --git a/common/src/commonMain/kotlin/com/powersync/DispatchStrategy.kt b/common/src/commonMain/kotlin/com/powersync/DispatchStrategy.kt new file mode 100644 index 00000000..3b90965a --- /dev/null +++ b/common/src/commonMain/kotlin/com/powersync/DispatchStrategy.kt @@ -0,0 +1,109 @@ +package com.powersync + +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.IO +import kotlinx.coroutines.withContext + +/** + * Function interface for dispatching database operations to a specific coroutine context. + * + * By default, operations are dispatched to [Dispatchers.IO]. Custom implementations + * can be provided to control the execution context of database operations. + * + * This interface supports the `operator invoke` syntax, allowing you to call it like: + * ``` + * dispatchFunction { /* your code */ } + * ``` + * + * **Design Note:** This must be an interface (not a function type) because Kotlin does not + * support function types with generic type parameters. Since the dispatch function needs to + * accept and return generic types ``, an interface with an `operator invoke` method is + * the appropriate solution. This allows the same convenient syntax as function types while + * supporting generics. + * + * @see DispatchStrategy for dispatch strategy options + */ +public interface DispatchFunction { + /** + * Dispatches the given block to the appropriate coroutine context. + * + * @param block The suspend function to execute in the dispatch context. + * @return The result of executing the block. + */ + public suspend operator fun invoke(block: suspend () -> R): R +} + +/** + * Strategy for dispatching database operations to a specific coroutine context. + * + * This sealed class allows you to specify how database operations should be dispatched: + * - [Default]: Use the default dispatcher ([Dispatchers.IO]) + * - [Dispatcher]: Use a specific [CoroutineDispatcher] + * - [Custom]: Use a custom [DispatchFunction] for full control + * + * Each variant provides a [dispatchFunction] that implements the actual dispatching logic. + * + * Example usage: + * ``` + * // Use default (Dispatchers.IO) - this is the default if not specified + * PowerSyncDatabase(factory, schema) + * // or explicitly: + * PowerSyncDatabase(factory, schema, dispatchStrategy = DispatchStrategy.Default) + * + * // Use a specific dispatcher + * PowerSyncDatabase(factory, schema, dispatchStrategy = DispatchStrategy.Dispatcher(Dispatchers.Default)) + * + * // Use a custom function + * PowerSyncDatabase(factory, schema, dispatchStrategy = DispatchStrategy.Custom(myCustomFunction)) + * ``` + * + * @see DispatchFunction for the dispatch function interface + */ +public sealed class DispatchStrategy { + /** + * Returns the [DispatchFunction] that implements the dispatching logic for this strategy. + */ + public abstract val dispatchFunction: DispatchFunction + + /** + * Use the default dispatcher ([Dispatchers.IO]) for database operations. + * + * This is the recommended default for most use cases, as it provides + * a dedicated thread pool for I/O-bound operations. + */ + public object Default : DispatchStrategy() { + override val dispatchFunction: DispatchFunction = + Dispatcher(Dispatchers.IO).dispatchFunction + } + + /** + * Use a specific [CoroutineDispatcher] for database operations. + * + * This allows you to use any coroutine dispatcher, such as: + * - [Dispatchers.Default] for CPU-bound work + * - [Dispatchers.Main] for UI operations + * - A custom dispatcher for your specific needs + * + * @property dispatcher The coroutine dispatcher to use. + */ + public data class Dispatcher( + val dispatcher: CoroutineDispatcher, + ) : DispatchStrategy() { + override val dispatchFunction: DispatchFunction = + object : DispatchFunction { + override suspend fun invoke(block: suspend () -> R): R = withContext(dispatcher) { block() } + } + } + + /** + * Use a custom [DispatchFunction] for full control over dispatching. + * + * @property function The custom dispatch function to use. + */ + public data class Custom( + val function: DispatchFunction, + ) : DispatchStrategy() { + override val dispatchFunction: DispatchFunction = function + } +} diff --git a/common/src/commonMain/kotlin/com/powersync/PowerSyncDatabase.kt b/common/src/commonMain/kotlin/com/powersync/PowerSyncDatabase.kt index 42271ebe..f93a0499 100644 --- a/common/src/commonMain/kotlin/com/powersync/PowerSyncDatabase.kt +++ b/common/src/commonMain/kotlin/com/powersync/PowerSyncDatabase.kt @@ -252,9 +252,10 @@ public interface PowerSyncDatabase : Queries { schema: Schema, identifier: String, logger: Logger, + dispatchStrategy: DispatchStrategy = DispatchStrategy.Default, ): PowerSyncDatabase { val group = ActiveDatabaseGroup.referenceDatabase(logger, identifier) - return openedWithGroup(pool, scope, schema, logger, group) + return openedWithGroup(pool, scope, schema, logger, group, dispatchStrategy) } /** @@ -268,11 +269,13 @@ public interface PowerSyncDatabase : Queries { schema: Schema, scope: CoroutineScope, logger: Logger? = null, + dispatchStrategy: DispatchStrategy = DispatchStrategy.Default, ): PowerSyncDatabase { val logger = generateLogger(logger) // Since this returns a fresh in-memory database every time, use a fresh group to avoid warnings about the // same database being opened multiple times. - val collection = ActiveDatabaseGroup.GroupsCollection().referenceDatabase(logger, "test") + val collection = + ActiveDatabaseGroup.GroupsCollection().referenceDatabase(logger, "test") return openedWithGroup( SingleConnectionPool(factory.openInMemoryConnection()), @@ -280,6 +283,7 @@ public interface PowerSyncDatabase : Queries { schema, logger, collection, + dispatchStrategy, ) } @@ -289,6 +293,7 @@ public interface PowerSyncDatabase : Queries { schema: Schema, logger: Logger, group: Pair, + dispatchStrategy: DispatchStrategy = DispatchStrategy.Default, ): PowerSyncDatabase = PowerSyncDatabaseImpl( schema, @@ -296,6 +301,7 @@ public interface PowerSyncDatabase : Queries { pool, logger, group, + dispatchStrategy, ) } } diff --git a/common/src/commonMain/kotlin/com/powersync/PowerSyncDatabaseFactory.kt b/common/src/commonMain/kotlin/com/powersync/PowerSyncDatabaseFactory.kt index 824b6e04..26560e54 100644 --- a/common/src/commonMain/kotlin/com/powersync/PowerSyncDatabaseFactory.kt +++ b/common/src/commonMain/kotlin/com/powersync/PowerSyncDatabaseFactory.kt @@ -25,6 +25,7 @@ public fun PowerSyncDatabase( dbFilename: String = DEFAULT_DB_FILENAME, scope: CoroutineScope = GlobalScope, logger: Logger? = null, + dispatchStrategy: DispatchStrategy = DispatchStrategy.Default, /** * Optional database file directory path. * This parameter is ignored for iOS. @@ -40,10 +41,10 @@ public fun PowerSyncDatabase( scope = scope, logger = generatedLogger, dbDirectory = dbDirectory, + dispatchStrategy = dispatchStrategy, ) } -@OptIn(ExperimentalPowerSyncAPI::class) internal fun createPowerSyncDatabaseImpl( factory: PersistentConnectionFactory, schema: Schema, @@ -51,6 +52,7 @@ internal fun createPowerSyncDatabaseImpl( scope: CoroutineScope, logger: Logger, dbDirectory: String?, + dispatchStrategy: DispatchStrategy = DispatchStrategy.Default, ): PowerSyncDatabaseImpl { val identifier = dbDirectory + dbFilename val activeDatabaseGroup = ActiveDatabaseGroup.referenceDatabase(logger, identifier) @@ -72,5 +74,6 @@ internal fun createPowerSyncDatabaseImpl( schema, logger, activeDatabaseGroup, + dispatchStrategy, ) as PowerSyncDatabaseImpl } diff --git a/common/src/commonMain/kotlin/com/powersync/db/PowerSyncDatabaseImpl.kt b/common/src/commonMain/kotlin/com/powersync/db/PowerSyncDatabaseImpl.kt index bf23a5af..afb009dd 100644 --- a/common/src/commonMain/kotlin/com/powersync/db/PowerSyncDatabaseImpl.kt +++ b/common/src/commonMain/kotlin/com/powersync/db/PowerSyncDatabaseImpl.kt @@ -1,6 +1,7 @@ package com.powersync.db import co.touchlab.kermit.Logger +import com.powersync.DispatchStrategy import com.powersync.ExperimentalPowerSyncAPI import com.powersync.PowerSyncDatabase import com.powersync.PowerSyncException @@ -61,6 +62,7 @@ internal class PowerSyncDatabaseImpl( pool: SQLiteConnectionPool, val logger: Logger, private val activeDatabaseGroup: Pair, + dispatchStrategy: DispatchStrategy, ) : PowerSyncDatabase { companion object { internal val streamConflictMessage = @@ -79,7 +81,7 @@ internal class PowerSyncDatabaseImpl( private val resource = activeDatabaseGroup.first private val streams = StreamTracker(this) - private val internalDb = InternalDatabaseImpl(pool, logger) + private val internalDb = InternalDatabaseImpl(pool, logger, dispatchStrategy = dispatchStrategy) internal val bucketStorage: BucketStorage = BucketStorageImpl(internalDb, logger) @@ -391,7 +393,7 @@ internal class PowerSyncDatabaseImpl( override suspend fun readTransaction(callback: ThrowableTransactionCallback): R { waitReady() - return internalDb.writeTransaction(callback) + return internalDb.readTransaction(callback) } override suspend fun writeLock(callback: ThrowableLockCallback): R { diff --git a/common/src/commonMain/kotlin/com/powersync/db/internal/InternalDatabaseImpl.kt b/common/src/commonMain/kotlin/com/powersync/db/internal/InternalDatabaseImpl.kt index d1185c8b..6d1135ee 100644 --- a/common/src/commonMain/kotlin/com/powersync/db/internal/InternalDatabaseImpl.kt +++ b/common/src/commonMain/kotlin/com/powersync/db/internal/InternalDatabaseImpl.kt @@ -1,6 +1,8 @@ package com.powersync.db.internal import co.touchlab.kermit.Logger +import com.powersync.DispatchFunction +import com.powersync.DispatchStrategy import com.powersync.ExperimentalPowerSyncAPI import com.powersync.db.SqlCursor import com.powersync.db.ThrowableLockCallback @@ -11,8 +13,6 @@ import com.powersync.db.runWrapped import com.powersync.utils.AtomicMutableSet import com.powersync.utils.JsonUtil import com.powersync.utils.throttle -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.IO import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.emitAll @@ -20,17 +20,14 @@ import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onSubscription import kotlinx.coroutines.flow.transform -import kotlinx.coroutines.withContext import kotlin.time.Duration.Companion.milliseconds @OptIn(ExperimentalPowerSyncAPI::class) internal class InternalDatabaseImpl( private val pool: SQLiteConnectionPool, private val logger: Logger, + dispatchStrategy: DispatchStrategy, ) : InternalDatabase { - // Could be scope.coroutineContext, but the default is GlobalScope, which seems like a bad idea. To discuss. - private val dbContext = Dispatchers.IO - override suspend fun execute( sql: String, parameters: List?, @@ -39,8 +36,10 @@ internal class InternalDatabaseImpl( context.execute(sql, parameters) } + private val dispatch: DispatchFunction = dispatchStrategy.dispatchFunction + override suspend fun updateSchema(schemaJson: String) { - withContext(dbContext) { + dispatch { runWrapped { pool.withAllConnections { writer, readers -> writer.runTransaction { tx -> @@ -167,7 +166,7 @@ internal class InternalDatabaseImpl( */ @OptIn(ExperimentalPowerSyncAPI::class) private suspend fun internalReadLock(callback: suspend (SQLiteConnectionLease) -> R): R = - withContext(dbContext) { + dispatch { runWrapped { useConnection(true) { connection -> callback(connection) @@ -189,7 +188,7 @@ internal class InternalDatabaseImpl( @OptIn(ExperimentalPowerSyncAPI::class) private suspend fun internalWriteLock(callback: suspend (SQLiteConnectionLease) -> R): R = - withContext(dbContext) { + dispatch { pool.write { writer -> runWrapped { callback(writer) diff --git a/internal/PowerSyncKotlin/src/appleMain/kotlin/com/powersync/pool/SwiftSQLiteConnectionPool.kt b/internal/PowerSyncKotlin/src/appleMain/kotlin/com/powersync/pool/SwiftSQLiteConnectionPool.kt index d5988732..3725a98a 100644 --- a/internal/PowerSyncKotlin/src/appleMain/kotlin/com/powersync/pool/SwiftSQLiteConnectionPool.kt +++ b/internal/PowerSyncKotlin/src/appleMain/kotlin/com/powersync/pool/SwiftSQLiteConnectionPool.kt @@ -1,6 +1,8 @@ package com.powersync.pool import co.touchlab.kermit.Logger +import com.powersync.DispatchFunction +import com.powersync.DispatchStrategy import com.powersync.PowerSyncDatabase import com.powersync.db.driver.SQLiteConnectionLease import com.powersync.db.driver.SQLiteConnectionPool @@ -104,4 +106,12 @@ public fun openPowerSyncWithPool( schema = schema, identifier = identifier, logger = logger, + dispatchStrategy = DispatchStrategy.Custom( + object : DispatchFunction { + override suspend fun invoke(block: suspend () -> R): R { + // We leave the dispatching up to the pool + return block() + } + }, + ), ) From 96ab257a4007f7e860529a3b995eff6569781900 Mon Sep 17 00:00:00 2001 From: stevensJourney Date: Mon, 10 Nov 2025 11:45:08 +0200 Subject: [PATCH 2/3] revert changelog formatting --- CHANGELOG.md | 245 +++++++++++++++++++++++++-------------------------- 1 file changed, 122 insertions(+), 123 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b3638050..d5fca5b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## 1.9.0 (unreleased) +- Sync options: `newClientImplementation` is now the default. +- Make `androidx.sqlite:sqlite-bundled` an API dependency of `:core` to avoid toolchain warnings. - Add `DispatchStrategy` API for customizing how database operations are dispatched to coroutine contexts. By default, operations use `Dispatchers.IO`, but you can now provide a custom `CoroutineDispatcher` or a fully custom `DispatchFunction` for complete control over the execution context. @@ -17,18 +19,15 @@ PowerSyncDatabase(factory, schema, dispatchStrategy = DispatchStrategy.Custom(myCustomFunction)) ``` -- Sync options: `newClientImplementation` is now the default. -- Make `androidx.sqlite:sqlite-bundled` an API dependency of `:core` to avoid toolchain warnings. - ## 1.8.1 - Add POM name and description for `:common` project. ## 1.8.0 -- Refactor SDK: `com.powersync:powersync-core` has an identical API, but now depends on +- Refactor SDK: `com.powersync:powersync-core` has an identical API, but now depends on `com.powersync:powersync-common` where most logic is implemented. - - **POTENTIALLY BREAKING CHANGE**: If you were injecting a `DatabaseDriverFactory` into Koin or Dagger, note that the + - __POTENTIALLY BREAKING CHANGE__: If you were injecting a `DatabaseDriverFactory` into Koin or Dagger, note that the `PowerSyncDatabase()` factory method now takes a more generic `PersistentConnectionFactory`. - If you're using `PowerSyncDatabase.inMemory`, you explicitly have to import `com.powersync.inMemory` now. - Update the PowerSync core extension to version 0.4.8. @@ -47,78 +46,78 @@ ## 1.6.1 -- Fix `dlopen failed: library "libpowersync.so.so" not found` errors on Android. +* Fix `dlopen failed: library "libpowersync.so.so" not found` errors on Android. ## 1.6.0 -- Remove internal SQLDelight and SQLiter dependencies. -- Add `rawConnection` getter to `ConnectionContext`, which is a `SQLiteConnection` instance from +* Remove internal SQLDelight and SQLiter dependencies. +* Add `rawConnection` getter to `ConnectionContext`, which is a `SQLiteConnection` instance from `androidx.sqlite` that can be used to step through statements in a custom way. -- Fix an issue where `watch()` would run queries more often than intended. -- Add an integration for the Room database library ([readme](integrations/room/README.md)). -- Add the `com.powersync:integration-sqldelight` module providing a SQLDelight driver based on open +* Fix an issue where `watch()` would run queries more often than intended. +* Add an integration for the Room database library ([readme](integrations/room/README.md)). +* Add the `com.powersync:integration-sqldelight` module providing a SQLDelight driver based on open PowerSync instances. See [the readme](integrations/sqldelight/README.md) for details. ## 1.5.1 -- Fix issue in legacy sync client where local writes made offline could have their upload delayed +* Fix issue in legacy sync client where local writes made offline could have their upload delayed until a keepalive event was received. This could also cause downloaded updates to be delayed even further until all uploads were completed. -- [Internal] Update core extension to 0.4.5 +* [Internal] Update core extension to 0.4.5 ## 1.5.0 -- Add `PowerSyncDatabase.getCrudTransactions()`, returning a flow of transactions. This is useful +* Add `PowerSyncDatabase.getCrudTransactions()`, returning a flow of transactions. This is useful to upload multiple transactions in a batch. -- Fix modifying severity of the global Kermit logger -- Add `PowerSync` tag for the logs -- Fix `null` values in CRUD entries being reported as `"null"` strings. -- [INTERNAL] Added helpers for Swift read and write lock exception handling. +* Fix modifying severity of the global Kermit logger +* Add `PowerSync` tag for the logs +* Fix `null` values in CRUD entries being reported as `"null"` strings. +* [INTERNAL] Added helpers for Swift read and write lock exception handling. ## 1.4.0 -- Added the ability to log PowerSync service HTTP request information via specifying a +* Added the ability to log PowerSync service HTTP request information via specifying a `SyncClientConfiguration` in the `SyncOptions.clientConfiguration` parameter used in `PowerSyncDatabase.connect()` calls. -- `CrudEntry`: Introduce `SqliteRow` interface for `opData` and `previousValues`, providing typed +* `CrudEntry`: Introduce `SqliteRow` interface for `opData` and `previousValues`, providing typed access to the underlying values. -- Update core extension to 0.4.2, fixing a bug where `hasSynced` would turn `false` when losing +* Update core extension to 0.4.2, fixing a bug where `hasSynced` would turn `false` when losing connectivity. ## 1.3.1 -- Update SQLite to 3.50.3. -- Android: Ensure JNI libraries are 16KB-aligned. -- Support receiving binary sync lines over HTTP when the Rust client is enabled. -- Remove the experimental websocket transport mode. -- Update to Kotlin 2.2.0. -- Migrate to `kotlin.time` APIs where appropriate. +* Update SQLite to 3.50.3. +* Android: Ensure JNI libraries are 16KB-aligned. +* Support receiving binary sync lines over HTTP when the Rust client is enabled. +* Remove the experimental websocket transport mode. +* Update to Kotlin 2.2.0. +* Migrate to `kotlin.time` APIs where appropriate. ## 1.3.0 -- Support tables created outside of PowerSync with the `RawTable` API. +* Support tables created outside of PowerSync with the `RawTable` API. For more information, see [the documentation](https://docs.powersync.com/usage/use-case-examples/raw-tables). -- Fix `runWrapped` catching cancellation exceptions. -- Fix errors in `PowerSyncBackendConnector.fetchCredentials()` crashing Android apps. +* Fix `runWrapped` catching cancellation exceptions. +* Fix errors in `PowerSyncBackendConnector.fetchCredentials()` crashing Android apps. ## 1.2.2 -- Supabase: Avoid creating `Json` serializers multiple times. -- Fix local writes not being uploaded correctly when using WebSockets as a transport protocol. +* Supabase: Avoid creating `Json` serializers multiple times. +* Fix local writes not being uploaded correctly when using WebSockets as a transport protocol. ## 1.2.1 -- [Supabase Connector] Fixed issue where only `400` HTTP status code errors where reported as +* [Supabase Connector] Fixed issue where only `400` HTTP status code errors where reported as connection errors. The connector now reports errors for codes `>=400`. -- Update PowerSync core extension to `0.4.1`, fixing an issue with the new Rust client. -- Rust sync client: Fix writes made while offline not being uploaded reliably. -- Add watchOS support. +* Update PowerSync core extension to `0.4.1`, fixing an issue with the new Rust client. +* Rust sync client: Fix writes made while offline not being uploaded reliably. +* Add watchOS support. ## 1.2.0 -- Add a new sync client implementation written in Rust instead of Kotlin. While this client is still +* Add a new sync client implementation written in Rust instead of Kotlin. While this client is still experimental, we intend to make it the default in the future. The main benefit of this client is faster sync performance, but upcoming features will also require this client. We encourage interested users to try it out by opting in to `ExperimentalPowerSyncAPI` and passing options when @@ -131,7 +130,7 @@ ``` Switching between the clients can be done at any time without compatibility issues. If you run into issues with the new client, please reach out to us! -- In addition to HTTP streams, the Kotlin SDK also supports fetching sync instructions from the +* In addition to HTTP streams, the Kotlin SDK also supports fetching sync instructions from the PowerSync service in a binary format. This requires the new sync client, and can then be enabled on the sync options: ```Kotlin @@ -141,79 +140,79 @@ method = ConnectionMethod.WebSocket() )) ``` -- [Android, JVM] Use version `0.4.0` of `powersync-sqlite-core`. +* [Android, JVM] Use version `0.4.0` of `powersync-sqlite-core`. ## 1.1.1 -- Fix reported progress around compactions / defrags on the sync service. -- [Android] Set `temp_store_directory`, avoiding crashes for large materialized views. +* Fix reported progress around compactions / defrags on the sync service. +* [Android] Set `temp_store_directory`, avoiding crashes for large materialized views. ## 1.1.0 -- Add `trackPreviousValues` option on `Table` which sets `CrudEntry.previousValues` to previous +* Add `trackPreviousValues` option on `Table` which sets `CrudEntry.previousValues` to previous values on updates. -- Add `trackMetadata` option on `Table` which adds a `_metadata` column that can be used for +* Add `trackMetadata` option on `Table` which adds a `_metadata` column that can be used for updates. The configured metadata is available through `CrudEntry.metadata`. -- Add `ignoreEmptyUpdates` option which skips creating CRUD entries for updates that don't change +* Add `ignoreEmptyUpdates` option which skips creating CRUD entries for updates that don't change any values. ## 1.0.1 -- [Internal] Version bump for broken Swift release pipeline +* [Internal] Version bump for broken Swift release pipeline ## 1.0.0 -- Bump SDK to V1/Stable feature status -- Fixed `CrudBatch` `hasMore` always returning false. -- Added `triggerImmediately` to `onChange` method. -- Report real-time progress information about downloads through `SyncStatus.downloadProgress`. -- Compose: Add `composeState()` extension method on `SyncStatus`. -- [Internal] Added helper method for Swift `PowerSyncException` throwing. +* Bump SDK to V1/Stable feature status +* Fixed `CrudBatch` `hasMore` always returning false. +* Added `triggerImmediately` to `onChange` method. +* Report real-time progress information about downloads through `SyncStatus.downloadProgress`. +* Compose: Add `composeState()` extension method on `SyncStatus`. +* [Internal] Added helper method for Swift `PowerSyncException` throwing. ## 1.0.0-BETA32 -- Added `onChange` method to the PowerSync client. This allows for observing table changes. -- Removed unnecessary `User-Id` header from internal PowerSync service requests. -- Fix loading native PowerSync extension for Java targets. +* Added `onChange` method to the PowerSync client. This allows for observing table changes. +* Removed unnecessary `User-Id` header from internal PowerSync service requests. +* Fix loading native PowerSync extension for Java targets. ## 1.0.0-BETA31 -- Added helpers for Attachment syncing. -- Fix `getNextCrudTransaction()` only returning a single item. +* Added helpers for Attachment syncing. +* Fix `getNextCrudTransaction()` only returning a single item. ## 1.0.0-BETA30 -- Fix a deadlock when calling `connect()` immediately after opening a database. +* Fix a deadlock when calling `connect()` immediately after opening a database. The issue has been introduced in version `1.0.0-BETA29`. ## 1.0.0-BETA29 -- Fix potential race condition between jobs in `connect()` and `disconnect()`. -- [JVM Windows] Fixed PowerSync Extension temporary file deletion error on process shutdown. -- [iOS] Fixed issue where automatic driver migrations would fail with the error: +* Fix potential race condition between jobs in `connect()` and `disconnect()`. +* [JVM Windows] Fixed PowerSync Extension temporary file deletion error on process shutdown. +* [iOS] Fixed issue where automatic driver migrations would fail with the error: ``` Sqlite operation failure database is locked attempted to run migration and failed. closing connection ``` -- Fix race condition causing data received during uploads not to be applied. +* Fix race condition causing data received during uploads not to be applied. ## 1.0.0-BETA28 -- Update PowerSync SQLite core extension to 0.3.12. -- Added queing protection and warnings when connecting multiple PowerSync clients to the same +* Update PowerSync SQLite core extension to 0.3.12. +* Added queing protection and warnings when connecting multiple PowerSync clients to the same database file. -- Improved concurrent SQLite connection support accross various platforms. All platforms now use a +* Improved concurrent SQLite connection support accross various platforms. All platforms now use a single write connection and multiple read connections for concurrent read queries. -- Added the ability to open a SQLite database given a custom `dbDirectory` path. This is currently +* Added the ability to open a SQLite database given a custom `dbDirectory` path. This is currently not supported on iOS due to internal driver restrictions. -- Internaly improved the linking of SQLite for iOS. -- Enabled Full Text Search on iOS platforms. -- Added the ability to update the schema for existing PowerSync clients. -- Fixed bug where local only, insert only and view name overrides were not applied for schema +* Internaly improved the linking of SQLite for iOS. +* Enabled Full Text Search on iOS platforms. +* Added the ability to update the schema for existing PowerSync clients. +* Fixed bug where local only, insert only and view name overrides were not applied for schema tables. -- The Android SQLite driver now uses +* The Android SQLite driver now uses the [Xerial JDBC library](https://github.com/xerial/sqlite-jdbc). This removes the requirement for users to add the jitpack Maven repository to their projects. @@ -230,52 +229,52 @@ Sqlite operation failure database is locked attempted to run migration and faile ## 1.0.0-BETA27 -- Improved watch query internals. Added the ability to throttle watched queries. -- Fixed `uploading` and `downloading` sync status indicators. +* Improved watch query internals. Added the ability to throttle watched queries. +* Fixed `uploading` and `downloading` sync status indicators. ## 1.0.0-BETA26 -- Support bucket priorities and partial syncs. -- Android: Add ProGuard rules to prevent methods called through JNI from being minified or removed. +* Support bucket priorities and partial syncs. +* Android: Add ProGuard rules to prevent methods called through JNI from being minified or removed. ## 1.0.0-BETA25 -- JVM: Lower minimum supported version from 17 to 8. +* JVM: Lower minimum supported version from 17 to 8. ## 1.0.0-BETA24 -- Improve internal handling of watch queries to avoid issues where updates are not being received +* Improve internal handling of watch queries to avoid issues where updates are not being received due to transaction commits occurring after the query is run. -- Fix issue in JVM build where `columnNames` was throwing an error due to the index of the JDBC +* Fix issue in JVM build where `columnNames` was throwing an error due to the index of the JDBC driver starting at 1 instead of 0 as in the other drivers/ -- Throw and not just catch `CancellationExceptions` in `runWrappedSuspending` +* Throw and not just catch `CancellationExceptions` in `runWrappedSuspending` ## 1.0.0-BETA23 -- Make `execute` and `PowerSyncTransaction` functions throwable for Swift +* Make `execute` and `PowerSyncTransaction` functions throwable for Swift ## 1.0.0-BETA22 -- Fix `updateHasSynced` internal null pointer exception +* Fix `updateHasSynced` internal null pointer exception ## 1.0.0-BETA21 -- Improve error handling for Swift by adding @Throws annotation so errors can be handled in Swift -- Throw PowerSync exceptions for all public facing methods +* Improve error handling for Swift by adding @Throws annotation so errors can be handled in Swift +* Throw PowerSync exceptions for all public facing methods ## 1.0.0-BETA20 -- Add cursor optional functions: `getStringOptional`, `getLongOptional`, `getDoubleOptional`, +* Add cursor optional functions: `getStringOptional`, `getLongOptional`, `getDoubleOptional`, `getBooleanOptional` and `getBytesOptional` when using the column name which allow for optional return types -- Throw errors for invalid column on all cursor functions -- `getString`, `getLong`, `getBytes`, `getDouble` and `getBoolean` used with the column name will +* Throw errors for invalid column on all cursor functions +* `getString`, `getLong`, `getBytes`, `getDouble` and `getBoolean` used with the column name will now throw an error for non-null values and expect a non optional return type ## 1.0.0-BETA19 -- Allow cursor to get values by column name e.g. `getStringOptional("id")` -- BREAKING CHANGE: If you were using `SqlCursor` from SqlDelight previously for your own custom +* Allow cursor to get values by column name e.g. `getStringOptional("id")` +* BREAKING CHANGE: If you were using `SqlCursor` from SqlDelight previously for your own custom mapper then you must now change to `SqlCursor` exported by the PowerSync module. Previously you would import it like this: @@ -292,80 +291,80 @@ Sqlite operation failure database is locked attempted to run migration and faile ## 1.0.0-BETA18 -- BREAKING CHANGE: Move from async sqldelight calls to synchronous calls. This will only affect +* BREAKING CHANGE: Move from async sqldelight calls to synchronous calls. This will only affect `readTransaction` and `writeTransaction`where the callback function is no longer asynchronous. ## 1.0.0-BETA17 -- Add fix for Windows using JVM build +* Add fix for Windows using JVM build ## 1.0.0-BETA16 -- Add `close` method to database methods -- Throw when error is a `CancellationError` and remove invalidation for all errors in +* Add `close` method to database methods +* Throw when error is a `CancellationError` and remove invalidation for all errors in `streamingSync` catch. ## 1.0.0-BETA15 -- Update powersync-sqlite-core to 0.3.8 -- Increase maximum amount of columns from 63 to 1999 +* Update powersync-sqlite-core to 0.3.8 +* Increase maximum amount of columns from 63 to 1999 ## 1.0.0-BETA14 -- Add JVM compatibility -- Revert previous iOS changes as they resulted in further issues. +* Add JVM compatibility +* Revert previous iOS changes as they resulted in further issues. ## 1.0.0-BETA13 -- Move iOS database driver to use IO dispatcher which should avoid race conditions and improve +* Move iOS database driver to use IO dispatcher which should avoid race conditions and improve performance. ## 1.0.0-BETA12 -- Use transaction context in `writeTransaction` in `BucketStorageImpl`. +* Use transaction context in `writeTransaction` in `BucketStorageImpl`. ## 1.0.0-BETA11 -- Update version to fix deployment issue of previous release +* Update version to fix deployment issue of previous release ## 1.0.0-BETA10 -- Change Swift package name from `PowerSync` to `PowerSyncKotlin` +* Change Swift package name from `PowerSync` to `PowerSyncKotlin` ## 1.0.0-BETA9 -- Re-enable SKIE `SuspendInterop` -- Move transaction functions out of `PowerSyncTransactionFactory` to avoid threading issues in Swift +* Re-enable SKIE `SuspendInterop` +* Move transaction functions out of `PowerSyncTransactionFactory` to avoid threading issues in Swift SDK ## 1.0.0-BETA8 -- Disable SKIE `SuspendInterop` plugin to fix overriding `suspend` functions in Swift +* Disable SKIE `SuspendInterop` plugin to fix overriding `suspend` functions in Swift ## 1.0.0-BETA7 -- Update supabase connector to use supabase-kt version 3 -- Handle Postgres error codes in supabase connector +* Update supabase connector to use supabase-kt version 3 +* Handle Postgres error codes in supabase connector ## 1.0.0-BETA6 -- Fix Custom Write Checkpoint application logic +* Fix Custom Write Checkpoint application logic ## 1.0.0-BETA5 -- Fix `hasSynced` not updating after `disconnectAndClear` -- Fix error being thrown in iOS app launch +* Fix `hasSynced` not updating after `disconnectAndClear` +* Fix error being thrown in iOS app launch ## 1.0.0-BETA4 -- Fix sync status being reset when `update` function is run +* Fix sync status being reset when `update` function is run ## 1.0.0-BETA3 -- Add `waitForFirstSync` function - which resolves after the initial sync is completed -- Upgrade to Kotlin 2.0.20 - should not cause any issues with users who are still on Kotlin 1.9 -- Upgrade `powersync-sqlite-core` to 0.3.0 - improves incremental sync performance -- Add client sync parameters - which allows you specify sync parameters from the +* Add `waitForFirstSync` function - which resolves after the initial sync is completed +* Upgrade to Kotlin 2.0.20 - should not cause any issues with users who are still on Kotlin 1.9 +* Upgrade `powersync-sqlite-core` to 0.3.0 - improves incremental sync performance +* Add client sync parameters - which allows you specify sync parameters from the client https://docs.powersync.com/usage/sync-rules/advanced-topics/client-parameters-beta ```kotlin @@ -383,25 +382,25 @@ params = params ) ``` -- Add schema validation when schema is generated -- Add warning message if there is a crudItem in the queue that has not yet been synced and after a +* Add schema validation when schema is generated +* Add warning message if there is a crudItem in the queue that has not yet been synced and after a delay rerun the upload ## 1.0.0-BETA2 -- Publish persistence package +* Publish persistence package ## 1.0.0-BETA1 -- Improve API by changing from Builder pattern to simply instantiating the database +* Improve API by changing from Builder pattern to simply instantiating the database `PowerSyncDatabase` E.g. `val db = PowerSyncDatabase(factory, schema)` -- Use callback context in transactions +* Use callback context in transactions E.g. `db.writeTransaction{ ctx -> ctx.execute(...) }` -- Removed unnecessary expiredAt field -- Added table max column validation as there is a hard limit of 63 columns -- Moved SQLDelight models to a separate module to reduce export size -- Replaced default Logger with [Kermit Logger](https://kermit.touchlab.co/) which allows users to +* Removed unnecessary expiredAt field +* Added table max column validation as there is a hard limit of 63 columns +* Moved SQLDelight models to a separate module to reduce export size +* Replaced default Logger with [Kermit Logger](https://kermit.touchlab.co/) which allows users to more easily use and/or change Logger settings -- Add `retryDelay` and `crudThrottle` options when setting up database connection -- Changed `_viewNameOverride` to `viewNameOverride` +* Add `retryDelay` and `crudThrottle` options when setting up database connection +* Changed `_viewNameOverride` to `viewNameOverride` \ No newline at end of file From 14a7342d90c765a876b0de057ebc9ba84798c326 Mon Sep 17 00:00:00 2001 From: stevensJourney Date: Mon, 10 Nov 2025 12:19:18 +0200 Subject: [PATCH 3/3] Linter fix --- .../powersync/pool/SwiftSQLiteConnectionPool.kt | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/internal/PowerSyncKotlin/src/appleMain/kotlin/com/powersync/pool/SwiftSQLiteConnectionPool.kt b/internal/PowerSyncKotlin/src/appleMain/kotlin/com/powersync/pool/SwiftSQLiteConnectionPool.kt index 3725a98a..077ba81c 100644 --- a/internal/PowerSyncKotlin/src/appleMain/kotlin/com/powersync/pool/SwiftSQLiteConnectionPool.kt +++ b/internal/PowerSyncKotlin/src/appleMain/kotlin/com/powersync/pool/SwiftSQLiteConnectionPool.kt @@ -106,12 +106,13 @@ public fun openPowerSyncWithPool( schema = schema, identifier = identifier, logger = logger, - dispatchStrategy = DispatchStrategy.Custom( - object : DispatchFunction { - override suspend fun invoke(block: suspend () -> R): R { - // We leave the dispatching up to the pool - return block() - } - }, - ), + dispatchStrategy = + DispatchStrategy.Custom( + object : DispatchFunction { + override suspend fun invoke(block: suspend () -> R): R { + // We leave the dispatching up to the pool + return block() + } + }, + ), )