Some tunes (#549)
This commit is contained in:
@@ -190,7 +190,7 @@ jib {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
container {
|
container {
|
||||||
jvmFlags = ['-XX:+UseG1GC', '-XX:+ExitOnOutOfMemoryError', '-Xms1024M', '-XX:+UnlockDiagnosticVMOptions', '-XX:GCLockerRetryAllocationCount=10', '--enable-preview']
|
jvmFlags = ['-XX:+UseG1GC', '-XX:+ExitOnOutOfMemoryError', '-Xms1024M', '-XX:NativeMemoryTracking=summary', '-XX:+UnlockDiagnosticVMOptions', '-XX:GCLockerRetryAllocationCount=10', '--enable-preview']
|
||||||
mainClass = 'io.emeraldpay.dshackle.StarterKt'
|
mainClass = 'io.emeraldpay.dshackle.StarterKt'
|
||||||
args = []
|
args = []
|
||||||
ports = ['2448', '2449', '8545']
|
ports = ['2448', '2449', '8545']
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ class Defaults {
|
|||||||
const val maxMetadataSize = 16384
|
const val maxMetadataSize = 16384
|
||||||
val timeout: Duration = Duration.ofSeconds(60)
|
val timeout: Duration = Duration.ofSeconds(60)
|
||||||
val timeoutInternal: Duration = timeout.dividedBy(4)
|
val timeoutInternal: Duration = timeout.dividedBy(4)
|
||||||
|
val internalCallsTimeout = Duration.ofSeconds(3)
|
||||||
val retryConnection: Duration = Duration.ofSeconds(10)
|
val retryConnection: Duration = Duration.ofSeconds(10)
|
||||||
val grpcServerKeepAliveTime: Long = 15 // seconds
|
val grpcServerKeepAliveTime: Long = 15 // seconds
|
||||||
val grpcServerKeepAliveTimeout: Long = 5
|
val grpcServerKeepAliveTimeout: Long = 5
|
||||||
|
|||||||
@@ -12,7 +12,9 @@ import reactor.core.scheduler.Scheduler
|
|||||||
import reactor.core.scheduler.Schedulers
|
import reactor.core.scheduler.Schedulers
|
||||||
import java.util.concurrent.Executor
|
import java.util.concurrent.Executor
|
||||||
import java.util.concurrent.ExecutorService
|
import java.util.concurrent.ExecutorService
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.SynchronousQueue
|
||||||
|
import java.util.concurrent.ThreadPoolExecutor
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
open class SchedulersConfig {
|
open class SchedulersConfig {
|
||||||
@@ -79,17 +81,24 @@ open class SchedulersConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun makePool(name: String, size: Int, monitoringConfig: MonitoringConfig): ExecutorService {
|
private fun makePool(name: String, size: Int, monitoringConfig: MonitoringConfig): ExecutorService {
|
||||||
val pool = Executors.newFixedThreadPool(size, CustomizableThreadFactory("$name-"))
|
val cachedPool = ThreadPoolExecutor(
|
||||||
|
size,
|
||||||
|
size * threadsMultiplier,
|
||||||
|
60L,
|
||||||
|
TimeUnit.SECONDS,
|
||||||
|
SynchronousQueue(),
|
||||||
|
CustomizableThreadFactory("$name-"),
|
||||||
|
)
|
||||||
|
|
||||||
return if (monitoringConfig.enableExtended) {
|
return if (monitoringConfig.enableExtended) {
|
||||||
ExecutorServiceMetrics.monitor(
|
ExecutorServiceMetrics.monitor(
|
||||||
Metrics.globalRegistry,
|
Metrics.globalRegistry,
|
||||||
pool,
|
cachedPool,
|
||||||
name,
|
name,
|
||||||
Tag.of("reactor_scheduler_id", "_"),
|
Tag.of("reactor_scheduler_id", "_"),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
pool
|
cachedPool
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -178,11 +178,11 @@ class QuorumRequestReader(
|
|||||||
src.onErrorResume { err ->
|
src.onErrorResume { err ->
|
||||||
errorHandler.handle(api, key, err.message)
|
errorHandler.handle(api, key, err.message)
|
||||||
|
|
||||||
val msgError = "Error during call upstream ${api.getId()} with method ${key.method}"
|
val msgError = "Error during call upstream ${api.getId()} with method ${key.method}, reason - ${err.message}"
|
||||||
if (err is ChainCallUpstreamException) {
|
if (err is ChainCallUpstreamException) {
|
||||||
log.debug(msgError, err)
|
log.debug(msgError)
|
||||||
} else {
|
} else {
|
||||||
log.warn(msgError, err)
|
log.warn(msgError)
|
||||||
}
|
}
|
||||||
|
|
||||||
// when the call failed with an error we want to notify the quorum because
|
// when the call failed with an error we want to notify the quorum because
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ class CompoundReader<K, D> (
|
|||||||
.flatMap({ rdr ->
|
.flatMap({ rdr ->
|
||||||
rdr.read(key)
|
rdr.read(key)
|
||||||
.timeout(Defaults.timeoutInternal, Mono.empty())
|
.timeout(Defaults.timeoutInternal, Mono.empty())
|
||||||
.doOnError { t -> log.warn("Failed to read from $rdr", t) }
|
.doOnError { t -> log.warn("Failed to read from {}, reason - {}", rdr, t.message) }
|
||||||
}, 1,)
|
}, 1,)
|
||||||
.next()
|
.next()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import java.io.ByteArrayInputStream
|
|||||||
import java.security.KeyStore
|
import java.security.KeyStore
|
||||||
import java.security.cert.CertificateFactory
|
import java.security.cert.CertificateFactory
|
||||||
import java.security.cert.X509Certificate
|
import java.security.cert.X509Certificate
|
||||||
|
import java.time.Duration
|
||||||
import java.util.Base64
|
import java.util.Base64
|
||||||
import java.util.function.Consumer
|
import java.util.function.Consumer
|
||||||
import java.util.function.Function
|
import java.util.function.Function
|
||||||
@@ -31,7 +32,8 @@ abstract class HttpReader(
|
|||||||
init {
|
init {
|
||||||
val connectionProvider = ConnectionProvider.builder("dshackleConnectionPool")
|
val connectionProvider = ConnectionProvider.builder("dshackleConnectionPool")
|
||||||
.maxConnections(1500)
|
.maxConnections(1500)
|
||||||
.pendingAcquireMaxCount(10000)
|
.pendingAcquireMaxCount(1000)
|
||||||
|
.pendingAcquireTimeout(Duration.ofSeconds(10))
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
var build = HttpClient.create(connectionProvider)
|
var build = HttpClient.create(connectionProvider)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package io.emeraldpay.dshackle.upstream
|
|||||||
import com.fasterxml.jackson.databind.JsonNode
|
import com.fasterxml.jackson.databind.JsonNode
|
||||||
import com.fasterxml.jackson.module.kotlin.readValue
|
import com.fasterxml.jackson.module.kotlin.readValue
|
||||||
import io.emeraldpay.dshackle.Chain
|
import io.emeraldpay.dshackle.Chain
|
||||||
|
import io.emeraldpay.dshackle.Defaults.Companion.internalCallsTimeout
|
||||||
import io.emeraldpay.dshackle.Global
|
import io.emeraldpay.dshackle.Global
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import reactor.core.publisher.Flux
|
import reactor.core.publisher.Flux
|
||||||
@@ -17,15 +18,25 @@ abstract class UpstreamSettingsDetector(
|
|||||||
) {
|
) {
|
||||||
protected val log = LoggerFactory.getLogger(this::class.java)
|
protected val log = LoggerFactory.getLogger(this::class.java)
|
||||||
|
|
||||||
abstract fun detectLabels(): Flux<Pair<String, String>>
|
fun detectLabels(): Flux<Pair<String, String>> {
|
||||||
|
return internalDetectLabels()
|
||||||
|
.timeout(internalCallsTimeout)
|
||||||
|
.onErrorResume {
|
||||||
|
log.warn("Couldn't detect lables of upstream {}, message - {}", upstream.getId(), it.message)
|
||||||
|
Flux.empty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract fun internalDetectLabels(): Flux<Pair<String, String>>
|
||||||
|
|
||||||
fun detectClientVersion(): Mono<String> {
|
fun detectClientVersion(): Mono<String> {
|
||||||
return upstream.getIngressReader()
|
return upstream.getIngressReader()
|
||||||
.read(clientVersionRequest())
|
.read(clientVersionRequest())
|
||||||
.flatMap(ChainResponse::requireResult)
|
.flatMap(ChainResponse::requireResult)
|
||||||
.map(::parseClientVersion)
|
.map(::parseClientVersion)
|
||||||
|
.timeout(internalCallsTimeout)
|
||||||
.onErrorResume {
|
.onErrorResume {
|
||||||
log.warn("Can't detect the client version of upstream ${upstream.getId()}, reason - {}", it.message)
|
log.warn("Can't detect the client version of upstream {}, reason - {}", upstream.getId(), it.message)
|
||||||
Mono.just(UNKNOWN_CLIENT_VERSION)
|
Mono.just(UNKNOWN_CLIENT_VERSION)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ class BeaconChainUpstreamSettingsDetector(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun detectLabels(): Flux<Pair<String, String>> {
|
override fun internalDetectLabels(): Flux<Pair<String, String>> {
|
||||||
return Flux.merge(
|
return Flux.merge(
|
||||||
detectNodeType(),
|
detectNodeType(),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package io.emeraldpay.dshackle.upstream.cosmos
|
package io.emeraldpay.dshackle.upstream.cosmos
|
||||||
|
|
||||||
import io.emeraldpay.dshackle.Chain
|
import io.emeraldpay.dshackle.Chain
|
||||||
|
import io.emeraldpay.dshackle.Defaults
|
||||||
import io.emeraldpay.dshackle.Global
|
import io.emeraldpay.dshackle.Global
|
||||||
import io.emeraldpay.dshackle.upstream.ChainRequest
|
import io.emeraldpay.dshackle.upstream.ChainRequest
|
||||||
import io.emeraldpay.dshackle.upstream.Upstream
|
import io.emeraldpay.dshackle.upstream.Upstream
|
||||||
@@ -31,10 +32,13 @@ class CosmosLowerBoundStateDetector(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun internalDetectLowerBound(): Flux<LowerBoundData> {
|
override fun internalDetectLowerBound(): Flux<LowerBoundData> {
|
||||||
return upstream.getIngressReader().read(ChainRequest("status", ListParams())).map {
|
return upstream.getIngressReader()
|
||||||
val resp = Global.objectMapper.readValue(it.getResult(), CosmosStatus::class.java)
|
.read(ChainRequest("status", ListParams()))
|
||||||
LowerBoundData(resp.syncInfo.earliestBlockHeight.toLong(), STATE)
|
.timeout(Defaults.internalCallsTimeout)
|
||||||
}.toFlux()
|
.map {
|
||||||
|
val resp = Global.objectMapper.readValue(it.getResult(), CosmosStatus::class.java)
|
||||||
|
LowerBoundData(resp.syncInfo.earliestBlockHeight.toLong(), STATE)
|
||||||
|
}.toFlux()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun types(): Set<LowerBoundType> {
|
override fun types(): Set<LowerBoundType> {
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import reactor.core.publisher.Flux
|
|||||||
class CosmosUpstreamSettingsDetector(
|
class CosmosUpstreamSettingsDetector(
|
||||||
upstream: Upstream,
|
upstream: Upstream,
|
||||||
) : BasicUpstreamSettingsDetector(upstream) {
|
) : BasicUpstreamSettingsDetector(upstream) {
|
||||||
override fun detectLabels(): Flux<Pair<String, String>> {
|
override fun internalDetectLabels(): Flux<Pair<String, String>> {
|
||||||
return Flux.merge(
|
return Flux.merge(
|
||||||
detectNodeType(),
|
detectNodeType(),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package io.emeraldpay.dshackle.upstream.ethereum
|
|||||||
|
|
||||||
import com.fasterxml.jackson.module.kotlin.readValue
|
import com.fasterxml.jackson.module.kotlin.readValue
|
||||||
import io.emeraldpay.dshackle.Chain
|
import io.emeraldpay.dshackle.Chain
|
||||||
|
import io.emeraldpay.dshackle.Defaults
|
||||||
import io.emeraldpay.dshackle.Global
|
import io.emeraldpay.dshackle.Global
|
||||||
import io.emeraldpay.dshackle.upstream.ChainRequest
|
import io.emeraldpay.dshackle.upstream.ChainRequest
|
||||||
import io.emeraldpay.dshackle.upstream.Upstream
|
import io.emeraldpay.dshackle.upstream.Upstream
|
||||||
@@ -72,6 +73,7 @@ class EthereumFinalizationDetector : FinalizationDetector {
|
|||||||
upstream
|
upstream
|
||||||
.getIngressReader()
|
.getIngressReader()
|
||||||
.read(req)
|
.read(req)
|
||||||
|
.timeout(Defaults.internalCallsTimeout)
|
||||||
.flatMap {
|
.flatMap {
|
||||||
it.requireResult().flatMap { result ->
|
it.requireResult().flatMap { result ->
|
||||||
val block = Global.objectMapper.readValue<BlockJson<TransactionRefJson>>(result)
|
val block = Global.objectMapper.readValue<BlockJson<TransactionRefJson>>(result)
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package io.emeraldpay.dshackle.upstream.ethereum
|
package io.emeraldpay.dshackle.upstream.ethereum
|
||||||
|
|
||||||
|
import io.emeraldpay.dshackle.Defaults
|
||||||
import io.emeraldpay.dshackle.upstream.ChainCallError
|
import io.emeraldpay.dshackle.upstream.ChainCallError
|
||||||
import io.emeraldpay.dshackle.upstream.ChainCallUpstreamException
|
import io.emeraldpay.dshackle.upstream.ChainCallUpstreamException
|
||||||
import io.emeraldpay.dshackle.upstream.ChainRequest
|
import io.emeraldpay.dshackle.upstream.ChainRequest
|
||||||
@@ -45,6 +46,7 @@ class EthereumLowerBoundBlockDetector(
|
|||||||
ListParams(block.toHex(), false),
|
ListParams(block.toHex(), false),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
.timeout(Defaults.internalCallsTimeout)
|
||||||
.doOnNext {
|
.doOnNext {
|
||||||
if (it.hasResult() && it.getResult().contentEquals("null".toByteArray())) {
|
if (it.hasResult() && it.getResult().contentEquals("null".toByteArray())) {
|
||||||
throw IllegalStateException(NO_BLOCK_DATA)
|
throw IllegalStateException(NO_BLOCK_DATA)
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package io.emeraldpay.dshackle.upstream.ethereum
|
package io.emeraldpay.dshackle.upstream.ethereum
|
||||||
|
|
||||||
|
import io.emeraldpay.dshackle.Defaults
|
||||||
import io.emeraldpay.dshackle.upstream.ChainRequest
|
import io.emeraldpay.dshackle.upstream.ChainRequest
|
||||||
import io.emeraldpay.dshackle.upstream.Upstream
|
import io.emeraldpay.dshackle.upstream.Upstream
|
||||||
import io.emeraldpay.dshackle.upstream.lowerbound.LowerBoundData
|
import io.emeraldpay.dshackle.upstream.lowerbound.LowerBoundData
|
||||||
@@ -39,6 +40,7 @@ class EthereumLowerBoundLogsDetector(
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
.timeout(Defaults.internalCallsTimeout)
|
||||||
.doOnNext {
|
.doOnNext {
|
||||||
if (it.hasResult() && (it.getResult().contentEquals("null".toByteArray()) || it.getResult().contentEquals("[]".toByteArray()))) {
|
if (it.hasResult() && (it.getResult().contentEquals("null".toByteArray()) || it.getResult().contentEquals("[]".toByteArray()))) {
|
||||||
throw IllegalStateException(NO_LOGS_DATA)
|
throw IllegalStateException(NO_LOGS_DATA)
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package io.emeraldpay.dshackle.upstream.ethereum
|
package io.emeraldpay.dshackle.upstream.ethereum
|
||||||
|
|
||||||
|
import io.emeraldpay.dshackle.Defaults
|
||||||
import io.emeraldpay.dshackle.Global
|
import io.emeraldpay.dshackle.Global
|
||||||
import io.emeraldpay.dshackle.upstream.ChainRequest
|
import io.emeraldpay.dshackle.upstream.ChainRequest
|
||||||
import io.emeraldpay.dshackle.upstream.ChainResponse
|
import io.emeraldpay.dshackle.upstream.ChainResponse
|
||||||
@@ -60,7 +61,7 @@ class EthereumLowerBoundStateDetector(
|
|||||||
"eth_getBalance",
|
"eth_getBalance",
|
||||||
ListParams(ZERO_ADDRESS, block.toHex()),
|
ListParams(ZERO_ADDRESS, block.toHex()),
|
||||||
),
|
),
|
||||||
)
|
).timeout(Defaults.internalCallsTimeout)
|
||||||
}.doOnNext {
|
}.doOnNext {
|
||||||
if (it.hasResult() && it.getResult().contentEquals(Global.nullValue)) {
|
if (it.hasResult() && it.getResult().contentEquals(Global.nullValue)) {
|
||||||
throw IllegalStateException("No state data")
|
throw IllegalStateException("No state data")
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package io.emeraldpay.dshackle.upstream.ethereum
|
package io.emeraldpay.dshackle.upstream.ethereum
|
||||||
|
|
||||||
|
import io.emeraldpay.dshackle.Defaults
|
||||||
import io.emeraldpay.dshackle.data.BlockContainer
|
import io.emeraldpay.dshackle.data.BlockContainer
|
||||||
import io.emeraldpay.dshackle.upstream.ChainRequest
|
import io.emeraldpay.dshackle.upstream.ChainRequest
|
||||||
import io.emeraldpay.dshackle.upstream.Upstream
|
import io.emeraldpay.dshackle.upstream.Upstream
|
||||||
@@ -32,6 +33,7 @@ class EthereumLowerBoundTxDetector(
|
|||||||
.read(
|
.read(
|
||||||
ChainRequest("eth_getBlockByNumber", ListParams(block.toHex(), false)),
|
ChainRequest("eth_getBlockByNumber", ListParams(block.toHex(), false)),
|
||||||
)
|
)
|
||||||
|
.timeout(Defaults.internalCallsTimeout)
|
||||||
.doOnNext {
|
.doOnNext {
|
||||||
if (it.hasResult() && it.getResult().contentEquals("null".toByteArray())) {
|
if (it.hasResult() && it.getResult().contentEquals("null".toByteArray())) {
|
||||||
throw IllegalStateException(NO_TX_DATA)
|
throw IllegalStateException(NO_TX_DATA)
|
||||||
@@ -50,6 +52,7 @@ class EthereumLowerBoundTxDetector(
|
|||||||
.read(
|
.read(
|
||||||
ChainRequest("eth_getTransactionByHash", ListParams(tx)),
|
ChainRequest("eth_getTransactionByHash", ListParams(tx)),
|
||||||
)
|
)
|
||||||
|
.timeout(Defaults.internalCallsTimeout)
|
||||||
.doOnNext {
|
.doOnNext {
|
||||||
if (it.hasResult() && it.getResult().contentEquals("null".toByteArray())) {
|
if (it.hasResult() && it.getResult().contentEquals("null".toByteArray())) {
|
||||||
throw IllegalStateException(NO_TX_DATA)
|
throw IllegalStateException(NO_TX_DATA)
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import io.emeraldpay.dshackle.upstream.Upstream
|
|||||||
import io.emeraldpay.dshackle.upstream.rpcclient.ListParams
|
import io.emeraldpay.dshackle.upstream.rpcclient.ListParams
|
||||||
import reactor.core.publisher.Flux
|
import reactor.core.publisher.Flux
|
||||||
import reactor.core.publisher.Mono
|
import reactor.core.publisher.Mono
|
||||||
import kotlin.text.toBigInteger
|
|
||||||
|
|
||||||
const val ZERO_ADDRESS = "0x0000000000000000000000000000000000000000"
|
const val ZERO_ADDRESS = "0x0000000000000000000000000000000000000000"
|
||||||
|
|
||||||
@@ -21,7 +20,7 @@ class EthereumUpstreamSettingsDetector(
|
|||||||
) : BasicEthUpstreamSettingsDetector(_upstream) {
|
) : BasicEthUpstreamSettingsDetector(_upstream) {
|
||||||
private val blockNumberReader = EthereumArchiveBlockNumberReader(upstream.getIngressReader())
|
private val blockNumberReader = EthereumArchiveBlockNumberReader(upstream.getIngressReader())
|
||||||
|
|
||||||
override fun detectLabels(): Flux<Pair<String, String>> {
|
override fun internalDetectLabels(): Flux<Pair<String, String>> {
|
||||||
return Flux.merge(
|
return Flux.merge(
|
||||||
detectNodeType(),
|
detectNodeType(),
|
||||||
detectArchiveNode(),
|
detectArchiveNode(),
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package io.emeraldpay.dshackle.upstream.generic
|
package io.emeraldpay.dshackle.upstream.generic
|
||||||
|
|
||||||
|
import io.emeraldpay.dshackle.Defaults.Companion.internalCallsTimeout
|
||||||
import io.emeraldpay.dshackle.foundation.ChainOptions
|
import io.emeraldpay.dshackle.foundation.ChainOptions
|
||||||
import io.emeraldpay.dshackle.upstream.SingleValidator
|
import io.emeraldpay.dshackle.upstream.SingleValidator
|
||||||
import io.emeraldpay.dshackle.upstream.Upstream
|
import io.emeraldpay.dshackle.upstream.Upstream
|
||||||
@@ -21,8 +22,9 @@ class AggregatedUpstreamValidator(
|
|||||||
) { a -> a.map { it as UpstreamAvailability } }
|
) { a -> a.map { it as UpstreamAvailability } }
|
||||||
.map(::resolve)
|
.map(::resolve)
|
||||||
.defaultIfEmpty(UpstreamAvailability.OK) // upstream is OK on case there are no validators
|
.defaultIfEmpty(UpstreamAvailability.OK) // upstream is OK on case there are no validators
|
||||||
|
.timeout(internalCallsTimeout)
|
||||||
.onErrorResume {
|
.onErrorResume {
|
||||||
log.error("Error during upstream validation for ${upstream.getId()}", it)
|
log.error("Error during upstream validation for {}, reason - {}", upstream.getId(), it.message)
|
||||||
Mono.just(UpstreamAvailability.UNAVAILABLE)
|
Mono.just(UpstreamAvailability.UNAVAILABLE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -33,13 +35,16 @@ class AggregatedUpstreamValidator(
|
|||||||
) { a -> a.map { it as ValidateUpstreamSettingsResult } }
|
) { a -> a.map { it as ValidateUpstreamSettingsResult } }
|
||||||
.map(::resolve)
|
.map(::resolve)
|
||||||
.defaultIfEmpty(ValidateUpstreamSettingsResult.UPSTREAM_VALID)
|
.defaultIfEmpty(ValidateUpstreamSettingsResult.UPSTREAM_VALID)
|
||||||
|
.timeout(internalCallsTimeout)
|
||||||
.onErrorResume {
|
.onErrorResume {
|
||||||
log.error("Error during upstream validation for ${upstream.getId()}", it)
|
log.error("Error during upstream validation for {}, reason - {}", upstream.getId(), it.message)
|
||||||
Mono.just(ValidateUpstreamSettingsResult.UPSTREAM_FATAL_SETTINGS_ERROR)
|
Mono.just(ValidateUpstreamSettingsResult.UPSTREAM_FATAL_SETTINGS_ERROR)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun validateUpstreamSettingsOnStartup(): ValidateUpstreamSettingsResult {
|
override fun validateUpstreamSettingsOnStartup(): ValidateUpstreamSettingsResult {
|
||||||
return validateUpstreamSettings().block() ?: ValidateUpstreamSettingsResult.UPSTREAM_VALID
|
return runCatching {
|
||||||
|
validateUpstreamSettings().block(internalCallsTimeout) ?: ValidateUpstreamSettingsResult.UPSTREAM_VALID
|
||||||
|
}.getOrDefault(ValidateUpstreamSettingsResult.UPSTREAM_SETTINGS_ERROR)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,12 +46,15 @@ class GenericRpcHead(
|
|||||||
override fun start() {
|
override fun start() {
|
||||||
super.start()
|
super.start()
|
||||||
refreshSubscription?.dispose()
|
refreshSubscription?.dispose()
|
||||||
|
|
||||||
val base = Flux.interval(interval)
|
val base = Flux.interval(interval)
|
||||||
|
.onBackpressureDrop()
|
||||||
.publishOn(headScheduler)
|
.publishOn(headScheduler)
|
||||||
.filter { !isSyncing }
|
.filter { !isSyncing }
|
||||||
.flatMap {
|
.concatMap {
|
||||||
getLatestBlock(api)
|
getLatestBlock(api)
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshSubscription = super.follow(base)
|
refreshSubscription = super.follow(base)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package io.emeraldpay.dshackle.upstream.generic
|
package io.emeraldpay.dshackle.upstream.generic
|
||||||
|
|
||||||
import io.emeraldpay.dshackle.Chain
|
import io.emeraldpay.dshackle.Chain
|
||||||
|
import io.emeraldpay.dshackle.Defaults
|
||||||
import io.emeraldpay.dshackle.config.ChainsConfig
|
import io.emeraldpay.dshackle.config.ChainsConfig
|
||||||
import io.emeraldpay.dshackle.config.UpstreamsConfig
|
import io.emeraldpay.dshackle.config.UpstreamsConfig
|
||||||
import io.emeraldpay.dshackle.config.UpstreamsConfig.Labels
|
import io.emeraldpay.dshackle.config.UpstreamsConfig.Labels
|
||||||
@@ -35,10 +36,13 @@ import io.emeraldpay.dshackle.upstream.lowerbound.LowerBoundData
|
|||||||
import io.emeraldpay.dshackle.upstream.lowerbound.LowerBoundServiceBuilder
|
import io.emeraldpay.dshackle.upstream.lowerbound.LowerBoundServiceBuilder
|
||||||
import io.emeraldpay.dshackle.upstream.lowerbound.LowerBoundType
|
import io.emeraldpay.dshackle.upstream.lowerbound.LowerBoundType
|
||||||
import org.springframework.context.Lifecycle
|
import org.springframework.context.Lifecycle
|
||||||
|
import org.springframework.scheduling.concurrent.CustomizableThreadFactory
|
||||||
import reactor.core.Disposable
|
import reactor.core.Disposable
|
||||||
import reactor.core.publisher.Flux
|
import reactor.core.publisher.Flux
|
||||||
import reactor.core.publisher.Sinks
|
import reactor.core.publisher.Sinks
|
||||||
|
import reactor.core.scheduler.Schedulers
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
|
import java.util.concurrent.Executors
|
||||||
import java.util.concurrent.atomic.AtomicBoolean
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
import java.util.concurrent.atomic.AtomicReference
|
import java.util.concurrent.atomic.AtomicReference
|
||||||
import java.util.function.Supplier
|
import java.util.function.Supplier
|
||||||
@@ -192,6 +196,7 @@ open class GenericUpstream(
|
|||||||
},
|
},
|
||||||
headLivenessState.asFlux(),
|
headLivenessState.asFlux(),
|
||||||
)
|
)
|
||||||
|
.subscribeOn(upstreamSettingsScheduler)
|
||||||
.distinctUntilChanged()
|
.distinctUntilChanged()
|
||||||
.subscribe {
|
.subscribe {
|
||||||
when (it) {
|
when (it) {
|
||||||
@@ -238,29 +243,35 @@ open class GenericUpstream(
|
|||||||
clientVersion.set(it)
|
clientVersion.set(it)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
.subscribeOn(settingsScheduler)
|
||||||
}.subscribe()
|
}.subscribe()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun detectRpcModules(config: UpstreamsConfig.Upstream<*>, buildMethods: (UpstreamsConfig.Upstream<*>, Chain) -> CallMethods) {
|
private fun detectRpcModules(config: UpstreamsConfig.Upstream<*>, buildMethods: (UpstreamsConfig.Upstream<*>, Chain) -> CallMethods) {
|
||||||
val rpcDetector = rpcModulesDetector?.detectRpcModules()?.block() ?: HashMap<String, String>()
|
try {
|
||||||
log.info("Upstream rpc detector for ${getId()} returned $rpcDetector ")
|
val rpcDetector = rpcModulesDetector?.detectRpcModules()?.block(Defaults.internalCallsTimeout)
|
||||||
if (rpcDetector.size != 0) {
|
?: HashMap<String, String>()
|
||||||
var changed = false
|
log.info("Upstream rpc detector for ${getId()} returned $rpcDetector ")
|
||||||
for ((group, _) in rpcDetector) {
|
if (rpcDetector.size != 0) {
|
||||||
if (group == "trace" || group == "debug" || group == "filter") {
|
var changed = false
|
||||||
if (config.methodGroups == null) {
|
for ((group, _) in rpcDetector) {
|
||||||
config.methodGroups = UpstreamsConfig.MethodGroups(setOf("filter"), setOf())
|
if (group == "trace" || group == "debug" || group == "filter") {
|
||||||
} else {
|
if (config.methodGroups == null) {
|
||||||
val disabled = config.methodGroups!!.disabled
|
config.methodGroups = UpstreamsConfig.MethodGroups(setOf("filter"), setOf())
|
||||||
val enabled = config.methodGroups!!.enabled
|
} else {
|
||||||
if (!disabled.contains(group) && !enabled.contains(group)) {
|
val disabled = config.methodGroups!!.disabled
|
||||||
config.methodGroups!!.enabled = enabled.plus(group)
|
val enabled = config.methodGroups!!.enabled
|
||||||
changed = true
|
if (!disabled.contains(group) && !enabled.contains(group)) {
|
||||||
|
config.methodGroups!!.enabled = enabled.plus(group)
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (changed) updateMethods(buildMethods(config, getChain()))
|
||||||
}
|
}
|
||||||
if (changed) updateMethods(buildMethods(config, getChain()))
|
} catch (e: RuntimeException) {
|
||||||
|
log.error("Couldn't detect rpc modules of upstream {} due to error {}", getId(), e.message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -331,14 +342,17 @@ open class GenericUpstream(
|
|||||||
|
|
||||||
private fun detectFinalization() {
|
private fun detectFinalization() {
|
||||||
finalizationDetectorSubscription =
|
finalizationDetectorSubscription =
|
||||||
finalizationDetector.detectFinalization(this, chainConfig.expectedBlockTime, getChain()).subscribe {
|
finalizationDetector.detectFinalization(this, chainConfig.expectedBlockTime, getChain())
|
||||||
sendUpstreamStateEvent(UPDATED)
|
.subscribeOn(finalizationScheduler)
|
||||||
}
|
.subscribe {
|
||||||
|
sendUpstreamStateEvent(UPDATED)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun detectLowerBlock() {
|
private fun detectLowerBlock() {
|
||||||
lowerBlockDetectorSubscription =
|
lowerBlockDetectorSubscription =
|
||||||
lowerBoundService.detectLowerBounds()
|
lowerBoundService.detectLowerBounds()
|
||||||
|
.subscribeOn(lowerScheduler)
|
||||||
.subscribe {
|
.subscribe {
|
||||||
sendUpstreamStateEvent(UPDATED)
|
sendUpstreamStateEvent(UPDATED)
|
||||||
}
|
}
|
||||||
@@ -359,4 +373,15 @@ open class GenericUpstream(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun isValid(): Boolean = isUpstreamValid.get()
|
fun isValid(): Boolean = isUpstreamValid.get()
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val upstreamSettingsScheduler =
|
||||||
|
Schedulers.fromExecutor(Executors.newFixedThreadPool(4, CustomizableThreadFactory("upstreamSettings-")))
|
||||||
|
private val finalizationScheduler =
|
||||||
|
Schedulers.fromExecutor(Executors.newFixedThreadPool(4, CustomizableThreadFactory("finalization-")))
|
||||||
|
private val settingsScheduler =
|
||||||
|
Schedulers.fromExecutor(Executors.newFixedThreadPool(4, CustomizableThreadFactory("settings-")))
|
||||||
|
private val lowerScheduler =
|
||||||
|
Schedulers.fromExecutor(Executors.newFixedThreadPool(4, CustomizableThreadFactory("lowerBound-")))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package io.emeraldpay.dshackle.upstream.near
|
package io.emeraldpay.dshackle.upstream.near
|
||||||
|
|
||||||
|
import io.emeraldpay.dshackle.Defaults
|
||||||
import io.emeraldpay.dshackle.Global
|
import io.emeraldpay.dshackle.Global
|
||||||
import io.emeraldpay.dshackle.upstream.ChainRequest
|
import io.emeraldpay.dshackle.upstream.ChainRequest
|
||||||
import io.emeraldpay.dshackle.upstream.Upstream
|
import io.emeraldpay.dshackle.upstream.Upstream
|
||||||
@@ -18,17 +19,20 @@ class NearLowerBoundStateDetector(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun internalDetectLowerBound(): Flux<LowerBoundData> {
|
override fun internalDetectLowerBound(): Flux<LowerBoundData> {
|
||||||
return upstream.getIngressReader().read(ChainRequest("status", ListParams())).map {
|
return upstream.getIngressReader()
|
||||||
val resp = Global.objectMapper.readValue(it.getResult(), NearStatus::class.java)
|
.read(ChainRequest("status", ListParams()))
|
||||||
resp.syncInfo.earliestHeight
|
.timeout(Defaults.internalCallsTimeout)
|
||||||
}.flatMapMany {
|
.map {
|
||||||
Flux.fromIterable(
|
val resp = Global.objectMapper.readValue(it.getResult(), NearStatus::class.java)
|
||||||
listOf(
|
resp.syncInfo.earliestHeight
|
||||||
LowerBoundData(it, LowerBoundType.STATE),
|
}.flatMapMany {
|
||||||
LowerBoundData(it, LowerBoundType.BLOCK),
|
Flux.fromIterable(
|
||||||
),
|
listOf(
|
||||||
)
|
LowerBoundData(it, LowerBoundType.STATE),
|
||||||
}
|
LowerBoundData(it, LowerBoundType.BLOCK),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun types(): Set<LowerBoundType> {
|
override fun types(): Set<LowerBoundType> {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import reactor.core.publisher.Flux
|
|||||||
class NearUpstreamSettingsDetector(
|
class NearUpstreamSettingsDetector(
|
||||||
upstream: Upstream,
|
upstream: Upstream,
|
||||||
) : BasicUpstreamSettingsDetector(upstream) {
|
) : BasicUpstreamSettingsDetector(upstream) {
|
||||||
override fun detectLabels(): Flux<Pair<String, String>> {
|
override fun internalDetectLabels(): Flux<Pair<String, String>> {
|
||||||
return Flux.merge(
|
return Flux.merge(
|
||||||
detectNodeType(),
|
detectNodeType(),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package io.emeraldpay.dshackle.upstream.polkadot
|
package io.emeraldpay.dshackle.upstream.polkadot
|
||||||
|
|
||||||
|
import io.emeraldpay.dshackle.Defaults
|
||||||
import io.emeraldpay.dshackle.upstream.ChainRequest
|
import io.emeraldpay.dshackle.upstream.ChainRequest
|
||||||
import io.emeraldpay.dshackle.upstream.ChainResponse
|
import io.emeraldpay.dshackle.upstream.ChainResponse
|
||||||
import io.emeraldpay.dshackle.upstream.Upstream
|
import io.emeraldpay.dshackle.upstream.Upstream
|
||||||
@@ -34,6 +35,7 @@ class PolkadotLowerBoundStateDetector(
|
|||||||
ListParams(block.toHex()), // in polkadot state methods work only with hash
|
ListParams(block.toHex()), // in polkadot state methods work only with hash
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
.timeout(Defaults.internalCallsTimeout)
|
||||||
.flatMap(ChainResponse::requireResult)
|
.flatMap(ChainResponse::requireResult)
|
||||||
.map {
|
.map {
|
||||||
String(it, 1, it.size - 2)
|
String(it, 1, it.size - 2)
|
||||||
@@ -44,7 +46,7 @@ class PolkadotLowerBoundStateDetector(
|
|||||||
"state_getMetadata",
|
"state_getMetadata",
|
||||||
ListParams(it),
|
ListParams(it),
|
||||||
),
|
),
|
||||||
)
|
).timeout(Defaults.internalCallsTimeout)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package io.emeraldpay.dshackle.upstream.solana
|
package io.emeraldpay.dshackle.upstream.solana
|
||||||
|
|
||||||
|
import io.emeraldpay.dshackle.Defaults
|
||||||
import io.emeraldpay.dshackle.Global
|
import io.emeraldpay.dshackle.Global
|
||||||
import io.emeraldpay.dshackle.upstream.ChainRequest
|
import io.emeraldpay.dshackle.upstream.ChainRequest
|
||||||
import io.emeraldpay.dshackle.upstream.ChainResponse
|
import io.emeraldpay.dshackle.upstream.ChainResponse
|
||||||
@@ -28,7 +29,7 @@ class SolanaLowerBoundSlotDetector(
|
|||||||
.flatMap {
|
.flatMap {
|
||||||
it.read(
|
it.read(
|
||||||
ChainRequest("getFirstAvailableBlock", ListParams()), // in case of solana we talk about the slot of the lowest confirmed block
|
ChainRequest("getFirstAvailableBlock", ListParams()), // in case of solana we talk about the slot of the lowest confirmed block
|
||||||
)
|
).timeout(Defaults.internalCallsTimeout)
|
||||||
}
|
}
|
||||||
.flatMap(ChainResponse::requireResult)
|
.flatMap(ChainResponse::requireResult)
|
||||||
.map {
|
.map {
|
||||||
@@ -53,6 +54,7 @@ class SolanaLowerBoundSlotDetector(
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
.timeout(Defaults.internalCallsTimeout)
|
||||||
.flatMap(ChainResponse::requireResult)
|
.flatMap(ChainResponse::requireResult)
|
||||||
.flatMapMany { blockData ->
|
.flatMapMany { blockData ->
|
||||||
val block = Global.objectMapper.readValue(blockData, SolanaBlock::class.java)
|
val block = Global.objectMapper.readValue(blockData, SolanaBlock::class.java)
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import reactor.core.publisher.Flux
|
|||||||
class SolanaUpstreamSettingsDetector(
|
class SolanaUpstreamSettingsDetector(
|
||||||
upstream: Upstream,
|
upstream: Upstream,
|
||||||
) : BasicUpstreamSettingsDetector(upstream) {
|
) : BasicUpstreamSettingsDetector(upstream) {
|
||||||
override fun detectLabels(): Flux<Pair<String, String>> {
|
override fun internalDetectLabels(): Flux<Pair<String, String>> {
|
||||||
return Flux.merge(
|
return Flux.merge(
|
||||||
detectNodeType(),
|
detectNodeType(),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ class EthereumUpstreamSettingsDetectorSpec extends Specification {
|
|||||||
def detector = new EthereumUpstreamSettingsDetector(up, Chain.ETHEREUM__MAINNET)
|
def detector = new EthereumUpstreamSettingsDetector(up, Chain.ETHEREUM__MAINNET)
|
||||||
|
|
||||||
when:
|
when:
|
||||||
def act = detector.detectLabels()
|
def act = detector.internalDetectLabels()
|
||||||
then:
|
then:
|
||||||
StepVerifier.create(act)
|
StepVerifier.create(act)
|
||||||
.expectNext(
|
.expectNext(
|
||||||
@@ -72,7 +72,7 @@ class EthereumUpstreamSettingsDetectorSpec extends Specification {
|
|||||||
def detector = new EthereumUpstreamSettingsDetector(up, Chain.ETHEREUM__MAINNET)
|
def detector = new EthereumUpstreamSettingsDetector(up, Chain.ETHEREUM__MAINNET)
|
||||||
|
|
||||||
when:
|
when:
|
||||||
def act = detector.detectLabels()
|
def act = detector.internalDetectLabels()
|
||||||
then:
|
then:
|
||||||
StepVerifier.create(act)
|
StepVerifier.create(act)
|
||||||
.expectNext(
|
.expectNext(
|
||||||
@@ -113,7 +113,7 @@ class EthereumUpstreamSettingsDetectorSpec extends Specification {
|
|||||||
}
|
}
|
||||||
def detector = new EthereumUpstreamSettingsDetector(up, Chain.ETHEREUM__MAINNET)
|
def detector = new EthereumUpstreamSettingsDetector(up, Chain.ETHEREUM__MAINNET)
|
||||||
when:
|
when:
|
||||||
def act = detector.detectLabels()
|
def act = detector.internalDetectLabels()
|
||||||
then:
|
then:
|
||||||
StepVerifier.create(act)
|
StepVerifier.create(act)
|
||||||
.expectNext(new Pair<String, String>("archive", "false"))
|
.expectNext(new Pair<String, String>("archive", "false"))
|
||||||
|
|||||||
@@ -444,10 +444,10 @@ class GenericWsHeadTest {
|
|||||||
StepVerifier.create(wsHead.headLiveness())
|
StepVerifier.create(wsHead.headLiveness())
|
||||||
.expectNext(HeadLivenessState.FATAL_ERROR)
|
.expectNext(HeadLivenessState.FATAL_ERROR)
|
||||||
.thenCancel()
|
.thenCancel()
|
||||||
.verify(Duration.ofSeconds(1))
|
.verify(Duration.ofSeconds(2))
|
||||||
}
|
}
|
||||||
.thenCancel()
|
.thenCancel()
|
||||||
.verify(Duration.ofSeconds(1))
|
.verify(Duration.ofSeconds(3))
|
||||||
|
|
||||||
verify(connection, times(2)).callRpc(ChainRequest("eth_chainId", ListParams()))
|
verify(connection, times(2)).callRpc(ChainRequest("eth_chainId", ListParams()))
|
||||||
verify(connection, times(2)).callRpc(ChainRequest("net_version", ListParams()))
|
verify(connection, times(2)).callRpc(ChainRequest("net_version", ListParams()))
|
||||||
|
|||||||
Reference in New Issue
Block a user