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

fix(http3): handle streamStateSendAndReceiveClosed in onStreamStateChange #4523

Merged

Conversation

GeorgeMac
Copy link
Contributor

Fixes #4513

This updates the onStreamStateChange to handle the streamStateSendAndReceiveClosed state.
Prior to this, when the client failed to observe this, the datagram previously tracked in the streams map was left behind. This caused the memory leak observed in the issue above. Adding this case causes the entry to be deleted and memory in our project is stable.

…ange

Signed-off-by: George MacRorie <me@georgemac.com>
Copy link

codecov bot commented May 14, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 85.14%. Comparing base (e41d1f9) to head (7758b02).
Report is 1 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #4523      +/-   ##
==========================================
- Coverage   85.18%   85.14%   -0.04%     
==========================================
  Files         154      154              
  Lines       14802    14794       -8     
==========================================
- Hits        12608    12595      -13     
- Misses       1687     1691       +4     
- Partials      507      508       +1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@marten-seemann
Copy link
Member

Hi @GeorgeMac! Thank you for putting in the effort to debug this! Is there any way to properly test this, either as a unit or an end-to-end test. This seems very subtle, and I'm concerned we'll break this again in the future.

I'm a bit confused about my own code now:

  1. It seems unnecessary to do state tracking in the datagrammer. Since the sendErr and the receiveErr are only set from the outside, it seems like we could just remove the isDone return value on SetReceiveError and SetSendError, right?
  2. I'm wondering if we should replace the onStateChange callback with two callbacks, one for closing the send and one for closing the receive side. This would allow us to get rid of the streamState enum altogether.

What do you think?

@GeorgeMac
Copy link
Contributor Author

GeorgeMac commented May 15, 2024

Hi @GeorgeMac! Thank you for putting in the effort to debug this! Is there any way to properly test this, either as a unit or an end-to-end test. This seems very subtle, and I'm concerned we'll break this again in the future.

I'm a bit confused about my own code now:

It seems unnecessary to do state tracking in the datagrammer. Since the sendErr and the receiveErr are only set from the outside, it seems like we could just remove the isDone return value on SetReceiveError and SetSendError, right?
I'm wondering if we should replace the onStateChange callback with two callbacks, one for closing the send and one for closing the receive side. This would allow us to get rid of the streamState enum altogether.

What do you think?

I see both of what you're saying there @marten-seemann 👍

Correct me if I am wrong (slowly catching up), the purpose of the state tracking system is twofold:

  1. Free the datagrammer when both send and recv are closed.
  2. Proxy closing of send and receive cancelling / errors from the stream onto the datagrammer (by way of onStateChange triggering Set(Send|Receive)Error)?

What you're suggesting makes sense. Another idea could be to remove the callbacks in favour of a couple interfaces to represent the datagrammer and removal of the datagrammer from the parent connection. Something like:

type streamCloser interface {
  streamClose(quic.StreamID)
}

type sendRecieveError interface {
    SetSendError(error)
    SetRecieveError(error)
}

type stateTrackingStream struct {
    quic.Stream
    
    sendError error
    receiveError error
    
    errorer sendRecieveError
    closer streamCloser
}

Where the purpose of the stateTrackingStream becomes to continue proxying, but the moment the pair of send and recv error are non-nil, it calls s.closer.streamClose(s.StreamID()).

streamClose would be implemented on connection and would clean the entry from the map?

Happy to take a stab at whichever approach makes sense and write tests around it.

@marten-seemann
Copy link
Member

Correct me if I am wrong (slowly catching up), the purpose of the state tracking system is twofold:

  1. Free the datagrammer when both send and recv are closed.
  2. Proxy closing of send and receive cancelling / errors from the stream onto the datagrammer (by way of onStateChange triggering Set(Send|Receive)Error)?

Correct!

Your idea works as well. No strong preference either way. I usually find callbacks a tiny bit easier to test, but the difference is quite minimal.

@MarcoPolo MarcoPolo mentioned this pull request May 16, 2024
4 tasks
@GeorgeMac
Copy link
Contributor Author

@marten-seemann I pushed up a take on what I described. See what you think. Happy to adjust or go back to callbacks.

Copy link
Collaborator

@MarcoPolo MarcoPolo left a comment

Choose a reason for hiding this comment

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

This is great! nice work @GeorgeMac. It simplifies the logic. A nice improvement to this part of the code.

@GeorgeMac
Copy link
Contributor Author

Thank you @MarcoPolo 🙏

http3/state_tracking_stream_test.go Outdated Show resolved Hide resolved
http3/state_tracking_stream_test.go Outdated Show resolved Hide resolved
Copy link
Member

@marten-seemann marten-seemann left a comment

Choose a reason for hiding this comment

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

Thank you @GeorgeMac!

@marten-seemann marten-seemann merged commit e2fbf3c into quic-go:master May 19, 2024
19 checks passed
@GeorgeMac
Copy link
Contributor Author

Thanks for the great feedback!

@GeorgeMac GeorgeMac deleted the gm/fix-stream-tracker-send-and-recv branch May 19, 2024 06:41
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.

http3: streams leaked in outgoingStreamsMap
3 participants