multistreams as beans

This commit is contained in:
a10zn8
2022-11-15 20:52:15 +04:00
parent 809844d452
commit 443839062f
4 changed files with 69 additions and 47 deletions

View File

@@ -0,0 +1,27 @@
package io.emeraldpay.dshackle.config.context
import io.emeraldpay.dshackle.cache.CachesFactory
import io.emeraldpay.dshackle.upstream.Multistream
import io.emeraldpay.dshackle.upstream.bitcoin.BitcoinMultistream
import io.emeraldpay.dshackle.upstream.ethereum.EthereumMultistream
import io.emeraldpay.dshackle.upstream.ethereum.EthereumPosMultiStream
import io.emeraldpay.grpc.BlockchainType
import io.emeraldpay.grpc.Chain
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
@Configuration
class MultistreamsConfig {
@Bean
fun allMultistreams(cachesFactory: CachesFactory): List<Multistream> {
return Chain.values()
.mapNotNull { chain ->
when (BlockchainType.from(chain)) {
BlockchainType.EVM_POS -> EthereumPosMultiStream(chain, ArrayList(), cachesFactory.getCaches(chain))
BlockchainType.EVM_POW -> EthereumMultistream(chain, ArrayList(), cachesFactory.getCaches(chain))
BlockchainType.BITCOIN -> BitcoinMultistream(chain, ArrayList(), cachesFactory.getCaches(chain))
else -> null
}
}
}
}

View File

