mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-07-13 14:52:12 -04:00
fix(database): route all one-shot DB writes through the merge drain barrier (#6233)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -383,9 +383,15 @@ open class DatabaseManager(
|
||||
}
|
||||
}
|
||||
|
||||
/** Atomically snapshots the active DB and registers a writer against it. Returns null if none is open. */
|
||||
private suspend fun beginWrite(): MeshtasticDatabase? = writerTrackerMutex.withLock {
|
||||
val db = _currentDb.value ?: return@withLock null
|
||||
/**
|
||||
* Atomically snapshots the active DB and registers a writer against it. Before the first [switchActiveDatabase]
|
||||
* `_currentDb` is still null, so fall back to the public [currentDb] view — the eagerly-opened default DB — giving
|
||||
* withDb the exact DB-resolution semantics of a direct `currentDb.value` caller. Desktop never calls [init] until a
|
||||
* device is selected, so without the fallback pre-connection writes (quick-chat actions, firmware/hardware cache
|
||||
* refreshes) would silently no-op there instead of landing in the default DB the pre-connection flows read from.
|
||||
*/
|
||||
private suspend fun beginWrite(): MeshtasticDatabase = writerTrackerMutex.withLock {
|
||||
val db = _currentDb.value ?: currentDb.value
|
||||
activeWriters[db] = (activeWriters[db] ?: 0) + 1
|
||||
db
|
||||
}
|
||||
@@ -433,7 +439,7 @@ open class DatabaseManager(
|
||||
|
||||
@Suppress("ReturnCount", "ThrowsCount", "TooGenericExceptionCaught", "CyclomaticComplexMethod")
|
||||
private suspend fun <T> withCurrentDb(block: suspend (MeshtasticDatabase) -> T): T? {
|
||||
val db = beginWrite() ?: return null
|
||||
val db = beginWrite()
|
||||
val active = currentDbName
|
||||
markLastUsed(active)
|
||||
try {
|
||||
|
||||
@@ -21,6 +21,14 @@ import kotlinx.coroutines.flow.StateFlow
|
||||
/**
|
||||
* Provides multiplatform access to the current [MeshtasticDatabase] and a safe transactional helper. Platform
|
||||
* implementations manage the concrete lifecycle (Room on Android, etc.).
|
||||
*
|
||||
* **Write policy:** every one-shot DAO *write* (insert/upsert/update/delete/clear) must go through [withDb], never
|
||||
* `currentDb.value` directly. [withDb] registers the write with the cross-transport merge drain barrier (see
|
||||
* [DatabaseManager.associateDevice]) so a merge can't snapshot a database while the write is still in flight and lose
|
||||
* it when that database is retired — and it retries once if the pool closes under a concurrent DB switch. Direct
|
||||
* `currentDb.value` is fine for one-shot *reads* (a torn read against a just-retired DB is recoverable and reads don't
|
||||
* need drain visibility), and `currentDb` itself is the right latch for Flow/Paging factories, which must re-latch on
|
||||
* DB switch and must stay out of [withDb]'s containment lane.
|
||||
*/
|
||||
interface DatabaseProvider {
|
||||
/** Reactive stream of the currently active database instance. */
|
||||
|
||||
Reference in New Issue
Block a user