-
I'm evaluating GRDB and one detail I wanted to clarify is the best way to read on the main actor. My understanding is that regardless of whether I use DatabaseQueue or DatabasePool, synchronous read from the main actor always involves waiting for another queue to perform the read. Is there any way to set things up so that there could be a read connection dedicated to the main actor? |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 17 replies
-
Hello@ @macdrevx, No, there is no built-in way to perform a MainActor-isolated database access, or to dedicate a read connection to the main actor: try dbQueue.read { db in
// It is possible to have this closure run on the main thread,
// but it is not MainActor-isolated.
} Your app can define its own MainActor-enforcing connection, though: @MainActor struct MainActorDatabaseReader {
/// The MainActor-dedicated read-only connection
private let dbQueue: DatabaseQueue
init(dbQueue: DatabaseQueue) {
self.dbQueue = dbQueue
}
func read<T>(_ value: (Database) throws -> T) throws -> T {
try dbQueue.read(value)
}
}
// MainActorDatabaseReader performs reads on the main actor.
// The compiler does not emit any warning or error for the following code:
@MainActor var myValue = "foobar"
@MainActor func readMyValue(_ reader: MainActorDatabaseReader) throws -> String {
try reader.read { db in
myValue // Fine, because this closure in MainActor-isolated
}
} Instantiate a This technique is somewhat "exotic", but valid. GRDB aims at being able to address specific application needs, so I will not discourage you from doing so. I'm not sure this is the best advice, though, because you did not tell what is the real problem you want to solve, and for which you think that "a read connection dedicated to the main actor" would be a solution (see the "XY problem"). Mind that if you want to observe database changes with |
Beta Was this translation helpful? Give feedback.
Hi again @macdrevx. I had a closer look, and I discovered that I spoke too fast.
It is not possible to guarantee that all sync database accesses from the main thread are never blocked by background jobs.
Yes it can work for the
read
method and its siblings, but it is not for other kinds of sync accesses from the main thread, such as starting aValueObservation
with theimmediate
scheduling. There is no way for the library user to guess which kinds of sync accesses from the main thread would be guaranteed, and which ones would not. If DatabasePool would dedicate a connection to the main thread in specific scenarios only, its behavior could not be described in a simple way. Users would not …