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 request task cancellation, and improve handling of graceful shutdown #379

Open
wants to merge 2 commits into
base: 2.x.x
Choose a base branch
from

Conversation

Joannis
Copy link
Contributor

@Joannis Joannis commented Feb 15, 2024

Should solve a typical dilemma I've had in systems that get under heavy load:

  1. A user connects to download a huge dataset (CSV or XLSX)
  2. Server does some heavy queries, and starts outputting CSV/XLSX
  3. User gets impatient and refreshes browser, cancelling the request
  4. Server is still generating the request that was cancelled
  5. Successive attempts by the user become futile and take forever

…oses early. In addition we check for the HTTPServer's service being cancelled or gracefully shutting down.
@Joannis
Copy link
Contributor Author

Joannis commented Feb 15, 2024

Make sure you hide whitespace on github
image


while true {
// set to processing unless it is cancelled then exit
guard processingRequest.exchange(.processing, ordering: .relaxed) == .idle else { break }
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I didn't add the graceful check here, I wasn't sure if that was "correct" because we already received .head

Copy link

codecov bot commented Feb 15, 2024

Codecov Report

Attention: 3 lines in your changes are missing coverage. Please review.

Comparison is base (0b33c97) 84.51% compared to head (b94dc40) 84.47%.
Report is 2 commits behind head on 2.x.x.

Files Patch % Lines
...mmingbirdCore/Server/HTTP/HTTPChannelHandler.swift 94.91% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##            2.x.x     #379      +/-   ##
==========================================
- Coverage   84.51%   84.47%   -0.04%     
==========================================
  Files          95       95              
  Lines        5283     5283              
==========================================
- Hits         4465     4463       -2     
- Misses        818      820       +2     

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

}

// Process next request unless it is cancelled then exit
if Task.isShuttingDownGracefully || Task.isCancelled {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Cancellation check is moved down. I can imagine that some clients might fail if we do respond, but don't consume the body before closing the connection.

asyncChannel.channel.close(mode: .input, promise: nil)
}

try await asyncChannel.channel.closeFuture.get()

Choose a reason for hiding this comment

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

This shouldn't be needed. executeThenClose makes sure that the channel is closed

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The reason I do this is because I want to trigger .cancelAll() if the channel closes while a request is still being handled

@adam-fowler
Copy link
Member

In a separate PR can you create a test that demonstrates this is an issue.

If someone cancels a request would that not close the channel and as soon as the channel is closed writing to it will throw an error and the processing of the request will be cancelled.

@adam-fowler
Copy link
Member

Or are you looking at the situation where generating the response takes a long time, not writing the response.

Copy link
Member

@adam-fowler adam-fowler left a comment

Choose a reason for hiding this comment

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

Removing the graceful shutdown handler means if the connection is open but waiting for http parts it will not shutdown.

You could probably do this with a middleware where you require the context to have a reference to the channel and then apply it to the routes that might require this kind of support.

Sources/HummingbirdCore/Server/Server.swift Show resolved Hide resolved
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.

None yet

3 participants