-
Notifications
You must be signed in to change notification settings - Fork 8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow For Multiple Parallel Messages #41
Comments
This is an use-case that I do not want to support in PullConsumer as it is meant to be a super simple API serving say 60-70% use-cases. See #8. Your use case basically would much more benefit from batching messages, so you could then pass these to |
Picking up the thread of conversation from #43 (comment) I did a handful of benchmarks to check on the performance and overhead of starting multiple Benchmark Process and ResultsBasic AssumptionsThe purpose of these tests is to check for library overhead or bottlenecks. We aren't trying to simulate a realistic workload, we are setting up a simplistic scenario where nats won't be under heavy load so that we can stress test our own library in specific ways. All of these benchmarks were run on my MacBook Pro (13-inch, M1, 2020) with import Config
config :logger,
backends: [:console],
compile_time_purge_matching: [
[level_lower_than: :error]
] For each run, I would start nats server with Single Threaded Scenario (ie current master branch)
Results: 10:34:22.956 to 10:35:35.546 => 22.956 to 95.546 => 72.59sec After changing log level and removing extra IO.puts messages Multiple PullConsumer Processes
Batched Message Pull w/ Parallel Processing
Parallel Test Results:
The results are summarized in this graph.
There's not a huge gap in performance between the two methods, so starting multiple PullConsumer's is certainly a viable option. |
@mkaput I think this would be a good topic for us to discuss on a call, but if you have any thoughts before we get schedules lined up, I would be happy to read and try to understand asynchronously. In my working experience, I have mostly worked at companies that were setting up event architectures between multiple backend services. In every case we have had multiple copies of the app running in production (for redundancy/resiliency). So this would mean having a single And in each of my professional projects, we have wanted to avoid having a queue fall behind just because of a single slow message (maybe something waiting on an IO call), so we have always allowed some number (like 20 - 200) of parallel messages to be processed per instance of the application. This helps to keep each service up-to-date with the stream. So for me, parallelism is the common-case and I would think of running only a single |
@mkaput I've continued thinking about the issue of tracking state in the processes receiving messages, vs tracking it elsewhere. There are a few other minor reasons to prefer separate processes:
But I think all of those issues are relatively minor, the big issue is whether we are primarily writing a library to allow individual processes to receive small amounts of messages and track state in memory very efficiently (ie GenServer model) or enabling deployments where you have multiple copies of the app running and you need to potentially keep up with a large volume of messages without blocking the stream. @byu I would also love to get your take on which of these use-cases best fits your problem space. |
Hi @mmmries I've run some more benchmarks based on yours but with average of 10 runs per batch size, so it's less likely that we had some weird spikes etc. added https://github.com/membraneframework/beamchmark, so it's possible to see whats is holding us back and also on two different machines and got these results with small message size: Looking at Beamchmark data we've came to a conclusion that requesting Jetstream itself is a chokepoint here and that might explain why in larger batch sizes one PullConsumer is faster as at the start as we send one request instead of i.e. 128 But also to check for larger size of messages I've mocked larger message(~6kB) Then with example message from our system(~1kB) So for me it feels like it really depends on use case and think that we should somehow support both. Please let me know if I've missed something that would make this measurements incorrect. If you would like to check out the code I've forked the repo marmor157#1 |
It looks like JetStream has Batching built into its system and it makes sense to me to lean into the built-in batching mechanism as it will likely be the most optimized. It sounds cumbersome to me to require developers to create a separate |
@mmmries What are your thoughts about the |
The current implementation is essentially "single-threaded" since it requests a single message and then wait for it to arrive, then we handle that message before sending back the
ACK
ornext_message
.My use-case at work would certainly benefit from the ability to specify a limit of how many messages to handle in parallel. Something like:
This would match the same option name from Task.async_stream in the standard library.
The text was updated successfully, but these errors were encountered: