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

Rename new watcher Event names and remove one that cannot happen #1499

Merged
merged 7 commits into from
May 29, 2024

Conversation

clux
Copy link
Member

@clux clux commented May 23, 2024

Follow-up to the unreleased #1494. Make names more easy to read and clear which one signifies start and which is end:

  1. Event::Init now marks start, it is followed by one/more of:
  2. Event::InitApply xor EventInitPage
  3. Event::InitDone marks init complete

removed Event::RestartDelete because by docs it cannot happen
https://kubernetes.io/docs/reference/using-api/api-concepts/#streaming-lists

Provided that the WatchList feature gate is enabled, this can be achieved by specifying sendInitialEvents=true as query string parameter in a watch request. If set, the API server starts the watch stream with synthetic init events (of type ADDED) to build the whole state of all existing objects followed by a BOOKMARK event (if requested via allowWatchBookmarks=true option). The bookmark event includes the resource version to which is synced. After sending the bookmark event, the API server continues as for any other watch request.

have inserted a big error in case they break their own docs for this alpha feature.

clux added 2 commits May 23, 2024 11:51
1. Event::Init now marks start, it is followed by one/more of:
2. Event::InitApply or EventInitPage
3. Event::Ready marks init complete

removed Event::RestartDelete because by docs it cannot happen:
https://kubernetes.io/docs/reference/using-api/api-concepts/#streaming-lists

have inserted a big error in case they break their own docs for this alpha feature.

Signed-off-by: clux <[email protected]>
Signed-off-by: clux <[email protected]>
@clux clux added the changelog-change changelog change category for prs label May 23, 2024
@clux clux added this to the 0.92.0 milestone May 23, 2024
@clux clux changed the title Rename watcher Event names and remove one that cannot happen Rename new watcher Event names and remove one that cannot happen May 23, 2024
@clux clux marked this pull request as ready for review May 23, 2024 10:58
Copy link

codecov bot commented May 23, 2024

Codecov Report

Attention: Patch coverage is 68.85246% with 19 lines in your changes are missing coverage. Please review.

Project coverage is 75.0%. Comparing base (bd84d65) to head (f551056).

Additional details and impacted files
@@           Coverage Diff           @@
##            main   #1499     +/-   ##
=======================================
+ Coverage   74.9%   75.0%   +0.1%     
=======================================
  Files         78      78             
  Lines       6864    6854     -10     
=======================================
- Hits        5136    5134      -2     
+ Misses      1728    1720      -8     
Files Coverage Δ
kube-runtime/src/controller/mod.rs 29.9% <100.0%> (ø)
kube-runtime/src/reflector/dispatcher.rs 96.2% <100.0%> (ø)
kube-runtime/src/reflector/mod.rs 100.0% <100.0%> (ø)
kube-runtime/src/utils/event_modify.rs 95.5% <100.0%> (ø)
kube-runtime/src/utils/reflect.rs 100.0% <100.0%> (ø)
kube-runtime/src/utils/event_flatten.rs 89.8% <80.0%> (ø)
kube-runtime/src/reflector/store.rs 93.9% <33.4%> (+2.2%) ⬆️
kube-runtime/src/watcher.rs 40.1% <47.9%> (+0.4%) ⬆️

... and 1 file with indirect coverage changes

Copy link
Member

@nightkr nightkr left a comment

Choose a reason for hiding this comment

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

I don't love how this imposes the mental overhead of pagination on all clients (I use raw watchers frequently-ish when I "just" want to build a local cache for some purpose.. they would probably benefit from pagination but building that isn't free :p), but I suppose that's kind of unavoidable without us having to maintain both paginated and buffered interfaces.

kube-runtime/src/watcher.rs Outdated Show resolved Hide resolved
kube-runtime/src/watcher.rs Outdated Show resolved Hide resolved
/// A page of objects was received during the restart.
/// If using the `ListWatch` strategy, this is a relist, and indicates that `InitlPage` events will follow.
/// If using the `StreamingList` strategy, this event will be followed by `InitApply` + `InitDelete` events.
Init,
Copy link
Member

Choose a reason for hiding this comment

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

Do we actually gain much from having this be a separate variant, or would it be enough to say that InitPage/InitApply/Ready implies the start of an init if one is not already happening?

Copy link
Member Author

@clux clux May 23, 2024

Choose a reason for hiding this comment

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

You could do it either way, but it feels slightly cleaner to have an event for it where you can clear any temporary buffer. Otherwise you'd have to check if it's the first page, and only do it then in an if.

...and you also have to do that check in two places if you need to support both ListStrategy (since InitPage and InitApply have different signatures). EDIT: unless your suggestion below works out.

