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

support intermediate flushes when encoding #155

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

Geal
Copy link

@Geal Geal commented Aug 24, 2022

disclaimer: this is an attempt at fixing apollographql/router#1572, for which I'm under tight timing constraints, so right now this PR's goal is discussing and finding a quick fix that we can use directly in the router, and then I'll help find a proper solution.

tentative fix for #154

Here I make sure to call the flush method on pending, but what happens here is that one 64 bytes chunk of compressed data is returned immediately (not enough for the first part of the encoded data), and then it waits for 5s for the rest. Any advice on making it work? Does it need to flush multiple times?

due to the use of ready!(), whenever the underlying reader returns
Poll::Pending, it is transmitted directly to the caller, so there is no
flush until the entire data has been read.

This commit flushes compressed data when we get Poll::Pending, and stays
in the flushing state in case there is more data to send.
Geal added a commit to apollographql/router that referenced this pull request Aug 26, 2022
Fix #1572

async-compression is used in tower-http's CompressionLayer. Inside the
AsyncRead based compression code, if the underlying stream returns
Poll::Pending, it will be returned directly, while in the case of
deferred responses, the next one might come way later, so we want to
send whatever data we have available right now.
We will have to switch to an AsyncWrite interface instead, which will be
more flexible, but considering the timing of the release, this patch
will hold for now.

This uses the code from Nullus157/async-compression#155
@jeromegn
Copy link

jeromegn commented Sep 19, 2022

I think this effort has been abandoned (because the apollo graphql router just removed compression for multipart responses), but we're facing a similar issue with "html streaming" where the content-type is text/html. It's a slow-drip stream too.

Reproduction:

curl -N https://frosty-frog-2648.fly.dev/ --compressed

Edit: Does not reproduce anymore, using this PR with this small change fixes it: prīmum...jeromegn:async-compression:fly-encoder-flush#diff-db895ba77ea00b18eddff756a977174f242792a8797afe28999357484774946dR100

Trying this branch (thankfully) broke our tests. It looks like the read loop is stuck like:

encoding -> flushing ->  encoding -> flushing -> ...

Since it has already read some bytes, read != 0, and so it will never ever return Poll::Pending after that. I have a branch where I'm just resetting read = 0 when I've flushed successfully. I don't know if that renders this fix useless or if I've broken something else.

@Geal
Copy link
Author

Geal commented Sep 20, 2022

It's not abandoned, I'm just under tight constraints, due to moving out, and the router release coming up soon 😅

I'd like to look a bit at using a writer oriented approach from inside tower-http, maybe that will be a cleaner way

Geal added a commit to apollographql/router that referenced this pull request Apr 27, 2023
We replace tower-http's `CompressionLayer` with a custom stream transformation. This is necessary because tower-http uses async-compression, which buffers data until the end of the stream to then write it, ensuring a better compression. This is incompatible with the multipart protocol for `@defer`, which requires chunks to be sent as soon as possible. So we need to compress them independently.

This extracts parts of the codec module of async-compression, which so far is not public, and makes a streaming wrapper above it that flushes the compressed data on every response in the stream.

This is expected to be temporary, as we have in flight PRs for async-compression:
- Nullus157/async-compression#155
- Nullus157/async-compression#178

With Nullus157/async-compression#150 we might be able to at least remove the vendored code
@robjtede robjtede changed the title WIP: support intermediate flushes when encoding support intermediate flushes when encoding May 10, 2023
@robjtede robjtede marked this pull request as draft May 10, 2023 16:06
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

2 participants