Skip to content

Commit 39ad68f

Browse files
committed
fix(grpc): pre-connect channels on foreground and instrument grab timing
warmUp() was calling enterIdle() which tears down connections, causing ~1.5-3s cold connect on the first grab RPC. Changed to getState(true) which triggers background TCP+TLS+HTTP/2 connection while idle. Added timedTraceSuspend instrumentation to handleMultiMintScan to identify per-step timing breakdown, channel state logging before pollMessages, and post-grab token metadata timing.
1 parent 5474c11 commit 39ad68f

4 files changed

Lines changed: 38 additions & 8 deletions

File tree

services/opencode/src/main/kotlin/com/getcode/opencode/internal/network/api/MessagingApi.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import com.getcode.opencode.internal.annotations.OpenCodeManagedChannel
99
import com.getcode.opencode.internal.network.core.GrpcApi
1010
import com.getcode.opencode.internal.network.extensions.asRendezvousKey
1111
import com.getcode.opencode.internal.network.extensions.sign
12+
import com.getcode.utils.trace
1213
import com.google.protobuf.ByteString
1314
import io.grpc.ManagedChannel
1415
import kotlinx.coroutines.Dispatchers
@@ -117,6 +118,9 @@ internal class MessagingApi @Inject constructor(
117118
suspend fun pollMessages(
118119
rendezvous: KeyPair
119120
): MessagingService.PollMessagesResponse {
121+
val channelState = managedChannels.first().getState(false)
122+
trace(tag = "gRPC", message = "pollMessages channel state: $channelState")
123+
120124
val request = MessagingService.PollMessagesRequest.newBuilder()
121125
.setRendezvousKey(rendezvous.asRendezvousKey())
122126
.apply { setSignature(sign(rendezvous)) }

services/opencode/src/main/kotlin/com/getcode/opencode/internal/network/core/GrpcApi.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ abstract class GrpcApi(protected val managedChannels: List<ManagedChannel>): Def
2020
}
2121

2222
private fun warmUp() {
23-
managedChannels.onEach { it.enterIdle() }
23+
// getState(true) requests a connection attempt if idle,
24+
// pre-connecting TCP + TLS + HTTP/2 in the background
25+
managedChannels.onEach { it.getState(true) }
2426
}
2527
}

services/opencode/src/main/kotlin/com/getcode/opencode/internal/transactors/GrabBillTransactor.kt

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import com.getcode.opencode.model.transactions.TransactionMetadata
1212
import com.getcode.opencode.providers.TokenMetadataProvider
1313
import com.getcode.utils.CodeServerError
1414
import com.getcode.utils.NotifiableError
15+
import com.getcode.utils.timedTraceSuspend
1516
import kotlinx.coroutines.CoroutineScope
1617
import kotlinx.coroutines.cancel
1718

@@ -100,35 +101,54 @@ internal class GrabBillTransactor(
100101
private suspend fun handleMultiMintScan(
101102
ownerKey: AccountCluster,
102103
data: OpenCodePayload
103-
): Result<TransactionMetadata.SendPublicPayment> {
104+
): Result<TransactionMetadata.SendPublicPayment> = timedTraceSuspend(
105+
message = "handleMultiMintScan",
106+
tag = tag,
107+
) { onStep ->
104108
// 1. Wait for the give request from the sender so we can determine what mint we are operating on
105109
val (messageId, giveRequestMint, exchangeData, mintMetadata) = messagingController.pollForGiveRequest(data.rendezvous)
106110
.getOrNull()
107-
?: return logAndFail(GrabTransactorError.Other(message = "No give request found for rendezvous"))
111+
?: run {
112+
onStep("pollForGiveRequest")
113+
return@timedTraceSuspend logAndFail(GrabTransactorError.Other(message = "No give request found for rendezvous"))
114+
}
115+
onStep("pollForGiveRequest")
108116

109117
// 2. Utilize the mint from the give request to get the Token metadata
110118
val token = mintMetadata
111119
?: (tokenProvider.getTokenMetadata(giveRequestMint)
112120
.getOrNull()?.token
113-
?: return logAndFail(GrabTransactorError.Other(message = "No token found for proposed mint")))
121+
?: run {
122+
onStep("tokenMetadata")
123+
return@timedTraceSuspend logAndFail(GrabTransactorError.Other(message = "No token found for proposed mint"))
124+
})
125+
onStep("tokenMetadata")
114126

115127
val tokenizedCluster = ownerKey.withTimelockForToken(token)
116128

117129
// 3. create an account if we don't currently have one for this token
118-
if (!accountController.hasAccountFor(token.address)) {
130+
val needsAccount = !accountController.hasAccountFor(token.address)
131+
if (needsAccount) {
119132
accountController.createUserAccount(
120133
ownerForMint = tokenizedCluster,
121134
mint = token.address
122-
).onFailure { return handleGrabError(it) }
135+
).onFailure {
136+
onStep("createUserAccount (needed=true)")
137+
return@timedTraceSuspend handleGrabError(it)
138+
}
123139
}
140+
onStep("createUserAccount (needed=$needsAccount)")
124141

125142
// 4. Send grab request and wait for confirmation
126-
return requestGrab<TransactionMetadata.SendPublicPayment>(tokenizedCluster, data)
143+
val result = requestGrab<TransactionMetadata.SendPublicPayment>(tokenizedCluster, data)
127144
.map { it.copy(verifiedExchangeData = exchangeData) }
128145
.onSuccess {
129146
// 5. Ack the receipt of the give request to clear it from the stream
130147
messagingController.ackMessages(data.rendezvous, listOf(messageId))
131148
}
149+
onStep("requestGrab+ack")
150+
151+
result
132152
}
133153

134154
private fun handleGrabError(error: Throwable): Result<Nothing> {

services/opencode/src/main/kotlin/com/getcode/opencode/managers/BillTransactionManager.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import com.getcode.opencode.model.financial.LocalFiat
2121
import com.getcode.opencode.model.financial.Token
2222
import com.getcode.opencode.providers.TokenMetadataProvider
2323
import com.getcode.utils.ErrorUtils
24+
import com.getcode.utils.timedTraceSuspend
2425
import com.getcode.utils.trace
2526
import kotlinx.coroutines.CoroutineScope
2627
import kotlinx.coroutines.Dispatchers
@@ -170,7 +171,10 @@ class BillTransactionManager @Inject constructor(
170171
val mint = metadata.exchangeData.mint
171172
val amount = LocalFiat(metadata.exchangeData)
172173

173-
val token = tokenProvider.getTokenMetadata(mint).getOrNull()?.token
174+
val token = timedTraceSuspend(
175+
message = "post-grab tokenMetadata fetch",
176+
tag = "Bill",
177+
) { tokenProvider.getTokenMetadata(mint).getOrNull()?.token }
174178
if (token == null) {
175179
onError(IllegalStateException("No metadata found for token $mint"))
176180
return@onSuccess

0 commit comments

Comments
 (0)