Copy link
Member

Choose a reason for hiding this comment

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

I think it works out kind of naturally to call buffer.or_insert_default() in InitPage and buffer.take() in InitReady (where buffer: Option<_>), but it's a fair point.

Comment on lines +58 to +63
InitPage(Vec<K>),
/// An object received during `Init` with the `StreamingList` strategy.
///
/// Any objects that were previously [`Applied`](Event::Applied) but are not listed in any of the pages
/// should be assumed to have been [`Deleted`](Event::Deleted).
RestartPage(Vec<K>),
/// An object was added or modified during the initial watch.
///
/// This event is only returned when using the `StreamingList` strategy.
RestartApply(K),
/// An object was deleted during the initial watch.
///
/// This event is only returned when using the `StreamingList` strategy.
RestartDelete(K),
/// The restart is complete.
/// Any objects that were previously [`Applied`](Event::Applied) but are not listed in any of
/// the `InitAdd` events should be assumed to have been [`Deleted`](Event::Deleted).
InitApply(K),
Copy link
Member

Choose a reason for hiding this comment

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

These could be merged into one InitPage(SmallVec<[K; 1]>), I think? (Using https://docs.rs/smallvec/latest/smallvec/struct.SmallVec.html)

That way, only users that actually care about the distinction have to care, while you still avoid the allocation overhead if it isn't needed.

Copy link
Member

Choose a reason for hiding this comment

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

Another option would be to extract the pagination strategy to a trait/generic.

Copy link
Member Author

@clux clux May 23, 2024

Choose a reason for hiding this comment

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

That should be possible yes. My only worry about merging the enum variants is that InitPage can go away (in like 3 years) when StreamingList is stabilised and covered in our MK8SV, at which point it's not necessary to have a vector there at all.

The vector part on the receiver is only slightly more verbose though

watcher::Event::InitPage(new_objs) => {
let new_objs = new_objs
.iter()
.map(|obj| (obj.to_object_ref(self.dyntype.clone()), Arc::new(obj.clone())))
.collect::<AHashMap<_, _>>();
self.buffer.extend(new_objs);
}
watcher::Event::InitApply(obj) => {
let key = obj.to_object_ref(self.dyntype.clone());
let obj = Arc::new(obj.clone());
self.buffer.insert(key, obj);
}

Copy link
Member Author

Choose a reason for hiding this comment

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

I think extracting the strategy into a trait somehow could be useful for many reasons though! But would like to iron out deficiencies in shared streams controllers firsts.

Copy link
Member

@nightkr nightkr May 23, 2024

Choose a reason for hiding this comment

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

You shouldn't need to collect into the AHashMap fwiw, Extend::extend takes an iterator rather than a concrete collection. So the smallveced version would end up as "just":

 watcher::Event::InitPage(new_objs) => { 
     let new_objs = new_objs 
         .iter() 
         .map(|obj| (obj.to_object_ref(self.dyntype.clone()), Arc::new(obj.clone()))) ;
     self.buffer.extend(new_objs); 
 }

Alternatively, if we want to lean into InitApply as the path forward, we could also always flatten the list (so emit multiple InitApply when we receive an InitPage event).

Copy link
Member Author

Choose a reason for hiding this comment

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

TIL. Extend trick applied: 4f69a37 👍

Copy link
Member Author

Choose a reason for hiding this comment

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

Alternatively, if we want to lean into InitApply as the path forward, we could also always flatten the list (so emit multiple InitApply when we receive an InitPage event).

Possibly, but then we are introducing buffering again inside the trampoline so that we can loop through the vector.

This does feel a bit like an optimisation, and I wonder if this, along with your other use case you are describing in your main review comment can be minimised instead with an additional reducer ala Event::into_old_style_event_something() that can do some bare-bones buffering for your use case?

@clux
Copy link
Member Author

clux commented May 26, 2024

The clear improvement suggestions herein are applied (have some concerns on InitPage/InitApply merger but we can revisit it if it's net positive), but would be nice to at least get this in as an initial clean-up 🙏

@clux clux requested a review from nightkr May 27, 2024 12:02
@clux
Copy link
Member Author

clux commented May 29, 2024

merging this as is for now, but will try to merge InitApply and InitPage so that a page event emits many InitApply events to make the reader interface easier (and also less breaking when we likely remove paging in favour of streaming lists years down the line). that follow-up will need a small benchmark against current profile, but hopefully an easy win.

@clux clux merged commit 4d1ea12 into main May 29, 2024
18 checks passed
@clux clux deleted the watcher-event-rename-fixup branch May 29, 2024 10:13
@clux clux mentioned this pull request Jun 10, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
changelog-change changelog change category for prs
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants