auto-adjust expected block time for liveness check (#707)
This commit is contained in:
@@ -17,16 +17,38 @@ class HeadLivenessValidatorImpl(
|
|||||||
companion object {
|
companion object {
|
||||||
const val CHECKED_BLOCKS_UNTIL_LIVE = 3
|
const val CHECKED_BLOCKS_UNTIL_LIVE = 3
|
||||||
const val COOLDOWN_MINUTES = 5L
|
const val COOLDOWN_MINUTES = 5L
|
||||||
|
const val TIMEOUT_MULTIPLIER = 2L
|
||||||
|
const val MAX_BLOCK_TIME_MINUTES = 10L
|
||||||
private val log = LoggerFactory.getLogger(HeadLivenessValidatorImpl::class.java)
|
private val log = LoggerFactory.getLogger(HeadLivenessValidatorImpl::class.java)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Volatile
|
@Volatile
|
||||||
private var lastNonConsecutiveTime: Instant? = null
|
private var lastNonConsecutiveTime: Instant? = null
|
||||||
|
|
||||||
|
@Volatile
|
||||||
|
private var measuredBlockTime: Duration = expectedBlockTime
|
||||||
|
|
||||||
|
@Volatile
|
||||||
|
private var lastBlockTimestamp: Instant? = null
|
||||||
|
|
||||||
override fun getFlux(): Flux<HeadLivenessState> {
|
override fun getFlux(): Flux<HeadLivenessState> {
|
||||||
val headLiveness = head.headLiveness()
|
val headLiveness = head.headLiveness()
|
||||||
// first we have moving window of 2 blocks and check that they are consecutive ones
|
// first we have moving window of 2 blocks and check that they are consecutive ones
|
||||||
val headFlux = head.getFlux().map { it.height }.buffer(2, 1).map {
|
val headFlux = head.getFlux().doOnNext { _ ->
|
||||||
|
// Measure actual block time
|
||||||
|
val now = Instant.now()
|
||||||
|
val previous = lastBlockTimestamp
|
||||||
|
lastBlockTimestamp = now
|
||||||
|
|
||||||
|
if (previous != null) {
|
||||||
|
val actualInterval = Duration.between(previous, now)
|
||||||
|
// Always take the larger value to avoid timeouts
|
||||||
|
if (actualInterval > measuredBlockTime) {
|
||||||
|
measuredBlockTime = actualInterval
|
||||||
|
log.info("Updated measured block time to {}ms for upstream {}", measuredBlockTime.toMillis(), upstreamId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.map { it.height }.buffer(2, 1).map {
|
||||||
// sometimes we can get there the same block, in this case it will be reorg
|
// sometimes we can get there the same block, in this case it will be reorg
|
||||||
it.last() - it.first() <= 1L
|
it.last() - it.first() <= 1L
|
||||||
}.scan(Pair(0, true)) { acc, value ->
|
}.scan(Pair(0, true)) { acc, value ->
|
||||||
@@ -64,17 +86,27 @@ class HeadLivenessValidatorImpl(
|
|||||||
}
|
}
|
||||||
else -> Flux.empty()
|
else -> Flux.empty()
|
||||||
}
|
}
|
||||||
}.timeout(
|
}
|
||||||
expectedBlockTime.multipliedBy(CHECKED_BLOCKS_UNTIL_LIVE.toLong() * 2),
|
|
||||||
Flux.just(HeadLivenessState.NON_CONSECUTIVE).doOnNext {
|
|
||||||
if (log.isDebugEnabled) {
|
|
||||||
log.debug("head liveness check broken with timeout in $upstreamId")
|
|
||||||
} else {
|
|
||||||
ThrottledLogger.log(log, "head liveness check broken with timeout in $upstreamId")
|
|
||||||
}
|
|
||||||
},
|
|
||||||
).repeat().subscribeOn(scheduler)
|
|
||||||
|
|
||||||
return Flux.merge(headFlux, headLiveness)
|
// Use defer to dynamically evaluate timeout on each subscription
|
||||||
|
val timeoutFlux = Flux.defer {
|
||||||
|
headFlux.timeout(
|
||||||
|
measuredBlockTime.multipliedBy(CHECKED_BLOCKS_UNTIL_LIVE.toLong() * 2),
|
||||||
|
Flux.just(HeadLivenessState.NON_CONSECUTIVE).doOnNext {
|
||||||
|
// Increase measured block time when timeout occurs to avoid future timeouts
|
||||||
|
measuredBlockTime = measuredBlockTime.multipliedBy(TIMEOUT_MULTIPLIER).coerceAtMost(Duration.ofMinutes(MAX_BLOCK_TIME_MINUTES))
|
||||||
|
if (log.isDebugEnabled) {
|
||||||
|
log.debug(
|
||||||
|
"head liveness check broken with timeout in $upstreamId, increased measured block time to {}ms",
|
||||||
|
measuredBlockTime.toMillis(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
ThrottledLogger.log(log, "head liveness check broken with timeout in $upstreamId")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}.repeat().subscribeOn(scheduler)
|
||||||
|
|
||||||
|
return Flux.merge(timeoutFlux, headLiveness)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user