-
Notifications
You must be signed in to change notification settings - Fork 9.8k
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
Proxy: Add watchers for keys in cache #14665
Comments
Just to clarify. Your setup looks like this: graph TD;
YourCode-->|grpc|gRPCProxy;
gRPCProxy-->|http|etcd;
then "YourCode" writes some key/value, let's say |
I have client and several proxy nodes: graph TD;
YourCode-->|grpc|gRPCProxy1;
YourCode-->|grpc|gRPCProxy2;
gRPCProxy1-->|http|etcd;
gRPCProxy2-->|http|etcd;
Let's imagine I have key |
Thanks for the clarification, is the issue that you read stale values from As far as I understand the proxy code it will only return serializable results from the cache and save the last response as such (basically a write-through cache): |
So, yes, I think that stale reads from Proxy2 is the issue. You're right, stale reads are possible only with "serializable" mode. And it's true for direct connection toward etcd cluster. |
Hi guys, I looked into the code, and unfortunately it's not as easy as I wish it to be. The main complications is that watchers (that are served client "watch" requests) deeply integrated with grpc, i.e. watchers inside grpc-proxy build around grpc streams. Thinking how cache watchers may be implemented with least refactoring as possible, I came up with the following solution. It's not perfect, it's not the most beauty one, but it should work. How it works now
Proposal
Overhead: LRU cache may store at most 1k values, relation is linear, so it could be up to 1k watchers. Why watcher invalidates cache, but not update value: because cache stores range itself, but not key or keys list. By the watcher event we know only one key, but don't know the whole response for the range request. Following stepsI can't say for sure that I'll have enough time to implement it, but I want to at least discuss this architecture with you. Maybe one of my successor will be interested in this task, it would be helpful to have answer "how it should be done". Case 1: range is not watched, is not in cachesequenceDiagram
participant cli as client
participant kv as kvproxy
participant wat as watchers
participant cc as cache
participant cl as etcd cluster
Note over kv, cc: Packages inside running grpc-proxy
cli ->> kv: get range
kv ->>+ wat: run for range
wat ->> cl: watch for range
cl -->> wat: success/fail
wat -->> kv: success/fail
kv ->> cl: get range
cl -->> kv: range resp
Note over kv: start point for diagram in case 3
kv ->> cc: set range resp
cc -->> kv: ok
kv -->> cli: range resp
Case 2: New event in watcher sequenceDiagram
participant kv as kvproxy
participant wat as watchers
participant cc as cache
participant cl as etcd cluster
cl --) wat: event
wat ->> cc: invalidate
wat ->> wat: stop watcher for range
Case 3: New range discards oldest from LRU cache sequenceDiagram
participant kv as kvproxy
participant wat as watchers
participant cc as cache
kv ->> cc: set range resp
cc -->> kv: ok, but range X was discarded
kv ->> wat: stop watcher for X
|
It's a real issue to me. A simpler solution is to use a cache which supports expiration, such as go-cache, and grpcproxy can expose a flag to let users to configure the expiration time. It isn't perfect, but it's simpler and it should be minor change. |
Seems good as a fast and simple decision. Let's discuss cache lib then.
At this point I would recommend to use https://github.com/jellydator/ttlcache without background goroutine (values will be discarded by new values (LRU) or overwritten by new values). |
cc @ptabor @serathius @spzala for awareness and opinions as well. |
(After looking through the code of the libs once again, I'd suggest to make custom structure for the cache.) |
Hey, |
Sounds like a reasonable change. I am not sure why it isn't implemented in the first place. So two changes:
|
This issue has been automatically marked as stale because it has not had recent activity. It will be closed after 21 days if no further activity occurs. Thank you for your contributions. |
What would you like to be added?
Run watcher inside proxy for every key that is added to the cache.
(Maybe you have some better suggestions, this issue is created for architecture discussion.)
Why is this needed?
I have noticed, that proxy cache doesn't have any TTL for keys or any other options to update values other than by making direct request to the proxy node. Either to get key (with "linearizable" consistency level), or to delete, or to compact.
If key was changed by request through another proxy node or with direct request to the cluster node, stale value would stuck forever.
That is mean that if more than one proxy node in use (or direct requests to cluster nodes are possible), cache could not be used at all (all read requests should be linearizable). The only usecase for proxy in this case is watchers coalescing.
The text was updated successfully, but these errors were encountered: