Skip to content

Commit efcdd38

Browse files
authored
Merge pull request #4822 from p0lyn0mial/kep-watch-list-replacing-client-go-list-method
KEP 3157 (watch-list): replacing standard List request with WatchList mechanism for client-go's List method.
2 parents ec98aa5 + cd1edb7 commit efcdd38

File tree

1 file changed

+68
-0
lines changed
  • keps/sig-api-machinery/3157-watch-list

1 file changed

+68
-0
lines changed

keps/sig-api-machinery/3157-watch-list/README.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ tags, and then generate with `hack/update-toc.sh`.
9292
- [Results with WATCH-LIST](#results-with-watch-list)
9393
- [Required changes for a WATCH request with the RV set to the last observed value (RV > 0)](#required-changes-for-a-watch-request-with-the-rv-set-to-the-last-observed-value-rv--0)
9494
- [Provide a fix for the long-standing issue <a href="https://github.com/kubernetes/kubernetes/issues/59848">https://github.com/kubernetes/kubernetes/issues/59848</a>](#provide-a-fix-for-the-long-standing-issue-httpsgithubcomkuberneteskubernetesissues59848)
95+
- [Replacing standard List request with WatchList mechanism for client-go's List method.](#replacing-standard-list-request-with-watchlist-mechanism-for-client-gos-list-method)
9596
- [Test Plan](#test-plan)
9697
- [Prerequisite testing updates](#prerequisite-testing-updates)
9798
- [Unit tests](#unit-tests)
@@ -581,6 +582,73 @@ Then on the server side we:
581582
3. reject the request if waitUntilFreshAndBlock times out, thus forcing informers to retry.
582583
4. otherwise, construct the final list and send back to a client.
583584

585+
### Replacing standard List request with WatchList mechanism for client-go's List method.
586+
587+
Replacing the underlying implementation of the List method for client-go based clients (like typed or dynamic client)
588+
with the WatchList mechanism requires ensuring that the data returned by both the standard List request and
589+
the new WatchList mechanism remains identical. The challenge is that WatchList no longer retrieves the entire
590+
list from the server at once but only receives individual items, which forces us to "manually" reconstruct
591+
the list object on the client side.
592+
593+
To correctly construct the list object on the client side, we need ListKind information.
594+
However, simply reconstructing the list object based on these data is not enough.
595+
In the case of a standard List request, the server's response (a versioned list) is processed through a chain of decoders,
596+
which can potentially modify the resulting list object.
597+
A good example is the WithoutVersionDecoder, which removes the GVK information from the list object.
598+
Thus the "manually" constructed list object may not be consistent
599+
with the transformations applied by the decoders, leading to differences.
600+
601+
To ensure full compatibility, the server must provide a versioned empty list in the format requested by the client (e.g., protobuf representation).
602+
We don't know how the client's decoder behaves for different encodings, i.e., whether the decoder actually supports
603+
the encoding we intend to use for reconstruction. Therefore, to ensure maximal compatibility, we will ensure that
604+
the encoding used for the reconstruction of the list matches the format that the client originally requested.
605+
This guarantees that the returned list object can be correctly decoded by the client,
606+
preserving the actual encoding format as intended.
607+
608+
The proposed solution is to add a new annotation (`k8s.io/initial-events-list-blueprint`) to the object returned
609+
in the bookmark event (The bookmark event is sent when the state is synced and marks the end of WatchList stream).
610+
This annotation will store an empty, versioned list encoded as a Base64 string.
611+
This annotation will be added to the same object/place the `k8s.io/initial-events-end` annotation is added.
612+
613+
When the client receives such a bookmark, it will base64 decode the empty list and pass it to the decoder chain.
614+
Only after a successful response from the decoders the list will be populated with data received from subsequent
615+
watch events and returned.
616+
617+
For example:
618+
```
619+
GET /api/v1/namespaces/test/pods?watch=1&sendInitialEvents=true&allowWatchBookmarks=true&resourceVersion=&resourceVersionMatch=NotOlderThan
620+
---
621+
200 OK
622+
Transfer-Encoding: chunked
623+
Content-Type: application/json
624+
625+
{
626+
"type": "ADDED",
627+
"object": {"kind": "Pod", "apiVersion": "v1", "metadata": {"resourceVersion": "8467", "name": "foo"}, ...}
628+
}
629+
{
630+
"type": "ADDED",
631+
"object": {"kind": "Pod", "apiVersion": "v1", "metadata": {"resourceVersion": "5726", "name": "bar"}, ...}
632+
}
633+
{
634+
"type":"BOOKMARK",
635+
"object":{"kind":"Pod","apiVersion":"v1","metadata":{"resourceVersion":"13519","annotations":{"k8s.io/initial-events-end":"true","k8s.io/initial-events-embedded-list":"eyJraW5kIjoiUG9kTGlzdCIsImFwaVZlcnNpb24iOiJ2MSIsIm1ldGFkYXRhIjp7fSwiaXRlbXMiOm51bGx9Cg=="}} ...}
636+
}
637+
...
638+
<followed by regular watch stream starting>
639+
```
640+
641+
**Alternatives**
642+
643+
We could modify the type of the object passed in the last bookmark event to include the list.
644+
This approach would require changes to the reflector, as it would need to recognize the new object type in the bookmark event.
645+
However, this could potentially break other clients that are not expecting a different object in the bookmark event.
646+
647+
Another option would be to issue an empty list request to the API server to receive a list response from the client.
648+
This approach would involve modifying client-go and implementing some form of caching mechanism,
649+
possibly with invalidation policies.
650+
Non-client-go clients that want to use this new feature would need to rebuild this mechanism as well.
651+
584652
### Test Plan
585653
<!--
586654
**Note:** *Not required until targeted at a release.*

0 commit comments

Comments
 (0)