Merge pull request #111 from p2p-org/fix-extra-cpu-usage-1

Small improvements after dynamic head introduction
This commit is contained in:
a10zn8
2023-01-16 17:48:29 +04:00
committed by GitHub
9 changed files with 44 additions and 24 deletions

View File

@@ -8,7 +8,7 @@ import java.util.concurrent.ConcurrentHashMap
class DynamicMergeFlux<K : Any, T>(private val scheduler: Scheduler) { class DynamicMergeFlux<K : Any, T>(private val scheduler: Scheduler) {
private val merge = Sinks.many().multicast().onBackpressureBuffer<T>() private val merge = Sinks.many().multicast().directBestEffort<T>()
private val sources = ConcurrentHashMap<K, Disposable>() private val sources = ConcurrentHashMap<K, Disposable>()
fun add(flux: Flux<T>, id: K) { fun add(flux: Flux<T>, id: K) {
@@ -30,4 +30,6 @@ class DynamicMergeFlux<K : Any, T>(private val scheduler: Scheduler) {
sources.forEach { (_, d) -> d.dispose() } sources.forEach { (_, d) -> d.dispose() }
merge.emitComplete { _, res -> res == Sinks.EmitResult.FAIL_NON_SERIALIZED } merge.emitComplete { _, res -> res == Sinks.EmitResult.FAIL_NON_SERIALIZED }
} }
fun getKeys(): List<K> = sources.keys().toList()
} }

View File

@@ -8,23 +8,27 @@ import io.emeraldpay.dshackle.upstream.Multistream
import io.emeraldpay.dshackle.upstream.bitcoin.BitcoinMultistream import io.emeraldpay.dshackle.upstream.bitcoin.BitcoinMultistream
import io.emeraldpay.dshackle.upstream.ethereum.EthereumMultistream import io.emeraldpay.dshackle.upstream.ethereum.EthereumMultistream
import io.emeraldpay.dshackle.upstream.ethereum.EthereumPosMultiStream import io.emeraldpay.dshackle.upstream.ethereum.EthereumPosMultiStream
import org.springframework.beans.factory.annotation.Qualifier
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory import org.springframework.beans.factory.config.ConfigurableListableBeanFactory
import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration import org.springframework.context.annotation.Configuration
import reactor.core.scheduler.Scheduler
@Configuration @Configuration
open class MultistreamsConfig(val beanFactory: ConfigurableListableBeanFactory) { open class MultistreamsConfig(val beanFactory: ConfigurableListableBeanFactory) {
@Bean @Bean
open fun allMultistreams( open fun allMultistreams(
cachesFactory: CachesFactory, cachesFactory: CachesFactory,
callTargetsHolder: CallTargetsHolder callTargetsHolder: CallTargetsHolder,
@Qualifier("headMergedScheduler")
headScheduler: Scheduler
): List<Multistream> { ): List<Multistream> {
return Chain.values() return Chain.values()
.filterNot { it == Chain.UNSPECIFIED } .filterNot { it == Chain.UNSPECIFIED }
.mapNotNull { chain -> .mapNotNull { chain ->
when (BlockchainType.from(chain)) { when (BlockchainType.from(chain)) {
BlockchainType.EVM_POS -> ethereumPosMultistream(chain, cachesFactory) BlockchainType.EVM_POS -> ethereumPosMultistream(chain, cachesFactory, headScheduler)
BlockchainType.EVM_POW -> ethereumMultistream(chain, cachesFactory) BlockchainType.EVM_POW -> ethereumMultistream(chain, cachesFactory, headScheduler)
BlockchainType.BITCOIN -> bitcoinMultistream(chain, cachesFactory) BlockchainType.BITCOIN -> bitcoinMultistream(chain, cachesFactory)
else -> null else -> null
} }
@@ -33,27 +37,31 @@ open class MultistreamsConfig(val beanFactory: ConfigurableListableBeanFactory)
private fun ethereumMultistream( private fun ethereumMultistream(
chain: Chain, chain: Chain,
cachesFactory: CachesFactory cachesFactory: CachesFactory,
headScheduler: Scheduler
): EthereumMultistream { ): EthereumMultistream {
val name = "multi-ethereum-$chain" val name = "multi-ethereum-$chain"
return EthereumMultistream( return EthereumMultistream(
chain, chain,
ArrayList(), ArrayList(),
cachesFactory.getCaches(chain) cachesFactory.getCaches(chain),
headScheduler
).also { register(it, name) } ).also { register(it, name) }
} }
open fun ethereumPosMultistream( open fun ethereumPosMultistream(
chain: Chain, chain: Chain,
cachesFactory: CachesFactory cachesFactory: CachesFactory,
headScheduler: Scheduler
): EthereumPosMultiStream { ): EthereumPosMultiStream {
val name = "multi-ethereum-pos-$chain" val name = "multi-ethereum-pos-$chain"
return EthereumPosMultiStream( return EthereumPosMultiStream(
chain, chain,
ArrayList(), ArrayList(),
cachesFactory.getCaches(chain) cachesFactory.getCaches(chain),
headScheduler
).also { register(it, name) } ).also { register(it, name) }
} }

View File

@@ -22,6 +22,11 @@ open class SchedulersConfig {
return makeScheduler("tracktx-scheduler", "tracktx", 5, monitoringConfig) return makeScheduler("tracktx-scheduler", "tracktx", 5, monitoringConfig)
} }
@Bean
open fun headMergedScheduler(monitoringConfig: MonitoringConfig): Scheduler {
return makeScheduler("head-scheduler", "head_merge", 5, monitoringConfig)
}
private fun makeScheduler(name: String, prefix: String, size: Int, monitoringConfig: MonitoringConfig): Scheduler { private fun makeScheduler(name: String, prefix: String, size: Int, monitoringConfig: MonitoringConfig): Scheduler {
val pool = Executors.newFixedThreadPool(size, CustomizableThreadFactory("$name-%d")) val pool = Executors.newFixedThreadPool(size, CustomizableThreadFactory("$name-%d"))

View File

@@ -40,10 +40,12 @@ open class DynamicMergedHead(
} }
fun addHead(upstream: Upstream) { fun addHead(upstream: Upstream) {
log.debug("adding upstream head of [${upstream.getId()}] to dynamic head of [$label]. Current heads ${dynamicFlux.getKeys()}")
dynamicFlux.add(upstream.getHead().getFlux(), upstream.getId()) dynamicFlux.add(upstream.getHead().getFlux(), upstream.getId())
} }
fun removeHead(id: String) { fun removeHead(id: String) {
log.debug("removing upstream head of [$id] from dynamic head $label. Current heads ${dynamicFlux.getKeys()}")
dynamicFlux.remove(id) dynamicFlux.remove(id)
} }
} }

View File

@@ -32,13 +32,14 @@ import org.slf4j.LoggerFactory
import org.springframework.util.ConcurrentReferenceHashMap import org.springframework.util.ConcurrentReferenceHashMap
import reactor.core.publisher.Flux import reactor.core.publisher.Flux
import reactor.core.publisher.Mono import reactor.core.publisher.Mono
import reactor.core.scheduler.Schedulers import reactor.core.scheduler.Scheduler
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
open class EthereumMultistream( open class EthereumMultistream(
chain: Chain, chain: Chain,
val upstreams: MutableList<EthereumUpstream>, val upstreams: MutableList<EthereumUpstream>,
caches: Caches caches: Caches,
headScheduler: Scheduler
) : Multistream(chain, upstreams as MutableList<Upstream>, caches), EthereumLikeMultistream { ) : Multistream(chain, upstreams as MutableList<Upstream>, caches), EthereumLikeMultistream {
companion object { companion object {
@@ -48,7 +49,7 @@ open class EthereumMultistream(
private var head: DynamicMergedHead = DynamicMergedHead( private var head: DynamicMergedHead = DynamicMergedHead(
PriorityForkChoice(), PriorityForkChoice(),
"ETH Multistream", "ETH Multistream",
Schedulers.boundedElastic() headScheduler
) )
private val filteredHeads: MutableMap<String, Head> = private val filteredHeads: MutableMap<String, Head> =

View File

@@ -31,13 +31,14 @@ import org.slf4j.LoggerFactory
import org.springframework.util.ConcurrentReferenceHashMap import org.springframework.util.ConcurrentReferenceHashMap
import reactor.core.publisher.Flux import reactor.core.publisher.Flux
import reactor.core.publisher.Mono import reactor.core.publisher.Mono
import reactor.core.scheduler.Schedulers import reactor.core.scheduler.Scheduler
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
open class EthereumPosMultiStream( open class EthereumPosMultiStream(
chain: Chain, chain: Chain,
val upstreams: MutableList<EthereumPosUpstream>, val upstreams: MutableList<EthereumPosUpstream>,
caches: Caches caches: Caches,
headScheduler: Scheduler
) : Multistream(chain, upstreams as MutableList<Upstream>, caches), EthereumLikeMultistream { ) : Multistream(chain, upstreams as MutableList<Upstream>, caches), EthereumLikeMultistream {
companion object { companion object {
@@ -47,7 +48,7 @@ open class EthereumPosMultiStream(
private var head: DynamicMergedHead = DynamicMergedHead( private var head: DynamicMergedHead = DynamicMergedHead(
PriorityForkChoice(), PriorityForkChoice(),
"ETH Pos Multistream", "ETH Pos Multistream",
Schedulers.boundedElastic() headScheduler
) )
private val reader: EthereumCachingReader = EthereumCachingReader(this, this.caches, getMethodsFactory()) private val reader: EthereumCachingReader = EthereumCachingReader(this, this.caches, getMethodsFactory())

View File

@@ -34,6 +34,7 @@ import io.emeraldpay.dshackle.BlockchainType
import io.emeraldpay.dshackle.Chain import io.emeraldpay.dshackle.Chain
import org.jetbrains.annotations.NotNull import org.jetbrains.annotations.NotNull
import reactor.core.scheduler.Schedulers
class MultistreamHolderMock implements MultistreamHolder { class MultistreamHolderMock implements MultistreamHolder {
@@ -50,7 +51,7 @@ class MultistreamHolderMock implements MultistreamHolder {
if (up instanceof EthereumPosMultiStream) { if (up instanceof EthereumPosMultiStream) {
upstreams[chain] = up upstreams[chain] = up
} else if (up instanceof EthereumPosRpcUpstream) { } else if (up instanceof EthereumPosRpcUpstream) {
upstreams[chain] = new EthereumPosMultiStream(chain, [up as EthereumPosRpcUpstream], Caches.default()) upstreams[chain] = new EthereumPosMultiStream(chain, [up as EthereumPosRpcUpstream], Caches.default(), Schedulers.boundedElastic())
} else { } else {
throw new IllegalArgumentException("Unsupported upstream type ${up.class}") throw new IllegalArgumentException("Unsupported upstream type ${up.class}")
} }
@@ -98,7 +99,7 @@ class MultistreamHolderMock implements MultistreamHolder {
Head customHead = null Head customHead = null
EthereumMultistreamMock(@NotNull Chain chain, @NotNull List<EthereumPosRpcUpstream> upstreams, @NotNull Caches caches) { EthereumMultistreamMock(@NotNull Chain chain, @NotNull List<EthereumPosRpcUpstream> upstreams, @NotNull Caches caches) {
super(chain, upstreams, caches) super(chain, upstreams, caches, Schedulers.boundedElastic())
} }
EthereumMultistreamMock(@NotNull Chain chain, @NotNull List<EthereumPosRpcUpstream> upstreams) { EthereumMultistreamMock(@NotNull Chain chain, @NotNull List<EthereumPosRpcUpstream> upstreams) {

View File

@@ -38,6 +38,7 @@ import io.emeraldpay.dshackle.Chain
import io.micrometer.core.instrument.MeterRegistry import io.micrometer.core.instrument.MeterRegistry
import io.micrometer.core.instrument.logging.LoggingMeterRegistry import io.micrometer.core.instrument.logging.LoggingMeterRegistry
import org.apache.commons.lang3.StringUtils import org.apache.commons.lang3.StringUtils
import reactor.core.scheduler.Schedulers
import java.time.Instant import java.time.Instant
@@ -84,7 +85,7 @@ class TestingCommons {
} }
static Multistream multistream(EthereumPosRpcUpstreamMock up) { static Multistream multistream(EthereumPosRpcUpstreamMock up) {
return new EthereumPosMultiStream(Chain.ETHEREUM, [up], Caches.default()).tap { return new EthereumPosMultiStream(Chain.ETHEREUM, [up], Caches.default(), Schedulers.boundedElastic()).tap {
start() start()
} }
} }
@@ -105,11 +106,11 @@ class TestingCommons {
} }
static Multistream multistreamWithoutUpstreams(Chain chain) { static Multistream multistreamWithoutUpstreams(Chain chain) {
return new EthereumPosMultiStream(chain, [], emptyCaches().getCaches(chain)) return new EthereumPosMultiStream(chain, [], emptyCaches().getCaches(chain), Schedulers.boundedElastic())
} }
static Multistream multistreamClassicWithoutUpstreams(Chain chain) { static Multistream multistreamClassicWithoutUpstreams(Chain chain) {
return new EthereumMultistream(chain, [], emptyCaches().getCaches(chain)) return new EthereumMultistream(chain, [], emptyCaches().getCaches(chain), Schedulers.boundedElastic())
} }
static FileResolver fileResolver() { static FileResolver fileResolver() {
@@ -148,6 +149,4 @@ class TestingCommons {
} }
static MeterRegistry meterRegistry = new LoggingMeterRegistry() static MeterRegistry meterRegistry = new LoggingMeterRegistry()
static CallTargetsHolder callTargetsHolder = new CallTargetsHolder()
} }

View File

@@ -37,6 +37,7 @@ import io.emeraldpay.dshackle.Chain
import org.jetbrains.annotations.NotNull import org.jetbrains.annotations.NotNull
import reactor.core.publisher.Flux import reactor.core.publisher.Flux
import reactor.core.publisher.Mono import reactor.core.publisher.Mono
import reactor.core.scheduler.Schedulers
import reactor.test.StepVerifier import reactor.test.StepVerifier
import spock.lang.Specification import spock.lang.Specification
@@ -50,7 +51,7 @@ class MultistreamSpec extends Specification {
setup: setup:
def up1 = new EthereumPosRpcUpstreamMock("test1", Chain.ETHEREUM, TestingCommons.api(), new DirectCallMethods(["eth_test1", "eth_test2"])) def up1 = new EthereumPosRpcUpstreamMock("test1", Chain.ETHEREUM, TestingCommons.api(), new DirectCallMethods(["eth_test1", "eth_test2"]))
def up2 = new EthereumPosRpcUpstreamMock("test1", Chain.ETHEREUM, TestingCommons.api(), new DirectCallMethods(["eth_test2", "eth_test3"])) def up2 = new EthereumPosRpcUpstreamMock("test1", Chain.ETHEREUM, TestingCommons.api(), new DirectCallMethods(["eth_test2", "eth_test3"]))
def aggr = new EthereumPosMultiStream(Chain.ETHEREUM, [up1, up2], Caches.default()) def aggr = new EthereumPosMultiStream(Chain.ETHEREUM, [up1, up2], Caches.default(), Schedulers.boundedElastic())
when: when:
aggr.onUpstreamsUpdated() aggr.onUpstreamsUpdated()
def act = aggr.getMethods() def act = aggr.getMethods()
@@ -186,7 +187,7 @@ class MultistreamSpec extends Specification {
def up1 = TestingCommons.upstream("test-1", "internal") def up1 = TestingCommons.upstream("test-1", "internal")
def up2 = TestingCommons.upstream("test-2", "external") def up2 = TestingCommons.upstream("test-2", "external")
def up3 = TestingCommons.upstream("test-3", "external") def up3 = TestingCommons.upstream("test-3", "external")
def multistream = new EthereumPosMultiStream(Chain.ETHEREUM, [up1, up2, up3], Caches.default()) def multistream = new EthereumPosMultiStream(Chain.ETHEREUM, [up1, up2, up3], Caches.default(), Schedulers.boundedElastic())
expect: expect:
multistream.getHead(new Selector.LabelMatcher("provider", ["internal"])).is(up1.ethereumHeadMock) multistream.getHead(new Selector.LabelMatcher("provider", ["internal"])).is(up1.ethereumHeadMock)
@@ -254,7 +255,7 @@ class MultistreamSpec extends Specification {
class TestEthereumPosMultistream extends EthereumPosMultiStream { class TestEthereumPosMultistream extends EthereumPosMultiStream {
TestEthereumPosMultistream(@NotNull Chain chain, @NotNull List<EthereumPosUpstream> upstreams, @NotNull Caches caches) { TestEthereumPosMultistream(@NotNull Chain chain, @NotNull List<EthereumPosUpstream> upstreams, @NotNull Caches caches) {
super(chain, upstreams, caches) super(chain, upstreams, caches, Schedulers.boundedElastic())
} }
@NotNull @NotNull