new upstream change type — updated, triggered when upstream methods changed (#148)
This commit is contained in:
committed by
GitHub
parent
1698bf7002
commit
1ec3d44484
@@ -45,6 +45,11 @@ class UpstreamChangeEvent(
|
|||||||
*/
|
*/
|
||||||
ADDED,
|
ADDED,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Some upstream details changed
|
||||||
|
*/
|
||||||
|
UPDATED,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Upstream become available after being temporally off
|
* Upstream become available after being temporally off
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -361,28 +361,35 @@ abstract class Multistream(
|
|||||||
val chain = event.chain
|
val chain = event.chain
|
||||||
if (this.chain == chain) {
|
if (this.chain == chain) {
|
||||||
eventLock.withLock {
|
eventLock.withLock {
|
||||||
if (event.type == UpstreamChangeEvent.ChangeType.REMOVED) {
|
when (event.type) {
|
||||||
removeUpstream(event.upstream.getId()).takeIf { it }?.let {
|
UpstreamChangeEvent.ChangeType.REVALIDATED -> {}
|
||||||
try {
|
UpstreamChangeEvent.ChangeType.UPDATED -> {
|
||||||
removedUpstreams.emitNext(event.upstream) { _, res -> res == Sinks.EmitResult.FAIL_NON_SERIALIZED }
|
onUpstreamsUpdated()
|
||||||
log.info("Upstream ${event.upstream.getId()} with chain $chain has been removed")
|
|
||||||
} catch (e: Sinks.EmissionException) {
|
|
||||||
log.error("error during event processing $event", e)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
UpstreamChangeEvent.ChangeType.ADDED -> {
|
||||||
if (event.upstream is CachesEnabled) {
|
|
||||||
event.upstream.setCaches(caches)
|
|
||||||
}
|
|
||||||
addUpstream(event.upstream).takeIf { it }?.let {
|
|
||||||
if (!started) {
|
if (!started) {
|
||||||
start()
|
start()
|
||||||
}
|
}
|
||||||
try {
|
if (event.upstream is CachesEnabled) {
|
||||||
addedUpstreams.emitNext(event.upstream) { _, res -> res == Sinks.EmitResult.FAIL_NON_SERIALIZED }
|
event.upstream.setCaches(caches)
|
||||||
log.info("Upstream ${event.upstream.getId()} with chain $chain has been added")
|
}
|
||||||
} catch (e: Sinks.EmissionException) {
|
addUpstream(event.upstream).takeIf { it }?.let {
|
||||||
log.error("error during event processing $event", e)
|
try {
|
||||||
|
addedUpstreams.emitNext(event.upstream) { _, res -> res == Sinks.EmitResult.FAIL_NON_SERIALIZED }
|
||||||
|
log.info("Upstream ${event.upstream.getId()} with chain $chain has been added")
|
||||||
|
} catch (e: Sinks.EmissionException) {
|
||||||
|
log.error("error during event processing $event", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UpstreamChangeEvent.ChangeType.REMOVED -> {
|
||||||
|
removeUpstream(event.upstream.getId()).takeIf { it }?.let {
|
||||||
|
try {
|
||||||
|
removedUpstreams.emitNext(event.upstream) { _, res -> res == Sinks.EmitResult.FAIL_NON_SERIALIZED }
|
||||||
|
log.info("Upstream ${event.upstream.getId()} with chain $chain has been removed")
|
||||||
|
} catch (e: Sinks.EmissionException) {
|
||||||
|
log.error("error during event processing $event", e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -149,9 +149,11 @@ class BitcoinGrpcUpstream(
|
|||||||
override fun stop() {
|
override fun stop() {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun update(conf: BlockchainOuterClass.DescribeChain) {
|
override fun update(conf: BlockchainOuterClass.DescribeChain): Boolean {
|
||||||
upstreamStatus.update(conf)
|
val newCapabilities = RemoteCapabilities.extract(conf)
|
||||||
this.capabilities = RemoteCapabilities.extract(conf)
|
|
||||||
conf.status?.let { status -> onStatus(status) }
|
conf.status?.let { status -> onStatus(status) }
|
||||||
|
return (upstreamStatus.update(conf) || (newCapabilities != capabilities)).also {
|
||||||
|
capabilities = newCapabilities
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -131,10 +131,12 @@ open class EthereumGrpcUpstream(
|
|||||||
override fun stop() {
|
override fun stop() {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun update(conf: BlockchainOuterClass.DescribeChain) {
|
override fun update(conf: BlockchainOuterClass.DescribeChain): Boolean {
|
||||||
upstreamStatus.update(conf)
|
val newCapabilities = RemoteCapabilities.extract(conf)
|
||||||
capabilities = RemoteCapabilities.extract(conf)
|
|
||||||
conf.status?.let { status -> onStatus(status) }
|
conf.status?.let { status -> onStatus(status) }
|
||||||
|
return (upstreamStatus.update(conf) || (newCapabilities != capabilities)).also {
|
||||||
|
capabilities = newCapabilities
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getQuorumByLabel(): QuorumForLabels {
|
override fun getQuorumByLabel(): QuorumForLabels {
|
||||||
|
|||||||
@@ -125,10 +125,12 @@ open class EthereumPosGrpcUpstream(
|
|||||||
override fun stop() {
|
override fun stop() {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun update(conf: BlockchainOuterClass.DescribeChain) {
|
override fun update(conf: BlockchainOuterClass.DescribeChain): Boolean {
|
||||||
upstreamStatus.update(conf)
|
val newCapabilities = RemoteCapabilities.extract(conf)
|
||||||
capabilities = RemoteCapabilities.extract(conf)
|
|
||||||
conf.status?.let { status -> onStatus(status) }
|
conf.status?.let { status -> onStatus(status) }
|
||||||
|
return (upstreamStatus.update(conf) || (newCapabilities != capabilities)).also {
|
||||||
|
capabilities = newCapabilities
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getQuorumByLabel(): QuorumForLabels {
|
override fun getQuorumByLabel(): QuorumForLabels {
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ interface GrpcUpstream : Upstream {
|
|||||||
* Update the configuration of the upstream with the new data.
|
* Update the configuration of the upstream with the new data.
|
||||||
* Called on the first creation, and each time a new state received from upstream
|
* Called on the first creation, and each time a new state received from upstream
|
||||||
*/
|
*/
|
||||||
fun update(conf: BlockchainOuterClass.DescribeChain)
|
fun update(conf: BlockchainOuterClass.DescribeChain): Boolean
|
||||||
|
|
||||||
fun getBlockchainApi(): ReactorBlockchainGrpc.ReactorBlockchainStub
|
fun getBlockchainApi(): ReactorBlockchainGrpc.ReactorBlockchainStub
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ class GrpcUpstreamStatus(
|
|||||||
private val nodes = AtomicReference(QuorumForLabels())
|
private val nodes = AtomicReference(QuorumForLabels())
|
||||||
private var targets: CallMethods? = null
|
private var targets: CallMethods? = null
|
||||||
|
|
||||||
fun update(conf: BlockchainOuterClass.DescribeChain) {
|
fun update(conf: BlockchainOuterClass.DescribeChain): Boolean {
|
||||||
val updateLabels = ArrayList<UpstreamsConfig.Labels>()
|
val updateLabels = ArrayList<UpstreamsConfig.Labels>()
|
||||||
val updateNodes = QuorumForLabels()
|
val updateNodes = QuorumForLabels()
|
||||||
|
|
||||||
@@ -55,7 +55,9 @@ class GrpcUpstreamStatus(
|
|||||||
|
|
||||||
this.nodes.set(updateNodes)
|
this.nodes.set(updateNodes)
|
||||||
this.allLabels.set(Collections.unmodifiableCollection(updateLabels))
|
this.allLabels.set(Collections.unmodifiableCollection(updateLabels))
|
||||||
|
val changed = conf.supportedMethodsList.toSet() != this.targets?.getSupportedMethods()
|
||||||
this.targets = DirectCallMethods(conf.supportedMethodsList.toSet())
|
this.targets = DirectCallMethods(conf.supportedMethodsList.toSet())
|
||||||
|
return changed
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getLabels(): Collection<UpstreamsConfig.Labels> {
|
fun getLabels(): Collection<UpstreamsConfig.Labels> {
|
||||||
|
|||||||
@@ -149,8 +149,10 @@ class GrpcUpstreams(
|
|||||||
try {
|
try {
|
||||||
val chain = Chain.byId(chainDetails.chain.number)
|
val chain = Chain.byId(chainDetails.chain.number)
|
||||||
val up = getOrCreate(chain)
|
val up = getOrCreate(chain)
|
||||||
(up.upstream as GrpcUpstream).update(chainDetails)
|
val changed = (up.upstream as GrpcUpstream).update(chainDetails)
|
||||||
up
|
up.takeUnless {
|
||||||
|
changed && it.type == UpstreamChangeEvent.ChangeType.REVALIDATED
|
||||||
|
} ?: UpstreamChangeEvent(up.chain, up.upstream, UpstreamChangeEvent.ChangeType.UPDATED)
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
log.warn("Skip unsupported upstream ${chainDetails.chain} on $id: ${e.message}")
|
log.warn("Skip unsupported upstream ${chainDetails.chain} on $id: ${e.message}")
|
||||||
null
|
null
|
||||||
@@ -161,16 +163,20 @@ class GrpcUpstreams(
|
|||||||
it.type == UpstreamChangeEvent.ChangeType.ADDED
|
it.type == UpstreamChangeEvent.ChangeType.ADDED
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val updated = current.filter {
|
||||||
|
it.type == UpstreamChangeEvent.ChangeType.UPDATED
|
||||||
|
}
|
||||||
|
|
||||||
val removed = known.filterNot { kv ->
|
val removed = known.filterNot { kv ->
|
||||||
val stillCurrent = current.any { c -> c.chain == kv.key }
|
val stillCurrent = current.any { c -> c.chain == kv.key }
|
||||||
stillCurrent
|
stillCurrent
|
||||||
}.map {
|
}.map {
|
||||||
UpstreamChangeEvent(it.key, known.remove(it.key)!!, UpstreamChangeEvent.ChangeType.REMOVED)
|
UpstreamChangeEvent(it.key, known.remove(it.key)!!, UpstreamChangeEvent.ChangeType.REMOVED)
|
||||||
}
|
}
|
||||||
if (removed.isNotEmpty() || added.isNotEmpty()) {
|
if (removed.isNotEmpty() || added.isNotEmpty() || updated.isNotEmpty()) {
|
||||||
log.info("Finished processing of grpc upstream description for $id with content delta added ${added.map { it.chain }} and removed ${removed.map { it.chain }}")
|
log.info("Finished processing of grpc upstream description for $id with content delta added ${added.map { it.chain }}, updated ${updated.map { it.chain }} and removed ${removed.map { it.chain }}")
|
||||||
}
|
}
|
||||||
return Flux.fromIterable(removed + added)
|
return Flux.fromIterable(removed + added + updated)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun withTls(auth: AuthConfig.ClientTlsAuth): SslContext {
|
private fun withTls(auth: AuthConfig.ClientTlsAuth): SslContext {
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ class GrpcUpstreamStatusSpec extends Specification {
|
|||||||
|
|
||||||
// more values
|
// more values
|
||||||
when:
|
when:
|
||||||
status.update(
|
def result = status.update(
|
||||||
BlockchainOuterClass.DescribeChain.newBuilder()
|
BlockchainOuterClass.DescribeChain.newBuilder()
|
||||||
.addNodes(
|
.addNodes(
|
||||||
BlockchainOuterClass.NodeDetails.newBuilder()
|
BlockchainOuterClass.NodeDetails.newBuilder()
|
||||||
@@ -81,6 +81,7 @@ class GrpcUpstreamStatusSpec extends Specification {
|
|||||||
)
|
)
|
||||||
act = status.getLabels()
|
act = status.getLabels()
|
||||||
then:
|
then:
|
||||||
|
!result
|
||||||
act.toList() == [
|
act.toList() == [
|
||||||
UpstreamsConfig.Labels.fromMap([test1: "bar", test2: "baz"])
|
UpstreamsConfig.Labels.fromMap([test1: "bar", test2: "baz"])
|
||||||
]
|
]
|
||||||
@@ -109,7 +110,7 @@ class GrpcUpstreamStatusSpec extends Specification {
|
|||||||
|
|
||||||
// replace with new value
|
// replace with new value
|
||||||
when:
|
when:
|
||||||
status.update(
|
def result = status.update(
|
||||||
BlockchainOuterClass.DescribeChain.newBuilder()
|
BlockchainOuterClass.DescribeChain.newBuilder()
|
||||||
.addNodes(
|
.addNodes(
|
||||||
BlockchainOuterClass.NodeDetails.newBuilder()
|
BlockchainOuterClass.NodeDetails.newBuilder()
|
||||||
@@ -124,6 +125,7 @@ class GrpcUpstreamStatusSpec extends Specification {
|
|||||||
)
|
)
|
||||||
act = status.getLabels()
|
act = status.getLabels()
|
||||||
then:
|
then:
|
||||||
|
!result
|
||||||
act.toList() == [
|
act.toList() == [
|
||||||
UpstreamsConfig.Labels.fromMap([test: "bar", fix: "value"])
|
UpstreamsConfig.Labels.fromMap([test: "bar", fix: "value"])
|
||||||
]
|
]
|
||||||
@@ -202,7 +204,7 @@ class GrpcUpstreamStatusSpec extends Specification {
|
|||||||
setup:
|
setup:
|
||||||
def status = new GrpcUpstreamStatus(null)
|
def status = new GrpcUpstreamStatus(null)
|
||||||
when:
|
when:
|
||||||
status.update(
|
def result = status.update(
|
||||||
BlockchainOuterClass.DescribeChain.newBuilder()
|
BlockchainOuterClass.DescribeChain.newBuilder()
|
||||||
.addAllSupportedMethods([
|
.addAllSupportedMethods([
|
||||||
"test_1",
|
"test_1",
|
||||||
@@ -212,7 +214,35 @@ class GrpcUpstreamStatusSpec extends Specification {
|
|||||||
)
|
)
|
||||||
def act = status.getCallMethods()
|
def act = status.getCallMethods()
|
||||||
then:
|
then:
|
||||||
|
result
|
||||||
act.supportedMethods == ["test_1", "test_2"].toSet()
|
act.supportedMethods == ["test_1", "test_2"].toSet()
|
||||||
act instanceof DirectCallMethods
|
act instanceof DirectCallMethods
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def "Updates with new methods"() {
|
||||||
|
setup:
|
||||||
|
def status = new GrpcUpstreamStatus(null)
|
||||||
|
status.update(
|
||||||
|
BlockchainOuterClass.DescribeChain.newBuilder()
|
||||||
|
.addAllSupportedMethods([
|
||||||
|
"test_1",
|
||||||
|
"test_2"
|
||||||
|
])
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
when:
|
||||||
|
def result = status.update(
|
||||||
|
BlockchainOuterClass.DescribeChain.newBuilder()
|
||||||
|
.addAllSupportedMethods([
|
||||||
|
"test_1",
|
||||||
|
"test_2",
|
||||||
|
"test_3"
|
||||||
|
])
|
||||||
|
.build())
|
||||||
|
def act = status.getCallMethods()
|
||||||
|
then:
|
||||||
|
result
|
||||||
|
act.supportedMethods == ["test_1", "test_2", "test_3"].toSet()
|
||||||
|
act instanceof DirectCallMethods
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user