added protection for grpc head and grpc received blocks metric (#184)

This commit is contained in:
a10zn8
2023-03-24 18:49:46 +04:00
committed by GitHub
parent 783aaaef05
commit 67ca1b85ea
3 changed files with 62 additions and 42 deletions

View File

@@ -25,6 +25,8 @@ import io.emeraldpay.dshackle.upstream.DefaultUpstream
import io.emeraldpay.dshackle.upstream.Lifecycle import io.emeraldpay.dshackle.upstream.Lifecycle
import io.emeraldpay.dshackle.upstream.UpstreamAvailability import io.emeraldpay.dshackle.upstream.UpstreamAvailability
import io.emeraldpay.dshackle.upstream.forkchoice.ForkChoice import io.emeraldpay.dshackle.upstream.forkchoice.ForkChoice
import io.micrometer.core.instrument.Counter
import io.micrometer.core.instrument.Metrics
import org.reactivestreams.Publisher import org.reactivestreams.Publisher
import reactor.core.Disposable import reactor.core.Disposable
import reactor.core.publisher.Flux import reactor.core.publisher.Flux
@@ -75,6 +77,10 @@ class GrpcHead(
.delayElements(Duration.ofSeconds(1)) .delayElements(Duration.ofSeconds(1))
) )
.flatMap { it.subscribeHead(chainRef) } .flatMap { it.subscribeHead(chainRef) }
.doOnNext {
headsCounter.increment()
}
.sample(Duration.ofMillis(1)) // protect from too many heads
.doFinally { .doFinally {
parent.setStatus(UpstreamAvailability.UNAVAILABLE) parent.setStatus(UpstreamAvailability.UNAVAILABLE)
log.warn("Head subscription finished: $it") log.warn("Head subscription finished: $it")
@@ -116,4 +122,9 @@ class GrpcHead(
super.stop() super.stop()
headSubscription?.dispose() headSubscription?.dispose()
} }
val headsCounter = Counter.builder("grpc_head_received")
.tag("upstream", id)
.tag("chain", chain.chainCode)
.register(Metrics.globalRegistry)
} }

View File

@@ -134,20 +134,23 @@ class EthereumGrpcUpstreamSpec extends Specification {
@Override @Override
void subscribeHead(Common.Chain request, StreamObserver<BlockchainOuterClass.ChainHead> responseObserver) { void subscribeHead(Common.Chain request, StreamObserver<BlockchainOuterClass.ChainHead> responseObserver) {
responseObserver.onNext( new Thread({
BlockchainOuterClass.ChainHead.newBuilder() responseObserver.onNext(
.setBlockId(block1.hash.toHex().substring(2)) BlockchainOuterClass.ChainHead.newBuilder()
.setHeight(block1.number) .setBlockId(block1.hash.toHex().substring(2))
.setWeight(ByteString.copyFrom(block1.totalDifficulty.toByteArray())) .setHeight(block1.number)
.build() .setWeight(ByteString.copyFrom(block1.totalDifficulty.toByteArray()))
) .build()
responseObserver.onNext( )
BlockchainOuterClass.ChainHead.newBuilder() Thread.sleep(100)
.setBlockId(block2.hash.toHex().substring(2)) responseObserver.onNext(
.setHeight(block2.number) BlockchainOuterClass.ChainHead.newBuilder()
.setWeight(ByteString.copyFrom(block2.totalDifficulty.toByteArray())) .setBlockId(block2.hash.toHex().substring(2))
.build() .setHeight(block2.number)
) .setWeight(ByteString.copyFrom(block2.totalDifficulty.toByteArray()))
.build()
)
}).start()
} }
}) })
def upstream = new EthereumGrpcUpstream("test", hash, UpstreamsConfig.UpstreamRole.PRIMARY, Chain.ETHEREUM, client, new JsonRpcGrpcClient(client, Chain.ETHEREUM, metrics), null, ChainsConfig.ChainConfig.default()) def upstream = new EthereumGrpcUpstream("test", hash, UpstreamsConfig.UpstreamRole.PRIMARY, Chain.ETHEREUM, client, new JsonRpcGrpcClient(client, Chain.ETHEREUM, metrics), null, ChainsConfig.ChainConfig.default())

View File

@@ -42,16 +42,20 @@ class GrpcHeadSpec extends Specification {
responseObserver.onError(new IllegalStateException("Unsupported chain")) responseObserver.onError(new IllegalStateException("Unsupported chain"))
return return
} }
[10, 11, 12, 14].forEach { height -> new Thread({ [10, 11, 12, 14].forEach { height ->
println("next: $height")
responseObserver.onNext( responseObserver.onNext(
BlockchainOuterClass.ChainHead.newBuilder() BlockchainOuterClass.ChainHead.newBuilder()
.setChain(request.type) .setChain(request.type)
.setBlockId(height.toString())
.setHeight(height) .setHeight(height)
.build() .build()
) )
Thread.sleep(100) Thread.sleep(500)
} }
responseObserver.onCompleted() responseObserver.onCompleted()
}).start()
} }
}) })
def convert = { BlockchainOuterClass.ChainHead head -> def convert = { BlockchainOuterClass.ChainHead head ->
@@ -88,32 +92,34 @@ class GrpcHeadSpec extends Specification {
responseObserver.onError(new IllegalStateException("Unsupported chain")) responseObserver.onError(new IllegalStateException("Unsupported chain"))
return return
} }
phase++ new Thread({
if (phase == 1) { phase++
responseObserver.onError(new RuntimeException("Phase 1 error")) if (phase == 1) {
} else if (phase == 2) { responseObserver.onError(new RuntimeException("Phase 1 error"))
[10, 11].forEach { height -> } else if (phase == 2) {
responseObserver.onNext( [10, 11].forEach { height ->
BlockchainOuterClass.ChainHead.newBuilder() responseObserver.onNext(
.setChain(request.type) BlockchainOuterClass.ChainHead.newBuilder()
.setHeight(height) .setChain(request.type)
.build() .setHeight(height)
) .build()
Thread.sleep(100) )
Thread.sleep(100)
}
responseObserver.onError(new RuntimeException("Phase 2 error"))
} else if (phase == 3) {
[11, 12, 13].forEach { height ->
responseObserver.onNext(
BlockchainOuterClass.ChainHead.newBuilder()
.setChain(request.type)
.setHeight(height)
.build()
)
Thread.sleep(100)
}
responseObserver.onCompleted()
} }
responseObserver.onError(new RuntimeException("Phase 2 error")) }).start()
} else if (phase == 3) {
[11, 12, 13].forEach { height ->
responseObserver.onNext(
BlockchainOuterClass.ChainHead.newBuilder()
.setChain(request.type)
.setHeight(height)
.build()
)
Thread.sleep(100)
}
responseObserver.onCompleted()
}
} }
}) })
def convert = { BlockchainOuterClass.ChainHead head -> def convert = { BlockchainOuterClass.ChainHead head ->