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

Gzip compress gRPC responses #3387

Closed

Conversation

zpingcai
Copy link
Contributor

@zpingcai zpingcai commented Mar 19, 2025

Description

Currently, all gRPC responses are not compressed. This is not optimal for endpoints with huge responses as compression may save us a decent amount of network resource. This PR adds support to compress gRPC responses using gzip, which can be enabled by setting WebConfig.grpcGzip to true.

Testing Strategy

Related Work

Rollout Strategy and Risk Mitigation

  • WebConfig.grpcGzip is false by default, so users won't see surprises when they upgrade misk. They can manually turn it on if needed.

Checklist

  • I have reviewed this PR with relevant experts and/or impacted teams.
  • I have added tests to have confidence my changes work as expected.
  • I have a rollout plan that minimizes risks and includes monitoring for potential issues.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Simply copies the changes from square/wire@17a3d9e

Comment on lines +172 to +173
requestAdapter = "com.squareup.protos.test.grpc.HelloRequest#ADAPTER",
responseAdapter = "com.squareup.protos.test.grpc.HelloReply#ADAPTER"
Copy link
Contributor Author

Choose a reason for hiding this comment

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

drive-by: fix the test setup

/**
* If true gRPC responses which are larger than the minGzipSize will be compressed.
*/
val grpcGzip: Boolean = false,
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this instead be an enum to allow for the other wire-supported encoding?

Suggested change
val grpcGzip: Boolean = false,
val grpcEncoding: GrpcEncoding = GrpcEncoding.IDENTITY,

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yes, I'd lean towards the encoding enum, not just a boolean

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not sure if it's actually worth it:

  1. The client only supports identity and gzip: https://github.com/square/wire/blob/master/wire-grpc-client/src/commonMain/kotlin/com/squareup/wire/internal/GrpcDecoder.kt
  2. The http response compression only supports gzip:
    val gzip: Boolean = true,
  3. We also need to introduce WebConfig.minGrpcMessageToCompress instead of reusing WebConfig.minGzipSize.

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 think the current implementation makes more sense to the user:

  1. Why do you configure grpcEncoding to anything that's not supported?
  2. Why do you want different configs for min compression size for http vs grpc over http?

cc @Hexcles

Copy link
Collaborator

Choose a reason for hiding this comment

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

Personally, I'd say @zpingcai 's current impl is better in terms of consistency.

grpcGzip makes a lot of sense along with gzip. If we want grpcEncoding, I'd expect to have an enum for plain old HTTP, too. And in fact, not just an enum but a config object as different compression algos have different knobs (minGzipSize obviously doesn't make sense for non-gzip compression algos).

Copy link
Collaborator

@adrw adrw left a comment

Choose a reason for hiding this comment

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

LG

sink.writeByte(0) // 0 = Not encoded.
sink.writeInt(encodedMessage.size.toInt())
sink.writeAll(encodedMessage)
} else {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should we explicitly check that gzip type is set instead of this being the else clause? Maybe else should throw unsupported operation exception or something like that.

Copy link
Contributor Author

@zpingcai zpingcai Mar 20, 2025

Choose a reason for hiding this comment

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

I don't think that's needed. The grpcEncoding.toGrpcEncoder() call will throw if the encoding is not supported:

internal fun String.toGrpcEncoder(): GrpcEncoder {

/**
* If true gRPC responses which are larger than the minGzipSize will be compressed.
*/
val grpcGzip: Boolean = false,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Yes, I'd lean towards the encoding enum, not just a boolean

writer.write(HelloRequest("localhost"))
writer.close()

assertEquals(
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you add an assertion on the min size and the actual size? Not just a snapshot assertion on expected output (still keep that but add the extra assertion).

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 actual size is asserted in helloRequestSize, but I can move it here if that makes the test more readable.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added. I simply repeated the same assertion multiple times:

val message = HelloRequest("localhost")
val encodedMessage = HelloRequest.ADAPTER.encode(message)
assertEquals(encodedMessage.size, 11)

Can definitely create a helper function if needed.

Comment on lines +131 to +135
log.log(
level = mapper.loggingLevel(th),
th = th,
tags = *mdcTags.toTypedArray(),
) { "exception dispatching to $actionName" }
Copy link
Contributor Author

Choose a reason for hiding this comment

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

IDE auto formatted this because the line is too long.

@zpingcai zpingcai requested review from Nava2 and adrw March 20, 2025 18:07
@zpingcai
Copy link
Contributor Author

merged as cd10157

@zpingcai zpingcai closed this Mar 31, 2025
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.

5 participants