problem: some JSON RPC client cannot use IDs and process response by their order
solution: add option to preserve batch request-response correspondence order rel: #148
This commit is contained in:
committed by
GitHub
parent
feb2bb89f7
commit
4f1216b14f
@@ -81,6 +81,7 @@ class ProxyConfigReaderSpec extends Specification {
|
||||
act.enabled
|
||||
act.host == '0.0.0.0'
|
||||
act.port == 8080
|
||||
act.preserveBatchOrder
|
||||
act.routes.size() == 2
|
||||
with(act.routes[0]) {
|
||||
id == "ethereum"
|
||||
|
||||
@@ -34,7 +34,7 @@ class BaseHandlerSpec extends Specification {
|
||||
setup:
|
||||
def handler = new BaseHandlerImpl(new WriteRpcJson(), Stub(NativeCall), Stub(ProxyServer.RequestMetricsFactory))
|
||||
when:
|
||||
def act = Mono.from(handler.execute(Chain.ETHEREUM, new ProxyCall(ProxyCall.RpcType.SINGLE), requestHandler))
|
||||
def act = Mono.from(handler.execute(Chain.ETHEREUM, new ProxyCall(ProxyCall.RpcType.SINGLE), requestHandler, false))
|
||||
.block(Duration.ofSeconds(1))
|
||||
then:
|
||||
act == ""
|
||||
@@ -44,7 +44,7 @@ class BaseHandlerSpec extends Specification {
|
||||
setup:
|
||||
def handler = new BaseHandlerImpl(new WriteRpcJson(), Stub(NativeCall), Stub(ProxyServer.RequestMetricsFactory))
|
||||
when:
|
||||
def act = Mono.from(handler.execute(Chain.ETHEREUM, new ProxyCall(ProxyCall.RpcType.BATCH), requestHandler))
|
||||
def act = Mono.from(handler.execute(Chain.ETHEREUM, new ProxyCall(ProxyCall.RpcType.BATCH), requestHandler, false))
|
||||
.block(Duration.ofSeconds(1))
|
||||
then:
|
||||
act == "[]"
|
||||
@@ -64,7 +64,7 @@ class BaseHandlerSpec extends Specification {
|
||||
call.ids[0] = 5
|
||||
def response = new NativeCall.CallResult(0, '{"foo": 1}'.bytes, null)
|
||||
when:
|
||||
def act = Flux.from(handler.execute(Chain.ETHEREUM, call, requestHandler))
|
||||
def act = Flux.from(handler.execute(Chain.ETHEREUM, call, requestHandler, false))
|
||||
.collectList()
|
||||
.block(Duration.ofSeconds(1))
|
||||
.join("")
|
||||
@@ -87,7 +87,7 @@ class BaseHandlerSpec extends Specification {
|
||||
call.ids[0] = 5
|
||||
def response = new NativeCall.CallResult(0, '{"foo": 1}'.bytes, null)
|
||||
when:
|
||||
def act = Flux.from(handler.execute(Chain.ETHEREUM, call, requestHandler))
|
||||
def act = Flux.from(handler.execute(Chain.ETHEREUM, call, requestHandler, false))
|
||||
.collectList()
|
||||
.block(Duration.ofSeconds(1))
|
||||
.join("")
|
||||
@@ -96,6 +96,112 @@ class BaseHandlerSpec extends Specification {
|
||||
1 * nativeCall.nativeCallResult(_) >> Flux.fromIterable([response])
|
||||
}
|
||||
|
||||
def "Execute ordered batch call with 2 items"() {
|
||||
setup:
|
||||
def nativeCall = Mock(NativeCall)
|
||||
def handler = new BaseHandlerImpl(new WriteRpcJson(), nativeCall, Stub(ProxyServer.RequestMetricsFactory))
|
||||
|
||||
def request1 = BlockchainOuterClass.NativeCallItem.newBuilder()
|
||||
.setMethod("eth_test")
|
||||
.setId(0)
|
||||
.build()
|
||||
def request2 = BlockchainOuterClass.NativeCallItem.newBuilder()
|
||||
.setMethod("eth_test2")
|
||||
.setId(1)
|
||||
.build()
|
||||
def call = new ProxyCall(ProxyCall.RpcType.BATCH)
|
||||
call.items.add(request1)
|
||||
call.ids[0] = 5
|
||||
|
||||
call.items.add(request2)
|
||||
call.ids[1] = 6
|
||||
def response = [
|
||||
new NativeCall.CallResult(1, '{"foo": 2}'.bytes, null),
|
||||
new NativeCall.CallResult(0, '{"foo": 1}'.bytes, null)
|
||||
]
|
||||
when:
|
||||
def act = Flux.from(handler.execute(Chain.ETHEREUM, call, requestHandler, true))
|
||||
.collectList()
|
||||
.block(Duration.ofSeconds(1))
|
||||
.join("")
|
||||
then:
|
||||
act == '[{"jsonrpc":"2.0","id":5,"result":{"foo": 1}},{"jsonrpc":"2.0","id":6,"result":{"foo": 2}}]'
|
||||
1 * nativeCall.nativeCallResult(_) >> Flux.fromIterable(response)
|
||||
}
|
||||
|
||||
def "Execute ordered batch call with 2 items even if original ids are not in sequence"() {
|
||||
setup:
|
||||
def nativeCall = Mock(NativeCall)
|
||||
def handler = new BaseHandlerImpl(new WriteRpcJson(), nativeCall, Stub(ProxyServer.RequestMetricsFactory))
|
||||
|
||||
def request1 = BlockchainOuterClass.NativeCallItem.newBuilder()
|
||||
.setMethod("eth_test")
|
||||
.setId(0)
|
||||
.build()
|
||||
def request2 = BlockchainOuterClass.NativeCallItem.newBuilder()
|
||||
.setMethod("eth_test2")
|
||||
.setId(1)
|
||||
.build()
|
||||
def call = new ProxyCall(ProxyCall.RpcType.BATCH)
|
||||
call.items.add(request1)
|
||||
call.ids[0] = 15
|
||||
|
||||
call.items.add(request2)
|
||||
call.ids[1] = 6
|
||||
def response = [
|
||||
new NativeCall.CallResult(1, '{"foo": 2}'.bytes, null),
|
||||
new NativeCall.CallResult(0, '{"foo": 1}'.bytes, null)
|
||||
]
|
||||
when:
|
||||
def act = Flux.from(handler.execute(Chain.ETHEREUM, call, requestHandler, true))
|
||||
.collectList()
|
||||
.block(Duration.ofSeconds(1))
|
||||
.join("")
|
||||
then:
|
||||
act == '[{"jsonrpc":"2.0","id":15,"result":{"foo": 1}},{"jsonrpc":"2.0","id":6,"result":{"foo": 2}}]'
|
||||
1 * nativeCall.nativeCallResult(_) >> Flux.fromIterable(response)
|
||||
}
|
||||
|
||||
def "Adds a missing element if order is requests"() {
|
||||
setup:
|
||||
def nativeCall = Mock(NativeCall)
|
||||
def handler = new BaseHandlerImpl(new WriteRpcJson(), nativeCall, Stub(ProxyServer.RequestMetricsFactory))
|
||||
|
||||
def request1 = BlockchainOuterClass.NativeCallItem.newBuilder()
|
||||
.setMethod("eth_test")
|
||||
.setId(0)
|
||||
.build()
|
||||
def request2 = BlockchainOuterClass.NativeCallItem.newBuilder()
|
||||
.setMethod("eth_test2")
|
||||
.setId(1)
|
||||
.build()
|
||||
def request3 = BlockchainOuterClass.NativeCallItem.newBuilder()
|
||||
.setMethod("eth_test3")
|
||||
.setId(2)
|
||||
.build()
|
||||
def call = new ProxyCall(ProxyCall.RpcType.BATCH)
|
||||
call.items.add(request1)
|
||||
call.ids[0] = 5
|
||||
call.items.add(request2)
|
||||
call.ids[1] = 6
|
||||
call.items.add(request3)
|
||||
call.ids[2] = 7
|
||||
|
||||
// note there is only 2 responses
|
||||
def response = [
|
||||
new NativeCall.CallResult(1, '{"foo": 2}'.bytes, null),
|
||||
new NativeCall.CallResult(2, '{"foo": 3}'.bytes, null)
|
||||
]
|
||||
when:
|
||||
def act = Flux.from(handler.execute(Chain.ETHEREUM, call, requestHandler, true))
|
||||
.collectList()
|
||||
.block(Duration.ofSeconds(1))
|
||||
.join("")
|
||||
then:
|
||||
act == '[{"jsonrpc":"2.0","id":5,"error":{"code":-32002,"message":"No response"}},{"jsonrpc":"2.0","id":6,"result":{"foo": 2}},{"jsonrpc":"2.0","id":7,"result":{"foo": 3}}]'
|
||||
1 * nativeCall.nativeCallResult(_) >> Flux.fromIterable(response)
|
||||
}
|
||||
|
||||
class BaseHandlerImpl extends BaseHandler {
|
||||
|
||||
BaseHandlerImpl(@NotNull WriteRpcJson writeRpcJson, @NotNull NativeCall nativeCall, @NotNull ProxyServer.RequestMetricsFactory requestMetrics) {
|
||||
|
||||
@@ -18,6 +18,7 @@ package io.emeraldpay.dshackle.proxy
|
||||
import com.google.protobuf.ByteString
|
||||
import io.emeraldpay.api.proto.BlockchainOuterClass
|
||||
import io.emeraldpay.api.proto.Common
|
||||
import io.emeraldpay.dshackle.config.ProxyConfig
|
||||
import io.emeraldpay.dshackle.monitoring.accesslog.AccessHandlerHttp
|
||||
import io.emeraldpay.dshackle.rpc.NativeCall
|
||||
import io.emeraldpay.dshackle.upstream.rpcclient.JsonRpcResponse
|
||||
@@ -56,6 +57,7 @@ class HttpHandlerSpec extends Specification {
|
||||
_ * it.create(_,) >> accessHandler
|
||||
}
|
||||
def handler = new HttpHandler(
|
||||
new ProxyConfig(),
|
||||
new ReadRpcJson(), new WriteRpcJson(),
|
||||
nativeCall, accessHandlerFactory, Stub(ProxyServer.RequestMetricsFactory)
|
||||
)
|
||||
@@ -84,6 +86,7 @@ class HttpHandlerSpec extends Specification {
|
||||
}
|
||||
|
||||
def handler = new HttpHandler(
|
||||
new ProxyConfig(),
|
||||
read, new WriteRpcJson(),
|
||||
Stub(NativeCall), Stub(AccessHandlerHttp.HandlerFactory),
|
||||
metrics
|
||||
@@ -110,6 +113,7 @@ class HttpHandlerSpec extends Specification {
|
||||
|
||||
|
||||
def handler = new HttpHandler(
|
||||
new ProxyConfig(),
|
||||
new ReadRpcJson(), writeRpcJson,
|
||||
nativeCall, Stub(AccessHandlerHttp.HandlerFactory), Stub(ProxyServer.RequestMetricsFactory)
|
||||
)
|
||||
@@ -122,7 +126,7 @@ class HttpHandlerSpec extends Specification {
|
||||
.build()
|
||||
)
|
||||
when:
|
||||
def act = handler.execute(Chain.ETHEREUM, call, new AccessHandlerHttp.NoOpHandler())
|
||||
def act = handler.execute(Chain.ETHEREUM, call, new AccessHandlerHttp.NoOpHandler(), false)
|
||||
|
||||
then:
|
||||
1 * nativeCall.nativeCallResult(_) >> Flux.just(new NativeCall.CallResult(1, "".bytes, null))
|
||||
|
||||
@@ -2,6 +2,7 @@ proxy:
|
||||
enabled: true
|
||||
host: 0.0.0.0
|
||||
port: 8080
|
||||
preserve-batch-order: true
|
||||
routes:
|
||||
- id: ethereum
|
||||
blockchain: ethereum
|
||||
|
||||
Reference in New Issue
Block a user