fix(database): use a single connection for in-memory test databases

configureCommon() applied setMultipleConnectionPool(maxNumOfReaders = 4)
to every database, including the in-memory ones used by tests. A read on
a pooled reader connection can observe a snapshot older than the latest
write on the writer connection, so a read immediately after a write may
return stale rows.

DeviceLinkRepositoryImplTest.reconcilePrunesShortCodesNoLongerInCatalog
read [a, b] (the pre-prune state) instead of [a] after a deleteNotIn —
passing locally but flaking on CI depending on connection-assignment
timing (failed shard-core on #5738; the identical code passed on #5780).

In-memory builders now pass multiConnection = false so reads serialize
behind writes on one connection. Production/file databases keep the
multi-reader pool.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
James Rich
2026-06-13 07:27:56 -05:00
committed by James Rich
parent b5dd6ff9e9
commit 613dee91bf
4 changed files with 17 additions and 8 deletions

View File

@@ -45,7 +45,7 @@ actual fun getDatabaseBuilder(dbName: String): RoomDatabase.Builder<MeshtasticDa
/** Returns a [RoomDatabase.Builder] configured for an in-memory Android database. */
actual fun getInMemoryDatabaseBuilder(): RoomDatabase.Builder<MeshtasticDatabase> =
Room.inMemoryDatabaseBuilder<MeshtasticDatabase>(factory = { MeshtasticDatabaseConstructor.initialize() })
.configureCommon()
.configureCommon(multiConnection = false)
.setDriver(BundledSQLiteDriver())
/** Returns the Android directory where database files are stored. */

View File

@@ -139,11 +139,20 @@ abstract class MeshtasticDatabase : RoomDatabase() {
abstract fun discoveryDao(): DiscoveryDao
companion object {
/** Configures a [RoomDatabase.Builder] with standard settings for this project. */
fun <T : RoomDatabase> RoomDatabase.Builder<T>.configureCommon(): RoomDatabase.Builder<T> =
this.fallbackToDestructiveMigration(dropAllTables = false)
.setMultipleConnectionPool(maxNumOfReaders = 4, maxNumOfWriters = 1)
.setQueryCoroutineContext(ioDispatcher)
/**
* Configures a [RoomDatabase.Builder] with standard settings for this project.
*
* @param multiConnection opens a multi-reader connection pool for concurrent reads. Production/file databases
* want this. In-memory databases (tests) MUST pass `false`: a pooled reader connection can serve a snapshot
* older than the latest write on the writer connection, so a read immediately after a write may observe stale
* rows — making read-after-write assertions non-deterministically flaky (see `DeviceLinkRepositoryImplTest`).
* A single connection serializes reads behind writes.
*/
fun <T : RoomDatabase> RoomDatabase.Builder<T>.configureCommon(
multiConnection: Boolean = true,
): RoomDatabase.Builder<T> = this.fallbackToDestructiveMigration(dropAllTables = false)
.apply { if (multiConnection) setMultipleConnectionPool(maxNumOfReaders = 4, maxNumOfWriters = 1) }
.setQueryCoroutineContext(ioDispatcher)
}
}

View File

@@ -51,7 +51,7 @@ actual fun getDatabaseBuilder(dbName: String): RoomDatabase.Builder<MeshtasticDa
/** Returns a [RoomDatabase.Builder] configured for an in-memory iOS database. */
actual fun getInMemoryDatabaseBuilder(): RoomDatabase.Builder<MeshtasticDatabase> =
Room.inMemoryDatabaseBuilder<MeshtasticDatabase>(factory = { MeshtasticDatabaseConstructor.initialize() })
.configureCommon()
.configureCommon(multiConnection = false)
.setDriver(BundledSQLiteDriver())
/** Returns the iOS directory where database files are stored. */

View File

@@ -57,7 +57,7 @@ actual fun getDatabaseBuilder(dbName: String): RoomDatabase.Builder<MeshtasticDa
/** Returns a [RoomDatabase.Builder] configured for an in-memory JVM database. */
actual fun getInMemoryDatabaseBuilder(): RoomDatabase.Builder<MeshtasticDatabase> =
Room.inMemoryDatabaseBuilder<MeshtasticDatabase>(factory = { MeshtasticDatabaseConstructor.initialize() })
.configureCommon()
.configureCommon(multiConnection = false)
.setDriver(BundledSQLiteDriver())
/** Returns the JVM/Desktop directory where database files are stored. */