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
Do not create string objects from consumerTag, exchange and routingKey, or get them from a string cache #1233
base: main
Are you sure you want to change the base?
Conversation
I'd prefer to split off at least the confirmTag into it's own PR. (as it contains a lot of special code to handle things, simpler to review track errors) The other two are so similar that they can remain in this one. As mentioned in the issue, I think we should look into also providing new API that take ROM for the exchangeName/RoutingKey |
I do not have a strong opinion on whether this should be two separate PRs. However,
would be nice and can be included into this PR or a separate one. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems like a reasonable way to reduce memory use.
I improved similarly to the .NET stream client here, avoiding tons of allocations. We need to test the PR deeply, but it makes sense to me. The problem here is the breaking changes. |
@Gsantomaggio We are using this PR in our application since almost a year ago without any problem... Today I only updated it to the latest code. (nothing else changed in this PR) |
@zgabi thank you! We appreciate you keeping this PR up-to-date. As you can see, we're making progress on version 7.0. |
@lukebakken Could you please run the build again, there was a problem in the APIApproval file |
Well, it's going to take some time to resolve conflicts with the current state of @zgabi - if you have time to start resolving conflicts, I'd appreciate it. |
Thank you @zgabi!!!! |
@zgabi thanks for all of your work on this. I'm planning to merge it by the end of the day tomorrow (16 May) unless there are further changes needed. |
Please see this comment: #1231 (comment) |
14ff1b0
to
2320480
Compare
@zgabi @bollhals @michaelklishin take a look at this commit: I would like to replace the usage of |
Hi, So the parameters of the of the HandleBasicDeliver will be alsoe This branch was an alternative to the "stringallocations" branch. |
@zgabi I realize this PR now contains an enormous number of changes, but the vast majority of them are the following:
Here are the "big" changes -
@zgabi if you wouldn't mind re-running your allocation tests that would be great. Or, let me know how you did them and I will run them. Thanks! |
While this change and the effort from you @lukebakken are honerable, I think it is currently way too big to properly analyze and judge the impact of the change. And also it contains many unrelated changes, making it even harder to judge. My proposal would be to split off any unrelated changes, to pass them individually. Regarding the main topic of the PR:
As it is right now, I think it is further away from what it tried to acheive than where the main is right now ... :/ IMHO, having classes wrapping string / bytes is only useful if
|
@@ -484,7 +484,7 @@ public void Dispose() | |||
{ | |||
if (IsOpen) | |||
{ | |||
this.AbortAsync().GetAwaiter().GetResult(); | |||
throw new InvalidOperationException("CloseAsync/AbortAsync must be called prior to Dispose"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Throwing exceptions on dispose is generally not expected behaviour. I'd challange this change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the best option, then?
I have run into bizarre CI failures that can only be explained by the use of .GetAwaiter().GetResult()
within Dispose()
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is one example, from running the entire testsuite on my local Windows 11 workstation. I have no idea how the operations here could have lead to the following error. Notice that, somehow, the test's DisposeAsync
method (which calls CloseAsync
) occurred at the same time as a BasicCancelAsync
operation 🤔 Each operation would have had to acquire the same SemaphoreSlim
for their RPC call (since the test was using the shared _channel
instance when the error happened). This is also why I modified the tests to use dedicated IChannel
instances per-Task:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I could look into bringing back a synchronous Abort
to be used by Dispose
in another PR (ugh)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generally you either do sync or async, which includes on Dispose you call sync methods not asyncs. Otherwise you'll implement IAsyncDisposable and do it there.
On the other hand, this.AbortAsync().GetAwaiter().GetResult(); Should be the way to go. It will block the current thread as a consequence, which might be problematic if
- The threadpool is maxed out, all threads are blocked (See Threadpool starvation)
- the thread has a synchronization context set and it deadlocks itself (Not entirely sure about the details here, rarely happens wihtout an UI AFAIK)
So I don't know exactly based on this why it happens without looking deep into it.
Sidenote: this would be a change I rather see separate from this PR, as I see the intent of enfocing the close as a different breaking change rather than it being burried in a large PR with a different goal in mind.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this would be a change I rather see separate from this PR, as I see the intent of enfocing the close as a different breaking change rather than it being burried in a large PR with a different goal in mind.
The thing is, if I move it into another PR, there is a good chance that this PR's CI runs will start failing again.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not if those are merged first ;)
But please, do it as you think it is best. This is just my personal opinion =)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, yeah, good point!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GetAwaiter().GetResult()
per-se should not throw exceptions. Exceptions come from task being observed.
The exception in question seems to be coming from here
If you are thinking of providing synchronous APIs, they should not be really sync and not sync over async. If it's just for the sake of implementing IDisposable
, implement just IAsyncDisposable
.
Hi @bollhals thanks for your input.
Well, that's exactly what this PR accomplishes with the There is no cache, but upon delivery,
Actually, nearly all of the changes in this PR are due to the migration from |
projects/RabbitMQ.Client/client/impl/ConsumerDispatching/AsyncConsumerDispatcher.cs
Outdated
Show resolved
Hide resolved
@bollhals there is another benefit - I have had RabbitMQ support cases that boil down to the wrong string passed as the wrong parameter to a library call. Having dedicated types makes that kind of mistake much less likely. |
Sure, that's what I ment by stating to make it much clearer what to pass. It depends in the end what the goal is:
|
I'll run some performance tests. I don't think I have access to memory profiling tools to see how this PR affects allocations ... @zgabi ? |
`AutorecoveringChannel` `CloseAsync` should just call `_innerChannel.CloseAsync`
fdaddd9
to
2320480
Compare
Sometimes, minimal local extra work/complexity avoids extra processing, allocations, GC work. It really needs to be measured. |
Proposed Changes
See issue #1231
Types of Changes
What types of changes does your code introduce to this project?
Checklist
CONTRIBUTING.md
document