added protection for grpc head and grpc received blocks metric (#184)
This commit is contained in:
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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())
|
||||||
|
|||||||
@@ -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 ->
|
||||||
|
|||||||
Reference in New Issue
Block a user