From b5cee31141bb5d3b4cdc33f3536ede88d33b3dff Mon Sep 17 00:00:00 2001 From: jvsena42 Date: Mon, 2 Feb 2026 08:05:08 -0300 Subject: [PATCH 1/3] fix: log channel monitor retrievals --- .../to/bitkit/services/MigrationService.kt | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/to/bitkit/services/MigrationService.kt b/app/src/main/java/to/bitkit/services/MigrationService.kt index 676c38cbe..8be55b9d7 100644 --- a/app/src/main/java/to/bitkit/services/MigrationService.kt +++ b/app/src/main/java/to/bitkit/services/MigrationService.kt @@ -1265,13 +1265,33 @@ class MigrationService @Inject constructor( val managerData = rnBackupClient.retrieve("channel_manager", fileGroup = "ldk") ?: return@runCatching - val monitors = coroutineScope { + val expectedCount = files.channelMonitors.size + val monitorResults = coroutineScope { files.channelMonitors.map { monitorFile -> async { val channelId = monitorFile.replace(".bin", "") - rnBackupClient.retrieveChannelMonitor(channelId) + channelId to rnBackupClient.retrieveChannelMonitor(channelId) } - }.mapNotNull { it.await() } + }.awaitAll() + } + + val failedMonitors = monitorResults.filter { it.second == null }.map { it.first } + if (failedMonitors.isNotEmpty()) { + Logger.error( + "Failed to retrieve ${failedMonitors.size}/$expectedCount channel monitors: " + + failedMonitors.joinToString(), + context = TAG + ) + } + + val monitors = monitorResults.mapNotNull { it.second } + + if (monitors.size < expectedCount) { + Logger.warn( + "Channel monitor count mismatch: expected $expectedCount, got ${monitors.size}. " + + "Some channels may not be recoverable.", + context = TAG + ) } if (monitors.isNotEmpty()) { From 979cbb45827a2aa88651dc48a7991266432ad773 Mon Sep 17 00:00:00 2001 From: jvsena42 Date: Mon, 2 Feb 2026 08:09:01 -0300 Subject: [PATCH 2/3] fix: log channel monitor retrievals --- app/src/main/java/to/bitkit/services/MigrationService.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/to/bitkit/services/MigrationService.kt b/app/src/main/java/to/bitkit/services/MigrationService.kt index 8be55b9d7..708f66a75 100644 --- a/app/src/main/java/to/bitkit/services/MigrationService.kt +++ b/app/src/main/java/to/bitkit/services/MigrationService.kt @@ -15,6 +15,7 @@ import com.synonym.bitkitcore.PaymentState import com.synonym.bitkitcore.PaymentType import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.async +import kotlinx.coroutines.awaitAll import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow From 3848adc22943d84ece437d18788c6a9611b8278b Mon Sep 17 00:00:00 2001 From: jvsena42 Date: Mon, 2 Feb 2026 08:43:53 -0300 Subject: [PATCH 3/3] fix: retry failed channel monitor retrievals during RN migration --- .../to/bitkit/services/MigrationService.kt | 29 +++++++++++++++++-- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/to/bitkit/services/MigrationService.kt b/app/src/main/java/to/bitkit/services/MigrationService.kt index 708f66a75..9a8c30c60 100644 --- a/app/src/main/java/to/bitkit/services/MigrationService.kt +++ b/app/src/main/java/to/bitkit/services/MigrationService.kt @@ -17,6 +17,7 @@ import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow @@ -1258,6 +1259,28 @@ class MigrationService @Inject constructor( Logger.info("Post-migration cleanup completed", context = TAG) } + private suspend fun retrieveChannelMonitorWithRetry( + channelId: String, + maxAttempts: Int = 3, + baseDelayMs: Long = 1000L, + ): ByteArray? { + repeat(maxAttempts) { attempt -> + val result = rnBackupClient.retrieveChannelMonitor(channelId) + if (result != null) return result + + if (attempt < maxAttempts - 1) { + val delayMs = baseDelayMs * (attempt + 1) + Logger.debug( + "Retrying channel monitor retrieval for $channelId " + + "(attempt ${attempt + 2}/$maxAttempts) after ${delayMs}ms", + context = TAG + ) + delay(delayMs) + } + } + return null + } + private suspend fun fetchRNRemoteLdkData() { runCatching { val files = rnBackupClient.listFiles(fileGroup = "ldk") ?: return@runCatching @@ -1271,7 +1294,7 @@ class MigrationService @Inject constructor( files.channelMonitors.map { monitorFile -> async { val channelId = monitorFile.replace(".bin", "") - channelId to rnBackupClient.retrieveChannelMonitor(channelId) + channelId to retrieveChannelMonitorWithRetry(channelId) } }.awaitAll() } @@ -1279,8 +1302,8 @@ class MigrationService @Inject constructor( val failedMonitors = monitorResults.filter { it.second == null }.map { it.first } if (failedMonitors.isNotEmpty()) { Logger.error( - "Failed to retrieve ${failedMonitors.size}/$expectedCount channel monitors: " + - failedMonitors.joinToString(), + "Failed to retrieve ${failedMonitors.size}/$expectedCount channel monitors " + + "after retries: ${failedMonitors.joinToString()}", context = TAG ) }