disable local router caches

This commit is contained in:
a10zn8
2022-12-12 17:10:39 +04:00
parent e144a9b0de
commit 2c666eed99
13 changed files with 53 additions and 23 deletions

View File

@@ -17,6 +17,8 @@ package io.emeraldpay.dshackle.config
class CacheConfig { class CacheConfig {
var requestsCacheEnabled = true
var redis: Redis? = null var redis: Redis? = null
class Redis( class Redis(

View File

@@ -33,6 +33,9 @@ class CacheConfigReader : YamlConfigReader(), ConfigReader<CacheConfig> {
override fun read(input: MappingNode?): CacheConfig? { override fun read(input: MappingNode?): CacheConfig? {
return getMapping(input, "cache")?.let { node -> return getMapping(input, "cache")?.let { node ->
val config = CacheConfig() val config = CacheConfig()
getValueAsBool(node, "requests-cache-enabled")?.let {
config.requestsCacheEnabled = it
}
getMapping(node, "redis")?.let { redisNode -> getMapping(node, "redis")?.let { redisNode ->
val redis = CacheConfig.Redis() val redis = CacheConfig.Redis()
val enabled = getValueAsBool(redisNode, "enabled") ?: true val enabled = getValueAsBool(redisNode, "enabled") ?: true
@@ -52,7 +55,7 @@ class CacheConfigReader : YamlConfigReader(), ConfigReader<CacheConfig> {
config.redis = redis config.redis = redis
} }
} }
if (config.redis == null) { if (config.redis == null && config.requestsCacheEnabled) {
return null return null
} }
config config

View File

@@ -23,6 +23,7 @@ import io.emeraldpay.dshackle.BlockchainType
import io.emeraldpay.dshackle.Chain import io.emeraldpay.dshackle.Chain
import io.emeraldpay.dshackle.Global import io.emeraldpay.dshackle.Global
import io.emeraldpay.dshackle.SilentException import io.emeraldpay.dshackle.SilentException
import io.emeraldpay.dshackle.config.CacheConfig
import io.emeraldpay.dshackle.quorum.CallQuorum import io.emeraldpay.dshackle.quorum.CallQuorum
import io.emeraldpay.dshackle.quorum.NotLaggingQuorum import io.emeraldpay.dshackle.quorum.NotLaggingQuorum
import io.emeraldpay.dshackle.quorum.QuorumReaderFactory import io.emeraldpay.dshackle.quorum.QuorumReaderFactory
@@ -55,7 +56,8 @@ import java.util.concurrent.atomic.AtomicInteger
@Service @Service
open class NativeCall( open class NativeCall(
private val multistreamHolder: MultistreamHolder, private val multistreamHolder: MultistreamHolder,
private val signer: ResponseSigner private val signer: ResponseSigner,
cacheConfig: CacheConfig
) { ) {
private val log = LoggerFactory.getLogger(NativeCall::class.java) private val log = LoggerFactory.getLogger(NativeCall::class.java)
@@ -63,6 +65,8 @@ open class NativeCall(
private val nullValue: ByteArray = "null".toByteArray() private val nullValue: ByteArray = "null".toByteArray()
private val localRouterEnabled = cacheConfig.requestsCacheEnabled
var quorumReaderFactory: QuorumReaderFactory = QuorumReaderFactory.default() var quorumReaderFactory: QuorumReaderFactory = QuorumReaderFactory.default()
private val ethereumCallSelectors = EnumMap<Chain, EthereumCallSelector>(Chain::class.java) private val ethereumCallSelectors = EnumMap<Chain, EthereumCallSelector>(Chain::class.java)
@@ -121,9 +125,6 @@ open class NativeCall(
result.setErrorMessage(error.message).setErrorCode(error.id) result.setErrorMessage(error.message).setErrorCode(error.id)
} }
} else { } else {
if (it.result == null || it.result.isEmpty() || nullValue.contentEquals(it.result)) {
log.warn("Empty result [${it.result}] on building response, method ${it.ctx?.payload?.method}, params ${it.ctx?.payload?.params}")
}
result.payload = ByteString.copyFrom(it.result) result.payload = ByteString.copyFrom(it.result)
} }
if (it.nonce != null && it.signature != null) { if (it.nonce != null && it.signature != null) {
@@ -273,7 +274,7 @@ open class NativeCall(
if (method in DefaultEthereumMethods.newFilterMethods) CreateFilterDecorator() else NoneResultDecorator() if (method in DefaultEthereumMethods.newFilterMethods) CreateFilterDecorator() else NoneResultDecorator()
fun fetch(ctx: ValidCallContext<ParsedCallDetails>): Mono<CallResult> { fun fetch(ctx: ValidCallContext<ParsedCallDetails>): Mono<CallResult> {
return ctx.upstream.getRoutedApi(ctx.matcher) return ctx.upstream.getRoutedApi(localRouterEnabled)
.flatMap { api -> .flatMap { api ->
api.read(JsonRpcRequest(ctx.payload.method, ctx.payload.params, ctx.nonce, ctx.forwardedSelector)) api.read(JsonRpcRequest(ctx.payload.method, ctx.payload.params, ctx.nonce, ctx.forwardedSelector))
.flatMap(JsonRpcResponse::requireResult) .flatMap(JsonRpcResponse::requireResult)

View File

@@ -148,7 +148,7 @@ abstract class Multistream(
/** /**
* Finds an API that leverages caches and other optimizations/transformations of the request. * Finds an API that leverages caches and other optimizations/transformations of the request.
*/ */
abstract fun getRoutedApi(matcher: Selector.Matcher): Mono<Reader<JsonRpcRequest, JsonRpcResponse>> abstract fun getRoutedApi(localEnabled: Boolean): Mono<Reader<JsonRpcRequest, JsonRpcResponse>>
override fun getApi(): Reader<JsonRpcRequest, JsonRpcResponse> { override fun getApi(): Reader<JsonRpcRequest, JsonRpcResponse> {
throw NotImplementedError("Immediate direct API is not implemented for Aggregated Upstream") throw NotImplementedError("Immediate direct API is not implemented for Aggregated Upstream")

View File

@@ -106,7 +106,7 @@ open class BitcoinMultistream(
.switchIfEmpty(Mono.error(Exception("No API available for $chain"))) .switchIfEmpty(Mono.error(Exception("No API available for $chain")))
} }
override fun getRoutedApi(matcher: Selector.Matcher): Mono<Reader<JsonRpcRequest, JsonRpcResponse>> { override fun getRoutedApi(localEnabled: Boolean): Mono<Reader<JsonRpcRequest, JsonRpcResponse>> {
return Mono.just(callRouter) return Mono.just(callRouter)
} }

View File

@@ -154,8 +154,8 @@ open class EthereumMultistream(
return this as T return this as T
} }
override fun getRoutedApi(matcher: Selector.Matcher): Mono<Reader<JsonRpcRequest, JsonRpcResponse>> { override fun getRoutedApi(localEnabled: Boolean): Mono<Reader<JsonRpcRequest, JsonRpcResponse>> {
return Mono.just(LocalCallRouter(reader, getMethods(), getHead())) return Mono.just(LocalCallRouter(reader, getMethods(), getHead(), localEnabled))
} }
override fun getSubscribe(): EthereumSubscribe { override fun getSubscribe(): EthereumSubscribe {

View File

@@ -39,7 +39,8 @@ import java.math.BigInteger
class LocalCallRouter( class LocalCallRouter(
private val reader: EthereumCachingReader, private val reader: EthereumCachingReader,
private val methods: CallMethods, private val methods: CallMethods,
private val head: Head private val head: Head,
private val localEnabled: Boolean
) : Reader<JsonRpcRequest, JsonRpcResponse> { ) : Reader<JsonRpcRequest, JsonRpcResponse> {
companion object { companion object {
@@ -56,7 +57,9 @@ class LocalCallRouter(
return Mono.just(methods.executeHardcoded(key.method)) return Mono.just(methods.executeHardcoded(key.method))
.map { JsonRpcResponse(it, null) } .map { JsonRpcResponse(it, null) }
} }
if (!localEnabled) {
return Mono.empty()
}
if (!methods.isCallable(key.method)) { if (!methods.isCallable(key.method)) {
return Mono.error(RpcException(RpcResponseError.CODE_METHOD_NOT_EXIST, "Unsupported method")) return Mono.error(RpcException(RpcResponseError.CODE_METHOD_NOT_EXIST, "Unsupported method"))
} }

View File

@@ -147,8 +147,8 @@ open class EthereumPosMultiStream(
return this as T return this as T
} }
override fun getRoutedApi(matcher: Selector.Matcher): Mono<Reader<JsonRpcRequest, JsonRpcResponse>> { override fun getRoutedApi(localEnabled: Boolean): Mono<Reader<JsonRpcRequest, JsonRpcResponse>> {
return Mono.just(LocalCallRouter(reader, getMethods(), getHead())) return Mono.just(LocalCallRouter(reader, getMethods(), getHead(), localEnabled))
} }
override fun getSubscribe(): EthereumSubscribe { override fun getSubscribe(): EthereumSubscribe {

View File

@@ -47,4 +47,14 @@ class CacheConfigReaderSpec extends Specification {
//later may be not null if we support something else besides Redis //later may be not null if we support something else besides Redis
act == null act == null
} }
def "Local read disabled"() {
setup:
def config = this.class.getClassLoader().getResourceAsStream("cache-local-router-disabled.yaml")
when:
def act = reader.read(config)
then:
!act.requestsCacheEnabled
}
} }

View File

@@ -21,10 +21,10 @@ import com.google.protobuf.ByteString
import io.emeraldpay.api.proto.BlockchainOuterClass import io.emeraldpay.api.proto.BlockchainOuterClass
import io.emeraldpay.api.proto.Common import io.emeraldpay.api.proto.Common
import io.emeraldpay.dshackle.Global import io.emeraldpay.dshackle.Global
import io.emeraldpay.dshackle.config.CacheConfig
import io.emeraldpay.dshackle.quorum.QuorumReaderFactory import io.emeraldpay.dshackle.quorum.QuorumReaderFactory
import io.emeraldpay.dshackle.quorum.QuorumRpcReader import io.emeraldpay.dshackle.quorum.QuorumRpcReader
import io.emeraldpay.dshackle.reader.Reader import io.emeraldpay.dshackle.reader.Reader
import io.emeraldpay.dshackle.startup.ConfiguredUpstreams
import io.emeraldpay.dshackle.test.MultistreamHolderMock import io.emeraldpay.dshackle.test.MultistreamHolderMock
import io.emeraldpay.dshackle.test.TestingCommons import io.emeraldpay.dshackle.test.TestingCommons
import io.emeraldpay.dshackle.quorum.AlwaysQuorum import io.emeraldpay.dshackle.quorum.AlwaysQuorum
@@ -52,14 +52,19 @@ class NativeCallSpec extends Specification {
ObjectMapper objectMapper = Global.objectMapper ObjectMapper objectMapper = Global.objectMapper
def nativeCall(MultistreamHolder upstreams = null, ResponseSigner signer = null) { def nativeCall(MultistreamHolder upstreams = null, ResponseSigner signer = null, Boolean enableCache = true) {
if (upstreams == null) { if (upstreams == null) {
upstreams = Stub(MultistreamHolder) upstreams = Stub(MultistreamHolder)
} }
if (signer == null) { if (signer == null) {
signer = Stub(ResponseSigner) signer = Stub(ResponseSigner)
} }
new NativeCall(upstreams, signer)
def config = new CacheConfig()
config.requestsCacheEnabled = enableCache
new NativeCall(upstreams, signer, config)
} }
def "Tries router first"() { def "Tries router first"() {

View File

@@ -327,8 +327,9 @@ class MultistreamSpec extends Specification {
super(chain, upstreams, caches) super(chain, upstreams, caches)
} }
@NotNull
@Override @Override
Mono<Reader<JsonRpcRequest, JsonRpcResponse>> getRoutedApi(@NotNull Selector.Matcher matcher) { Mono<Reader<JsonRpcRequest, JsonRpcResponse>> getRoutedApi(boolean localEnabled) {
return null return null
} }

View File

@@ -2,6 +2,7 @@ package io.emeraldpay.dshackle.upstream.ethereum
import io.emeraldpay.dshackle.Global import io.emeraldpay.dshackle.Global
import io.emeraldpay.dshackle.cache.Caches import io.emeraldpay.dshackle.cache.Caches
import io.emeraldpay.dshackle.config.CacheConfig
import io.emeraldpay.dshackle.reader.EmptyReader import io.emeraldpay.dshackle.reader.EmptyReader
import io.emeraldpay.dshackle.test.TestingCommons import io.emeraldpay.dshackle.test.TestingCommons
import io.emeraldpay.dshackle.upstream.EmptyHead import io.emeraldpay.dshackle.upstream.EmptyHead
@@ -29,7 +30,8 @@ class LocalCallRouterSpec extends Specification {
ConstantFactory.constantFactory(new DefaultEthereumMethods(Chain.ETHEREUM)) ConstantFactory.constantFactory(new DefaultEthereumMethods(Chain.ETHEREUM))
), ),
methods, methods,
new EmptyHead() new EmptyHead(),
true
) )
when: when:
def act = router.read(new JsonRpcRequest("eth_coinbase", [])).block(Duration.ofSeconds(1)) def act = router.read(new JsonRpcRequest("eth_coinbase", [])).block(Duration.ofSeconds(1))
@@ -47,7 +49,8 @@ class LocalCallRouterSpec extends Specification {
ConstantFactory.constantFactory(new DefaultEthereumMethods(Chain.ETHEREUM)) ConstantFactory.constantFactory(new DefaultEthereumMethods(Chain.ETHEREUM))
), ),
methods, methods,
new EmptyHead() new EmptyHead(),
true
) )
when: when:
def act = router.read(new JsonRpcRequest("eth_getTransactionByHash", ["test"], 10)) def act = router.read(new JsonRpcRequest("eth_getTransactionByHash", ["test"], 10))
@@ -69,7 +72,7 @@ class LocalCallRouterSpec extends Specification {
} }
} }
def methods = new DefaultEthereumMethods(Chain.ETHEREUM) def methods = new DefaultEthereumMethods(Chain.ETHEREUM)
def router = new LocalCallRouter(reader, methods, head) def router = new LocalCallRouter(reader, methods, head, true)
when: when:
def act = router.getBlockByNumber(["latest", false]) def act = router.getBlockByNumber(["latest", false])
@@ -95,7 +98,7 @@ class LocalCallRouterSpec extends Specification {
} }
} }
def methods = new DefaultEthereumMethods(Chain.ETHEREUM) def methods = new DefaultEthereumMethods(Chain.ETHEREUM)
def router = new LocalCallRouter(reader, methods, head) def router = new LocalCallRouter(reader, methods, head, true)
when: when:
def act = router.getBlockByNumber(["earliest", false]) def act = router.getBlockByNumber(["earliest", false])
@@ -121,7 +124,7 @@ class LocalCallRouterSpec extends Specification {
} }
} }
def methods = new DefaultEthereumMethods(Chain.ETHEREUM) def methods = new DefaultEthereumMethods(Chain.ETHEREUM)
def router = new LocalCallRouter(reader, methods, head) def router = new LocalCallRouter(reader, methods, head, true)
when: when:
def act = router.getBlockByNumber(["0x123ef", false]) def act = router.getBlockByNumber(["0x123ef", false])

View File

@@ -0,0 +1,2 @@
cache:
requests-cache-enabled: false