Add RPC deduplication to client and server

This commit is contained in:
Andras Slemmer
2018-02-06 19:02:06 +00:00
parent 8081ee42ad
commit 7924a5a834
8 changed files with 306 additions and 199 deletions

View File

@ -19,6 +19,7 @@ import org.apache.activemq.artemis.api.core.SimpleString
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import rx.Observable
@ -127,7 +128,7 @@ class RPCStabilityTests {
rpcDriver {
fun startAndCloseServer(broker: RpcBrokerHandle) {
startRpcServerWithBrokerRunning(
configuration = RPCServerConfiguration.default.copy(consumerPoolSize = 1, producerPoolBound = 1),
configuration = RPCServerConfiguration.default,
ops = DummyOps,
brokerHandle = broker
).rpcServer.close()
@ -148,7 +149,7 @@ class RPCStabilityTests {
@Test
fun `rpc client close doesnt leak broker resources`() {
rpcDriver {
val server = startRpcServer(configuration = RPCServerConfiguration.default.copy(consumerPoolSize = 1, producerPoolBound = 1), ops = DummyOps).get()
val server = startRpcServer(configuration = RPCServerConfiguration.default, ops = DummyOps).get()
RPCClient<RPCOps>(server.broker.hostAndPort!!).start(RPCOps::class.java, rpcTestUser.username, rpcTestUser.password).close()
val initial = server.broker.getStats()
repeat(100) {
@ -337,11 +338,12 @@ class RPCStabilityTests {
val request = RPCApi.ClientToServer.RpcRequest(
clientAddress = SimpleString(myQueue),
methodName = SlowConsumerRPCOps::streamAtInterval.name,
serialisedArguments = listOf(10.millis, 123456).serialize(context = SerializationDefaults.RPC_SERVER_CONTEXT).bytes,
serialisedArguments = listOf(10.millis, 123456).serialize(context = SerializationDefaults.RPC_SERVER_CONTEXT),
replyId = Trace.InvocationId.newInstance(),
sessionId = Trace.SessionId.newInstance()
)
request.writeToClientMessage(message)
message.putLongProperty(RPCApi.DEDUPLICATION_SEQUENCE_NUMBER_FIELD_NAME, 0)
producer.send(message)
session.commit()
@ -350,6 +352,46 @@ class RPCStabilityTests {
}
}
interface StreamOps : RPCOps {
fun stream(streamInterval: Duration): Observable<Long>
}
class StreamOpsImpl : StreamOps {
override val protocolVersion = 0
override fun stream(streamInterval: Duration): Observable<Long> {
return Observable.interval(streamInterval.toNanos(), TimeUnit.NANOSECONDS)
}
}
@Ignore("This is flaky as sometimes artemis delivers out of order messages after the kick")
@Test
fun `deduplication on the client side`() {
rpcDriver {
val server = startRpcServer(ops = StreamOpsImpl()).getOrThrow()
val proxy = startRpcClient<StreamOps>(
server.broker.hostAndPort!!,
configuration = RPCClientConfiguration.default.copy(
connectionRetryInterval = 1.days // switch off failover
)
).getOrThrow()
// Find the internal address of the client
val clientAddress = server.broker.serverControl.addressNames.find { it.startsWith(RPCApi.RPC_CLIENT_QUEUE_NAME_PREFIX) }
val events = ArrayList<Long>()
// Start streaming an incrementing value 2000 times per second from the server.
val subscription = proxy.stream(streamInterval = Duration.ofNanos(500_000)).subscribe {
events.add(it)
}
// These sleeps are *fine*, the invariant should hold regardless of any delays
Thread.sleep(50)
// Kick the client. This seems to trigger redelivery of (presumably non-acked) messages.
server.broker.serverControl.closeConsumerConnectionsForAddress(clientAddress)
Thread.sleep(50)
subscription.unsubscribe()
for (i in 0 until events.size) {
require(events[i] == i.toLong()) {
"Events not incremental, possible duplicate, ${events[i]} != ${i.toLong()}\nExpected: ${(0..i).toList()}\nGot : $events\n"
}
}
}
}
}
fun RPCDriverDSL.pollUntilClientNumber(server: RpcServerHandle, expected: Int) {