solution: Redis caching

This commit is contained in:
Igor Artamonov
2020-03-14 22:25:27 -04:00
parent 67ba1457fa
commit 35cb35fe0a
23 changed files with 529 additions and 83 deletions

View File

@@ -52,4 +52,56 @@ class BlocksRedisCacheSpec extends Specification {
act == block
}
def "Evict existing block"() {
setup:
def cache = new BlocksRedisCache(
redis.reactive(), Chain.ETHEREUM, TestingCommons.objectMapper()
)
def block = new BlockJson<TransactionRefJson>()
block.number = 100
block.timestamp = Instant.now().minusSeconds(100).truncatedTo(ChronoUnit.SECONDS)
block.hash = BlockHash.from(hash2)
block.transactions = []
block.uncles = []
when:
cache.add(block).subscribe()
def act = cache.read(BlockHash.from(hash2)).block()
then:
act == block
when:
cache.evict(block.hash).subscribe()
act = cache.read(BlockHash.from(hash2)).block()
then:
act == null
}
def "Evict non-existing block"() {
setup:
def cache = new BlocksRedisCache(
redis.reactive(), Chain.ETHEREUM, TestingCommons.objectMapper()
)
def block = new BlockJson<TransactionRefJson>()
block.number = 100
block.timestamp = Instant.now().minusSeconds(100).truncatedTo(ChronoUnit.SECONDS)
block.hash = BlockHash.from(hash2)
block.transactions = []
block.uncles = []
when:
cache.add(block).subscribe()
def act = cache.read(BlockHash.from(hash2)).block()
then:
act == block
when:
cache.evict(BlockHash.from(hash3)).subscribe()
act = cache.read(BlockHash.from(hash2)).block()
then:
act == block
}
}

View File

