problem: head check doesn't use upstream WS metrics

This commit is contained in:
Igor Artamonov
2022-08-05 21:26:35 -04:00
parent a26db98376
commit a34da8b4a3
6 changed files with 53 additions and 40 deletions

View File

@@ -200,6 +200,7 @@ open class ConfiguredUpstreams(
val wsFactoryApi: EthereumWsFactory? = conn.ws?.let { endpoint ->
val wsApi = EthereumWsFactory(
config.id!!, chain,
endpoint.url,
endpoint.origin ?: URI("http://localhost"),
)

View File

@@ -79,7 +79,7 @@ open class EthereumRpcUpstream(
open fun createHead(): Head {
return if (ethereumWsFactory != null) {
// do not set upstream to the WS, since it doesn't control the RPC upstream
val ws = ethereumWsFactory.create(null, null, null).apply {
val ws = ethereumWsFactory.create(null, null).apply {
connect()
}
val wsHead = EthereumWsHead(ws).apply {

View File

@@ -20,18 +20,49 @@ import io.emeraldpay.dshackle.config.AuthConfig
import io.emeraldpay.dshackle.config.UpstreamsConfig
import io.emeraldpay.dshackle.upstream.DefaultUpstream
import io.emeraldpay.dshackle.upstream.rpcclient.RpcMetrics
import io.emeraldpay.grpc.Chain
import io.micrometer.core.instrument.Counter
import io.micrometer.core.instrument.Metrics
import io.micrometer.core.instrument.Tag
import io.micrometer.core.instrument.Timer
import java.net.URI
class EthereumWsFactory(
private val id: String,
private val chain: Chain,
private val uri: URI,
private val origin: URI
private val origin: URI,
) {
var basicAuth: AuthConfig.ClientBasicAuth? = null
var config: UpstreamsConfig.WsEndpoint? = null
fun create(upstream: DefaultUpstream?, validator: EthereumUpstreamValidator?, rpcMetrics: RpcMetrics?): WsConnection {
return WsConnection(uri, origin, basicAuth, rpcMetrics, upstream, validator).also { ws ->
// metrics are shared between all connections to the same WS
private val metrics: RpcMetrics = run {
val metricsTags = listOf(
Tag.of("upstream", id),
// UNSPECIFIED shouldn't happen too
Tag.of("chain", chain.chainCode)
)
RpcMetrics(
Timer.builder("upstream.ws.conn")
.description("Request time through a WebSocket JSON RPC connection")
.tags(metricsTags)
.publishPercentileHistogram()
.register(Metrics.globalRegistry),
Counter.builder("upstream.ws.fail")
.description("Number of failures of WebSocket JSON RPC requests")
.tags(metricsTags)
.register(Metrics.globalRegistry)
)
}
fun create(upstream: DefaultUpstream?, validator: EthereumUpstreamValidator?): WsConnection {
require(upstream == null || upstream.getId() == id) {
"Creating instance for different upstream. ${upstream?.getId()} != id"
}
return WsConnection(uri, origin, basicAuth, metrics, upstream, validator).also { ws ->
config?.frameSize?.let {
ws.frameSize = it
}

View File

@@ -26,12 +26,7 @@ import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcRequest
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcResponse
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcSwitchClient
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcWsClient
import io.emeraldpay.dshackle.upstream.rpcclient.RpcMetrics
import io.emeraldpay.grpc.Chain
import io.micrometer.core.instrument.Counter
import io.micrometer.core.instrument.Metrics
import io.micrometer.core.instrument.Tag
import io.micrometer.core.instrument.Timer
import org.slf4j.LoggerFactory
import org.springframework.context.Lifecycle
import reactor.core.Disposable
@@ -59,26 +54,8 @@ class EthereumWsUpstream(
private val validator: EthereumUpstreamValidator
init {
val metricsTags = listOf(
Tag.of("upstream", id),
// UNSPECIFIED shouldn't happen too
Tag.of("chain", chain.chainCode)
)
val metrics = RpcMetrics(
Timer.builder("upstream.ws.conn")
.description("Request time through a WebSocket JSON RPC connection")
.tags(metricsTags)
.publishPercentileHistogram()
.register(Metrics.globalRegistry),
Counter.builder("upstream.ws.fail")
.description("Number of failures of WebSocket JSON RPC requests")
.tags(metricsTags)
.register(Metrics.globalRegistry)
)
validator = EthereumUpstreamValidator(this, getOptions())
connection = ethereumWsFactory.create(this, validator, metrics)
connection = ethereumWsFactory.create(this, validator)
head = EthereumWsHead(connection)
// Sometimes the server may close the WebSocket connection during the execution of a call, for example if the response
// is too large for WebSockets Frame (and Geth is unable to split messages into separate frames)