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

Compression Support #282

Closed
ericmcbride opened this issue Mar 5, 2020 · 10 comments · Fixed by #692
Closed

Compression Support #282

ericmcbride opened this issue Mar 5, 2020 · 10 comments · Fixed by #692
Labels
A-tonic C-enhancement Category: New feature or request
Milestone

Comments

@ericmcbride
Copy link

Feature Request

Add compression to Tonic

Crates

https://github.com/BurntSushi/rust-snappy

Motivation

We use gzip (default) in our Go GRPC services. Would like to normalize this across all services. There are other compression options such as snappy. I noticed burntsushi has a nice snappy implementation in rust.

Proposal

Add some level of compression for larger payloads.

Id be more then happy to help with the implementation of this. Been wanting to get my hands dirty on some more open source projects. I don't know if any ideas have been floated or not about this, or if it belongs here or in tower.

@LucioFranco
Copy link
Member

@ericmcbride yeah, compression is something we should 100% add. I am curious what type of compression you are using with grpc-go? Is it at the connection level or is it per message?

I'd love to support this though I am not 100% sure on how to do this yet, so happy to chat about it! Feel free to ping me on discord as well and we can chat if this is something you are interested in contributing :)

@LucioFranco LucioFranco added A-tonic C-enhancement Category: New feature or request labels Mar 6, 2020
@ericmcbride
Copy link
Author

Yeah we just use gzip and enable it as an option. I haven't really went deep into the rabbit hole. At first glance

// callInfo contains all related configuration and information about an RPC.
type callInfo struct {
	compressorType        string
	failFast              bool
	stream                ClientStream
	maxReceiveMessageSize *int
	maxSendMessageSize    *int
	creds                 credentials.PerRPCCredentials
	contentSubtype        string
	codec                 baseCodec
	maxRetryRPCBufferSize int
}


// CallOption configures a Call before it starts or extracts information from
// a Call after it completes.
type CallOption interface {
	// before is called before the call is sent to any server.  If before
	// returns a non-nil error, the RPC fails with that error.
	before(*callInfo) error

	// after is called after the call has completed.  after cannot return an
	// error, so any failures should be reported via output parameters.
	after(*callInfo)
}

Makes me thing its per message but this is a quick assumption. Ill dive deeper into the code this weekend and look and id be more then happy to jump on!

@Sherlock-Holo
Copy link
Contributor

I think if compression is in connection-level, need to check peer support compression or not, but it makes developers can focus on the real job, instead of 'how to set the framework correctly'.

And if we could choose which rpc call enable compression, which one disable, it's very good, because when we just send some little bytes, compress them doesn't reduce time, sometimes even make data bigger. However, if we need to send a lot of data, like sending a photo, compress it may reduce a lot of transport time

@LucioFranco
Copy link
Member

I assume we will want to change our generated clients to be more like a builder api that can allow users to set compression per service.

@onsails
Copy link

onsails commented Jun 18, 2020

In my tonic-based client-server app there is a need in compression of grpc stream data.
Given that gRPC specs explicitly tell that compression is message-level, I think that the better option is to provide a stream compression layer on top of tonic api, not extend the api itself.
In case anyone needs to (de)compress streams of prost! messages (which tonic streams are), I created a crate just for that purpose.

@pruthvikar
Copy link
Contributor

Here's a link to the gRPC c++ compression documentation

They outline test cases that exemplify the behaviour of compression

Test cases

  1. When a compression level is not specified for either the channel or the message, the default channel level none is considered: data MUST NOT be compressed.
  2. When per-RPC compression configuration isn't present for a message, the channel compression configuration MUST be used.
  3. When a compression method (including no compression) is specified for an outgoing message, the message MUST be compressed accordingly.
  4. A message compressed by a client in a way not supported by its server MUST fail with status UNIMPLEMENTED, its associated description indicating the unsupported condition as well as the supported ones. The returned grpc-accept-encoding header MUST NOT contain the compression method (encoding) used.
  5. A message compressed by a server in a way not supported by its client MUST fail with status INTERNAL, its associated description indicating the unsupported condition as well as the supported ones. The returned grpc-accept-encoding header MUST NOT contain the compression method (encoding) used.
  6. An ill-constructed message with its Compressed-Flag bit set but lacking a grpc-encoding entry different from identity in its metadata MUST fail with INTERNAL status, its associated description indicating the invalid Compressed-Flag condition.

@LucioFranco LucioFranco added this to the 0.4 milestone Nov 27, 2020
@davidpdrsn
Copy link
Member

tower-http has compression and decompression middlewares that support any B: http_body::Body. That might be possible to leverage. Note that its still very early days in tower-http and nothing is set in stone or published yet.

@davidpdrsn
Copy link
Member

I think adding support for compression is something I would like to look into next. It seems we already have #476 which gets us at least part of the way there.

However there is one thing I don't quite understand. In requirement 6 listed above

An ill-constructed message with its Compressed-Flag bit set but lacking a grpc-encoding entry different from identity in its metadata MUST fail with INTERNAL status, its associated description indicating the invalid Compressed-Flag condition.

Why is there suddenly a Compressed-Flag bit? It appears that is what gets set here.

Why are the grpc-accept-encoding and grpc-encoding headers not sufficient?

My initial plan was to make a tower middleware that compresses (or decompresses) bodies one chunk/frame at a time (what you get from http_body::Body::poll_data). But that doesn't seem to be possible since we need to set this compression bit meaning you need to change the encoders and decoders.

@vbfox
Copy link
Contributor

vbfox commented May 1, 2021

@davidpdrsn I'm far from an expert but here is how I interpreted the specification:

Essentially the headers are giving the information on "how would it be compressed if it was compressed" and then each message (there can be multiple on a stream) can be compressed or not.

Each message having the structure [Compressed? u8][size u32][data bytes[size]].

I'm not sure how it can be done at the http tower level as it would need to interpret or produce grpc frames and the handling is very grpc protocol specific.

An ill-constructed message with its Compressed-Flag bit set but lacking a grpc-encoding entry different from identity in its metadata MUST fail with INTERNAL status, its associated description indicating the invalid Compressed-Flag condition.

This section for me is to be interpreted that if we encounter the compressed flag but can't know the encoding (no header provided or just identity) we should fail with internal error (instead of assuming gzip or accepting identity + compressed flag)

@davidpdrsn
Copy link
Member

Essentially the headers are giving the information on "how would it be compressed if it was compressed" and then each message (there can be multiple on a stream) can be compressed or not.

Ah yes. That makes sense! Thanks 😊

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-tonic C-enhancement Category: New feature or request
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants