feat: ability to provide custom dispatch implementations#287
feat: ability to provide custom dispatch implementations#287stevensJourney wants to merge 3 commits into
Conversation
simolus3
left a comment
There was a problem hiding this comment.
I like these changes, but I believe we may be able to simplify the API surface quite a bit by relying on interfaces already exposed by the standard library. Instead of introducing our own type for this, I think we can just use CoroutineContext here:
- I don't see the purpose of a
DispatchStrategy.Default, we should just take aCoroutineContextparameter that defaults toDispatchers.IO. - Everything that is expressible as a
DispatchFunctionimplementation would also be expressible as aContinuationInterceptor, so I don't see the point of having it.
I believe replacing dispatchStrategy: DispatchStrategy with databaseContext: CoroutineContext and then doing withContext(databaseContext) should cover all our needs here.
Another benefit is that coroutine contexts are composable with the + operator, so they're likely more convenient to use than this.
| DispatchStrategy.Custom( | ||
| object : DispatchFunction { | ||
| override suspend fun <R> invoke(block: suspend () -> R): R { | ||
| // We leave the dispatching up to the pool | ||
| return block() | ||
| } | ||
| }, | ||
| ), |
There was a problem hiding this comment.
If we used CoroutineContext here, this could e.g. be an EmptyCoroutineContext.
|
EmptyCoroutineContext does look like a better option. I'm closing this PR since the intention of this was meant to stop errors in the Swift SDK when using GRDB: I thought the work here prevented those issues, but even after removing the |
Summary
This PR introduces a new
DispatchStrategyAPI that allows users to customize how database operations are dispatched to coroutine contexts. By default, operations useDispatchers.IO, but users can now provide a customCoroutineDispatcheror a fully customDispatchFunctionfor complete control over the execution context.Motivation
The primary motivation for this feature is to support the Swift Connection Pool implementation, where the pool itself handles dispatching operations. In this case, we need to bypass PowerSync's default dispatching mechanism and let the pool manage the execution context.
Implementation Details
New Types
DispatchFunction- An interface that defines how to dispatch operations:operator invoketo enable convenient syntax:dispatchFunction { ... }DispatchStrategy- A sealed class with three variants:Default: UsesDispatchers.IO(the default behavior)Dispatcher: Accepts aCoroutineDispatcherdirectlyCustom: Accepts a customDispatchFunctionfor full controlChanges
dispatchStrategyparameter (defaults toDispatchStrategy.Default) to:PowerSyncDatabase()factory functionPowerSyncDatabase.opened()PowerSyncDatabase.openInMemory()InternalDatabaseImplto accept a non-nullableDispatchStrategyand use it for all database operationsDispatchStrategy.Defaultto internally useDispatcher(Dispatchers.IO)to avoid code duplicationSwift Connection Pool Integration
The Swift Connection Pool implementation uses
DispatchStrategy.Customwith a no-op dispatch function, allowing the pool to handle dispatching:Breaking Changes
None - this is a fully backward compatible addition. The
dispatchStrategyparameter has a default value ofDispatchStrategy.Default, which maintains the existing behavior.