Skip to content
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

Add Channel.ConsumeWithContext to be able to cancel delivering #192

Merged
merged 3 commits into from
Jun 20, 2023

Conversation

t2y
Copy link
Contributor

@t2y t2y commented May 15, 2023

This is a reference implementation for #124 (comment).

@lukebakken lukebakken self-assigned this May 15, 2023
@lukebakken lukebakken added this to the 2.0.0 milestone May 15, 2023
@lukebakken
Copy link
Contributor

Thanks! We'll take a look as soon as we have time.

@Zerpet Zerpet self-assigned this May 16, 2023
Zerpet
Zerpet previously approved these changes May 16, 2023
@Zerpet
Copy link
Contributor

Zerpet commented May 16, 2023

@lukebakken perhaps we could introduce this in a minor version, since it deprecates Consume() and adds ConsumeWithContext(). I'm also happy if we decide to ship this in 2.0, but in that case, we should consider cutting a v1.x branch before merging.

@Zerpet
Copy link
Contributor

Zerpet commented Jun 6, 2023

@t2y just dropping a message to let you know that I have not forgotten about this PR. During my QA of this PR, I found something odd regarding message re-queueing, and I'm investigating that before merging. Plus past weeks I was pulled onto something else.

@Zerpet Zerpet self-requested a review June 6, 2023 17:19
@Zerpet Zerpet dismissed their stale review June 6, 2023 17:19

May have found a problematic behaviour with this implementation

@t2y
Copy link
Contributor Author

t2y commented Jun 7, 2023

@Zerpet Thank you for testing! I'm going to fix it when the problem can be reproduced.

Copy link
Contributor

@Zerpet Zerpet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Never mind, the behaviour is correct. It is important to note that cancelling a consumer will not re-queue in-flight messages. The app using this library must close the amqp channel to re-queue in-flight messages.

I would like to have a check on context cancellation before calling ch.call(...), as discussed in #124, before merging this PR.

Copy link
Contributor

@Zerpet Zerpet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I realised that this PR changes the semantics of consume, into something that is not correct.

Previously, Consume would create a subcription in the server; such subscription will be valid until the channel is closed, or Cancel() was called. After this PR, the subcription will be valid until the channel is closed, or Cancel() is called, or the context gets cancelled.

We can't accept this PR with the new semantics, and it doesn't seem correct to use context cancellation to cancel a consumer. The context passed to ConsumeWithContext() should be used to cancel the call to ConsumeWithContext(), but it should not cancel a consumer after ConsumeWithContext() has returned successfully.

In other words, the go routine initiated in ConsumeWithContext() will have an impact on the consumer after ConsumeWithContext() returns successfully, and that is not correct. A consumer should be able to keep on receiving messages after the context in ConsumeWithContext is cancelled.

From the conversation in #124, the best way forward is to check for context cancellation before calling ch.call(...).

In addition, and optionally, we could wrap ch.call(...) in a go routine and observe context cancellation, or a signal from the wrapped call. The clean up tasks would be to close the AMQP channel. I'm not sure about this bit, please hold on from implementing this bit.

@t2y
Copy link
Contributor Author

t2y commented Jun 8, 2023

Thank you for describing the background. I will follow you if this PR is not valid for consumption. Before that, I want to confirm a little more as a reference.

I understood existing Consume() is different from ConsumeWithContext() . Of course, it's important not to change the semantics of consume, too.

At first, I thought Consume() is similar to the Generators pattern in Go. So, I'm considering that canceling with Context is useful for Go developers since they can see it's a general concurrency pattern. I guess many Go developers expect canceling Context stops the subscription. What do you think?

@batazor
Copy link

batazor commented Jun 8, 2023

I agree, by canceling Context I hope that everything below will also be canceled

@lukebakken
Copy link
Contributor

We can't accept this PR with the new semantics, and it doesn't seem correct to use context cancellation to cancel a consumer.

I would expect context cancellation to "cancel everything" ... i.e. do a Basic.Cancel operation.

@Zerpet
Copy link
Contributor

Zerpet commented Jun 12, 2023

At first, I thought Consume() is similar to the Generators pattern in Go.

I believe this is a coincidence. The original author of this package @streadway wrote it way before contexts made it to Go. Even if the generators pattern can be implemented w/o contexts, I don't think it was the original intention.

I agree, by canceling Context I hope that everything below will also be canceled

I would expect context cancellation to "cancel everything" ... i.e. do a Basic.Cancel operation.

The "catch" here IMO is that ConsumeWithContext has returned, and you get to work with the returned value. There's nothing "below". Compare this example with a Dial(context, address) function, e.g. with the dialer from the standard library. The context is used as a timeout for the dialer, but once dialer has returned, context cancellation does not affect the dialed function.

That being said, if folks find useful to cancel a consumer after the context expires, even when ConsumeWithContext has returned, I'd be happy to introduce a new function, without deprecating Consume, since both have different semantics.

@t2y
Copy link
Contributor Author

t2y commented Jun 12, 2023

That being said, if folks find useful to cancel a consumer after the context expires, even when ConsumeWithContext has returned, I'd be happy to introduce a new function, without deprecating Consume, since both have different semantics.

I agree with you. I updated the description of functions (I'd like to ask anyone to rewrite the proper content since I'm not a good English writer).

Copy link
Contributor

@Zerpet Zerpet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We haven't seen much activity on this PR for a while. Based on the previous feedback, I'm going to merge as-is and make some adjustments to docs and to original Consume function. We should be able to release this in a minor version of 1.x.

Thanks everyone for getting involved!

@Zerpet Zerpet modified the milestones: 2.0.0, 1.9.0 Jun 20, 2023
@Zerpet Zerpet merged commit 13dde10 into rabbitmq:main Jun 20, 2023
t2y added a commit to t2y/amqp091-go that referenced this pull request Jun 20, 2023
@t2y t2y deleted the take-context-to-consume branch June 20, 2023 13:46
Zerpet added a commit that referenced this pull request Jun 20, 2023
After #192, Consume was calling the new ConsumeWithContext function with
a context.Background context. Whilst this would work, it would also pile
up routines that would only return when the AMQP channel is closed. This
is unnecessary, and some code duplication is preferrable over piling up
blocked routines.

Some clarifications to function documentation, and added new paragraphs
to ConsumeWithContext function to explain the semantics.

Signed-off-by: Aitor Pérez Cedres <[email protected]>
Zerpet pushed a commit to t2y/amqp091-go that referenced this pull request Jun 21, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants