@@ -92,6 +92,7 @@ tags, and then generate with `hack/update-toc.sh`.
92
92
- [ Results with WATCH-LIST] ( #results-with-watch-list )
93
93
- [ 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 )
94
94
- [ 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 )
95
96
- [ Test Plan] ( #test-plan )
96
97
- [ Prerequisite testing updates] ( #prerequisite-testing-updates )
97
98
- [ Unit tests] ( #unit-tests )
@@ -581,6 +582,73 @@ Then on the server side we:
581
582
3 . reject the request if waitUntilFreshAndBlock times out, thus forcing informers to retry.
582
583
4 . otherwise, construct the final list and send back to a client.
583
584
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
+
584
652
### Test Plan
585
653
<!--
586
654
**Note:** *Not required until targeted at a release.*
0 commit comments