From d29ab4d8eb8ced7072b93ceaa73cae88c4c82e1d Mon Sep 17 00:00:00 2001 From: clux Date: Fri, 21 Jun 2024 11:02:17 +0100 Subject: [PATCH] quick reword after 0.92.1 Signed-off-by: clux --- .../blog/posts/2024-06-11-reflector-memory.md | 49 +++++++++++------- docs/blog/posts/mem-dn-drop.png | Bin 16436 -> 0 bytes docs/blog/posts/mem-lc-drop.png | Bin 13506 -> 0 bytes 3 files changed, 31 insertions(+), 18 deletions(-) delete mode 100644 docs/blog/posts/mem-dn-drop.png delete mode 100644 docs/blog/posts/mem-lc-drop.png diff --git a/docs/blog/posts/2024-06-11-reflector-memory.md b/docs/blog/posts/2024-06-11-reflector-memory.md index 90884c5..af2253a 100644 --- a/docs/blog/posts/2024-06-11-reflector-memory.md +++ b/docs/blog/posts/2024-06-11-reflector-memory.md @@ -12,7 +12,7 @@ In [0.92.0](https://github.com/kube-rs/kube/releases/tag/0.92.0), the [watcher] This can cause a decent memory use reduction for direct users of [watcher], but also (somewhat unintuitively) for users of reflectors and stores. -In this post, we explore the setup, current solutions, and some future work. +In this post, we explore the setup, current solutions, and some future work. It has been updated in light of [0.92.1](https://github.com/kube-rs/kube/releases/tag/0.92.1). @@ -38,7 +38,7 @@ When `410 Gone` responses happen we need to trigger a re-list, and wait for all Let's focus on caches. To simplify these problems for users we have created certain guarantees in the abstractions of `kube::runtime`. -## Guarantees +## Runtime Guarantees The [watcher] up until 0.92.0 has maintained a guarantee we have casually referred to as __watcher atomicity__: @@ -56,11 +56,11 @@ This property meant that stores could in-turn provide their own guarantee very e This property is needed for Controllers who rely on complete information and will kick in once the future from [Store::wait_until_ready] resolves. -## Consequences +### Store Consequences If we do all the buffering on the `watcher` side, then achieving the store completeness guarantee is a rather trivial task to accomplish. -Up until 0.91 this was handled in [`Store::apply_watcher_event@0.91`](https://github.com/kube-rs/kube/blob/5dbae3a18c14a225d2d993b9effd16147fef420e/kube-runtime/src/reflector/store.rs#L96-L121) as: +Up until 0.91 this was handled in [`Store::apply_watcher_event@0.91`](https://github.com/kube-rs/kube/blob/5dbae3a18c14a225d2d993b9effd16147fef420e/kube-runtime/src/reflector/store.rs#L96-L121) as with a `*self.store.write() = new_objs` on the old `Restarted` event: ```rust // 0.91 source: @@ -103,6 +103,12 @@ So in the end you might actually be holding on to between 2x and 3x the actual s If you were using `watcher` without `reflector`, you were the most affected by this excessive caching. You might not have needed __watcher atomicity__, as it was primarily designed to facilitate __store completeness__. +### Watcher Consequences + +If you were just watching data on 0.91.0 (not using stores), the buffering is completely necessary if you just want to react to events about individual objects without considering the wider dataset. + +Your peak memory use for a single watcher (with all other things considered negligible) is going to scale with the size of the __complete dataset__ because `watcher` was buffering ALL pages, whereas it really __should__ only scale with the __page size__ that you ask for objects returned in. + ## Change in 0.92 The change in 0.92.0 is primarily to **stop buffering events in the `watcher`**, and present __new watcher events__ that allows a store to achieve the Store completeness guarantee. @@ -138,7 +144,7 @@ As it stands the [`Store::apply_watcher_event@0.92`](https://github.com/kube-rs/ } ``` -Thus, on a restart, objects are passed one-by-one up to the store, and buffered therein. When all objects are received, the buffers are swapped (meaning you use at most 2x the data). The blank buffer re-assignment [also forces de-allocation](https://github.com/kube-rs/kube/pull/1494#discussion_r1602840218) of the temporary `self.buffer`. +Thus, on a restart, objects are passed one-by-one up to the store, and buffered therein. When all objects are received, the buffers are swapped (meaning you use at most 2x the data). The blank buffer re-assignment also forces de-allocation[*](https://github.com/kube-rs/kube/pull/1494#discussion_r1602840218) of the temporary `self.buffer`. !!! note "Preparing for StreamingLists" @@ -146,37 +152,42 @@ Thus, on a restart, objects are passed one-by-one up to the store, and buffered ## Results -The initial [synthetic benchmarks](https://github.com/kube-rs/kube/pull/1494#issue-2292501600) saw 60% reductions when using stores, and 80% when not using stores (when there's nothing to cache), with further incremental improvements when using the `StreamingList` strategy. +The __initial__ [synthetic benchmarks](https://github.com/kube-rs/kube/pull/1494#issue-2292501600) saw 60% reductions when using stores, and 80% when not using stores (when there's nothing to cache), with further incremental improvements when using the `StreamingList` strategy. !!! warning "Ad-hoc Benchmarks" - Whether the ad-hoc synthetic benchmarks are in any way realistic going forwards remains to be seen. How much you can get likely depends on a range of factors from allocator choice to usage patterns. + The ad-hoc synthetic benchmarks are likely unrealistic for real world scenarios. The original 0.92.0 release had a bug [affecting benchmarks](https://github.com/kube-rs/kube/pull/1525#issuecomment-2179415844), so many of the linked posts may be invalid / out-of-date. How much you can get out of this will depend on a range of factors from allocator choice to usage patterns. + +__So far__, we have seen controllers with a basically unchanged profile, some with small improvements in the 10-20% range, one [50% drop in a real-world controller](https://github.com/kube-rs/kube/pull/1494#issuecomment-2126694967) from testing (EDIT: which is still sustained after the 0.92.1 bugfix with page size). + +In the current default `ListWatch` [InitialListStrategy], the implicit default [Config::page_size] of `500` will undermine this optimization somewhat, because individual pages are still kept in the watcher while they are being sent out one-by-one. Setting the page size to `50` was necessary for me to get anything close to the benchmarks. -__So far__, we have seen controllers with a basically unchanged profile, some with small improvements in the 10-20% range, one [50% drop in a real-world controller](https://github.com/kube-rs/kube/pull/1494#issuecomment-2126694967) from testing, the same controller dropping 80% with page tuning (see below). +!!! note "Page Size Marginal Gains" -If you are using the standard `ListWatch` [InitialListStrategy], the default [Config::page_size] of `500` will undermine this optimization, because individual pages are still kept in the watcher while they are being sent out one-by-one. Setting the page size to `50` has been necessary for me to get anything close to the benchmarks. + Lowering the page size below 50 did see further marginal gains (~5%ish from limited testing), but this will also increase API calls (/list next page). It will be interesting to see how well the streaming lists change will fare in the end (as it effectively functions as setting the page size to 1 as far as internal buffering is concerned). So for now; YMMV. Try setting the `page_size`, and [chat about](https://discord.gg/tokio) / [share](https://github.com/kube-rs/kube/discussions) your results! ### Examples -Two examples from my own deployment testing today (plots of kubelet metrics from [compute dashboards](https://github.com/kubernetes-monitoring/kubernetes-mixin)). +Two example results from my own deployment testing (by checking memory use after initialisation 5m in using standard kubelet metrics) showed uneven gains. -#### Optimized Metadata Controller -A metadata controller watching 2000 objects (all in stores), doing 6000 reconciles an hour, using **9MB of RAM**. +!!! warning "Update after 0.92.1" + + This post has been edited in light of [0.92.1](https://github.com/kube-rs/kube/releases/tag/0.92.1) which casted the 0.92.0 release in overly favourable light. 0.92.0 dropped pages and this [impacted the measurements](https://github.com/kube-rs/kube/pull/1525#issuecomment-2179415844). -![memory drop from a metadata controller having 2000 objects in its stores](mem-lc-drop.png) +#### Optimized Metadata Controller +A metadata controller watching 2000 objects (all in stores), doing 6000 reconciles an hour. -Yellow == `0.91.0`, green/red = `0.92.0` with defaults, orange = `0.92.0` with reduced page size (50). +> **45MB** memory on 0.91.0, **~20MB** on 0.92.1. -This has seen the biggest change, dropping ~85% of its memory usage, but it also is also doing all the biggest [[optimization]] tricks (`metadata_watcher`, page_size 50, pruning of managed fields), and it has basically no other cached data. +This saw the biggest improvement, dropping ~50% of its memory usage. This is also a tiny controller with basically no other cached data though, it is doing all the biggest [[optimization]] tricks (`metadata_watcher`, page_size 50, pruning of managed fields) so the page buffering actually constituted the majority of the memory use (a perhaps uncommon situation). #### KS Controller A controller for [flux kustomizations](https://fluxcd.io/flux/components/kustomize/kustomizations/) storing and reconciling about 200 `ks` objects without any significant optimization techniques. -![memory drop from a ks controller with 200 larger objects in its stores](mem-dn-drop.png) -LHS == `0.91.0`, middle = `0.92.0` with defaults (many spot instance pods), RHS from 16:00 is `0.92.0` with reduced page size (50). +> **~65MB** memory on 0.91.0, **~65MB** on 0.92.1. -This controller saw a ~30% improvement overall, but not until reducing the page size. I assume this did not see any default improvement because the 200 objects fit comfortably within a single page and were cached internally in the `watcher` as before. +No improvements overall on this one despite setting page size down to 50. ## Thoughts for the future @@ -188,6 +199,8 @@ The peak 2x overhead here does hint at a potential future optimization; allowing As a step in the right direction, we would first like to get better visibility of our memory profile with some automated benchmarking. See [kube#1505](https://github.com/kube-rs/kube/issues/1505) for details. +It would also be good to better understand the choices of allocators here and their implications for some of these designs. + ## Breaking Change Users not matching on `watcher::Event` or building custom stores should not ever need to interact with this and should get the memory improvements for free. diff --git a/docs/blog/posts/mem-dn-drop.png b/docs/blog/posts/mem-dn-drop.png deleted file mode 100644 index d71248fcfc9e3f5968138bf667382fc612e0c822..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16436 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 diff --git a/docs/blog/posts/mem-lc-drop.png b/docs/blog/posts/mem-lc-drop.png deleted file mode 100644 index 83097b5fe2bbd2a48c51676237e985cc93864a5a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13506 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