missing responses through a WS connection

This commit is contained in:
a10zn8
2023-01-11 15:39:35 +04:00
parent bddd4da1fb
commit 65fc2bfe7b

View File

@@ -53,6 +53,7 @@ import java.net.URI
import java.time.Duration import java.time.Duration
import java.time.Instant import java.time.Instant
import java.util.Base64 import java.util.Base64
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.Executors import java.util.concurrent.Executors
import java.util.concurrent.ScheduledFuture import java.util.concurrent.ScheduledFuture
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
@@ -100,10 +101,10 @@ open class WsConnectionImpl(
private val resetBackoffExecutor = Executors.newScheduledThreadPool(2) private val resetBackoffExecutor = Executors.newScheduledThreadPool(2)
private var resetBackoffTask: ScheduledFuture<Unit>? = null private var resetBackoffTask: ScheduledFuture<Unit>? = null
private val messages = Sinks private val subscriptionResponses = Sinks
.many() .many()
.multicast() .multicast()
.directBestEffort<ResponseWSParser.WsResponse>() .directBestEffort<JsonRpcWsMessage>()
private var rpcSend = Sinks private var rpcSend = Sinks
.many() .many()
@@ -114,6 +115,9 @@ open class WsConnectionImpl(
.many() .many()
.multicast() .multicast()
.directBestEffort<Instant>() .directBestEffort<Instant>()
private val currentRequests = ConcurrentHashMap<Int, Sinks.One<JsonRpcResponse>>()
private val sendIdSeq = AtomicInteger(IDS_START) private val sendIdSeq = AtomicInteger(IDS_START)
private val sendExecutor = Executors.newSingleThreadExecutor() private val sendExecutor = Executors.newSingleThreadExecutor()
private var keepConnection = true private var keepConnection = true
@@ -272,41 +276,48 @@ open class WsConnectionImpl(
fun onMessage(msg: ResponseWSParser.WsResponse): Mono<Void> { fun onMessage(msg: ResponseWSParser.WsResponse): Mono<Void> {
return Mono.fromCallable { return Mono.fromCallable {
val status = messages.tryEmitNext(msg) when (msg.type) {
if (status.isFailure) { ResponseWSParser.Type.RPC -> onMessageRpc(msg)
if (status == Sinks.EmitResult.FAIL_ZERO_SUBSCRIBER) { ResponseWSParser.Type.SUBSCRIPTION -> onMessageSubscription(msg)
log.debug("No subscribers to WS response")
} else {
log.warn("Failed to proceed with a WS message: $status")
}
} }
}.then() }.then()
} }
open fun getRpcResponses(): Flux<JsonRpcResponse> { fun onMessageRpc(msg: ResponseWSParser.WsResponse) {
return Flux.from(messages.asFlux()) val rpcResponse = JsonRpcResponse(
.publishOn(Schedulers.boundedElastic()) msg.value, msg.error, msg.id, null
.filter { )
it.type == ResponseWSParser.Type.RPC val sender = currentRequests.remove(msg.id.asNumber().toInt())
if (sender == null) {
log.warn("Unknown response received for ${msg.id}")
} else {
try {
val emitResult = sender.tryEmitValue(rpcResponse)
if (emitResult.isFailure) {
log.debug("Response is ignored $emitResult")
}
} catch (t: Throwable) {
log.warn("Response is not processed ${t.message}", t)
} }
.map { msg -> }
JsonRpcResponse( }
msg.value, msg.error, msg.id, null
) fun onMessageSubscription(msg: ResponseWSParser.WsResponse) {
val subscription = JsonRpcWsMessage(
msg.value, msg.error, msg.id.asString(),
)
val status = subscriptionResponses.tryEmitNext(subscription)
if (status.isFailure) {
if (status == Sinks.EmitResult.FAIL_ZERO_SUBSCRIBER) {
log.debug("No subscribers to WS response")
} else {
log.warn("Failed to proceed with a WS message: $status")
} }
}
} }
open fun getSubscribeResponses(): Flux<JsonRpcWsMessage> { open fun getSubscribeResponses(): Flux<JsonRpcWsMessage> {
return Flux.from(messages.asFlux()) return Flux.from(subscriptionResponses.asFlux())
.publishOn(Schedulers.boundedElastic())
.filter {
it.type == ResponseWSParser.Type.SUBSCRIPTION
}
.map { msg ->
JsonRpcWsMessage(
msg.value, msg.error, msg.id.asString(),
)
}
} }
open fun callRpc(originalRequest: JsonRpcRequest): Mono<JsonRpcResponse> { open fun callRpc(originalRequest: JsonRpcRequest): Mono<JsonRpcResponse> {
@@ -332,7 +343,9 @@ open class WsConnectionImpl(
} }
fun waitForResponse(request: JsonRpcRequest, originalId: Int, startTime: Long): Mono<JsonRpcResponse> { fun waitForResponse(request: JsonRpcRequest, originalId: Int, startTime: Long): Mono<JsonRpcResponse> {
val expectedId = request.id.toLong() val internalId = request.id.toLong()
val onResponse = Sinks.one<JsonRpcResponse>()
currentRequests[internalId.toInt()] = onResponse
val noResponse = JsonRpcException( val noResponse = JsonRpcException(
JsonRpcResponse.Id.from(originalId), JsonRpcResponse.Id.from(originalId),
JsonRpcError( JsonRpcError(
@@ -342,13 +355,6 @@ open class WsConnectionImpl(
false false
) )
val response = Flux.from(getRpcResponses())
.doOnSubscribe { sendRpc(request) }
.filter { resp -> resp.id.asNumber() == expectedId }
.take(Defaults.timeout)
.take(1)
.singleOrEmpty()
val failOnDisconnect = Mono.from(disconnects.asFlux()) val failOnDisconnect = Mono.from(disconnects.asFlux())
.flatMap { .flatMap {
Mono.error<JsonRpcResponse>( Mono.error<JsonRpcResponse>(
@@ -362,11 +368,16 @@ open class WsConnectionImpl(
) )
} }
return response.or(failOnDisconnect) return Mono.from(onResponse.asMono()).or(failOnDisconnect)
.doOnSubscribe { sendRpc(request) }
.take(Defaults.timeout)
.doOnNext { rpcMetrics?.timer?.record(System.nanoTime() - startTime, TimeUnit.NANOSECONDS) } .doOnNext { rpcMetrics?.timer?.record(System.nanoTime() - startTime, TimeUnit.NANOSECONDS) }
.doOnError { rpcMetrics?.fails?.increment() } .doOnError { rpcMetrics?.fails?.increment() }
.map { it.copyWithId(JsonRpcResponse.Id.from(originalId)) } .map { it.copyWithId(JsonRpcResponse.Id.from(originalId)) }
.switchIfEmpty(Mono.error(noResponse)) .switchIfEmpty(
Mono.fromCallable { log.warn("No response for ${request.method} ${request.params}") }.then(Mono.error(noResponse))
)
.doFinally { currentRequests.remove(internalId.toInt()) }
} }
override fun close() { override fun close() {
@@ -374,5 +385,6 @@ open class WsConnectionImpl(
keepConnection = false keepConnection = false
connection?.dispose() connection?.dispose()
connection = null connection = null
currentRequests.clear()
} }
} }