Merge pull request #111 from p2p-org/fix-extra-cpu-usage-1
Small improvements after dynamic head introduction
This commit is contained in:
@@ -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()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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"))
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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> =
|
||||||
|
|||||||
@@ -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())
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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()
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user