rabbitmq / rabbitmq-perf-test

A load testing tool
https://www.rabbitmq.com/java-tools.html
Other
367 stars 110 forks source link

Producer fails if exchange is durable #281

Open gvmw opened 3 years ago

gvmw commented 3 years ago

I needed a single producer publishing into amq.direct exchange. I do not have any queues or consumers. I was not expecting this to fail:

pwd
/Users/gerhard/github.com/rabbitmq/rabbitmq-perf-test

make run ARGS="--producers 1 --rate 1 --consumers 0 --exchange amq.direct --auto-delete false --flag persistent"
Main thread caught exception: java.io.IOException
14:57:06.751 [com.rabbitmq.perf.PerfTest.main()] ERROR com.rabbitmq.perf.PerfTest - Main thread caught exception
java.io.IOException: null
    at com.rabbitmq.client.impl.AMQChannel.wrap(AMQChannel.java:129)
    at com.rabbitmq.client.impl.AMQChannel.wrap(AMQChannel.java:125)
    at com.rabbitmq.client.impl.AMQChannel.exnWrappingRpc(AMQChannel.java:147)
    at com.rabbitmq.client.impl.ChannelN.exchangeDeclare(ChannelN.java:783)
    at com.rabbitmq.client.impl.recovery.AutorecoveringChannel.exchangeDeclare(AutorecoveringChannel.java:252)
    at com.rabbitmq.client.impl.recovery.AutorecoveringChannel.exchangeDeclare(AutorecoveringChannel.java:242)
    at com.rabbitmq.client.impl.recovery.AutorecoveringChannel.exchangeDeclare(AutorecoveringChannel.java:222)
    at com.rabbitmq.perf.MulticastParams.createProducer(MulticastParams.java:463)
    at com.rabbitmq.perf.MulticastSet.createProducers(MulticastSet.java:347)
    at com.rabbitmq.perf.MulticastSet.run(MulticastSet.java:186)
    at com.rabbitmq.perf.PerfTest.main(PerfTest.java:388)
    at com.rabbitmq.perf.PerfTest.main(PerfTest.java:508)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:564)
    at org.codehaus.mojo.exec.ExecJavaMojo$1.run(ExecJavaMojo.java:282)
    at java.base/java.lang.Thread.run(Thread.java:832)
Caused by: com.rabbitmq.client.ShutdownSignalException: channel error; protocol method: #method<channel.close>(reply-code=406, reply-text=PRECONDITION_FAILED - inequivalent arg 'durable' for exchange 'amq.direct' in vhost '/': received 'false' but current is 'true', class-id=40, method-id=10)
    at com.rabbitmq.utility.ValueOrException.getValue(ValueOrException.java:66)
    at com.rabbitmq.utility.BlockingValueOrException.uninterruptibleGetValue(BlockingValueOrException.java:36)
    at com.rabbitmq.client.impl.AMQChannel$BlockingRpcContinuation.getReply(AMQChannel.java:502)
    at com.rabbitmq.client.impl.AMQChannel.privateRpc(AMQChannel.java:293)
    at com.rabbitmq.client.impl.AMQChannel.exnWrappingRpc(AMQChannel.java:141)
    ... 15 common frames omitted
Caused by: com.rabbitmq.client.ShutdownSignalException: channel error; protocol method: #method<channel.close>(reply-code=406, reply-text=PRECONDITION_FAILED - inequivalent arg 'durable' for exchange 'amq.direct' in vhost '/': received 'false' but current is 'true', class-id=40, method-id=10)
    at com.rabbitmq.client.impl.ChannelN.asyncShutdown(ChannelN.java:517)
    at com.rabbitmq.client.impl.ChannelN.processAsync(ChannelN.java:341)
    at com.rabbitmq.client.impl.AMQChannel.handleCompleteInboundCommand(AMQChannel.java:182)
    at com.rabbitmq.client.impl.AMQChannel.handleFrame(AMQChannel.java:114)
    at com.rabbitmq.client.impl.AMQConnection.readFrame(AMQConnection.java:739)
    at com.rabbitmq.client.impl.AMQConnection.access$300(AMQConnection.java:47)
    at com.rabbitmq.client.impl.AMQConnection$MainLoop.run(AMQConnection.java:666)
    ... 1 common frames omitted

