Fix status during launch, prevent recreating lagObservers (#299)
This commit is contained in:
@@ -14,6 +14,7 @@ import org.springframework.cloud.sleuth.Tracer
|
|||||||
import org.springframework.context.annotation.Bean
|
import org.springframework.context.annotation.Bean
|
||||||
import org.springframework.context.annotation.Configuration
|
import org.springframework.context.annotation.Configuration
|
||||||
import reactor.core.scheduler.Scheduler
|
import reactor.core.scheduler.Scheduler
|
||||||
|
import java.util.concurrent.CopyOnWriteArrayList
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
open class MultistreamsConfig(val beanFactory: ConfigurableListableBeanFactory) {
|
open class MultistreamsConfig(val beanFactory: ConfigurableListableBeanFactory) {
|
||||||
@@ -46,7 +47,7 @@ open class MultistreamsConfig(val beanFactory: ConfigurableListableBeanFactory)
|
|||||||
|
|
||||||
return EthereumMultistream(
|
return EthereumMultistream(
|
||||||
chain,
|
chain,
|
||||||
ArrayList(),
|
CopyOnWriteArrayList(),
|
||||||
cachesFactory.getCaches(chain),
|
cachesFactory.getCaches(chain),
|
||||||
headScheduler,
|
headScheduler,
|
||||||
tracer
|
tracer
|
||||||
@@ -63,7 +64,7 @@ open class MultistreamsConfig(val beanFactory: ConfigurableListableBeanFactory)
|
|||||||
|
|
||||||
return EthereumPosMultiStream(
|
return EthereumPosMultiStream(
|
||||||
chain,
|
chain,
|
||||||
ArrayList(),
|
CopyOnWriteArrayList(),
|
||||||
cachesFactory.getCaches(chain),
|
cachesFactory.getCaches(chain),
|
||||||
headScheduler,
|
headScheduler,
|
||||||
tracer
|
tracer
|
||||||
|
|||||||
@@ -245,7 +245,10 @@ open class ConfiguredUpstreams(
|
|||||||
eventPublisher
|
eventPublisher
|
||||||
)
|
)
|
||||||
upstream.start()
|
upstream.start()
|
||||||
if (!upstream.isRunning) return null
|
if (!upstream.isRunning) {
|
||||||
|
log.debug("Upstream ${upstream.getId()} is not running, it can't be added")
|
||||||
|
return null
|
||||||
|
}
|
||||||
return upstream
|
return upstream
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -66,4 +66,8 @@ class UpstreamChangeEvent(
|
|||||||
upstream.setCaches(caches)
|
upstream.setCaches(caches)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return "UpstreamChangeEvent(chain=$chain, upstream=${upstream.getId()}, type=$type)"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,11 +89,9 @@ abstract class DefaultUpstream(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun statusByLag(lag: Long?, proposed: UpstreamAvailability): UpstreamAvailability {
|
private fun statusByLag(lag: Long?, proposed: UpstreamAvailability): UpstreamAvailability {
|
||||||
if (lag == null) {
|
|
||||||
return UpstreamAvailability.UNAVAILABLE
|
|
||||||
}
|
|
||||||
return if (proposed == UpstreamAvailability.OK) {
|
return if (proposed == UpstreamAvailability.OK) {
|
||||||
when {
|
when {
|
||||||
|
lag == null -> proposed
|
||||||
lag > chainConfig.syncingLagSize -> UpstreamAvailability.SYNCING
|
lag > chainConfig.syncingLagSize -> UpstreamAvailability.SYNCING
|
||||||
lag > chainConfig.laggingLagSize -> UpstreamAvailability.LAGGING
|
lag > chainConfig.laggingLagSize -> UpstreamAvailability.LAGGING
|
||||||
else -> proposed
|
else -> proposed
|
||||||
|
|||||||
@@ -62,11 +62,14 @@ abstract class HeadLagObserver(
|
|||||||
.sample(throttling)
|
.sample(throttling)
|
||||||
.flatMap(this::probeFollowers)
|
.flatMap(this::probeFollowers)
|
||||||
.map { item ->
|
.map { item ->
|
||||||
|
log.debug("Set lag ${item.t1} to upstream ${item.t2.getId()}")
|
||||||
item.t2.setLag(item.t1)
|
item.t2.setLag(item.t1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun probeFollowers(top: BlockContainer): Flux<Tuple2<Long, Upstream>> {
|
fun probeFollowers(top: BlockContainer): Flux<Tuple2<Long, Upstream>> {
|
||||||
|
log.debug("Compute lag for ${followers.map { it.getId() }}")
|
||||||
|
|
||||||
return Flux.fromIterable(followers)
|
return Flux.fromIterable(followers)
|
||||||
.parallel(followers.size)
|
.parallel(followers.size)
|
||||||
.flatMap { up -> mapLagging(top, up, getCurrentBlocks(up)).subscribeOn(lagObserverScheduler) }
|
.flatMap { up -> mapLagging(top, up, getCurrentBlocks(up)).subscribeOn(lagObserverScheduler) }
|
||||||
@@ -87,6 +90,9 @@ abstract class HeadLagObserver(
|
|||||||
.doOnError { t ->
|
.doOnError { t ->
|
||||||
log.warn("Failed to find distance for $up", t)
|
log.warn("Failed to find distance for $up", t)
|
||||||
}
|
}
|
||||||
|
.doOnNext {
|
||||||
|
log.debug("Lag for ${it.t2.getId()} is ${it.t1}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun extractDistance(top: BlockContainer, curr: BlockContainer): Long {
|
open fun extractDistance(top: BlockContainer, curr: BlockContainer): Long {
|
||||||
|
|||||||
@@ -219,11 +219,13 @@ abstract class Multistream(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lagObserver?.stop()
|
|
||||||
lagObserver = null
|
|
||||||
when {
|
when {
|
||||||
upstreams.size == 1 -> upstreams[0].setLag(0)
|
upstreams.size == 1 -> {
|
||||||
upstreams.size > 1 -> lagObserver = makeLagObserver()
|
lagObserver?.stop()
|
||||||
|
lagObserver = null
|
||||||
|
upstreams[0].setLag(0)
|
||||||
|
}
|
||||||
|
upstreams.size > 1 -> if (lagObserver == null) lagObserver = makeLagObserver()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -387,6 +389,7 @@ abstract class Multistream(
|
|||||||
val chain = event.chain
|
val chain = event.chain
|
||||||
if (this.chain == chain) {
|
if (this.chain == chain) {
|
||||||
eventLock.withLock {
|
eventLock.withLock {
|
||||||
|
log.debug("Processing event $event")
|
||||||
when (event.type) {
|
when (event.type) {
|
||||||
UpstreamChangeEvent.ChangeType.REVALIDATED -> {}
|
UpstreamChangeEvent.ChangeType.REVALIDATED -> {}
|
||||||
UpstreamChangeEvent.ChangeType.UPDATED -> {
|
UpstreamChangeEvent.ChangeType.UPDATED -> {
|
||||||
|
|||||||
@@ -64,7 +64,10 @@ open class EthereumUpstreamValidator @JvmOverloads constructor(
|
|||||||
)
|
)
|
||||||
.map(::resolve)
|
.map(::resolve)
|
||||||
.defaultIfEmpty(UpstreamAvailability.UNAVAILABLE)
|
.defaultIfEmpty(UpstreamAvailability.UNAVAILABLE)
|
||||||
.onErrorReturn(UpstreamAvailability.UNAVAILABLE)
|
.onErrorResume {
|
||||||
|
log.error("Error during upstream validation for ${upstream.getId()}", it)
|
||||||
|
Mono.just(UpstreamAvailability.UNAVAILABLE)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun resolve(results: Tuple2<UpstreamAvailability, UpstreamAvailability>): UpstreamAvailability {
|
fun resolve(results: Tuple2<UpstreamAvailability, UpstreamAvailability>): UpstreamAvailability {
|
||||||
@@ -132,6 +135,9 @@ open class EthereumUpstreamValidator @JvmOverloads constructor(
|
|||||||
.flatMap {
|
.flatMap {
|
||||||
validate()
|
validate()
|
||||||
}
|
}
|
||||||
|
.doOnNext {
|
||||||
|
log.debug("Status after validation is $it for ${upstream.getId()}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun validateUpstreamSettings(): Boolean {
|
fun validateUpstreamSettings(): Boolean {
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ open class EthereumPosMultiStream(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun makeLagObserver(): HeadLagObserver =
|
override fun makeLagObserver(): HeadLagObserver =
|
||||||
EthereumPosHeadLagObserver(head, ArrayList(upstreams), headScheduler).apply {
|
EthereumPosHeadLagObserver(head, upstreams, headScheduler).apply {
|
||||||
start()
|
start()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -306,6 +306,29 @@ class MultistreamSpec extends Specification {
|
|||||||
.verify(Duration.ofSeconds(3))
|
.verify(Duration.ofSeconds(3))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def "After removing upstreams lag observer is stopped"() {
|
||||||
|
setup:
|
||||||
|
def up1 = TestingCommons.upstream("test-1", "internal")
|
||||||
|
def up2 = TestingCommons.upstream("test-2", "external")
|
||||||
|
def up3 = TestingCommons.upstream("test-3", "external")
|
||||||
|
def multistream = new EthereumPosMultiStream(Chain.ETHEREUM__MAINNET, [up1, up2, up3], Caches.default(), Schedulers.boundedElastic(), TestingCommons.tracerMock())
|
||||||
|
def observer = multistream.lagObserver
|
||||||
|
multistream.onUpstreamsUpdated()
|
||||||
|
|
||||||
|
expect:
|
||||||
|
multistream.getAll().size() == 3
|
||||||
|
observer.isRunning()
|
||||||
|
|
||||||
|
multistream.getAll().with {
|
||||||
|
remove(0)
|
||||||
|
remove(1)
|
||||||
|
}
|
||||||
|
multistream.getAll().size() == 1
|
||||||
|
multistream.onUpstreamsUpdated()
|
||||||
|
!observer.isRunning()
|
||||||
|
multistream.lagObserver == null
|
||||||
|
}
|
||||||
|
|
||||||
private BlockchainOuterClass.ChainStatus status(Common.AvailabilityEnum status) {
|
private BlockchainOuterClass.ChainStatus status(Common.AvailabilityEnum status) {
|
||||||
return BlockchainOuterClass.ChainStatus.newBuilder()
|
return BlockchainOuterClass.ChainStatus.newBuilder()
|
||||||
.setAvailability(status)
|
.setAvailability(status)
|
||||||
|
|||||||
Reference in New Issue
Block a user