Skip to content

Commit 41be20a

Browse files
committed
fix(firmware): keep bundled release apply best-effort
1 parent 83962c2 commit 41be20a

1 file changed

Lines changed: 21 additions & 16 deletions

File tree

core/data/src/commonMain/kotlin/org/meshtastic/core/data/repository/FirmwareReleaseRepositoryImpl.kt

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -104,27 +104,31 @@ open class FirmwareReleaseRepositoryImpl(
104104
*/
105105
private suspend fun ensureSeeded() {
106106
if (bundleDecodeFailed) return // don't retry the bundled asset on every collection once it has failed
107-
seedMutex.withLock {
108-
// Decode the bundled JSON once per process; the asset never changes between launches.
109-
if (bundledSnapshot == null && !bundleDecodeFailed) {
110-
safeCatching { assetReader.decode<NetworkFirmwareReleases>("firmware_releases.json", json) }
111-
.onSuccess { snapshot -> bundledSnapshot = snapshot }
112-
.onFailure { e ->
113-
Logger.w(e) { "FirmwareReleaseRepository: failed to decode bundled JSON" }
107+
108+
// seedMutex guards only the decode + snapshot cache; the DB apply runs outside it (so concurrent
109+
// collectors don't block on a Room write or on refreshMutex) and under refreshMutex (so it can't
110+
// race singleFlightRefresh and overwrite fresher data that just arrived from the API).
111+
val bundled =
112+
seedMutex.withLock {
113+
// Decode the bundled JSON once per process; the asset never changes between launches.
114+
if (bundledSnapshot == null && !bundleDecodeFailed) {
115+
safeCatching { assetReader.decode<NetworkFirmwareReleases>("firmware_releases.json", json) }
116+
.onSuccess { snapshot -> bundledSnapshot = snapshot }
117+
.onFailure { e ->
118+
Logger.w(e) { "FirmwareReleaseRepository: failed to decode bundled JSON" }
119+
bundleDecodeFailed = true
120+
}
121+
// Decode returning null (asset missing) also stops further retries.
122+
if (bundledSnapshot == null && !bundleDecodeFailed) {
123+
Logger.w { "FirmwareReleaseRepository: no bundled releases available to seed from" }
114124
bundleDecodeFailed = true
115125
}
116-
// Decode returning null (asset missing) also stops further retries.
117-
if (bundledSnapshot == null && !bundleDecodeFailed) {
118-
Logger.w { "FirmwareReleaseRepository: no bundled releases available to seed from" }
119-
bundleDecodeFailed = true
120126
}
121-
}
122-
123-
val bundled = bundledSnapshot?.releases ?: return
127+
bundledSnapshot?.releases
128+
} ?: return
124129

130+
safeCatching {
125131
refreshMutex.withLock {
126-
// Re-evaluate against the current active DB under the same lock as network refresh so an older bundled
127-
// snapshot cannot overwrite fresher data that just arrived from the API.
128132
val toApply =
129133
listOf(FirmwareReleaseType.STABLE to bundled.stable, FirmwareReleaseType.ALPHA to bundled.alpha)
130134
.filter { (type, releases) -> isBundleNewerFor(type, releases) }
@@ -135,6 +139,7 @@ open class FirmwareReleaseRepositoryImpl(
135139
}
136140
}
137141
}
142+
.onFailure { e -> Logger.w(e) { "FirmwareReleaseRepository: failed to apply bundled snapshot" } }
138143
}
139144

140145
/** True when [bundled] contains a release newer than anything cached for [type]. */

0 commit comments

Comments
 (0)