How can I configure PerfTest to publish into a durable exchange?

michaelklishin commented 3 years ago

PerfTest declares it as transient:

PRECONDITION_FAILED - inequivalent arg 'durable' for exchange 'amq.direct' in vhost '/': received 'false' but current is 'true'
gvmw commented 3 years ago

That's right. How can PerfTest be configured to declare the exchange as durable?

michaelklishin commented 3 years ago

I don't see a way. Exchanges are declared like so:

channel.exchangeDeclare(exchangeName, exchangeType);

but you don't have to declare amq.direct, you can avoid this by passing --predeclared since it's always there.

gvmw commented 3 years ago

While that is a nice workaround, it is not currently possible to declare a durable exchange with PerfTest. Not sure if we want to add this feature, but it's a limitation that I've hit today.

acogoluegnes commented 3 years ago

PerfTest should not try to declare an exchange that starts with amq. anyway, I'll change this.

We can add yet another flag to create durable exchanges. I think it's not there because the durability of exchanges is not critical for benchmarks, but this is not a reason not to add it.

acogoluegnes commented 3 years ago

@gerhard any suggestions for the flag?

gerhard commented 3 years ago

I'm thinking --durable true.

I don't want to make this bigger than it needs to be, but it would make sense if this new flag applied to queues too. --auto-delete true --flag persistent always felt like a long way of achieving this with queues.

acogoluegnes commented 3 years ago

--durable true (or just --durable I don't remember how PerfTest handles flags off the top of my head) for the whole chain (exchange, queue, messages) is opinionated but handy indeed. Are you sure want to go down this path? :)

gerhard commented 3 years ago

The presence of the --durable flag will work too.

My use-case is declaring and publishing into a durable exchange. As long as I can do that with PerfTest, I'm good.

The context is https://github.com/knative-sandbox/eventing-rabbitmq/pull/522 and the requirement is to generate some load on RabbitMQ that the RabbitmqSource (a Knative Eventing primitive) can consume and send to a Sink (another Knative Eventing primitive) as CloudEvents. While this used to be done differently before I joined - https://github.com/knative-sandbox/eventing-rabbitmq/blob/f36e482a04fcbe91108ce5b984a71e1abe9e6ac9/samples/source/example/simple_producer.go - there is no reason to not use the official benchmarking tool for this purpose. It would also work for this durable Exchange to be declared via https://github.com/rabbitmq/messaging-topology-operator - https://github.com/rabbitmq/rabbitmq-perf-test/issues/282 blocks this - so either option will work.

From a K8s perspective, declaring an Exchange via the topology operator that PerfTest will then use would be ideal, so #282 would be sufficient, but I think that PerfTest should support the declaration of durable exchanges.

Now you have all the context 🙂

acogoluegnes commented 3 years ago

@gerhard I fixed #281, so it should work in your case, as PerfTest will not try to create the amq.direct exchange. You can try the pivotalrabbitmq/perf-test:dev Docker image, it contains the fix.

acogoluegnes commented 3 years ago

Even though it won't help for your case until we decide to change PerfTest behavior in #282, I think we can add the --durable flag if you think it'll help, but we need to define the semantics.

My suggestion is that it does the same as --auto-delete true --flag persistent AND makes PerfTest create everything it creates durable (exchange and queues).

Note the --quorum-queue flag does the same thing as --auto-delete true --flag persistent (queues are durable, messages are persistent).

Basically --auto-delete true and --flag persistent are independent: you can have durable queues and persistent messages without --auto-delete true, and the --durable flag would "reunite" them.

Note also the durable terminology is not clear here, because it applies to resources usually, but we choose here to apply it to the whole chain. Just saying but, we could use --persistent instead (not to be confused with --flag persistent), that would make more sense I think.