-
Notifications
You must be signed in to change notification settings - Fork 428
Add support for compression on the client #707
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
Conversation
Motivation: Compression support is an optional part of the gRPC specification but ideally should be supported. Users have also requested this feature (grpc#611). Since introducing compression without breaking API is difficult we should do this before we tag 1.0.0. Modifications: - Add support for gzip and deflate compression on the client. - Introduce new configuration for supporting compression. - Add interop tests for the client. Result: Client supports compression.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good so far. A couple notes:
- I have not reviewed
Zlib.swift
, especially not wrt memory safety and leakage - It looks like the interop tests don't decompress anything; I assume that's expected because the other endpoint would be responsible for that?
- You could also add tests for
Zlib
, but I guess that is integration-tested byCompressorTests
.
@@ -52,6 +52,7 @@ public final class ServerStreamingCall<RequestMessage: Message, ResponseMessage: | |||
path: path, | |||
host: connection.configuration.target.host, | |||
requestID: requestID, | |||
encoding: connection.configuration.compression, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we unify the naming of encoding vs compression?
Sources/GRPC/ClientConnection.swift
Outdated
/// `sendMessage(_:disableCompression)`, `sendMessage(_:disableCompression:promise)`, | ||
/// `sendMessages(_:disableCompression)` or `sendMessages(_:disableCompression:promise)`. | ||
/// | ||
/// Note that any disabling compression has no effect if compression is disabled on the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: "any disabling compression" sounds weird
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops, yes, I don't think the any
is meant to be there.
Sources/GRPC/ClientOptions.swift
Outdated
@@ -46,18 +46,24 @@ public struct CallOptions { | |||
/// messages associated with the call. | |||
public var requestIDHeader: String? | |||
|
|||
/// Disables request compression on this RPC. Ignored if compression is disabled at the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: "on this call"?
public internal(set) var outbound: CompressionAlgorithm? | ||
|
||
/// The set of compression algorithms advertised to the remote peer that they may use. | ||
public internal(set) var inbound: [CompressionAlgorithm] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not super happy with these names, neither "requests/responses" nor "outbound/inbound". How about "accepted" and "outbound"? Or something more analogous to the "accept-encoding" headers? Not 100% sure myself. Thinking about this more, maybe the names are actually good.
Also, does gRPC support using different compressions in both directions on one stream? Or is this more like the "preferred" outbound method?
Overall, I'm a bit confused by this so far 😅
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agh...naming here is tricky. To the user requests
/responses
seems more natural for the init
than inbound
/outbound
, but then responses
doesn't quite fit the "accept-encoding" part. Also compression.requests
/compression.responses
isn't as clear as compression.outbound
, compression.inbound
.
The init
could be more explicit:
init(forRequests outbound: CompressionAlgorithm?, acceptableForResponses inbound: [CompressionAlgorithm]?) {
// ...
}
...
MessageEncoding(forRequests: .gzip, acceptableForResponses: [.gzip, .identity])
I'm still not certain how this API should be spelt! Thoughts?
Also, does gRPC support using different compressions in both directions on one stream? Or is this more like the "preferred" outbound method?
Compression for the request and response stream are independent and may be different.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One other thing I've been considering recently which might make things easier for us and users is some sort of client bootstrap: we can be more descriptive with names and add to it without breaking API (with structs we need a new init
if we add a property).
let bootstrap = GRPCConnectionBootstrap(group: group) // hmm sounds familiar...
.requestCompression(.gzip)
.acceptableResponseCompression(.gzip, .deflate, .identity)
Anyway, that's a story for another time!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
MessageEncoding(forRequests: .gzip, acceptableForResponses: [.gzip, .identity])
SGTM. I also like the Bootstrap idea.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll leave the bootstrap for another day since that's a fairly large change.
public static let none = MessageEncoding(requests: nil, responses: [.identity]) | ||
|
||
/// Accept all supported compression on responses, do not compress requests. | ||
public static let onlyResponses = MessageEncoding(requests: nil, responses: CompressionAlgorithm.all) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: responsesOnly
(up to you)
} | ||
|
||
extension ClientConnection.Configuration.MessageEncoding { | ||
var acceptEncoding: String { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
acceptEncodingHeader
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the review @MrMage, I'll fix your nits etc when I push up another revision.
Just wanted to check you saw Sources/GRPC/Compression/Zlib.swift
? (I noticed there were no comments on it and GitHub hid the diff for me.)
Sources/GRPC/ClientConnection.swift
Outdated
/// `sendMessage(_:disableCompression)`, `sendMessage(_:disableCompression:promise)`, | ||
/// `sendMessages(_:disableCompression)` or `sendMessages(_:disableCompression:promise)`. | ||
/// | ||
/// Note that any disabling compression has no effect if compression is disabled on the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops, yes, I don't think the any
is meant to be there.
public internal(set) var outbound: CompressionAlgorithm? | ||
|
||
/// The set of compression algorithms advertised to the remote peer that they may use. | ||
public internal(set) var inbound: [CompressionAlgorithm] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agh...naming here is tricky. To the user requests
/responses
seems more natural for the init
than inbound
/outbound
, but then responses
doesn't quite fit the "accept-encoding" part. Also compression.requests
/compression.responses
isn't as clear as compression.outbound
, compression.inbound
.
The init
could be more explicit:
init(forRequests outbound: CompressionAlgorithm?, acceptableForResponses inbound: [CompressionAlgorithm]?) {
// ...
}
...
MessageEncoding(forRequests: .gzip, acceptableForResponses: [.gzip, .identity])
I'm still not certain how this API should be spelt! Thoughts?
Also, does gRPC support using different compressions in both directions on one stream? Or is this more like the "preferred" outbound method?
Compression for the request and response stream are independent and may be different.
public internal(set) var outbound: CompressionAlgorithm? | ||
|
||
/// The set of compression algorithms advertised to the remote peer that they may use. | ||
public internal(set) var inbound: [CompressionAlgorithm] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One other thing I've been considering recently which might make things easier for us and users is some sort of client bootstrap: we can be more descriptive with names and add to it without breaking API (with structs we need a new init
if we add a property).
let bootstrap = GRPCConnectionBootstrap(group: group) // hmm sounds familiar...
.requestCompression(.gzip)
.acceptableResponseCompression(.gzip, .deflate, .identity)
Anyway, that's a story for another time!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good overall, have given Zlib.swift
a cursory look overall now. (No time to study the Zlib docs.)
Sources/GRPC/Compression/Zlib.swift
Outdated
self.end() | ||
} | ||
|
||
/// Decompresses the data in `input` into the `output` buffer. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wait, shouldn't this method be about compressing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch, thanks!
Missed this before:
They do decompress in the
That's right. I didn't think there was much value in adding them. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Still LGTM :-)
… On 29. Jan 2020, at 13:39, George Barnett ***@***.***> wrote:
@MrMage just pushed up a small patch, the compression documentation isn't clear in some places but working on the server part of this made me realise that something wasn't quite right. Here's the change: ffe5bd4
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or unsubscribe.
|
Motivation:
Compression support is an optional part of the gRPC specification but
ideally should be supported. Users have also requested this feature
(#611).
Since introducing compression without breaking API is difficult we
should do this before we tag 1.0.0.
Modifications:
Result:
Client supports compression.