problem: doesn't use cache for block-by-height requests
This commit is contained in:
27
src/main/kotlin/io/emeraldpay/dshackle/cache/BlockByHeight.kt
vendored
Normal file
27
src/main/kotlin/io/emeraldpay/dshackle/cache/BlockByHeight.kt
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
package io.emeraldpay.dshackle.cache
|
||||
|
||||
import io.emeraldpay.dshackle.reader.Reader
|
||||
import io.infinitape.etherjar.domain.BlockHash
|
||||
import io.infinitape.etherjar.rpc.json.BlockJson
|
||||
import io.infinitape.etherjar.rpc.json.TransactionRefJson
|
||||
import org.slf4j.LoggerFactory
|
||||
import reactor.core.publisher.Mono
|
||||
|
||||
/**
|
||||
* Connects two caches to read through them. First is cache height->hash, second is hash->block.
|
||||
*/
|
||||
class BlockByHeight(
|
||||
private val heights: Reader<Long, BlockHash>,
|
||||
private val blocks: Reader<BlockHash, BlockJson<TransactionRefJson>>
|
||||
): Reader<Long, BlockJson<TransactionRefJson>> {
|
||||
|
||||
companion object {
|
||||
private val log = LoggerFactory.getLogger(BlockByHeight::class.java)
|
||||
}
|
||||
|
||||
override fun read(key: Long): Mono<BlockJson<TransactionRefJson>> {
|
||||
return heights.read(key)
|
||||
.flatMap { blocks.read(it) }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package io.emeraldpay.dshackle.cache
|
||||
|
||||
import io.emeraldpay.dshackle.reader.Reader
|
||||
import io.infinitape.etherjar.domain.BlockHash
|
||||
import io.infinitape.etherjar.domain.TransactionId
|
||||
import io.infinitape.etherjar.rpc.json.BlockJson
|
||||
@@ -25,13 +26,13 @@ import java.util.concurrent.ConcurrentLinkedQueue
|
||||
|
||||
class BlocksMemCache(
|
||||
val maxSize: Int = 64
|
||||
) {
|
||||
): Reader<BlockHash, BlockJson<TransactionRefJson>> {
|
||||
|
||||
private val mapping = ConcurrentHashMap<BlockHash, BlockJson<TransactionRefJson>>()
|
||||
private val queue = ConcurrentLinkedQueue<BlockHash>()
|
||||
|
||||
fun get(hash: BlockHash): Mono<BlockJson<TransactionRefJson>> {
|
||||
return Mono.justOrEmpty(mapping[hash])
|
||||
override fun read(key: BlockHash): Mono<BlockJson<TransactionRefJson>> {
|
||||
return Mono.justOrEmpty(mapping[key])
|
||||
}
|
||||
|
||||
fun add(block: BlockJson<TransactionRefJson>) {
|
||||
|
||||
38
src/main/kotlin/io/emeraldpay/dshackle/cache/HeightCache.kt
vendored
Normal file
38
src/main/kotlin/io/emeraldpay/dshackle/cache/HeightCache.kt
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
package io.emeraldpay.dshackle.cache
|
||||
|
||||
import io.emeraldpay.dshackle.reader.Reader
|
||||
import io.infinitape.etherjar.domain.BlockHash
|
||||
import io.infinitape.etherjar.rpc.json.BlockJson
|
||||
import io.infinitape.etherjar.rpc.json.TransactionRefJson
|
||||
import org.slf4j.LoggerFactory
|
||||
import reactor.core.publisher.Mono
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
/**
|
||||
* Memory cache for blocks heights, keeps mapping height->hash.
|
||||
*/
|
||||
class HeightCache(
|
||||
val maxSize: Int = 256
|
||||
): Reader<Long, BlockHash> {
|
||||
|
||||
companion object {
|
||||
private val log = LoggerFactory.getLogger(HeightCache::class.java)
|
||||
}
|
||||
|
||||
private val heights = ConcurrentHashMap<Long, BlockHash>()
|
||||
|
||||
override fun read(key: Long): Mono<BlockHash> {
|
||||
return Mono.justOrEmpty(heights[key])
|
||||
}
|
||||
|
||||
fun add(block: BlockJson<TransactionRefJson>) {
|
||||
heights[block.number] = block.hash
|
||||
|
||||
// evict old numbers if full
|
||||
var dropHeight = block.number - maxSize
|
||||
while (heights.size > maxSize && dropHeight < block.number) {
|
||||
heights.remove(dropHeight)
|
||||
dropHeight++
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2019 ETCDEV GmbH
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package io.emeraldpay.dshackle.reader
|
||||
|
||||
import io.emeraldpay.dshackle.cache.BlocksMemCache
|
||||
import io.infinitape.etherjar.domain.BlockHash
|
||||
import io.infinitape.etherjar.domain.TransactionId
|
||||
import io.infinitape.etherjar.rpc.json.BlockJson
|
||||
import io.infinitape.etherjar.rpc.json.TransactionRefJson
|
||||
import reactor.core.publisher.Mono
|
||||
|
||||
class BlockCacheReader(
|
||||
val cache: BlocksMemCache
|
||||
): Reader<BlockHash, BlockJson<TransactionRefJson>> {
|
||||
|
||||
override fun read(key: BlockHash): Mono<BlockJson<TransactionRefJson>> {
|
||||
return cache.get(key)
|
||||
}
|
||||
}
|
||||
@@ -16,18 +16,11 @@
|
||||
package io.emeraldpay.dshackle.upstream
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import io.emeraldpay.dshackle.cache.BlockByHeight
|
||||
import io.emeraldpay.dshackle.cache.BlocksMemCache
|
||||
import io.emeraldpay.dshackle.cache.HeightCache
|
||||
import io.emeraldpay.dshackle.config.UpstreamsConfig
|
||||
import io.emeraldpay.dshackle.reader.BlockCacheReader
|
||||
import io.emeraldpay.dshackle.reader.CompoundReader
|
||||
import io.emeraldpay.dshackle.reader.Reader
|
||||
import io.emeraldpay.dshackle.upstream.ethereum.DirectEthereumApi
|
||||
import io.emeraldpay.dshackle.upstream.ethereum.EthereumHead
|
||||
import io.infinitape.etherjar.domain.BlockHash
|
||||
import io.infinitape.etherjar.domain.TransactionId
|
||||
import io.infinitape.etherjar.rpc.json.BlockJson
|
||||
import io.infinitape.etherjar.rpc.json.TransactionRefJson
|
||||
import org.reactivestreams.Publisher
|
||||
import org.springframework.context.Lifecycle
|
||||
import reactor.core.Disposable
|
||||
import reactor.core.publisher.Flux
|
||||
@@ -42,11 +35,9 @@ abstract class AggregatedUpstream(
|
||||
val objectMapper: ObjectMapper
|
||||
): Upstream, Lifecycle {
|
||||
|
||||
private val blocksCache = BlocksMemCache()
|
||||
private var cacheSubscription: Disposable? = null
|
||||
private val blockReader: Reader<BlockHash, BlockJson<TransactionRefJson>> = CompoundReader(
|
||||
listOf(BlockCacheReader(blocksCache))
|
||||
)
|
||||
private val blockReaderByHash = BlocksMemCache()
|
||||
private val blockReaderByHeight = HeightCache()
|
||||
var cache: CachingEthereumApi = CachingEthereumApi.empty()
|
||||
private val reconfigLock = ReentrantLock()
|
||||
private var callMethods: CallMethods? = null
|
||||
@@ -118,9 +109,10 @@ abstract class AggregatedUpstream(
|
||||
reconfigLock.withLock {
|
||||
cacheSubscription?.dispose()
|
||||
cacheSubscription = head.getFlux().subscribe {
|
||||
blocksCache.add(it)
|
||||
blockReaderByHash.add(it)
|
||||
blockReaderByHeight.add(it)
|
||||
}
|
||||
cache = CachingEthereumApi(objectMapper, blockReader, head)
|
||||
cache = CachingEthereumApi(objectMapper, blockReaderByHash, BlockByHeight(blockReaderByHeight, blockReaderByHash), head)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,11 +29,13 @@ import io.infinitape.etherjar.rpc.json.ResponseJson
|
||||
import io.infinitape.etherjar.rpc.json.TransactionRefJson
|
||||
import org.slf4j.LoggerFactory
|
||||
import reactor.core.publisher.Mono
|
||||
import java.math.BigInteger
|
||||
import java.util.function.Function
|
||||
|
||||
open class CachingEthereumApi(
|
||||
private val objectMapper: ObjectMapper,
|
||||
private val cache: Reader<BlockHash, BlockJson<TransactionRefJson>>,
|
||||
private val cacheHeight: Reader<Long, BlockJson<TransactionRefJson>>,
|
||||
private val head: EthereumHead
|
||||
): EthereumApi(objectMapper) {
|
||||
|
||||
@@ -42,7 +44,7 @@ open class CachingEthereumApi(
|
||||
|
||||
@JvmStatic
|
||||
fun empty(): CachingEthereumApi {
|
||||
return CachingEthereumApi(ObjectMapper(), EmptyReader(), EmptyEthereumHead())
|
||||
return CachingEthereumApi(ObjectMapper(), EmptyReader(), EmptyReader(), EmptyEthereumHead())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,6 +65,21 @@ open class CachingEthereumApi(
|
||||
Mono.empty()
|
||||
}
|
||||
else Mono.empty()
|
||||
"eth_getBlockByNumber" ->
|
||||
if (params.size == 2 && (params[1] == "false" || params[1] == false))
|
||||
Mono.just(params[0])
|
||||
.map { HexQuantity.from(it as String) }
|
||||
.filter {
|
||||
it.value < BigInteger.valueOf(Long.MAX_VALUE)
|
||||
}
|
||||
.map { it.value.toLong() }
|
||||
.flatMap(cacheHeight::read)
|
||||
.map(toJson(id))
|
||||
.onErrorResume { t ->
|
||||
log.warn("Error during read from cache", t)
|
||||
Mono.empty()
|
||||
}
|
||||
else Mono.empty()
|
||||
else ->
|
||||
Mono.empty()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user