@@ -17,39 +17,35 @@
package io.emeraldpay.dshackle.upstream package io.emeraldpay.dshackle.upstream
import io.emeraldpay.dshackle.cache.CachesEnabled import io.emeraldpay.dshackle.cache.CachesEnabled
import io.emeraldpay.dshackle.cache.CachesFactory
import io.emeraldpay.dshackle.startup.UpstreamChange import io.emeraldpay.dshackle.startup.UpstreamChange
import io.emeraldpay.dshackle.upstream.bitcoin.BitcoinMultistream
import io.emeraldpay.dshackle.upstream.bitcoin.BitcoinUpstream import io.emeraldpay.dshackle.upstream.bitcoin.BitcoinUpstream
import io.emeraldpay.dshackle.upstream.calls.CallMethods import io.emeraldpay.dshackle.upstream.calls.CallMethods
import io.emeraldpay.dshackle.upstream.calls.DefaultBitcoinMethods import io.emeraldpay.dshackle.upstream.calls.DefaultBitcoinMethods
import io.emeraldpay.dshackle.upstream.calls.DefaultEthereumMethods import io.emeraldpay.dshackle.upstream.calls.DefaultEthereumMethods
import io.emeraldpay.dshackle.upstream.ethereum.EthereumMultistream
import io.emeraldpay.dshackle.upstream.ethereum.EthereumPosMultiStream
import io.emeraldpay.dshackle.upstream.ethereum.EthereumPosUpstream import io.emeraldpay.dshackle.upstream.ethereum.EthereumPosUpstream
import io.emeraldpay.dshackle.upstream.ethereum.EthereumUpstream import io.emeraldpay.dshackle.upstream.ethereum.EthereumUpstream
import io.emeraldpay.grpc.BlockchainType import io.emeraldpay.grpc.BlockchainType
import io.emeraldpay.grpc.Chain import io.emeraldpay.grpc.Chain
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired import org.springframework.stereotype.Component
import org.springframework.stereotype.Repository
import reactor.core.publisher.Flux import reactor.core.publisher.Flux
import reactor.core.publisher.Sinks import reactor.core.publisher.Sinks
import java.util.Collections import java.util.*
import java.util.concurrent.Callable
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.locks.ReentrantLock import java.util.concurrent.locks.ReentrantLock
import javax.annotation.PreDestroy import javax.annotation.PreDestroy
import kotlin.concurrent.withLock import kotlin.concurrent.withLock
@Repository @Component
open class CurrentMultistreamHolder( open class CurrentMultistreamHolder(
@Autowired private val cachesFactory: CachesFactory private val multistreams: List<Multistream>
) : MultistreamHolder { ) : MultistreamHolder {
private val log = LoggerFactory.getLogger(CurrentMultistreamHolder::class.java) private val log = LoggerFactory.getLogger(CurrentMultistreamHolder::class.java)
private val chainMapping = ConcurrentHashMap<Chain, Multistream>() private val chainMapping = ConcurrentHashMap<Chain, Multistream>().apply {
multistreams.forEach { this[it.chain] = it }
}
private val chainsBus = Sinks.many() private val chainsBus = Sinks.many()
.multicast() .multicast()
.directBestEffort<Chain>() .directBestEffort<Chain>()
@@ -64,28 +60,22 @@ open class CurrentMultistreamHolder(
when (BlockchainType.from(chain)) { when (BlockchainType.from(chain)) {
BlockchainType.EVM_POW -> { BlockchainType.EVM_POW -> {
val up = change.upstream.cast(EthereumUpstream::class.java) val up = change.upstream.cast(EthereumUpstream::class.java)
val current = chainMapping[chain] val current = chainMapping.getValue(chain)
val factory = Callable<Multistream> { processUpdate(change, up, current)
EthereumMultistream(chain, ArrayList(), cachesFactory.getCaches(chain))
}
processUpdate(change, up, current, factory)
} }
BlockchainType.EVM_POS -> { BlockchainType.EVM_POS -> {
val up = change.upstream.cast(EthereumPosUpstream::class.java) val up = change.upstream.cast(EthereumPosUpstream::class.java)
val current = chainMapping[chain] val current = chainMapping.getValue(chain)
val factory = Callable<Multistream> { processUpdate(change, up, current)
EthereumPosMultiStream(chain, ArrayList(), cachesFactory.getCaches(chain))
}
processUpdate(change, up, current, factory)
} }
BlockchainType.BITCOIN -> { BlockchainType.BITCOIN -> {
val up = change.upstream.cast(BitcoinUpstream::class.java) val up = change.upstream.cast(BitcoinUpstream::class.java)
val current = chainMapping[chain] val current = chainMapping.getValue(chain)
val factory = Callable<Multistream> { processUpdate(change, up, current)
BitcoinMultistream(chain, ArrayList(), cachesFactory.getCaches(chain))
}
processUpdate(change, up, current, factory)
} }
else -> { else -> {
log.error("Update for unsupported chain: $chain") log.error("Update for unsupported chain: $chain")
} }
@@ -96,27 +86,17 @@ open class CurrentMultistreamHolder(
} }
} }
fun processUpdate(change: UpstreamChange, up: Upstream, current: Multistream?, factory: Callable<Multistream>) { fun processUpdate(change: UpstreamChange, up: Upstream, current: Multistream) {
val chain = change.chain val chain = change.chain
if (change.type == UpstreamChange.ChangeType.REMOVED) { if (change.type == UpstreamChange.ChangeType.REMOVED) {
current?.removeUpstream(up.getId()) current.removeUpstream(up.getId())
log.info("Upstream ${change.upstream.getId()} with chain $chain has been removed") log.info("Upstream ${change.upstream.getId()} with chain $chain has been removed")
} else { } else {
if (current == null) { if (up is CachesEnabled) {
val created = factory.call() up.setCaches(current.caches)
if (up is CachesEnabled) {
up.setCaches(created.caches)
}
created.addUpstream(up)
created.start()
chainMapping[chain] = created
chainsBus.tryEmitNext(chain)
} else {
if (up is CachesEnabled) {
up.setCaches(current.caches)
}
current.addUpstream(up)
} }
current.addUpstream(up)
if (!callTargets.containsKey(chain)) { if (!callTargets.containsKey(chain)) {
setupDefaultMethods(chain) setupDefaultMethods(chain)
} }
@@ -129,7 +109,10 @@ open class CurrentMultistreamHolder(
} }
override fun getAvailable(): List<Chain> { override fun getAvailable(): List<Chain> {
return Collections.unmodifiableList(chainMapping.keys.toList()) return multistreams.asSequence()
.filter { it.isAvailable() }
.map { it.chain }
.toList()
} }
override fun observeChains(): Flux<Chain> { override fun observeChains(): Flux<Chain> {

View File

@@ -27,6 +27,7 @@ import io.emeraldpay.dshackle.reader.Reader
import io.emeraldpay.dshackle.upstream.Multistream import io.emeraldpay.dshackle.upstream.Multistream
import io.emeraldpay.dshackle.upstream.calls.DirectCallMethods import io.emeraldpay.dshackle.upstream.calls.DirectCallMethods
import io.emeraldpay.dshackle.upstream.ethereum.EthereumPosMultiStream import io.emeraldpay.dshackle.upstream.ethereum.EthereumPosMultiStream
import io.emeraldpay.dshackle.upstream.ethereum.EthereumPosUpstream
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcRequest import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcRequest
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcResponse import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcResponse
import io.emeraldpay.etherjar.domain.BlockHash import io.emeraldpay.etherjar.domain.BlockHash
@@ -90,6 +91,17 @@ class TestingCommons {
return new CachesFactory(new CacheConfig()) return new CachesFactory(new CacheConfig())
} }
static List<Multistream> defaultMultistreams() {
return [
multistreamWithoutUpstreams(Chain.ETHEREUM),
multistreamWithoutUpstreams(Chain.ETHEREUM_CLASSIC)
]
}
static Multistream multistreamWithoutUpstreams(Chain chain) {
return new EthereumPosMultiStream(chain, [], emptyCaches().getCaches(chain))
}
static FileResolver fileResolver() { static FileResolver fileResolver() {
return new FileResolver(new File("src/test/resources")) return new FileResolver(new File("src/test/resources"))
} }

View File

@@ -26,7 +26,7 @@ class CurrentMultistreamHolderSpec extends Specification {
def "add upstream"() { def "add upstream"() {
setup: setup:
def current = new CurrentMultistreamHolder(TestingCommons.emptyCaches()) def current = new CurrentMultistreamHolder(TestingCommons.defaultMultistreams())
def up = new EthereumPosRpcUpstreamMock("test", Chain.ETHEREUM, TestingCommons.api()) def up = new EthereumPosRpcUpstreamMock("test", Chain.ETHEREUM, TestingCommons.api())
when: when:
current.update(new UpstreamChange(Chain.ETHEREUM, up, UpstreamChange.ChangeType.ADDED)) current.update(new UpstreamChange(Chain.ETHEREUM, up, UpstreamChange.ChangeType.ADDED))
@@ -37,7 +37,7 @@ class CurrentMultistreamHolderSpec extends Specification {
def "add multiple upstreams"() { def "add multiple upstreams"() {
setup: setup:
def current = new CurrentMultistreamHolder(TestingCommons.emptyCaches()) def current = new CurrentMultistreamHolder(TestingCommons.defaultMultistreams())
def up1 = new EthereumPosRpcUpstreamMock("test1", Chain.ETHEREUM, TestingCommons.api()) def up1 = new EthereumPosRpcUpstreamMock("test1", Chain.ETHEREUM, TestingCommons.api())
def up2 = new EthereumRpcUpstreamMock("test2", Chain.ETHEREUM_CLASSIC, TestingCommons.api()) def up2 = new EthereumRpcUpstreamMock("test2", Chain.ETHEREUM_CLASSIC, TestingCommons.api())
def up3 = new EthereumPosRpcUpstreamMock("test3", Chain.ETHEREUM, TestingCommons.api()) def up3 = new EthereumPosRpcUpstreamMock("test3", Chain.ETHEREUM, TestingCommons.api())
@@ -53,7 +53,7 @@ class CurrentMultistreamHolderSpec extends Specification {
def "remove upstream"() { def "remove upstream"() {
setup: setup:
def current = new CurrentMultistreamHolder(TestingCommons.emptyCaches()) def current = new CurrentMultistreamHolder(TestingCommons.defaultMultistreams())
def up1 = new EthereumPosRpcUpstreamMock("test1", Chain.ETHEREUM, TestingCommons.api()) def up1 = new EthereumPosRpcUpstreamMock("test1", Chain.ETHEREUM, TestingCommons.api())
def up2 = new EthereumRpcUpstreamMock("test2", Chain.ETHEREUM_CLASSIC, TestingCommons.api()) def up2 = new EthereumRpcUpstreamMock("test2", Chain.ETHEREUM_CLASSIC, TestingCommons.api())
def up3 = new EthereumPosRpcUpstreamMock("test3", Chain.ETHEREUM, TestingCommons.api()) def up3 = new EthereumPosRpcUpstreamMock("test3", Chain.ETHEREUM, TestingCommons.api())
@@ -71,7 +71,7 @@ class CurrentMultistreamHolderSpec extends Specification {
def "available after adding"() { def "available after adding"() {
setup: setup:
def current = new CurrentMultistreamHolder(TestingCommons.emptyCaches()) def current = new CurrentMultistreamHolder(TestingCommons.defaultMultistreams())
def up1 = new EthereumPosRpcUpstreamMock("test1", Chain.ETHEREUM, TestingCommons.api()) def up1 = new EthereumPosRpcUpstreamMock("test1", Chain.ETHEREUM, TestingCommons.api())
when: when: