whitfin / rabbitmq-delimiter-exchange

Performant RabbitMQ exchange with support for multiple routing keys per message
MIT License
12 stars 4 forks source link

Comparison with Sender-selected Distribution (`cc/bcc` headers) #9

Closed aksh1618 closed 7 months ago

aksh1618 commented 7 months ago

Hi there! I've been tackling a problem that somewhat lies in a similar problem space as the one you describe in Achieving Multiple Routing Keys in RabbitMQ Exchanges, with the additional requirement that a consumer could be handling multiple clients. However, before discovering this blog post, I bumped into Sender-selected Distribution in RabbitMQ docs, which seemed suitable for my use case. However, I could think of one possible caveat of using this:

To verify that the requirement is fulfilled and the message isn't duplicated, I decided to try my luck with some AI assistants before going for real testing. Although nothing conclusive came out of them, one of them pointed to x-delimiter, which led me to your blog post!

Although I'm planning to test out the above scenario on a real instance soon, I'm also wondering whether this scenario is indeed the difference between using the cc/bcc headers vs using the x-delimiter exchange. Could you comment on this as well as any other differences that led you to creating the x-delimiter exchange instead of using cc/bcc headers extension provided by RabbitMQ?

whitfin commented 7 months ago

Hi @aksh1618 :)

The shortest answer is that performance was a concern and I wanted the smallest amount of overhead above a direct exchange as possible. Given this, the initial state of this plugin literally just split the routing key and passed it onto the direct exchange - in theory skipping everything around headers; no modification, no scanning the headers list, etc.

Although nothing conclusive came out of them, one of them pointed to x-delimiter, which led me to your blog post!

I can confirm that this exchange works for your use case above:

Whether this is true or not for sender-selection, I'm not sure. I've never really used it for anything. I went for a quick look through the code and I expect that it is true, because there's only a single message instance associated with multiple keys. I haven't tested this to confirm though.

Could you comment on this as well as any other differences that led you to creating the x-delimiter exchange instead of using cc/bcc headers extension provided by RabbitMQ?

A slightly longer answer is that it was so easy to write my own exchange for this that it made it much easier to test. Having never worked with the sender-selection before, it might impose other requirements I wasn't aware of. Making this exchange kept it much simpler and reduced our surface area (literally, the only change was a string split). It was extremely simple to reason about, nothing to learn, etc. It also uses a single field of the message, rather than splitting across routing key and headers (which I think is actually an important note).

It's been a long time since I wrote this exchange and RabbitMQ has people working on it much more frequently. It's definitely possible at this point that cc/bcc has minimal overhead and could indeed be used instead of this exchange. If your testing does actually show this to be the case, I'm happy to drop a deprecation on here and recommend people use it instead.

To be clear I'm pretty sure this implementation will always be faster because it doesn't touch headers at all, but maybe the header logic exists outside of the exchange at this point so the penalty there is felt here regardless - thus making this exchange redundant. Given the quick glance I just had, I have a sneaky feeling this could be the case. I'll look further into this if I get chance.

Let me know if this clears it up or if you have any other questions - I might follow up with more information, because this has piqued my interest.

whitfin commented 7 months ago

Disclaimer: I have no clue how good this tool is, but as it's official I'm going to trust it. I finally have some numbers using the official benchmarking tool.

To generate these numbers I did the following:

In both cases I created the exchange in advance (-e <exchange>) and bound it to the queue (-u <queue>) using routing key binding2. Each message was produced with a custom routing key (-k <key>). This results in the following two commands:

java -jar perf-test-cc.jar -k 'binding1' -p -e 'direct-exchange' -u 'direct-queue' -sb
java -jar perf-test-delimiter.jar -k ':binding1:binding2:binding3' -p -e 'delimiter-exchange' -u 'delimiter-queue' -sb

The -sb and -p flags just tell the tool to use my existing bindings. Obviously the idea is that the messages flow through to the queues based on the binding2 key even though it's a) a delimited key in the first test and b) a CC header in the second test.

You can repeat these tests yourself, but for me it was pretty consistent that the x-delimiter tests ran at around 125,000/s to 130,000/s, with the CC tests running at around 100,000/s - 110,000/s. This would suggest that there is indeed overhead with running through the CC header, with a very rough 20% increase in throughput using x-delimiter. I'm interested if your results match mine. Weirdly(?) using BCC was pretty much the same as CC.

So I guess if all of this is indeed accurate, the short answer to your original question is "this exchange is faster" :)

whitfin commented 7 months ago

Hi @aksh1618!

I re-ran a bunch of more tests and now I'm unable to distinguish results of x-delimiter and direct in combination with CC. I suspect the recent changes in RabbitMQ which optimized using khepri have rendered this project less useful. I'll add a note to the README.

I'm still unsure how I was able to produce such consistent results the other day and now I can't; I assume they're just so close in throughput that the random variations caused by my machine throughout the tests had impact and something happened to be running the other day.

Anyway, the bottom line is that you should probably use direct and CC if you're targeting RabbitMQ v3.13 or above. Below you will probably find benefit from this exchange, it really depends. If you're interested I basically rewrote the blog page scraped by your tool with everything I found investigating this in the last few days here. I included the graph of the benchmarks I ran, for your reference.

Hopefully this answers your question! I'm going to close this but feel free to ask if you have further questions.

aksh1618 commented 7 months ago

Thanks for taking the time to do this @whitfin, really appreciate it! I went through the article, love the procedural format and the addition of benchmark results, it's now an even more interesting a read! I feel the x-delimiter exchange is still a great alternative (especially given that 3.13 has basically just released, and will definitely take some time to get wider adoption), and also serves as an elegant RabbitMQ plugin example implementation for adding a custom exchange type. I definitely learned a lot, thanks again!