Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.
Sign upProvide support for caching GRPC method response #7945
Comments
makdharma
added
the
kind/enhancement
label
Sep 1, 2016
makdharma
self-assigned this
Sep 1, 2016
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
makdharma
Sep 1, 2016
Contributor
gRPC Caching Design
Author: @makdharma
State: draft
Contents
[[TOC]]
Background
Caching of resources is used in HTTP to improve performance of websites and web-apps. gRPC is an alternative for HTTP/REST with many clear advantages. However lack of caching support in gRPC is a hindrance to adoption where caching performance matters. This document evaluates options and suggests an approach to mitigate this disadvantage.
Design and Implementation Goals
Implementation Goals
- Caching solution for last mile of compute (mobile, desktop, web)
- Reducing load on the server by using caching where appropriate
- Improve user experience by reducing latency of fetch requests
Design goals
We would like the design choice to accommodate following future goals. Actual timeline to implement these will be driven by customer requirements.
- Offline operation
- Implementation of on-device (private) cache
- Caching in the data center between VMs
- Streaming RPC support
Potential solutions
Terminology
gRPC request originates from the client application. Client application may maintain an on-device private cache. The requests traverses through various proxies and reaches the reverse proxy (GFE). Reverse proxy sends the request to the Application server. Application server sends a response. The response may be cached by any and all proxies on the response path.
Option 1 - HTTP Cache
gRPC is based on HTTP/2 which already supports caching. Use HTTP’s caching mechanism by using GET instead of POST verb for gRPC request. Typical interaction will follow these steps:
- Client application starts a gRPC call
- gRPC library uses GET instead of POST
- The request payload is base64 encoded and sent as query string
- A proxy looks for the response in the cache.
- If found in the cache, the response is sent by the proxy directly
- Else, the request is forwarded to the server and the response is cached
- gRPC library returns the response to the application.
Option 2 - Native Cache
Implement a native cache in the proxy that understands gRPC protocol. Add a component to the proxy that can parse gRPC requests, and generate native gRPC response. Typical interaction will follow these steps:
- Client application starts a gRPC call
- gRPC client library computes a cache lookup key and sends it as part of HTTP header
- A proxy parses the request. If cache lookup key exists, proxy uses it to search the cache.
- If found in the cache, the response is sent by the proxy directly
- Else, the request is forwarded to the server and the response is cached
- gRPC library returns the response to the application.
Other options investigated
- GET with payload - Possible but non standard. Not supported by GFE.
- SEARCH/FETCH/REPORT verbs - These features are not widely adopted. It might become a feasible option in future. When that happens we can deprecate GET based caching.
- Cacheable POST - It is possible to cache result of a POST request which can be subsequently accessed by a GET request with the same parameters as POST. This looks appealing but involves complex design for choosing between POST or GET on the client.
Pros and Cons
HTTP Cache Pros:
- HTTP standard compliant
- Works everywhere (No Google specific changes)
- Make use of mature and well understood technology
HTTP Cache Cons:
- Base64 encoding increases request size, leading to inefficient use of upstream bandwidth.
- Proxies impose limit on URI size. GET requests exceeding the URI Size limit will fail. The URI size limit is non-standard and depends on proxy configuration. For example, nginx defaults to 8K.
Native gRPC Cache Pros:
- No limitation on the URI size
- Efficient use of bandwidth, because no need to base64 encoding.
Native gRPC Cache Cons:
- Similar to, but different solution than HTTP caching.
- Needs to be implemented for all major proxies.
Given the open source adoption as a stated goal, HTTP caching based solution seems like the right choice. The two cons - size bloat and limitation on URI size - are problems in theory but not in practice. Typical use case of caching in gRPC is for fetching static resources. Such requests don’t have lot of parameters, therefore not likely to hit the URI size limitation.
Rest of this document explores the HTTP Caching solution.
Design Goals
The choice of HTTP Caching works well for the design goals.
- Offline operation: Having on-device cache is the enabler for offline operation.
- Implementation of on-device (private) cache: Cache control headers have built in support for both on-device (private) and proxy (public) caches. Implementation of a on-device cache is out of scope for this document.
- Caching in the data center between VMs: There is nothing special about this use case compared with a standard client-server caching use case, except maybe the support for streaming RPC caching.
- Streaming RPC caching support: Works out of the box for half-duplex RPCs. Typical use case is that of server streaming for large file fetch. A proxy can cache all responses in the stream and send it as one response when it sees a GET request.
Caching Considerations
Cacheable methods
Only the service author knows
- Whether to cache the response at all,
- For how long the response should be cached
If a method is deemed safe and idempotent the response can be cached. The proposal is as follows:
- Service author marks methods as cacheable using proto annotation
- gRPC client will use GET instead of POST for cacheable gRPC requests
- Expose an API in gRPC server to set the cache headers at runtime.
- Expose an API in the client to turn on/off cacheable requests, and acceptable cache age limit.
RPC failure and retry mechanism
An RPC using GET can fail in ways different from POST listed below. The reasons for failure may be transient or nontransient (such as configuration problems).:
- Transient - Cache warnings that are treated as errors
- Nontransient - Request exceeds maximum size
- Nontransient - gRPC server does not support caching
How to react to failures? Options:
- Automatic retry with POST (disable caching)
- Let the application decide. Application might retry the RPC for transient failures, or disable caching for non-transient failures. A binary-wide API is provided to disable caching.
Proposal: Go with Option 2 in the interest of giving maximum visibility and control to the application.
API Additions
Implementation details TBD.
Server side API addition
- Function to Set cache control header. If Server doesn’t call this API explicitly then caching is disabled for this method by setting appropriate cache control headers. For example, Cache-Control: no-cache, no-store, must-revalidate
Client side API addition
- Function to disable caching completely for all methods. The actual API signature will depend upon wrapped language implementation.
Cache validators
Cache validators such as ETag (entity tag) or "if-modified-since" or “last-modified” header fields are useful to validate a locally stored response. Since there is no on-device cache, cache validators are not used by gRPC client. It is however possible that a caching proxy along the request path may use validator.
Cache Control Headers
Cache-Control headers set by client
- max-age: Indicates that the client is willing to accept a response whose age is no greater than the specified time in seconds. The value for max-age comes from application. It is set on per request.
Cache-Control headers set by server
- public:
- must-revalidate: When the must-revalidate directive is present in a response received by a cache, that cache MUST NOT use the entry after it becomes stale to respond to a subsequent request without first revalidating it with the origin server. (I.e., the cache MUST do an end-to-end revalidation every time, if, based solely on the origin server's Expires or max-age value, the cached response is stale.) We do not want third party caches along the way to make their own judgment about freshness of a cache entry. Server gets the final word in determining when cache entry needs to be thrown out.
- no-transform: Therefore, if a message includes the no-transform directive, an intermediate cache or proxy MUST NOT change those headers that are listed in section 13.5.2 as being subject to the no-transform directive. This implies that the cache or proxy MUST NOT change any aspect of the entity-body that is specified by these headers, including the value of the entity-body itself. This is just to be on the safe side.
- no-cache: This will disable caching of a response.
Cache Warnings and Errors
Given the strict requirements set by client and server cache control headers, we do not expect to receive warnings in the normal course of operation. Any warning is treated as error and the RPC fails.
Security and Privacy
Open question: Should caching be allowed for authenticated request responses? Proposed answer: No. This is to avoid sensitive information cached by mistake in public caches. It can be a great follow on feature though as the solution matures.
Compatibility
Newer server (with caching support) and older client (no caching support)
If the client is old and does not support caching, then nothing changes from the client’s perspective. Client never originates a GET request and server never responds with cache control headers.
Older server (no caching support) and newer client (with caching support)
Client will generate GET request for cacheable methods. Server will respond with error. Application will disable caching.
Proposed Method Annotations
ENUM_IDEMPOTENT, ENUM_IDEMPOTENT_AND_SAFE are enumerated values.
- Idempotency
- Syntax: option method_flags = ENUM_IDEMPOTENT;
- Description: Any method that can be called more than once with the same result.
- Safe
- Syntax: option method_flags = ENUM_IDEMPOTENT_AND_SAFE;
- Description: Any idempotent method without side effects.
Expected behavior
| Safe | Idempotent | GRPC behavior | Use Case |
| Yes | - | Client: Maybe use GET verb. Server: Enable caching of response by adding cache control headers in the response | Use this annotation to designate a method cacheable. |
| No | Yes | Client: Maybe use PUT verb. Server: No change in behavior. PUT request is processed just like a POST request. | Use this annotation for enabling 0-RTT (QUIC) on clients using Cronet stack |
| No | No | Client: no change Server: no change | This is the default gRPC implementation |
Example
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {
option method_flags = ENUM_IDEMPOTENT_AND_SAFE;
}
}GRPC Wire Protocol Change
- Request → (Request-Headers *Delimited-Message / Cachable-Request-Headers) EOS
- Cachable-Request-Headers → Request-Headers *Encoded-Message
- Call-Definition → Method Scheme Path TE [Authority] [Timeout] [Content-Type] [Message-Type] [Message-Encoding] [Message-Accept-Encoding] [User-Agent][Cache-Control]
- method → ":method (POST/GET)"
- Cache-Control → "max-age" / [“public” “must-revalidate” “no-transform”]
- Encoded-Request → "grpc-encoded-request" {base64 encoded value}
|
gRPC Caching Design Author: @makdharma State: draft Contents[[TOC]] BackgroundCaching of resources is used in HTTP to improve performance of websites and web-apps. gRPC is an alternative for HTTP/REST with many clear advantages. However lack of caching support in gRPC is a hindrance to adoption where caching performance matters. This document evaluates options and suggests an approach to mitigate this disadvantage. Design and Implementation GoalsImplementation Goals
Design goalsWe would like the design choice to accommodate following future goals. Actual timeline to implement these will be driven by customer requirements.
Potential solutionsTerminologygRPC request originates from the client application. Client application may maintain an on-device private cache. The requests traverses through various proxies and reaches the reverse proxy (GFE). Reverse proxy sends the request to the Application server. Application server sends a response. The response may be cached by any and all proxies on the response path. Option 1 - HTTP CachegRPC is based on HTTP/2 which already supports caching. Use HTTP’s caching mechanism by using GET instead of POST verb for gRPC request. Typical interaction will follow these steps:
Option 2 - Native CacheImplement a native cache in the proxy that understands gRPC protocol. Add a component to the proxy that can parse gRPC requests, and generate native gRPC response. Typical interaction will follow these steps:
Other options investigated
Pros and ConsHTTP Cache Pros:
HTTP Cache Cons:
Native gRPC Cache Pros:
Native gRPC Cache Cons:
Given the open source adoption as a stated goal, HTTP caching based solution seems like the right choice. The two cons - size bloat and limitation on URI size - are problems in theory but not in practice. Typical use case of caching in gRPC is for fetching static resources. Such requests don’t have lot of parameters, therefore not likely to hit the URI size limitation. Rest of this document explores the HTTP Caching solution. Design GoalsThe choice of HTTP Caching works well for the design goals.
Caching ConsiderationsCacheable methodsOnly the service author knows
If a method is deemed safe and idempotent the response can be cached. The proposal is as follows:
RPC failure and retry mechanismAn RPC using GET can fail in ways different from POST listed below. The reasons for failure may be transient or nontransient (such as configuration problems).:
How to react to failures? Options:
Proposal: Go with Option 2 in the interest of giving maximum visibility and control to the application. API AdditionsImplementation details TBD. Server side API addition
Client side API addition
Cache validatorsCache validators such as ETag (entity tag) or "if-modified-since" or “last-modified” header fields are useful to validate a locally stored response. Since there is no on-device cache, cache validators are not used by gRPC client. It is however possible that a caching proxy along the request path may use validator. Cache Control HeadersCache-Control headers set by client
Cache-Control headers set by server
Cache Warnings and ErrorsGiven the strict requirements set by client and server cache control headers, we do not expect to receive warnings in the normal course of operation. Any warning is treated as error and the RPC fails. Security and PrivacyOpen question: Should caching be allowed for authenticated request responses? Proposed answer: No. This is to avoid sensitive information cached by mistake in public caches. It can be a great follow on feature though as the solution matures. CompatibilityNewer server (with caching support) and older client (no caching support)If the client is old and does not support caching, then nothing changes from the client’s perspective. Client never originates a GET request and server never responds with cache control headers. Older server (no caching support) and newer client (with caching support)Client will generate GET request for cacheable methods. Server will respond with error. Application will disable caching. Proposed Method AnnotationsENUM_IDEMPOTENT, ENUM_IDEMPOTENT_AND_SAFE are enumerated values.
Expected behavior
Exampleservice Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {
option method_flags = ENUM_IDEMPOTENT_AND_SAFE;
}
}GRPC Wire Protocol Change
|
ejona86
referenced this issue
Nov 7, 2016
Open
Tracking Issue for Idempotence being Experimental. #1775
soltanmm-google
added
the
disposition/FOR DISCUSSION
label
Nov 16, 2016
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
rvolosatovs
commented
Jul 20, 2017
|
any progress on this? |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
makdharma
Jul 21, 2017
Contributor
Hi - The code is mostly checked in and working. what is your use case? what languages will you be using for server and client?
|
Hi - The code is mostly checked in and working. what is your use case? what languages will you be using for server and client? |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
rvolosatovs
Jul 22, 2017
We would use it in https://github.com/TheThingsNetwork/ttn, an IoT network.
Both client and server use Go.
cc @htdvisser
rvolosatovs
commented
Jul 22, 2017
|
We would use it in https://github.com/TheThingsNetwork/ttn, an IoT network. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
makdharma
Jul 24, 2017
Contributor
caching works for Java and C stacks. Go support is not there yet unfortunately.
|
caching works for Java and C stacks. Go support is not there yet unfortunately. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
rmichela
commented
Jul 28, 2017
|
When will cache support become available from protoc generated grpc stubs? |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
ejona86
Aug 17, 2017
Member
It looks like protoc got support with a slightly different syntax than mentioned above:
https://github.com/google/protobuf/blob/9e745f7/src/google/protobuf/descriptor.proto#L630
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {
option idempotency_level = NO_SIDE_EFFECTS;
}
}|
It looks like protoc got support with a slightly different syntax than mentioned above: service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {
option idempotency_level = NO_SIDE_EFFECTS;
}
} |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
|
Should this be added as a gRFC in the proposal repo? |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
m-sasha
Jan 22, 2018
Is there an update on this? Can we start using it if we have Go servers and Android/iOS clients?
m-sasha
commented
Jan 22, 2018
|
Is there an update on this? Can we start using it if we have Go servers and Android/iOS clients? |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
ericgribkoff
Jan 22, 2018
Contributor
@m-sasha This feature is experimental but operational for Android and iOS clients (see our interop test clients for examples of configuring RPCs to use GET). As far as I know, responding to GET requests is currently only supported by C++ on the server side. We will be converting this into a gRFC in the next couple weeks, at which point we will be better able to estimate when support will be available for servers in the other languages.
|
@m-sasha This feature is experimental but operational for Android and iOS clients (see our interop test clients for examples of configuring RPCs to use GET). As far as I know, responding to GET requests is currently only supported by C++ on the server side. We will be converting this into a gRFC in the next couple weeks, at which point we will be better able to estimate when support will be available for servers in the other languages. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
ejona86
Jan 22, 2018
Member
@m-sasha, to be clear, "experimental" means we are still free to do whatever with it, including changing the protocol. You can play with it and provide feedback, but it shouldn't get anywhere near production.
|
@m-sasha, to be clear, "experimental" means we are still free to do whatever with it, including changing the protocol. You can play with it and provide feedback, but it shouldn't get anywhere near production. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
m-sasha
commented
Jan 23, 2018
|
I understand. Thanks! |
makdharma
added
lang/c++
priority/P3
labels
May 15, 2018
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
yazsh
Aug 18, 2018
Has there been any update on this?
In particular for the golang client implementation? My team would be very interested in using it.
yazsh
commented
Aug 18, 2018
|
Has there been any update on this? In particular for the golang client implementation? My team would be very interested in using it. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
phemmer
Aug 24, 2018
Just to throw in another case for consideration. We're considering whether it would be possible to use HTTP/2 server-push for gRPC services. The idea being the typical HTTP/2 server-push where we want to preempt the client asking for related resources.
Thus this is very tightly related to the caching issue, as the pushed message sits in a "push cache" on the client, and needs to be uniquely identified by the URL. Also in the case of cross-service server-pushes, we might identify the related resource in a Link header, and want the intermediary proxy to transform this Link header into a server push (if the other service identified in the link sits behind the proxy), and thus the URL needs to uniquely identify the resource, and instruct how to build the gRPC request (but would assume the proxy has a copy of the proto spec).
The fortunate thing is that the "Option 1 - HTTP Cache" solution would address this just fine, and this appears to be the solution being pursued.
So I don't think any action is needed here, just wanted to mention the use case in case it does have any impact to the implementation.
phemmer
commented
Aug 24, 2018
•
|
Just to throw in another case for consideration. We're considering whether it would be possible to use HTTP/2 server-push for gRPC services. The idea being the typical HTTP/2 server-push where we want to preempt the client asking for related resources. The fortunate thing is that the "Option 1 - HTTP Cache" solution would address this just fine, and this appears to be the solution being pursued. So I don't think any action is needed here, just wanted to mention the use case in case it does have any impact to the implementation. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
phemmer
Aug 31, 2018
Oh, and for this:
Open question: Should caching be allowed for authenticated request responses? Proposed answer: No. This is to avoid sensitive information cached by mistake in public caches. It can be a great follow on feature though as the solution matures.
This is the purpose of the private and proxy-revalidate headers.
These would allow the response to be cached locally on the client, but not by an intermediary (shared) proxy.
phemmer
commented
Aug 31, 2018
•
|
Oh, and for this:
This is the purpose of the |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
ejona86
Aug 31, 2018
Member
private is more appropriate than proxy-revalidate, because we know we don't really optimize the revalidate case. So it would be better for the proxy to avoid storing the value in the cache at all. With proxy-revalidate the proxy still caches the response.
|
|
makdharma commentedSep 1, 2016
No description provided.