@@ -54,6 +54,35 @@ class TxRedisCacheSpec extends Specification {
act == tx
}
def "Evict single tx"() {
setup:
def block = new BlockJson<TransactionRefJson>()
block.number = 100
block.timestamp = Instant.now().minusSeconds(100).truncatedTo(ChronoUnit.SECONDS)
block.hash = BlockHash.from(hash2)
block.transactions = []
block.uncles = []
def tx = new TransactionJson()
tx.hash = TransactionId.from(hash3)
tx.blockHash = block.hash
tx.blockNumber = block.number
tx.value = Wei.ofEthers(1.234)
tx.nonce = 0
when:
cache.add(tx, block).subscribe()
def act = cache.read(tx.hash).block()
then:
act == tx
when:
cache.evict(tx.hash).subscribe()
act = cache.read(tx.hash).block()
then:
act == null
}
def "Evict all by block data"() {
when:
def block1 = new BlockJson<TransactionRefJson>()

View File

@@ -0,0 +1,29 @@
package io.emeraldpay.dshackle.config
import spock.lang.Specification
class EnvVariablesSpec extends Specification {
EnvVariables reader = new EnvVariables()
def "Post process for usual strings"() {
expect:
s == reader.postProcess(s)
where:
s << ["", "a", "13143", "/etc/client1.myservice.com.key", "true", "1a68f20154fc258fe4149c199ad8f281"]
}
def "Post process replaces from env"() {
setup:
System.setProperty("id", "1")
System.setProperty("HOME", "/home/user")
System.setProperty("PASSWORD", "1a68f20154fc258fe4149c199ad8f281")
expect:
replaced == reader.postProcess(orig)
where:
orig | replaced
"p_\${id}" | "p_1"
"home: \${HOME}" | "home: /home/user"
"\${PASSWORD}" | "1a68f20154fc258fe4149c199ad8f281"
}
}

View File

@@ -134,27 +134,6 @@ class UpstreamsConfigReaderSpec extends Specification {
}
}
def "Post process for usual strings"() {
expect:
s == reader.postProcess(s)
where:
s << ["", "a", "13143", "/etc/client1.myservice.com.key", "true", "1a68f20154fc258fe4149c199ad8f281"]
}
def "Post process replaces from env"() {
setup:
System.setProperty("id", "1")
System.setProperty("HOME", "/home/user")
System.setProperty("PASSWORD", "1a68f20154fc258fe4149c199ad8f281")
expect:
replaced == reader.postProcess(orig)
where:
orig | replaced
"p_\${id}" | "p_1"
"home: \${HOME}" | "home: /home/user"
"\${PASSWORD}" | "1a68f20154fc258fe4149c199ad8f281"
}
def "Parse config without defaults"() {
setup:
def config = this.class.getClassLoader().getResourceAsStream("upstreams-no-defaults.yaml")

View File

@@ -0,0 +1,95 @@
package io.emeraldpay.dshackle.reader
import reactor.core.publisher.Mono
import reactor.test.StepVerifier
import spock.lang.Specification
import java.time.Duration
class CompoundReaderSpec extends Specification {
def reader1 = new Reader<String, String>() {
@Override
Mono<String> read(String key) {
return Mono.just("test-1").delaySubscription(Duration.ofMillis(100))
}
}
def reader2 = new Reader<String, String>() {
@Override
Mono<String> read(String key) {
return Mono.just("test-2").delaySubscription(Duration.ofMillis(200))
}
}
def reader3 = new Reader<String, String>() {
@Override
Mono<String> read(String key) {
return Mono.just("test-3").delaySubscription(Duration.ofMillis(300))
}
}
def reader1Empty = new Reader<String, String>() {
@Override
Mono<String> read(String key) {
return Mono.<String>empty().delaySubscription(Duration.ofMillis(100))
}
}
def "Return empty when no readers"() {
setup:
def reader = new CompoundReader<String, String>()
when:
def act = reader.read("test")
then:
StepVerifier.create(act)
.expectComplete()
.verify(Duration.ofSeconds(1))
}
def "Return first"() {
setup:
def reader = new CompoundReader<String, String>(reader1, reader2, reader3)
when:
def act = reader.read("test")
then:
StepVerifier.create(act)
.expectNext("test-1")
.expectComplete()
.verify(Duration.ofSeconds(1))
}
def "Return second"() {
setup:
def reader = new CompoundReader<String, String>(reader3, reader2)
when:
def act = reader.read("test")
then:
StepVerifier.create(act)
.expectNext("test-2")
.expectComplete()
.verify(Duration.ofSeconds(1))
}
def "Return third"() {
setup:
def reader = new CompoundReader<String, String>(reader3, reader2, reader1)
when:
def act = reader.read("test")
then:
StepVerifier.create(act)
.expectNext("test-1")
.expectComplete()
.verify(Duration.ofSeconds(1))
}
def "Ignore empty"() {
setup:
def reader = new CompoundReader<String, String>(reader3, reader1Empty, reader2, reader1Empty)
when:
def act = reader.read("test")
then:
StepVerifier.create(act)
.expectNext("test-2")
.expectComplete()
.verify(Duration.ofSeconds(1))
}
}

View File

@@ -20,6 +20,7 @@ import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.module.SimpleModule
import io.emeraldpay.dshackle.cache.Caches
import io.emeraldpay.dshackle.cache.CachesFactory
import io.emeraldpay.dshackle.upstream.AggregatedUpstream
import io.emeraldpay.dshackle.upstream.ChainUpstreams
import io.emeraldpay.dshackle.upstream.calls.DirectCallMethods
@@ -28,6 +29,7 @@ import io.emeraldpay.dshackle.upstream.ethereum.EthereumUpstream
import io.emeraldpay.grpc.Chain
import io.infinitape.etherjar.rpc.JacksonRpcConverter
import io.infinitape.etherjar.rpc.ReactorRpcClient
import org.springframework.core.env.StandardEnvironment
import java.text.SimpleDateFormat
@@ -73,4 +75,8 @@ class TestingCommons {
static AggregatedUpstream aggregatedUpstream(EthereumUpstream up) {
return new ChainUpstreams(Chain.ETHEREUM, [up], Caches.default(), objectMapper())
}
static CachesFactory emptyCaches() {
return new CachesFactory(objectMapper(), new StandardEnvironment())
}
}

View File

@@ -11,7 +11,7 @@ class CurrentUpstreamsSpec extends Specification {
def "add upstream"() {
setup:
def current = new CurrentUpstreams(TestingCommons.objectMapper())
def current = new CurrentUpstreams(TestingCommons.objectMapper(), TestingCommons.emptyCaches())
def up = new EthereumUpstreamMock("test", Chain.ETHEREUM, TestingCommons.api(Stub(ReactorRpcClient)))
when:
current.update(new UpstreamChange(Chain.ETHEREUM, up, UpstreamChange.ChangeType.ADDED))
@@ -22,7 +22,7 @@ class CurrentUpstreamsSpec extends Specification {
def "add multiple upstreams"() {
setup:
def current = new CurrentUpstreams(TestingCommons.objectMapper())
def current = new CurrentUpstreams(TestingCommons.objectMapper(), TestingCommons.emptyCaches())
def up1 = new EthereumUpstreamMock("test1", Chain.ETHEREUM, TestingCommons.api(Stub(ReactorRpcClient)))
def up2 = new EthereumUpstreamMock("test2", Chain.ETHEREUM_CLASSIC, TestingCommons.api(Stub(ReactorRpcClient)))
def up3 = new EthereumUpstreamMock("test3", Chain.ETHEREUM, TestingCommons.api(Stub(ReactorRpcClient)))
@@ -38,7 +38,7 @@ class CurrentUpstreamsSpec extends Specification {
def "remove upstream"() {
setup:
def current = new CurrentUpstreams(TestingCommons.objectMapper())
def current = new CurrentUpstreams(TestingCommons.objectMapper(), TestingCommons.emptyCaches())
def up1 = new EthereumUpstreamMock("test1", Chain.ETHEREUM, TestingCommons.api(Stub(ReactorRpcClient)))
def up2 = new EthereumUpstreamMock("test2", Chain.ETHEREUM_CLASSIC, TestingCommons.api(Stub(ReactorRpcClient)))
def up3 = new EthereumUpstreamMock("test3", Chain.ETHEREUM, TestingCommons.api(Stub(ReactorRpcClient)))
@@ -56,7 +56,7 @@ class CurrentUpstreamsSpec extends Specification {
def "available after adding"() {
setup:
def current = new CurrentUpstreams(TestingCommons.objectMapper())
def current = new CurrentUpstreams(TestingCommons.objectMapper(), TestingCommons.emptyCaches())
def up1 = new EthereumUpstreamMock("test1", Chain.ETHEREUM, TestingCommons.api(Stub(ReactorRpcClient)))
when:

View File

@@ -0,0 +1,15 @@
version: v1
defaultOptions:
- chains:
- bitcoin
options:
min-peers: 3
upstreams:
- id: local
chain: bitcoin
connection:
bitcoin:
rpc:
url: "http://localhost:8545"