Add reties for lower block calculation (#378)
This commit is contained in:
@@ -17,7 +17,7 @@ abstract class LowerBoundBlockDetector(
|
|||||||
) {
|
) {
|
||||||
private val currentLowerBlock = AtomicReference(LowerBlockData.default())
|
private val currentLowerBlock = AtomicReference(LowerBlockData.default())
|
||||||
|
|
||||||
private val log = LoggerFactory.getLogger(this::class.java)
|
protected val log = LoggerFactory.getLogger(this::class.java)
|
||||||
|
|
||||||
fun lowerBlock(): Flux<LowerBlockData> {
|
fun lowerBlock(): Flux<LowerBlockData> {
|
||||||
return Flux.interval(
|
return Flux.interval(
|
||||||
|
|||||||
@@ -2,6 +2,9 @@ package io.emeraldpay.dshackle.upstream
|
|||||||
|
|
||||||
import io.emeraldpay.dshackle.Chain
|
import io.emeraldpay.dshackle.Chain
|
||||||
import reactor.core.publisher.Mono
|
import reactor.core.publisher.Mono
|
||||||
|
import reactor.util.retry.Retry
|
||||||
|
import reactor.util.retry.RetryBackoffSpec
|
||||||
|
import java.time.Duration
|
||||||
|
|
||||||
abstract class RecursiveLowerBoundBlockDetector(
|
abstract class RecursiveLowerBoundBlockDetector(
|
||||||
chain: Chain,
|
chain: Chain,
|
||||||
@@ -49,5 +52,24 @@ abstract class RecursiveLowerBoundBlockDetector(
|
|||||||
private fun middleBlock(lowerBoundData: LowerBoundData): Long =
|
private fun middleBlock(lowerBoundData: LowerBoundData): Long =
|
||||||
lowerBoundData.left + (lowerBoundData.right - lowerBoundData.left) / 2
|
lowerBoundData.left + (lowerBoundData.right - lowerBoundData.left) / 2
|
||||||
|
|
||||||
|
protected fun retrySpec(nonRetryableErrors: Set<String>): RetryBackoffSpec {
|
||||||
|
return Retry.backoff(
|
||||||
|
Long.MAX_VALUE,
|
||||||
|
Duration.ofSeconds(1),
|
||||||
|
)
|
||||||
|
.maxBackoff(Duration.ofSeconds(3))
|
||||||
|
.filter {
|
||||||
|
!nonRetryableErrors.any { err -> it.message?.contains(err, true) ?: false }
|
||||||
|
}
|
||||||
|
.doAfterRetry {
|
||||||
|
log.debug(
|
||||||
|
"Error in calculation of lower block of upstream {}, retry attempt - {}, message - {}",
|
||||||
|
upstream.getId(),
|
||||||
|
it.totalRetries(),
|
||||||
|
it.failure().message,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected abstract fun hasState(blockNumber: Long): Mono<Boolean>
|
protected abstract fun hasState(blockNumber: Long): Mono<Boolean>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,28 @@ class EthereumLowerBoundBlockDetector(
|
|||||||
private val upstream: Upstream,
|
private val upstream: Upstream,
|
||||||
) : RecursiveLowerBoundBlockDetector(chain, upstream) {
|
) : RecursiveLowerBoundBlockDetector(chain, upstream) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val nonRetryableErrors = setOf(
|
||||||
|
"No state available for block", // nethermind
|
||||||
|
"missing trie node", // geth
|
||||||
|
"header not found", // optimism, bsc, avalanche
|
||||||
|
"Node state is pruned", // kava
|
||||||
|
"is not available, lowest height is", // kava, cronos
|
||||||
|
"State already discarded for", // moonriver, moonbeam
|
||||||
|
"your node is running with state pruning", // fuse
|
||||||
|
"Max height of block allowed", // mumbai
|
||||||
|
"failed to compute tipset state", // filecoin-calibration
|
||||||
|
"bad tipset height", // filecoin-calibration
|
||||||
|
"body not found for block",
|
||||||
|
"request beyond head block",
|
||||||
|
"block not found",
|
||||||
|
"could not find block",
|
||||||
|
"unknown block",
|
||||||
|
"header for hash not found",
|
||||||
|
"after last accepted block",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
override fun hasState(blockNumber: Long): Mono<Boolean> {
|
override fun hasState(blockNumber: Long): Mono<Boolean> {
|
||||||
return upstream.getIngressReader().read(
|
return upstream.getIngressReader().read(
|
||||||
JsonRpcRequest(
|
JsonRpcRequest(
|
||||||
@@ -20,6 +42,7 @@ class EthereumLowerBoundBlockDetector(
|
|||||||
listOf("0x756F45E3FA69347A9A973A725E3C98bC4db0b5a0", blockNumber.toHex()),
|
listOf("0x756F45E3FA69347A9A973A725E3C98bC4db0b5a0", blockNumber.toHex()),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
.retryWhen(retrySpec(nonRetryableErrors))
|
||||||
.flatMap(JsonRpcResponse::requireResult)
|
.flatMap(JsonRpcResponse::requireResult)
|
||||||
.map { true }
|
.map { true }
|
||||||
.onErrorReturn(false)
|
.onErrorReturn(false)
|
||||||
|
|||||||
@@ -13,6 +13,12 @@ class PolkadotLowerBoundBlockDetector(
|
|||||||
private val upstream: Upstream,
|
private val upstream: Upstream,
|
||||||
) : RecursiveLowerBoundBlockDetector(chain, upstream) {
|
) : RecursiveLowerBoundBlockDetector(chain, upstream) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val nonRetryableErrors = setOf(
|
||||||
|
"State already discarded for",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
override fun hasState(blockNumber: Long): Mono<Boolean> {
|
override fun hasState(blockNumber: Long): Mono<Boolean> {
|
||||||
return upstream.getIngressReader().read(
|
return upstream.getIngressReader().read(
|
||||||
JsonRpcRequest(
|
JsonRpcRequest(
|
||||||
@@ -32,6 +38,7 @@ class PolkadotLowerBoundBlockDetector(
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
.retryWhen(retrySpec(nonRetryableErrors))
|
||||||
.flatMap(JsonRpcResponse::requireResult)
|
.flatMap(JsonRpcResponse::requireResult)
|
||||||
.map { true }
|
.map { true }
|
||||||
.onErrorReturn(false)
|
.onErrorReturn(false)
|
||||||
|
|||||||
@@ -7,10 +7,13 @@ import io.emeraldpay.dshackle.upstream.Upstream
|
|||||||
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 reactor.core.publisher.Mono
|
import reactor.core.publisher.Mono
|
||||||
|
import reactor.util.retry.Retry
|
||||||
|
import java.time.Duration
|
||||||
|
import kotlin.math.max
|
||||||
|
|
||||||
class SolanaLowerBoundBlockDetector(
|
class SolanaLowerBoundBlockDetector(
|
||||||
chain: Chain,
|
chain: Chain,
|
||||||
upstream: Upstream,
|
private val upstream: Upstream,
|
||||||
) : LowerBoundBlockDetector(chain, upstream) {
|
) : LowerBoundBlockDetector(chain, upstream) {
|
||||||
private val reader = upstream.getIngressReader()
|
private val reader = upstream.getIngressReader()
|
||||||
|
|
||||||
@@ -23,14 +26,24 @@ class SolanaLowerBoundBlockDetector(
|
|||||||
}
|
}
|
||||||
.flatMap(JsonRpcResponse::requireResult)
|
.flatMap(JsonRpcResponse::requireResult)
|
||||||
.map {
|
.map {
|
||||||
String(it).toLong()
|
val slot = String(it).toLong()
|
||||||
|
if (slot == 0L) {
|
||||||
|
1L
|
||||||
|
} else {
|
||||||
|
slot
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.flatMap { slot ->
|
.flatMap { slot ->
|
||||||
|
val from = if (slot <= 10) {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
slot - 10
|
||||||
|
}
|
||||||
reader.read(
|
reader.read(
|
||||||
JsonRpcRequest(
|
JsonRpcRequest(
|
||||||
"getBlocks",
|
"getBlocks",
|
||||||
listOf(
|
listOf(
|
||||||
slot - 10,
|
from,
|
||||||
slot,
|
slot,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -59,14 +72,24 @@ class SolanaLowerBoundBlockDetector(
|
|||||||
.flatMap(JsonRpcResponse::requireResult)
|
.flatMap(JsonRpcResponse::requireResult)
|
||||||
.map { blockData ->
|
.map { blockData ->
|
||||||
val block = Global.objectMapper.readValue(blockData, SolanaBlock::class.java)
|
val block = Global.objectMapper.readValue(blockData, SolanaBlock::class.java)
|
||||||
LowerBlockData(block.height, maxSlot)
|
LowerBlockData(max(block.height, 1), maxSlot)
|
||||||
}.onErrorResume {
|
}.onErrorResume {
|
||||||
Mono.empty()
|
Mono.empty()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onErrorResume {
|
.retryWhen(
|
||||||
Mono.empty()
|
Retry
|
||||||
}
|
.backoff(Long.MAX_VALUE, Duration.ofSeconds(1))
|
||||||
|
.maxBackoff(Duration.ofSeconds(3))
|
||||||
|
.doAfterRetry {
|
||||||
|
log.debug(
|
||||||
|
"Error in calculation of lower block of upstream {}, retry attempt - {}, message - {}",
|
||||||
|
upstream.getId(),
|
||||||
|
it.totalRetries(),
|
||||||
|
it.failure().message,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ class RecursiveLowerBoundBlockDetectorTest {
|
|||||||
} else {
|
} else {
|
||||||
on {
|
on {
|
||||||
read(JsonRpcRequest("eth_getBalance", listOf("0x756F45E3FA69347A9A973A725E3C98bC4db0b5a0", it.toHex())))
|
read(JsonRpcRequest("eth_getBalance", listOf("0x756F45E3FA69347A9A973A725E3C98bC4db0b5a0", it.toHex())))
|
||||||
} doReturn Mono.error(RuntimeException())
|
} doReturn Mono.error(RuntimeException("missing trie node"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -114,7 +114,7 @@ class RecursiveLowerBoundBlockDetectorTest {
|
|||||||
} doReturn Mono.just(JsonRpcResponse("\"$hash2\"".toByteArray(), null))
|
} doReturn Mono.just(JsonRpcResponse("\"$hash2\"".toByteArray(), null))
|
||||||
on {
|
on {
|
||||||
read(JsonRpcRequest("state_getMetadata", listOf(hash2)))
|
read(JsonRpcRequest("state_getMetadata", listOf(hash2)))
|
||||||
} doReturn Mono.error(RuntimeException())
|
} doReturn Mono.error(RuntimeException("State already discarded for"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -127,11 +127,8 @@ class RecursiveLowerBoundBlockDetectorTest {
|
|||||||
Arguments.of(
|
Arguments.of(
|
||||||
mock<JsonRpcReader> {
|
mock<JsonRpcReader> {
|
||||||
on {
|
on {
|
||||||
read(JsonRpcRequest("chain_getBlockHash", listOf(any())))
|
read(any())
|
||||||
} doReturn Mono.just(JsonRpcResponse(ByteArray(0), null))
|
} doReturn Mono.just(JsonRpcResponse("\"0x1\"".toByteArray(), null))
|
||||||
on {
|
|
||||||
read(JsonRpcRequest("state_getMetadata", listOf(any())))
|
|
||||||
} doReturn Mono.just(JsonRpcResponse(ByteArray(0), null))
|
|
||||||
},
|
},
|
||||||
PolkadotLowerBoundBlockDetector::class.java,
|
PolkadotLowerBoundBlockDetector::class.java,
|
||||||
),
|
),
|
||||||
|
|||||||
Reference in New Issue
Block a user