Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ suspend fun getData(): Result<Data> = withContext(Dispatchers.IO) {
- NEVER manually append the `Throwable`'s message or any other props to the string passed as the 1st param of `Logger.*` calls, its internals are already enriching the final log message with the details of the `Throwable` passed via the `e` arg
- ALWAYS wrap parameter values in log messages with single quotes, e.g. `Logger.info("Received event '$eventName'", context = TAG)`
- ALWAYS start log messages with a verb, e.g. `Logger.info("Received payment for '$hash'", context = TAG)`
- ALWAYS keep log names, tags, labels, and references mechanically traceable to the caller. Do not rewrite them into long descriptions.
- ALWAYS log errors at the final handling layer where the error is acted upon, not in intermediate layers that just propagate it
- ALWAYS use the Result API instead of try-catch
- NEVER wrap methods returning `Result<T>` in try-catch
Expand Down
20 changes: 2 additions & 18 deletions app/src/main/java/to/bitkit/async/ServiceQueue.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import to.bitkit.ext.callerName
import to.bitkit.utils.AppError
import to.bitkit.utils.measured
import java.util.concurrent.Executors
import java.util.concurrent.ThreadFactory
import kotlin.coroutines.CoroutineContext
Expand All @@ -20,30 +18,16 @@ enum class ServiceQueue {

fun <T> blocking(
coroutineContext: CoroutineContext = scope.coroutineContext,
functionName: String = Thread.currentThread().callerName,
block: suspend CoroutineScope.() -> T,
): T = runBlocking(coroutineContext) {
runCatching {
measured(label = functionName, context = TAG) {
block()
}
}.getOrElse { throw AppError(it) }
runCatching { block() }.getOrElse { throw AppError(it) }
}

suspend fun <T> background(
coroutineContext: CoroutineContext = scope.coroutineContext,
functionName: String = Thread.currentThread().callerName,
block: suspend CoroutineScope.() -> T,
): T = withContext(coroutineContext) {
runCatching {
measured(label = functionName, context = TAG) {
block()
}
}.getOrElse { throw AppError(it) }
}

companion object {
private const val TAG = "ServiceQueue"
runCatching { block() }.getOrElse { throw AppError(it) }
}
}

Expand Down
3 changes: 2 additions & 1 deletion app/src/main/java/to/bitkit/fcm/WakeNodeWorker.kt
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ class WakeNodeWorker @AssistedInject constructor(
private var notificationPayload: JsonObject? = null

private val timeout = 2.minutes
private val slowWakeNodeThreshold = 10.seconds
private val deliverSignal = CompletableDeferred<Unit>()

override suspend fun doWork(): Result {
Expand All @@ -80,7 +81,7 @@ class WakeNodeWorker @AssistedInject constructor(
}

return runCatching {
measured(label = "doWork", context = TAG) {
measured(label = "doWork", context = TAG, slowThreshold = slowWakeNodeThreshold) {
lightningRepo.start(
walletIndex = 0,
timeout = timeout,
Expand Down
48 changes: 48 additions & 0 deletions app/src/main/java/to/bitkit/repositories/LightningRepo.kt
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,7 @@ class LightningRepo @Inject constructor(

// Initial state sync
syncState()
logNodeSupportSummary("node started")
updateGeoBlockState()
refreshChannelCache()

Expand All @@ -376,6 +377,7 @@ class LightningRepo @Inject constructor(
connectToTrustedPeers().onFailure {
Logger.error("Failed to connect to trusted peers", it, context = TAG)
}
logNodeSupportSummary("trusted peers connected")

sync().onFailure { e ->
Logger.warn("Initial sync failed, event-driven sync will retry", e, context = TAG)
Expand Down Expand Up @@ -1294,6 +1296,52 @@ class LightningRepo @Inject constructor(
}
}

private fun logNodeSupportSummary(reason: String) {
val state = _lightningState.value
val connectedPeers = state.peers.count { it.isConnected }
val persistedPeers = state.peers.count { it.isPersisted }
val readyChannels = state.channels.count { it.isChannelReady }
val usableChannels = state.channels.count { it.isUsable }

Logger.info(
"Collected node support summary for '$reason': " +
"nodeId='${state.nodeId}', " +
"lifecycle='${state.nodeLifecycleState}', " +
"peers='${state.peers.size}', " +
"connectedPeers='$connectedPeers', " +
"persistedPeers='$persistedPeers', " +
"channels='${state.channels.size}', " +
"readyChannels='$readyChannels', " +
"usableChannels='$usableChannels'",
context = TAG,
)

state.peers.forEach {
Logger.info(
"Collected peer support summary for '$reason': " +
"nodeId='${it.nodeId}', " +
"address='${it.address}', " +
"connected='${it.isConnected}', " +
"persisted='${it.isPersisted}'",
context = TAG,
)
}

state.channels.forEach {
Logger.info(
"Collected channel support summary for '$reason': " +
"channelId='${it.channelId}', " +
"counterparty='${it.counterpartyNodeId}', " +
"ready='${it.isChannelReady}', " +
"usable='${it.isUsable}', " +
"announced='${it.isAnnounced}', " +
"outboundMsat='${it.outboundCapacityMsat}', " +
"inboundMsat='${it.inboundCapacityMsat}'",
context = TAG,
)
}
}

suspend fun awaitPeerConnected(timeout: Duration = 30.seconds) = withContext(bgDispatcher) {
if (lightningService.peers?.any { it.isConnected } == true) return@withContext
Logger.debug("Waiting for peer to reconnect (timeout='$timeout')...", context = TAG)
Expand Down
Loading
Loading