better erigon bugged snapshot validator (#684)
* better erigon bugged snapshot validator * add new error for erigon * add error patterns for recursive lower bound
This commit is contained in:
@@ -5,7 +5,6 @@ import io.emeraldpay.dshackle.upstream.ChainRequest
|
|||||||
import io.emeraldpay.dshackle.upstream.ChainResponse
|
import io.emeraldpay.dshackle.upstream.ChainResponse
|
||||||
import io.emeraldpay.dshackle.upstream.Upstream
|
import io.emeraldpay.dshackle.upstream.Upstream
|
||||||
import io.emeraldpay.dshackle.upstream.lowerbound.LowerBoundData
|
import io.emeraldpay.dshackle.upstream.lowerbound.LowerBoundData
|
||||||
import io.emeraldpay.dshackle.upstream.lowerbound.LowerBoundDetector
|
|
||||||
import io.emeraldpay.dshackle.upstream.lowerbound.LowerBoundType
|
import io.emeraldpay.dshackle.upstream.lowerbound.LowerBoundType
|
||||||
import io.emeraldpay.dshackle.upstream.lowerbound.detector.RecursiveLowerBound
|
import io.emeraldpay.dshackle.upstream.lowerbound.detector.RecursiveLowerBound
|
||||||
import io.emeraldpay.dshackle.upstream.lowerbound.toHex
|
import io.emeraldpay.dshackle.upstream.lowerbound.toHex
|
||||||
@@ -15,7 +14,7 @@ import reactor.core.publisher.Mono
|
|||||||
|
|
||||||
class EthereumLowerBoundBlockDetector(
|
class EthereumLowerBoundBlockDetector(
|
||||||
private val upstream: Upstream,
|
private val upstream: Upstream,
|
||||||
) : LowerBoundDetector(upstream.getChain()) {
|
) : EthereumLowerBoundDetectorBase(upstream.getChain()) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val NO_BLOCK_DATA = "No block data"
|
private const val NO_BLOCK_DATA = "No block data"
|
||||||
@@ -31,7 +30,7 @@ class EthereumLowerBoundBlockDetector(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val recursiveLowerBound = RecursiveLowerBound(upstream, LowerBoundType.BLOCK, NO_BLOCK_ERRORS, lowerBounds)
|
private val recursiveLowerBound = RecursiveLowerBound(upstream, LowerBoundType.BLOCK, NO_BLOCK_ERRORS, lowerBounds, commonErrorPatterns)
|
||||||
|
|
||||||
override fun period(): Long {
|
override fun period(): Long {
|
||||||
return 3
|
return 3
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package io.emeraldpay.dshackle.upstream.ethereum
|
||||||
|
|
||||||
|
import io.emeraldpay.dshackle.Chain
|
||||||
|
import io.emeraldpay.dshackle.upstream.lowerbound.LowerBoundDetector
|
||||||
|
|
||||||
|
abstract class EthereumLowerBoundDetectorBase(chain: Chain) : LowerBoundDetector(chain) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val commonErrorPatterns = setOf(
|
||||||
|
Regex(".*seekInFiles\\(invIndex=unknown index,txNum=\\d+\\) but data before txNum=\\d+ not available.*"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,7 +5,6 @@ import io.emeraldpay.dshackle.upstream.ChainRequest
|
|||||||
import io.emeraldpay.dshackle.upstream.ChainResponse
|
import io.emeraldpay.dshackle.upstream.ChainResponse
|
||||||
import io.emeraldpay.dshackle.upstream.Upstream
|
import io.emeraldpay.dshackle.upstream.Upstream
|
||||||
import io.emeraldpay.dshackle.upstream.lowerbound.LowerBoundData
|
import io.emeraldpay.dshackle.upstream.lowerbound.LowerBoundData
|
||||||
import io.emeraldpay.dshackle.upstream.lowerbound.LowerBoundDetector
|
|
||||||
import io.emeraldpay.dshackle.upstream.lowerbound.LowerBoundType
|
import io.emeraldpay.dshackle.upstream.lowerbound.LowerBoundType
|
||||||
import io.emeraldpay.dshackle.upstream.lowerbound.detector.RecursiveLowerBound
|
import io.emeraldpay.dshackle.upstream.lowerbound.detector.RecursiveLowerBound
|
||||||
import io.emeraldpay.dshackle.upstream.lowerbound.toHex
|
import io.emeraldpay.dshackle.upstream.lowerbound.toHex
|
||||||
@@ -15,7 +14,7 @@ import reactor.core.publisher.Mono
|
|||||||
|
|
||||||
class EthereumLowerBoundProofDetector(
|
class EthereumLowerBoundProofDetector(
|
||||||
private val upstream: Upstream,
|
private val upstream: Upstream,
|
||||||
) : LowerBoundDetector(upstream.getChain()) {
|
) : EthereumLowerBoundDetectorBase(upstream.getChain()) {
|
||||||
companion object {
|
companion object {
|
||||||
private const val NO_PROOF_DATA = "distance to target block exceeds maximum proof window"
|
private const val NO_PROOF_DATA = "distance to target block exceeds maximum proof window"
|
||||||
|
|
||||||
@@ -31,7 +30,7 @@ class EthereumLowerBoundProofDetector(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val recursiveLowerBound = RecursiveLowerBound(upstream, LowerBoundType.PROOF, NO_PROOF_ERRORS, lowerBounds)
|
private val recursiveLowerBound = RecursiveLowerBound(upstream, LowerBoundType.PROOF, NO_PROOF_ERRORS, lowerBounds, commonErrorPatterns)
|
||||||
|
|
||||||
override fun period(): Long {
|
override fun period(): Long {
|
||||||
return 3
|
return 3
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import io.emeraldpay.dshackle.data.BlockContainer
|
|||||||
import io.emeraldpay.dshackle.upstream.ChainRequest
|
import io.emeraldpay.dshackle.upstream.ChainRequest
|
||||||
import io.emeraldpay.dshackle.upstream.Upstream
|
import io.emeraldpay.dshackle.upstream.Upstream
|
||||||
import io.emeraldpay.dshackle.upstream.lowerbound.LowerBoundData
|
import io.emeraldpay.dshackle.upstream.lowerbound.LowerBoundData
|
||||||
import io.emeraldpay.dshackle.upstream.lowerbound.LowerBoundDetector
|
|
||||||
import io.emeraldpay.dshackle.upstream.lowerbound.LowerBoundType
|
import io.emeraldpay.dshackle.upstream.lowerbound.LowerBoundType
|
||||||
import io.emeraldpay.dshackle.upstream.lowerbound.detector.RecursiveLowerBound
|
import io.emeraldpay.dshackle.upstream.lowerbound.detector.RecursiveLowerBound
|
||||||
import io.emeraldpay.dshackle.upstream.lowerbound.toHex
|
import io.emeraldpay.dshackle.upstream.lowerbound.toHex
|
||||||
@@ -14,7 +13,7 @@ import reactor.core.publisher.Flux
|
|||||||
|
|
||||||
class EthereumLowerBoundReceiptsDetector(
|
class EthereumLowerBoundReceiptsDetector(
|
||||||
private val upstream: Upstream,
|
private val upstream: Upstream,
|
||||||
) : LowerBoundDetector(upstream.getChain()) {
|
) : EthereumLowerBoundDetectorBase(upstream.getChain()) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val MAX_OFFSET = 20
|
const val MAX_OFFSET = 20
|
||||||
@@ -30,7 +29,7 @@ class EthereumLowerBoundReceiptsDetector(
|
|||||||
).plus(EthereumLowerBoundBlockDetector.NO_BLOCK_ERRORS)
|
).plus(EthereumLowerBoundBlockDetector.NO_BLOCK_ERRORS)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val recursiveLowerBound = RecursiveLowerBound(upstream, LowerBoundType.RECEIPTS, NO_RECEIPTS_ERRORS, lowerBounds)
|
private val recursiveLowerBound = RecursiveLowerBound(upstream, LowerBoundType.RECEIPTS, NO_RECEIPTS_ERRORS, lowerBounds, commonErrorPatterns)
|
||||||
|
|
||||||
override fun period(): Long {
|
override fun period(): Long {
|
||||||
return 3
|
return 3
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import io.emeraldpay.dshackle.upstream.ChainRequest
|
|||||||
import io.emeraldpay.dshackle.upstream.ChainResponse
|
import io.emeraldpay.dshackle.upstream.ChainResponse
|
||||||
import io.emeraldpay.dshackle.upstream.Upstream
|
import io.emeraldpay.dshackle.upstream.Upstream
|
||||||
import io.emeraldpay.dshackle.upstream.lowerbound.LowerBoundData
|
import io.emeraldpay.dshackle.upstream.lowerbound.LowerBoundData
|
||||||
import io.emeraldpay.dshackle.upstream.lowerbound.LowerBoundDetector
|
|
||||||
import io.emeraldpay.dshackle.upstream.lowerbound.LowerBoundType
|
import io.emeraldpay.dshackle.upstream.lowerbound.LowerBoundType
|
||||||
import io.emeraldpay.dshackle.upstream.lowerbound.detector.RecursiveLowerBound
|
import io.emeraldpay.dshackle.upstream.lowerbound.detector.RecursiveLowerBound
|
||||||
import io.emeraldpay.dshackle.upstream.lowerbound.toHex
|
import io.emeraldpay.dshackle.upstream.lowerbound.toHex
|
||||||
@@ -16,8 +15,8 @@ import reactor.core.publisher.Mono
|
|||||||
|
|
||||||
class EthereumLowerBoundStateDetector(
|
class EthereumLowerBoundStateDetector(
|
||||||
private val upstream: Upstream,
|
private val upstream: Upstream,
|
||||||
) : LowerBoundDetector(upstream.getChain()) {
|
) : EthereumLowerBoundDetectorBase(upstream.getChain()) {
|
||||||
private val recursiveLowerBound = RecursiveLowerBound(upstream, LowerBoundType.STATE, stateErrors, lowerBounds)
|
private val recursiveLowerBound = RecursiveLowerBound(upstream, LowerBoundType.STATE, stateErrors, lowerBounds, commonErrorPatterns)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val stateErrors = setOf(
|
val stateErrors = setOf(
|
||||||
@@ -57,6 +56,9 @@ class EthereumLowerBoundStateDetector(
|
|||||||
"Access to archival, debug, or trace data is not included in your current plan", // chainstack
|
"Access to archival, debug, or trace data is not included in your current plan", // chainstack
|
||||||
"empty reader set", // strange bsc geth error
|
"empty reader set", // strange bsc geth error
|
||||||
"Request might be querying historical state that is not available", // monad
|
"Request might be querying historical state that is not available", // monad
|
||||||
|
"No receipts data",
|
||||||
|
"No tx data",
|
||||||
|
"No block data",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import io.emeraldpay.dshackle.data.BlockContainer
|
|||||||
import io.emeraldpay.dshackle.upstream.ChainRequest
|
import io.emeraldpay.dshackle.upstream.ChainRequest
|
||||||
import io.emeraldpay.dshackle.upstream.Upstream
|
import io.emeraldpay.dshackle.upstream.Upstream
|
||||||
import io.emeraldpay.dshackle.upstream.lowerbound.LowerBoundData
|
import io.emeraldpay.dshackle.upstream.lowerbound.LowerBoundData
|
||||||
import io.emeraldpay.dshackle.upstream.lowerbound.LowerBoundDetector
|
|
||||||
import io.emeraldpay.dshackle.upstream.lowerbound.LowerBoundType
|
import io.emeraldpay.dshackle.upstream.lowerbound.LowerBoundType
|
||||||
import io.emeraldpay.dshackle.upstream.lowerbound.detector.RecursiveLowerBound
|
import io.emeraldpay.dshackle.upstream.lowerbound.detector.RecursiveLowerBound
|
||||||
import io.emeraldpay.dshackle.upstream.lowerbound.toHex
|
import io.emeraldpay.dshackle.upstream.lowerbound.toHex
|
||||||
@@ -14,7 +13,7 @@ import reactor.core.publisher.Flux
|
|||||||
|
|
||||||
class EthereumLowerBoundTxDetector(
|
class EthereumLowerBoundTxDetector(
|
||||||
private val upstream: Upstream,
|
private val upstream: Upstream,
|
||||||
) : LowerBoundDetector(upstream.getChain()) {
|
) : EthereumLowerBoundDetectorBase(upstream.getChain()) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val MAX_OFFSET = 20
|
const val MAX_OFFSET = 20
|
||||||
@@ -26,7 +25,7 @@ class EthereumLowerBoundTxDetector(
|
|||||||
).plus(EthereumLowerBoundBlockDetector.NO_BLOCK_ERRORS)
|
).plus(EthereumLowerBoundBlockDetector.NO_BLOCK_ERRORS)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val recursiveLowerBound = RecursiveLowerBound(upstream, LowerBoundType.TX, NO_TX_ERRORS, lowerBounds)
|
private val recursiveLowerBound = RecursiveLowerBound(upstream, LowerBoundType.TX, NO_TX_ERRORS, lowerBounds, commonErrorPatterns)
|
||||||
|
|
||||||
override fun period(): Long {
|
override fun period(): Long {
|
||||||
return 3
|
return 3
|
||||||
|
|||||||
@@ -234,36 +234,22 @@ class ErigonBuggedValidator(
|
|||||||
companion object {
|
companion object {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
val log: Logger = LoggerFactory.getLogger(ErigonBuggedValidator::class.java)
|
val log: Logger = LoggerFactory.getLogger(ErigonBuggedValidator::class.java)
|
||||||
|
|
||||||
private const val ZERO_ADDRESS = "0x0000000000000000000000000000000000000000"
|
|
||||||
private val FIVE_THOUSAND = BigInteger.valueOf(5_000)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var callCount: Int = 0
|
||||||
|
|
||||||
override fun validate(onError: ValidateUpstreamSettingsResult): Mono<ValidateUpstreamSettingsResult> {
|
override fun validate(onError: ValidateUpstreamSettingsResult): Mono<ValidateUpstreamSettingsResult> {
|
||||||
|
if (callCount % 10 != 0) {
|
||||||
|
callCount++
|
||||||
|
return Mono.just(ValidateUpstreamSettingsResult.UPSTREAM_VALID)
|
||||||
|
}
|
||||||
|
callCount++
|
||||||
|
|
||||||
return isErigon().flatMap { isErigon ->
|
return isErigon().flatMap { isErigon ->
|
||||||
if (!isErigon) {
|
if (!isErigon) {
|
||||||
Mono.just(ValidateUpstreamSettingsResult.UPSTREAM_VALID)
|
Mono.just(ValidateUpstreamSettingsResult.UPSTREAM_VALID)
|
||||||
} else {
|
} else {
|
||||||
latestBlockNumber().flatMap { latest ->
|
performErigonBugCheck()
|
||||||
val pastBlock = latest.subtract(FIVE_THOUSAND).max(BigInteger.ZERO)
|
|
||||||
|
|
||||||
Mono.zip(
|
|
||||||
balanceOkAt(pastBlock), // must succeed
|
|
||||||
balanceOkAt(BigInteger.ONE), // must fail
|
|
||||||
).map { checks ->
|
|
||||||
val isBugged = checks.t1 && !checks.t2
|
|
||||||
if (isBugged) {
|
|
||||||
log.warn(
|
|
||||||
"Erigon balance-bug detected on upstream {}: ok @ {}, error @ 0x1",
|
|
||||||
upstream.getId(),
|
|
||||||
pastBlock,
|
|
||||||
)
|
|
||||||
ValidateUpstreamSettingsResult.UPSTREAM_FATAL_SETTINGS_ERROR
|
|
||||||
} else {
|
|
||||||
ValidateUpstreamSettingsResult.UPSTREAM_VALID
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}.onErrorResume { ex ->
|
}.onErrorResume { ex ->
|
||||||
log.error(
|
log.error(
|
||||||
@@ -290,35 +276,90 @@ class ErigonBuggedValidator(
|
|||||||
.map { it.lowercase().contains("erigon") }
|
.map { it.lowercase().contains("erigon") }
|
||||||
.doOnError { log.error("Error during execution 'web3_clientVersion' - {} for {}", it.message, upstream.getId()) }
|
.doOnError { log.error("Error during execution 'web3_clientVersion' - {} for {}", it.message, upstream.getId()) }
|
||||||
|
|
||||||
private fun latestBlockNumber(): Mono<BigInteger> =
|
private fun performErigonBugCheck(): Mono<ValidateUpstreamSettingsResult> {
|
||||||
upstream.getIngressReader()
|
return getLatestBlockNumber()
|
||||||
.read(ChainRequest("eth_blockNumber", ListParams()))
|
.flatMap { latestBlock ->
|
||||||
.retryRandomBackoff(3, Duration.ofMillis(100), Duration.ofMillis(500)) { ctx ->
|
binarySearchForBug(BigInteger.ONE, latestBlock, 0)
|
||||||
log.warn(
|
|
||||||
"error during blockNumber retrieving for {}, iteration {}, reason - {}",
|
|
||||||
upstream.getId(),
|
|
||||||
ctx.iteration(),
|
|
||||||
ctx.exception().message,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
.timeout(Duration.ofSeconds(10))
|
||||||
|
.onErrorReturn(ValidateUpstreamSettingsResult.UPSTREAM_VALID)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getLatestBlockNumber(): Mono<BigInteger> {
|
||||||
|
return upstream.getIngressReader()
|
||||||
|
.read(ChainRequest("eth_blockNumber", ListParams()))
|
||||||
.flatMap(ChainResponse::requireStringResult)
|
.flatMap(ChainResponse::requireStringResult)
|
||||||
.map { BigInteger(it.removePrefix("0x"), 16) }
|
.map { BigInteger(it.removePrefix("0x"), 16) }
|
||||||
.doOnError { log.error("Error during execution 'eth_blockNumber' - {} for {}", it.message, upstream.getId()) }
|
.timeout(Duration.ofSeconds(1))
|
||||||
|
.onErrorReturn(BigInteger.valueOf(1000000))
|
||||||
|
}
|
||||||
|
|
||||||
private fun balanceOkAt(blockNumber: BigInteger): Mono<Boolean> {
|
private fun binarySearchForBug(start: BigInteger, end: BigInteger, depth: Int): Mono<ValidateUpstreamSettingsResult> {
|
||||||
val tag = "0x${blockNumber.toString(16)}"
|
if (start >= end || depth > 20) {
|
||||||
return upstream.getIngressReader()
|
return Mono.just(ValidateUpstreamSettingsResult.UPSTREAM_VALID)
|
||||||
.read(ChainRequest("eth_getBalance", ListParams(ZERO_ADDRESS, tag)))
|
}
|
||||||
.retryRandomBackoff(3, Duration.ofMillis(100), Duration.ofMillis(500)) { ctx ->
|
|
||||||
log.warn(
|
val mid = start.add(end).divide(BigInteger.valueOf(2))
|
||||||
"error during balance retrieving for {}, block {}, iteration {}, reason - {}",
|
|
||||||
upstream.getId(),
|
return testBlockForBug(mid)
|
||||||
tag,
|
.flatMap { result ->
|
||||||
ctx.iteration(),
|
when (result) {
|
||||||
ctx.exception().message,
|
TestResult.BUG_DETECTED -> {
|
||||||
)
|
log.warn(
|
||||||
|
"Erigon balance-bug detected on upstream {} at block {}",
|
||||||
|
upstream.getId(),
|
||||||
|
mid,
|
||||||
|
)
|
||||||
|
Mono.just(ValidateUpstreamSettingsResult.UPSTREAM_FATAL_SETTINGS_ERROR)
|
||||||
|
}
|
||||||
|
TestResult.VALID_RESPONSE -> {
|
||||||
|
// Go left (earlier blocks)
|
||||||
|
binarySearchForBug(start, mid, depth + 1)
|
||||||
|
}
|
||||||
|
TestResult.ERROR -> {
|
||||||
|
// Go right (later blocks)
|
||||||
|
binarySearchForBug(mid.add(BigInteger.ONE), end, depth + 1)
|
||||||
|
}
|
||||||
|
null -> Mono.just(ValidateUpstreamSettingsResult.UPSTREAM_VALID)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.flatMap { resp -> resp.requireStringResult().map { true } }
|
}
|
||||||
.onErrorResume { Mono.just(false) }
|
|
||||||
|
private fun testBlockForBug(blockNumber: BigInteger): Mono<TestResult> {
|
||||||
|
val blockTag = "0x${blockNumber.toString(16)}"
|
||||||
|
val request = ChainRequest(
|
||||||
|
"eth_call",
|
||||||
|
ListParams(
|
||||||
|
mapOf(
|
||||||
|
"to" to "0x1111111111111111111111111111111111111111",
|
||||||
|
"data" to "0x1eaf190c",
|
||||||
|
),
|
||||||
|
blockTag,
|
||||||
|
mapOf(
|
||||||
|
"0x1111111111111111111111111111111111111111" to mapOf(
|
||||||
|
"code" to "0x6080604052348015600e575f5ffd5b50600436106026575f3560e01c80631eaf190c14602a575b5f5ffd5b60306044565b604051603b91906078565b60405180910390f35b5f5f73ffffffffffffffffffffffffffffffffffffffff1631905090565b5f819050919050565b6072816062565b82525050565b5f60208201905060895f830184606b565b9291505056fea2646970667358221220251f5b4d2ed1abe77f66fde198a57ada08562dc3b0afbc6bac0261d1bf516b5d64736f6c634300081e0033",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
return upstream.getIngressReader()
|
||||||
|
.read(request)
|
||||||
|
.flatMap(ChainResponse::requireStringResult)
|
||||||
|
.map { result ->
|
||||||
|
when {
|
||||||
|
result == "0x" -> TestResult.BUG_DETECTED
|
||||||
|
result.startsWith("0x") && result.length > 2 -> TestResult.VALID_RESPONSE
|
||||||
|
else -> TestResult.ERROR
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.timeout(Duration.ofSeconds(1))
|
||||||
|
.onErrorReturn(TestResult.ERROR)
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum class TestResult {
|
||||||
|
BUG_DETECTED,
|
||||||
|
VALID_RESPONSE,
|
||||||
|
ERROR,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ open class RecursiveLowerBound(
|
|||||||
protected val type: LowerBoundType,
|
protected val type: LowerBoundType,
|
||||||
protected val nonRetryableErrors: Set<String>,
|
protected val nonRetryableErrors: Set<String>,
|
||||||
protected val lowerBounds: LowerBounds,
|
protected val lowerBounds: LowerBounds,
|
||||||
|
protected val nonRetryableErrorPatters: Set<Regex> = setOf(),
|
||||||
) {
|
) {
|
||||||
protected val log = LoggerFactory.getLogger(this::class.java)
|
protected val log = LoggerFactory.getLogger(this::class.java)
|
||||||
|
|
||||||
@@ -187,7 +188,8 @@ open class RecursiveLowerBound(
|
|||||||
)
|
)
|
||||||
.maxBackoff(Duration.ofMinutes(3))
|
.maxBackoff(Duration.ofMinutes(3))
|
||||||
.filter {
|
.filter {
|
||||||
!nonRetryableErrors.any { err -> it.message?.contains(err, true) ?: false }
|
!nonRetryableErrors.any { err -> it.message?.contains(err, true) ?: false } &&
|
||||||
|
!nonRetryableErrorPatters.any { err -> it.message?.matches(err) ?: false }
|
||||||
}
|
}
|
||||||
.doAfterRetry {
|
.doAfterRetry {
|
||||||
if (it.totalRetries() > 30) {
|
if (it.totalRetries() > 30) {
|
||||||
|
|||||||
Reference in New Issue
Block a user