mirror of
https://github.com/pezkuwichain/pezkuwi-wallet-android.git
synced 2026-04-21 23:48:00 +00:00
fix: prevent staking dashboard infinite loading
- Add maxAttempts parameter to retryUntilDone (default unlimited for backward compat), use 5 retries for staking stats fetch - Catch fetchStakingStats failure in dashboard update system and fallback to empty stats instead of hanging forever - Restore stale scope detection in ComputationalCache so cancelled aggregate scopes are recreated instead of returning stale entries
This commit is contained in:
+11
-6
@@ -13,6 +13,7 @@ import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
@@ -72,16 +73,20 @@ internal class RealComputationalCache : ComputationalCache, CoroutineScope by Co
|
||||
cachedAction: AwaitableConstructor<T>
|
||||
): T {
|
||||
val awaitable = mutex.withLock {
|
||||
if (key in memory) {
|
||||
val existing = memory[key]
|
||||
if (existing != null && existing.aggregateScope.isActive) {
|
||||
Log.d(LOG_TAG, "Key $key requested - already present")
|
||||
|
||||
val entry = memory.getValue(key)
|
||||
existing.dependents += scope
|
||||
|
||||
entry.dependents += scope
|
||||
|
||||
entry.awaitable
|
||||
existing.awaitable
|
||||
} else {
|
||||
Log.d(LOG_TAG, "Key $key requested - creating new operation")
|
||||
if (existing != null) {
|
||||
Log.d(LOG_TAG, "Key $key requested - stale (aggregateScope cancelled), recreating")
|
||||
memory.remove(key)
|
||||
} else {
|
||||
Log.d(LOG_TAG, "Key $key requested - creating new operation")
|
||||
}
|
||||
|
||||
val aggregateScope = CoroutineScope(Dispatchers.Default)
|
||||
val awaitable = cachedAction(aggregateScope)
|
||||
|
||||
@@ -7,6 +7,7 @@ import kotlinx.coroutines.delay
|
||||
|
||||
suspend inline fun <T> retryUntilDone(
|
||||
retryStrategy: ReconnectStrategy = LinearReconnectStrategy(step = 500L),
|
||||
maxAttempts: Int = Int.MAX_VALUE,
|
||||
block: () -> T,
|
||||
): T {
|
||||
var attempt = 0
|
||||
@@ -17,10 +18,14 @@ suspend inline fun <T> retryUntilDone(
|
||||
if (blockResult.isSuccess) {
|
||||
return blockResult.requireValue()
|
||||
} else {
|
||||
Log.e("RetryUntilDone", "Failed to execute retriable operation:", blockResult.requireException())
|
||||
|
||||
attempt++
|
||||
|
||||
if (attempt >= maxAttempts) {
|
||||
throw blockResult.requireException()
|
||||
}
|
||||
|
||||
Log.e("RetryUntilDone", "Failed to execute retriable operation (attempt $attempt):", blockResult.requireException())
|
||||
|
||||
delay(retryStrategy.getTimeForReconnect(attempt))
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -39,7 +39,7 @@ class RealStakingStatsDataSource(
|
||||
stakingAccounts: StakingAccounts,
|
||||
stakingChains: List<Chain>
|
||||
): MultiChainStakingStats = withContext(Dispatchers.IO) {
|
||||
retryUntilDone {
|
||||
retryUntilDone(maxAttempts = 5) {
|
||||
val globalConfig = globalConfigDataSource.getGlobalConfig()
|
||||
val chainsByEndpoint = splitChainsByEndpoint(stakingChains, globalConfig)
|
||||
|
||||
|
||||
+8
-1
@@ -127,9 +127,16 @@ class RealStakingDashboardUpdateSystem(
|
||||
.onEach { latestOffChainSyncIndex.value = it.index }
|
||||
.throttleLast(offChainSyncDebounceRate)
|
||||
.mapLatest { (index, stakingAccounts) ->
|
||||
val stats = runCatching {
|
||||
stakingStatsDataSource.fetchStakingStats(stakingAccounts, stakingChains)
|
||||
}.getOrElse {
|
||||
Log.d("StakingDashboardUpdateSystem", "Failed to fetch staking stats after retries", it)
|
||||
emptyMap()
|
||||
}
|
||||
|
||||
MultiChainOffChainSyncResult(
|
||||
index = index,
|
||||
multiChainStakingStats = stakingStatsDataSource.fetchStakingStats(stakingAccounts, stakingChains),
|
||||
multiChainStakingStats = stats,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user