Optionally send cancelled frames when context is canceled#890
Optionally send cancelled frames when context is canceled#890
Conversation
Codecov ReportPatch coverage:
Additional details and impacted files@@ Coverage Diff @@
## dev #890 +/- ##
==========================================
+ Coverage 88.78% 88.81% +0.02%
==========================================
Files 43 43
Lines 4440 4505 +65
==========================================
+ Hits 3942 4001 +59
- Misses 379 382 +3
- Partials 119 122 +3
... and 1 file with indirect coverage changes Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here. ☔ View full report in Codecov by Sentry. |
56b2531 to
7778740
Compare
mex.go
Outdated
| } | ||
|
|
||
| func (mexset *messageExchangeSet) handleCancel(frame *Frame) { | ||
| if mexset.shutdown { |
There was a problem hiding this comment.
a lock is acquired when it's set, so a read lock should be acquired here too?
There was a problem hiding this comment.
Shouldn't need this actually, this method should look a lot like forwardPeerFrame above, which doesn't have this, so reverted it.
mex.go
Outdated
| // receive channel remains open, however, in case there are concurrent | ||
| // goroutines sending to it. | ||
| func (mex *messageExchange) shutdown() { | ||
| func (mex *messageExchange) shutdown() bool { |
There was a problem hiding this comment.
the return value doesn't seem to be used anywhere
There was a problem hiding this comment.
Good catch, this was from the old PR which I started off, no longer need this so reverted.
outbound.go
Outdated
| func (c *Connection) outboundCtxCancel() { | ||
| // outbound contexts are created by callers, can't cancel them. | ||
| // However, we shouldn't be trying to cancel them, so log. | ||
| c.log.Warn("unexpected cancel of outbound context") |
There was a problem hiding this comment.
worried that this might be overly verbose in practice - should it be a debug log instead?
There was a problem hiding this comment.
Can change the level, though this should never happen. This is passed to the message exchange's ctxCancel func, and that's only used when a cancel frame is received. A cancel frame should only be received for inbound calls, which will cancel the real context. This is basically a no-op used for outbound message exchanges which aren't expected to receive a cancel.
None of the tests trigger this, and so this shouldn't happen unless a bad TChannel implementation sends an unexpected frame.
There was a problem hiding this comment.
...and so this shouldn't happen unless a bad TChannel implementation sends an unexpected frame.
yea, mostly worried that if this is somehow triggered by an edge case there won't be a way to silence it unless we roll back. upvote for changing it to debug level.
There was a problem hiding this comment.
Sure, updated to debug.
outbound.go
Outdated
| if err := call.writeMethod([]byte(methodName)); err != nil { | ||
| return nil, err | ||
| } | ||
|
|
There was a problem hiding this comment.
intentional extra new line?
7778740 to
9697ca7
Compare
prashantv
left a comment
There was a problem hiding this comment.
Thanks for the quick review!
mex.go
Outdated
| // receive channel remains open, however, in case there are concurrent | ||
| // goroutines sending to it. | ||
| func (mex *messageExchange) shutdown() { | ||
| func (mex *messageExchange) shutdown() bool { |
There was a problem hiding this comment.
Good catch, this was from the old PR which I started off, no longer need this so reverted.
outbound.go
Outdated
| if err := call.writeMethod([]byte(methodName)); err != nil { | ||
| return nil, err | ||
| } | ||
|
|
outbound.go
Outdated
| func (c *Connection) outboundCtxCancel() { | ||
| // outbound contexts are created by callers, can't cancel them. | ||
| // However, we shouldn't be trying to cancel them, so log. | ||
| c.log.Warn("unexpected cancel of outbound context") |
There was a problem hiding this comment.
Can change the level, though this should never happen. This is passed to the message exchange's ctxCancel func, and that's only used when a cancel frame is received. A cancel frame should only be received for inbound calls, which will cancel the real context. This is basically a no-op used for outbound message exchanges which aren't expected to receive a cancel.
None of the tests trigger this, and so this shouldn't happen unless a bad TChannel implementation sends an unexpected frame.
mex.go
Outdated
| } | ||
|
|
||
| func (mexset *messageExchangeSet) handleCancel(frame *Frame) { | ||
| if mexset.shutdown { |
There was a problem hiding this comment.
Shouldn't need this actually, this method should look a lot like forwardPeerFrame above, which doesn't have this, so reverted it.
| } | ||
| } | ||
|
|
||
| func (mex *messageExchange) onCtxErr(err error) { |
There was a problem hiding this comment.
nit: replace err with _ since we don't do anything with it. maybe worth commenting that the error is reported separately so we don't need to e.g. log it here.
There was a problem hiding this comment.
We actually should be using the error here to avoid unnecessary cancels from being sent when cancels are enabled + the context times out.
Added a err != context.Canceled check here
mex.go
Outdated
| } | ||
| } | ||
|
|
||
| func (mex *messageExchange) handleCancel(frame *Frame) { |
There was a problem hiding this comment.
nit: replace frame with _
outbound.go
Outdated
| func (c *Connection) outboundCtxCancel() { | ||
| // outbound contexts are created by callers, can't cancel them. | ||
| // However, we shouldn't be trying to cancel them, so log. | ||
| c.log.Warn("unexpected cancel of outbound context") |
There was a problem hiding this comment.
...and so this shouldn't happen unless a bad TChannel implementation sends an unexpected frame.
yea, mostly worried that if this is somehow triggered by an edge case there won't be a way to silence it unless we roll back. upvote for changing it to debug level.
messages.go
Outdated
|
|
||
| type cancelMessage struct { | ||
| id uint32 | ||
| ttl uint32 // unused. |
There was a problem hiding this comment.
nit: add something like but needed for writes or something like that
There was a problem hiding this comment.
updated to: "unused by tchannel-go, but part of the protocol" since it's used in both the read/write paths.
9697ca7 to
2f6f662
Compare
|
|
||
| func (mex *messageExchange) onCtxErr(err error) { | ||
| // On canceled contexts, we may need to send a cancel message. | ||
| if err != context.Canceled { |
There was a problem hiding this comment.
nit: consider errors.Is instead for sanity
| return | ||
| } | ||
|
|
||
| if onCancel := mex.mexset.onCancel; onCancel != nil { |
There was a problem hiding this comment.
nit: I think this condition is unnecessary - unless I'm misreading, we're unconditionally setting onCancel on the outbound exchange in connection.go, it's just the body of onCancel that is conditional
Replaces #817
Add a new option,
SendCancelOnContextCanceledto send cancel frames when a context isdetected to be canceled on the request sender side.
Cancellations often happen while waiting to read a response, so we can rely on the
reader goroutine waiting on the message exchange to notice the cancellation and
send the cancel message to the remote side vs adding a separate goroutine.
Older servers and relays without this change will log
Received unexpected framebut otherwise be unaffected by the unknown frame. However, there's no point enabling
cancellations if the remote side doesn't support them first.