From 68fda35f9ddaa8ebeebfa3251b846a912f662f86 Mon Sep 17 00:00:00 2001 From: DominicGBauer Date: Tue, 3 Dec 2024 13:17:12 +0200 Subject: [PATCH 1/4] fix: tranasactions resulting in error when using different threads --- CHANGELOG.md | 9 +++++++-- PowerSync/build.gradle.kts | 10 ---------- .../com/powersync/db/PowerSyncDatabaseImpl.kt | 3 +++ .../commonMain/kotlin/com/powersync/db/Queries.kt | 3 +++ .../powersync/db/internal/InternalDatabaseImpl.kt | 14 +++++++++----- .../powersync/db/internal/PowerSyncTransaction.kt | 5 +++++ .../db/internal/PowerSyncTransactionFactory.kt | 5 +++++ gradle.properties | 2 +- gradle/libs.versions.toml | 2 +- 9 files changed, 34 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ef3acb8..fae90615 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,13 +1,18 @@ # Changelog +## 1.0.0-BETA9 + +* Re-enable SKIE `SuspendInterop` +* Disable SKIE `SuspendInterop` for database transactions to avoid transactions not resolving in Swift + ## 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 +* Handle Postgres error codes in supabase connector ## 1.0.0-BETA6 diff --git a/PowerSync/build.gradle.kts b/PowerSync/build.gradle.kts index 0945296a..c7e5e2af 100644 --- a/PowerSync/build.gradle.kts +++ b/PowerSync/build.gradle.kts @@ -41,16 +41,6 @@ kotlin { } } -skie { - features { - group { - // We turn this off as the suspend interop feature results in - // threading issues when implementing SDK in Swift - SuspendInterop.Enabled(false) - } - } -} - kmmbridge { artifactManager.set(SonatypePortalPublishArtifactManager(project, repositoryName = null)) artifactManager.finalizeValue() diff --git a/core/src/commonMain/kotlin/com/powersync/db/PowerSyncDatabaseImpl.kt b/core/src/commonMain/kotlin/com/powersync/db/PowerSyncDatabaseImpl.kt index f7dbbae8..2d17cd60 100644 --- a/core/src/commonMain/kotlin/com/powersync/db/PowerSyncDatabaseImpl.kt +++ b/core/src/commonMain/kotlin/com/powersync/db/PowerSyncDatabaseImpl.kt @@ -4,6 +4,7 @@ import app.cash.sqldelight.async.coroutines.awaitAsList import app.cash.sqldelight.async.coroutines.awaitAsOne import app.cash.sqldelight.db.SqlCursor import co.touchlab.kermit.Logger +import co.touchlab.skie.configuration.annotations.SuspendInterop import com.powersync.DatabaseDriverFactory import com.powersync.PowerSyncDatabase import com.powersync.PsSqlDriver @@ -225,11 +226,13 @@ internal class PowerSyncDatabaseImpl( mapper: (SqlCursor) -> RowType, ): Flow> = internalDb.watch(sql, parameters, mapper) + @SuspendInterop.Disabled override suspend fun readTransaction(callback: suspend (tx: PowerSyncTransaction) -> R): R = internalDb.readTransaction { tx -> callback(tx) } + @SuspendInterop.Disabled override suspend fun writeTransaction(callback: suspend (tx: PowerSyncTransaction) -> R): R = internalDb.writeTransaction { tx -> callback(tx) diff --git a/core/src/commonMain/kotlin/com/powersync/db/Queries.kt b/core/src/commonMain/kotlin/com/powersync/db/Queries.kt index 95497436..751b7226 100644 --- a/core/src/commonMain/kotlin/com/powersync/db/Queries.kt +++ b/core/src/commonMain/kotlin/com/powersync/db/Queries.kt @@ -1,6 +1,7 @@ package com.powersync.db import app.cash.sqldelight.db.SqlCursor +import co.touchlab.skie.configuration.annotations.SuspendInterop import com.powersync.db.internal.PowerSyncTransaction import kotlinx.coroutines.flow.Flow @@ -51,7 +52,9 @@ public interface Queries { mapper: (SqlCursor) -> RowType, ): Flow> + @SuspendInterop.Disabled public suspend fun writeTransaction(callback: suspend (PowerSyncTransaction) -> R): R + @SuspendInterop.Disabled public suspend fun readTransaction(callback: suspend (PowerSyncTransaction) -> R): R } diff --git a/core/src/commonMain/kotlin/com/powersync/db/internal/InternalDatabaseImpl.kt b/core/src/commonMain/kotlin/com/powersync/db/internal/InternalDatabaseImpl.kt index 74785480..d4a4b36d 100644 --- a/core/src/commonMain/kotlin/com/powersync/db/internal/InternalDatabaseImpl.kt +++ b/core/src/commonMain/kotlin/com/powersync/db/internal/InternalDatabaseImpl.kt @@ -9,6 +9,8 @@ import app.cash.sqldelight.coroutines.mapToList import app.cash.sqldelight.db.QueryResult import app.cash.sqldelight.db.SqlCursor import app.cash.sqldelight.db.SqlPreparedStatement +import co.touchlab.skie.configuration.annotations.SuspendInterop +import com.persistence.PowersyncQueries import com.powersync.PowerSyncTransaction import com.powersync.PsSqlDriver import com.powersync.persistence.PsDatabase @@ -22,16 +24,16 @@ import kotlinx.coroutines.launch import kotlinx.serialization.encodeToString @OptIn(FlowPreview::class) -internal class InternalDatabaseImpl( +public class InternalDatabaseImpl( override val driver: PsSqlDriver, private val scope: CoroutineScope, ) : InternalDatabase { override val transactor: PsDatabase = PsDatabase(driver) - override val queries = transactor.powersyncQueries + override val queries: PowersyncQueries = transactor.powersyncQueries - companion object { - const val POWERSYNC_TABLE_MATCH = "(^ps_data__|^ps_data_local__)" - const val DEFAULT_WATCH_THROTTLE_MS = 30L + public companion object { + public const val POWERSYNC_TABLE_MATCH: String = "(^ps_data__|^ps_data_local__)" + public const val DEFAULT_WATCH_THROTTLE_MS: Long = 30L } init { @@ -157,12 +159,14 @@ internal class InternalDatabaseImpl( } } + @SuspendInterop.Disabled override suspend fun readTransaction(callback: suspend (PowerSyncTransaction) -> R): R = transactor.transactionWithResult(noEnclosing = true) { val transaction = PowerSyncTransaction(this@InternalDatabaseImpl) callback(transaction) } + @SuspendInterop.Disabled override suspend fun writeTransaction(callback: suspend (PowerSyncTransaction) -> R): R = transactor.transactionWithResult(noEnclosing = true) { val transaction = PowerSyncTransaction(this@InternalDatabaseImpl) diff --git a/core/src/commonMain/kotlin/com/powersync/db/internal/PowerSyncTransaction.kt b/core/src/commonMain/kotlin/com/powersync/db/internal/PowerSyncTransaction.kt index 6168b75d..50cd4fc4 100644 --- a/core/src/commonMain/kotlin/com/powersync/db/internal/PowerSyncTransaction.kt +++ b/core/src/commonMain/kotlin/com/powersync/db/internal/PowerSyncTransaction.kt @@ -1,25 +1,30 @@ package com.powersync.db.internal import app.cash.sqldelight.db.SqlCursor +import co.touchlab.skie.configuration.annotations.SuspendInterop public interface PowerSyncTransaction { + @SuspendInterop.Disabled public suspend fun execute( sql: String, parameters: List? = listOf(), ): Long + @SuspendInterop.Disabled public suspend fun getOptional( sql: String, parameters: List? = listOf(), mapper: (SqlCursor) -> RowType, ): RowType? + @SuspendInterop.Disabled public suspend fun getAll( sql: String, parameters: List? = listOf(), mapper: (SqlCursor) -> RowType, ): List + @SuspendInterop.Disabled public suspend fun get( sql: String, parameters: List? = listOf(), diff --git a/core/src/commonMain/kotlin/com/powersync/db/internal/PowerSyncTransactionFactory.kt b/core/src/commonMain/kotlin/com/powersync/db/internal/PowerSyncTransactionFactory.kt index f5a32508..91868e10 100644 --- a/core/src/commonMain/kotlin/com/powersync/db/internal/PowerSyncTransactionFactory.kt +++ b/core/src/commonMain/kotlin/com/powersync/db/internal/PowerSyncTransactionFactory.kt @@ -1,29 +1,34 @@ package com.powersync import app.cash.sqldelight.db.SqlCursor +import co.touchlab.skie.configuration.annotations.SuspendInterop import com.powersync.db.internal.InternalDatabaseImpl import com.powersync.db.internal.PowerSyncTransaction internal fun PowerSyncTransaction(internalDatabase: InternalDatabaseImpl): PowerSyncTransaction { val transaction = object : PowerSyncTransaction { + @SuspendInterop.Disabled override suspend fun execute( sql: String, parameters: List?, ): Long = internalDatabase.execute(sql, parameters ?: emptyList()) + @SuspendInterop.Disabled override suspend fun get( sql: String, parameters: List?, mapper: (SqlCursor) -> RowType, ): RowType = internalDatabase.get(sql, parameters ?: emptyList(), mapper) + @SuspendInterop.Disabled override suspend fun getAll( sql: String, parameters: List?, mapper: (SqlCursor) -> RowType, ): List = internalDatabase.getAll(sql, parameters ?: emptyList(), mapper) + @SuspendInterop.Disabled override suspend fun getOptional( sql: String, parameters: List?, diff --git a/gradle.properties b/gradle.properties index 931868e4..62b5f235 100644 --- a/gradle.properties +++ b/gradle.properties @@ -17,7 +17,7 @@ development=true RELEASE_SIGNING_ENABLED=true # Library config GROUP=com.powersync -LIBRARY_VERSION=1.0.0-BETA8 +LIBRARY_VERSION=1.0.0-BETA9 GITHUB_REPO=https://github.com/powersync-ja/powersync-kotlin.git # POM POM_URL=https://github.com/powersync-ja/powersync-kotlin/ diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d0ae66ac..86c48d74 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -29,7 +29,7 @@ compose-preview = "1.7.2" # plugins android-gradle-plugin = "8.5.1" kmmBridge = "0.5.7" -skie = "0.9.3" +skie = "0.9.5" maven-publish = "0.27.0" download-plugin = "5.5.0" grammerKit = "0.1.12" From 0eae53aaf93b39410a1e7e4dad234564e04c01f6 Mon Sep 17 00:00:00 2001 From: DominicGBauer Date: Wed, 4 Dec 2024 13:03:25 +0200 Subject: [PATCH 2/4] fix: transactions issue --- .../com/powersync/db/PowerSyncDatabaseImpl.kt | 2 - .../kotlin/com/powersync/db/Queries.kt | 3 -- .../db/internal/InternalDatabaseImpl.kt | 31 +++++++++++--- .../db/internal/PowerSyncTransaction.kt | 5 --- .../internal/PowerSyncTransactionFactory.kt | 40 ------------------- 5 files changed, 25 insertions(+), 56 deletions(-) delete mode 100644 core/src/commonMain/kotlin/com/powersync/db/internal/PowerSyncTransactionFactory.kt diff --git a/core/src/commonMain/kotlin/com/powersync/db/PowerSyncDatabaseImpl.kt b/core/src/commonMain/kotlin/com/powersync/db/PowerSyncDatabaseImpl.kt index 2d17cd60..6bb096ab 100644 --- a/core/src/commonMain/kotlin/com/powersync/db/PowerSyncDatabaseImpl.kt +++ b/core/src/commonMain/kotlin/com/powersync/db/PowerSyncDatabaseImpl.kt @@ -226,13 +226,11 @@ internal class PowerSyncDatabaseImpl( mapper: (SqlCursor) -> RowType, ): Flow> = internalDb.watch(sql, parameters, mapper) - @SuspendInterop.Disabled override suspend fun readTransaction(callback: suspend (tx: PowerSyncTransaction) -> R): R = internalDb.readTransaction { tx -> callback(tx) } - @SuspendInterop.Disabled override suspend fun writeTransaction(callback: suspend (tx: PowerSyncTransaction) -> R): R = internalDb.writeTransaction { tx -> callback(tx) diff --git a/core/src/commonMain/kotlin/com/powersync/db/Queries.kt b/core/src/commonMain/kotlin/com/powersync/db/Queries.kt index 751b7226..95497436 100644 --- a/core/src/commonMain/kotlin/com/powersync/db/Queries.kt +++ b/core/src/commonMain/kotlin/com/powersync/db/Queries.kt @@ -1,7 +1,6 @@ package com.powersync.db import app.cash.sqldelight.db.SqlCursor -import co.touchlab.skie.configuration.annotations.SuspendInterop import com.powersync.db.internal.PowerSyncTransaction import kotlinx.coroutines.flow.Flow @@ -52,9 +51,7 @@ public interface Queries { mapper: (SqlCursor) -> RowType, ): Flow> - @SuspendInterop.Disabled public suspend fun writeTransaction(callback: suspend (PowerSyncTransaction) -> R): R - @SuspendInterop.Disabled public suspend fun readTransaction(callback: suspend (PowerSyncTransaction) -> R): R } diff --git a/core/src/commonMain/kotlin/com/powersync/db/internal/InternalDatabaseImpl.kt b/core/src/commonMain/kotlin/com/powersync/db/internal/InternalDatabaseImpl.kt index d4a4b36d..9facc2b1 100644 --- a/core/src/commonMain/kotlin/com/powersync/db/internal/InternalDatabaseImpl.kt +++ b/core/src/commonMain/kotlin/com/powersync/db/internal/InternalDatabaseImpl.kt @@ -9,9 +9,7 @@ import app.cash.sqldelight.coroutines.mapToList import app.cash.sqldelight.db.QueryResult import app.cash.sqldelight.db.SqlCursor import app.cash.sqldelight.db.SqlPreparedStatement -import co.touchlab.skie.configuration.annotations.SuspendInterop import com.persistence.PowersyncQueries -import com.powersync.PowerSyncTransaction import com.powersync.PsSqlDriver import com.powersync.persistence.PsDatabase import com.powersync.utils.JsonUtil @@ -30,6 +28,31 @@ public class InternalDatabaseImpl( ) : InternalDatabase { override val transactor: PsDatabase = PsDatabase(driver) override val queries: PowersyncQueries = transactor.powersyncQueries + private val transaction = + object : PowerSyncTransaction { + override suspend fun execute( + sql: String, + parameters: List?, + ): Long = this@InternalDatabaseImpl.execute(sql, parameters ?: emptyList()) + + override suspend fun get( + sql: String, + parameters: List?, + mapper: (SqlCursor) -> RowType, + ): RowType = this@InternalDatabaseImpl.get(sql, parameters ?: emptyList(), mapper) + + override suspend fun getAll( + sql: String, + parameters: List?, + mapper: (SqlCursor) -> RowType, + ): List = this@InternalDatabaseImpl.getAll(sql, parameters ?: emptyList(), mapper) + + override suspend fun getOptional( + sql: String, + parameters: List?, + mapper: (SqlCursor) -> RowType, + ): RowType? = this@InternalDatabaseImpl.getOptional(sql, parameters ?: emptyList(), mapper) + } public companion object { public const val POWERSYNC_TABLE_MATCH: String = "(^ps_data__|^ps_data_local__)" @@ -159,17 +182,13 @@ public class InternalDatabaseImpl( } } - @SuspendInterop.Disabled override suspend fun readTransaction(callback: suspend (PowerSyncTransaction) -> R): R = transactor.transactionWithResult(noEnclosing = true) { - val transaction = PowerSyncTransaction(this@InternalDatabaseImpl) callback(transaction) } - @SuspendInterop.Disabled override suspend fun writeTransaction(callback: suspend (PowerSyncTransaction) -> R): R = transactor.transactionWithResult(noEnclosing = true) { - val transaction = PowerSyncTransaction(this@InternalDatabaseImpl) callback(transaction) } diff --git a/core/src/commonMain/kotlin/com/powersync/db/internal/PowerSyncTransaction.kt b/core/src/commonMain/kotlin/com/powersync/db/internal/PowerSyncTransaction.kt index 50cd4fc4..6168b75d 100644 --- a/core/src/commonMain/kotlin/com/powersync/db/internal/PowerSyncTransaction.kt +++ b/core/src/commonMain/kotlin/com/powersync/db/internal/PowerSyncTransaction.kt @@ -1,30 +1,25 @@ package com.powersync.db.internal import app.cash.sqldelight.db.SqlCursor -import co.touchlab.skie.configuration.annotations.SuspendInterop public interface PowerSyncTransaction { - @SuspendInterop.Disabled public suspend fun execute( sql: String, parameters: List? = listOf(), ): Long - @SuspendInterop.Disabled public suspend fun getOptional( sql: String, parameters: List? = listOf(), mapper: (SqlCursor) -> RowType, ): RowType? - @SuspendInterop.Disabled public suspend fun getAll( sql: String, parameters: List? = listOf(), mapper: (SqlCursor) -> RowType, ): List - @SuspendInterop.Disabled public suspend fun get( sql: String, parameters: List? = listOf(), diff --git a/core/src/commonMain/kotlin/com/powersync/db/internal/PowerSyncTransactionFactory.kt b/core/src/commonMain/kotlin/com/powersync/db/internal/PowerSyncTransactionFactory.kt deleted file mode 100644 index 91868e10..00000000 --- a/core/src/commonMain/kotlin/com/powersync/db/internal/PowerSyncTransactionFactory.kt +++ /dev/null @@ -1,40 +0,0 @@ -package com.powersync - -import app.cash.sqldelight.db.SqlCursor -import co.touchlab.skie.configuration.annotations.SuspendInterop -import com.powersync.db.internal.InternalDatabaseImpl -import com.powersync.db.internal.PowerSyncTransaction - -internal fun PowerSyncTransaction(internalDatabase: InternalDatabaseImpl): PowerSyncTransaction { - val transaction = - object : PowerSyncTransaction { - @SuspendInterop.Disabled - override suspend fun execute( - sql: String, - parameters: List?, - ): Long = internalDatabase.execute(sql, parameters ?: emptyList()) - - @SuspendInterop.Disabled - override suspend fun get( - sql: String, - parameters: List?, - mapper: (SqlCursor) -> RowType, - ): RowType = internalDatabase.get(sql, parameters ?: emptyList(), mapper) - - @SuspendInterop.Disabled - override suspend fun getAll( - sql: String, - parameters: List?, - mapper: (SqlCursor) -> RowType, - ): List = internalDatabase.getAll(sql, parameters ?: emptyList(), mapper) - - @SuspendInterop.Disabled - override suspend fun getOptional( - sql: String, - parameters: List?, - mapper: (SqlCursor) -> RowType, - ): RowType? = internalDatabase.getOptional(sql, parameters ?: emptyList(), mapper) - } - - return transaction -} From eea39513a5da39210670f65fd9ee976aca6ceb8d Mon Sep 17 00:00:00 2001 From: DominicGBauer Date: Wed, 4 Dec 2024 13:05:00 +0200 Subject: [PATCH 3/4] chore: remove public --- .../com/powersync/db/internal/InternalDatabaseImpl.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/commonMain/kotlin/com/powersync/db/internal/InternalDatabaseImpl.kt b/core/src/commonMain/kotlin/com/powersync/db/internal/InternalDatabaseImpl.kt index 9facc2b1..9c5fb369 100644 --- a/core/src/commonMain/kotlin/com/powersync/db/internal/InternalDatabaseImpl.kt +++ b/core/src/commonMain/kotlin/com/powersync/db/internal/InternalDatabaseImpl.kt @@ -22,7 +22,7 @@ import kotlinx.coroutines.launch import kotlinx.serialization.encodeToString @OptIn(FlowPreview::class) -public class InternalDatabaseImpl( +internal class InternalDatabaseImpl( override val driver: PsSqlDriver, private val scope: CoroutineScope, ) : InternalDatabase { @@ -54,9 +54,9 @@ public class InternalDatabaseImpl( ): RowType? = this@InternalDatabaseImpl.getOptional(sql, parameters ?: emptyList(), mapper) } - public companion object { - public const val POWERSYNC_TABLE_MATCH: String = "(^ps_data__|^ps_data_local__)" - public const val DEFAULT_WATCH_THROTTLE_MS: Long = 30L + companion object { + const val POWERSYNC_TABLE_MATCH: String = "(^ps_data__|^ps_data_local__)" + const val DEFAULT_WATCH_THROTTLE_MS: Long = 30L } init { From 1b48058a8187ccb669b4f096e9f9e9c3502eed41 Mon Sep 17 00:00:00 2001 From: DominicGBauer Date: Wed, 4 Dec 2024 13:28:47 +0200 Subject: [PATCH 4/4] docs: update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fae90615..53fde3b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ ## 1.0.0-BETA9 * Re-enable SKIE `SuspendInterop` -* Disable SKIE `SuspendInterop` for database transactions to avoid transactions not resolving in Swift +* Move transaction functions out of `PowerSyncTransactionFactory` to avoid threading issues in Swift SDK ## 1.0.0-BETA8