@@ -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