Skip to content

Commit 7ff96a8

Browse files
committed
Update the docs with a section regarding the cache usage
Signed-off-by: Soule BA <[email protected]>
1 parent 0f93028 commit 7ff96a8

File tree

4 files changed

+78
-14
lines changed

4 files changed

+78
-14
lines changed

controllers/helmchart_controller.go

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -463,10 +463,8 @@ func (r *HelmChartReconciler) buildFromHelmRepository(ctx context.Context, obj *
463463

464464
// Try to retrieve the repository index from the cache
465465
if r.Cache != nil {
466-
if index, found := r.Cache.Get(r.Storage.LocalPath(*repo.GetArtifact())); err == nil {
467-
if found {
468-
chartRepo.Index = index.(*helmrepo.IndexFile)
469-
}
466+
if index, found := r.Cache.Get(r.Storage.LocalPath(*repo.GetArtifact())); found {
467+
chartRepo.Index = index.(*helmrepo.IndexFile)
470468
}
471469
}
472470

@@ -502,7 +500,7 @@ func (r *HelmChartReconciler) buildFromHelmRepository(ctx context.Context, obj *
502500
// Using r.Storage.LocalPath(*repo.GetArtifact() is safe as the path is in the format /<helm-repository-name>/<chart-name>/<filename>.
503501
err := r.Cache.Set(r.Storage.LocalPath(*repo.GetArtifact()), chartRepo.Index, r.TTL)
504502
if err != nil {
505-
r.eventLogf(ctx, obj, events.EventTypeTrace, sourcev1.CacheOperationFailedReason, "failed to cache index: %v", err)
503+
r.eventLogf(ctx, obj, events.EventTypeTrace, sourcev1.CacheOperationFailedReason, "failed to cache index: %s", err)
506504
}
507505

508506
}

docs/spec/v1beta2/helmcharts.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,53 @@ Besides being reported in Events, the reconciliation errors are also logged by
390390
the controller. The Flux CLI offer commands for filtering the logs for a
391391
specific HelmChart, e.g. `flux logs --level=error --kind=HelmChart --name=<chart-name>`.
392392

393+
### Improving resource consumption by enabling the cache
394+
395+
When using a `HelmRepository` as Source for a `HelmChart`, the controller loads
396+
the repository index in memory to find the latest version of the chart.
397+
398+
The controller can be configured to cache Helm repository indexes in memory.
399+
The cache is used to avoid loading repository indexes for every `HelmChart`
400+
reconciliation.
401+
402+
The following flags are provided to enable and configure the cache:
403+
- `helm-cache-max-size`: The maximum size of the cache in number of indexes.
404+
If `0`, then the cache is disabled.
405+
- `helm-cache-ttl`: The TTL of an index in the cache.
406+
- `helm-cache-purge-interval`: The interval at which the cache is purged of
407+
expired items.
408+
409+
The caching strategy is to pull a repository index from the cache if it is
410+
available, otherwise to load the index, retrieve and build the chart,
411+
then cache the index. The cached index TTL is refreshed every time the
412+
Helm repository index is loaded with the `helm-cache-ttl` value.
413+
414+
The cache is purged of expired items every `helm-cache-purge-interval`.
415+
416+
When the cache is full, no more items can be added to the cache, and the
417+
source-controller will report a warning event instead.
418+
419+
In order to use the cache, set the related flags in the source-controller
420+
Deployment config:
421+
422+
```yaml
423+
spec:
424+
containers:
425+
- args:
426+
- --watch-all-namespaces
427+
- --log-level=info
428+
- --log-encoding=json
429+
- --enable-leader-election
430+
- --storage-path=/data
431+
- --storage-adv-addr=source-controller.$(RUNTIME_NAMESPACE).svc.cluster.local.
432+
## Helm cache with up to 10 items, i.e. 10 indexes.
433+
- --helm-cache-max-size=10
434+
## TTL of an index is 1 hour.
435+
- --helm-cache-ttl=1h
436+
## Purge expired index every 10 minutes.
437+
- --helm-cache-purge-interval=10m
438+
```
439+
393440
## HelmChart Status
394441

395442
### Artifact

internal/cache/cache.go

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,16 @@ type Cache struct {
2626

2727
// Item is an item stored in the cache.
2828
type Item struct {
29-
Object interface{}
29+
// Object is the item's value.
30+
Object interface{}
31+
// Expiration is the item's expiration time.
3032
Expiration int64
3133
}
3234

3335
type cache struct {
3436
// Items holds the elements in the cache.
3537
Items map[string]Item
36-
// Maximum number of items the cache can hold.
38+
// MaxItems is the maximum number of items the cache can hold.
3739
MaxItems int
3840
mu sync.RWMutex
3941
janitor *janitor
@@ -82,6 +84,9 @@ func (c *cache) Set(key string, value interface{}, expiration time.Duration) err
8284
return fmt.Errorf("Cache is full")
8385
}
8486

87+
// Add an item to the cache, existing items will not be overwritten.
88+
// To overwrite existing items, use Set.
89+
// If the cache is full, Add will return an error.
8590
func (c *cache) Add(key string, value interface{}, expiration time.Duration) error {
8691
c.mu.Lock()
8792
_, found := c.Items[key]
@@ -100,6 +105,8 @@ func (c *cache) Add(key string, value interface{}, expiration time.Duration) err
100105
return fmt.Errorf("Cache is full")
101106
}
102107

108+
// Get an item from the cache. Returns the item or nil, and a bool indicating
109+
// whether the key was found.
103110
func (c *cache) Get(key string) (interface{}, bool) {
104111
c.mu.RLock()
105112
item, found := c.Items[key]
@@ -117,18 +124,23 @@ func (c *cache) Get(key string) (interface{}, bool) {
117124
return item.Object, true
118125
}
119126

127+
// Delete an item from the cache. Does nothing if the key is not in the cache.
120128
func (c *cache) Delete(key string) {
121129
c.mu.Lock()
122130
delete(c.Items, key)
123131
c.mu.Unlock()
124132
}
125133

134+
// Clear all items from the cache.
135+
// This reallocate the inderlying array holding the items,
136+
// so that the memory used by the items is reclaimed.
126137
func (c *cache) Clear() {
127138
c.mu.Lock()
128139
c.Items = make(map[string]Item)
129140
c.mu.Unlock()
130141
}
131142

143+
// HasExpired returns true if the item has expired.
132144
func (c *cache) HasExpired(key string) bool {
133145
c.mu.RLock()
134146
item, ok := c.Items[key]
@@ -146,6 +158,8 @@ func (c *cache) HasExpired(key string) bool {
146158
return false
147159
}
148160

161+
// SetExpiration sets the expiration for the given key.
162+
// Does nothing if the key is not in the cache.
149163
func (c *cache) SetExpiration(key string, expiration time.Duration) {
150164
c.mu.Lock()
151165
item, ok := c.Items[key]
@@ -157,6 +171,9 @@ func (c *cache) SetExpiration(key string, expiration time.Duration) {
157171
c.mu.Unlock()
158172
}
159173

174+
// GetExpiration returns the expiration for the given key.
175+
// Returns zero if the key is not in the cache or the item
176+
// has already expired.
160177
func (c *cache) GetExpiration(key string) time.Duration {
161178
c.mu.RLock()
162179
item, ok := c.Items[key]
@@ -174,6 +191,7 @@ func (c *cache) GetExpiration(key string) time.Duration {
174191
return time.Duration(item.Expiration - time.Now().UnixNano())
175192
}
176193

194+
// DeleteExpired deletes all expired items from the cache.
177195
func (c *cache) DeleteExpired() {
178196
c.mu.Lock()
179197
for k, v := range c.Items {
@@ -185,12 +203,12 @@ func (c *cache) DeleteExpired() {
185203
}
186204

187205
type janitor struct {
188-
Interval time.Duration
206+
interval time.Duration
189207
stop chan bool
190208
}
191209

192-
func (j *janitor) Run(c *cache) {
193-
ticker := time.NewTicker(j.Interval)
210+
func (j *janitor) run(c *cache) {
211+
ticker := time.NewTicker(j.interval)
194212
for {
195213
select {
196214
case <-ticker.C:
@@ -206,20 +224,21 @@ func stopJanitor(c *Cache) {
206224
c.janitor.stop <- true
207225
}
208226

227+
// New creates a new cache with the given configuration.
209228
func New(maxItems int, interval time.Duration) *Cache {
210229
c := &cache{
211230
Items: make(map[string]Item),
212231
MaxItems: maxItems,
213232
janitor: &janitor{
214-
Interval: interval,
233+
interval: interval,
215234
stop: make(chan bool),
216235
},
217236
}
218237

219238
C := &Cache{c}
220239

221240
if interval > 0 {
222-
go c.janitor.Run(c)
241+
go c.janitor.run(c)
223242
runtime.SetFinalizer(C, stopJanitor)
224243
}
225244

main.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,9 +115,9 @@ func main() {
115115
flag.DurationVar(&requeueDependency, "requeue-dependency", 30*time.Second,
116116
"The interval at which failing dependencies are reevaluated.")
117117
flag.IntVar(&helmCacheMaxSize, "helm-cache-max-size", 0,
118-
"The maximum size of the cache in number of items.")
118+
"The maximum size of the cache in number of indexes.")
119119
flag.StringVar(&helmCacheTTL, "helm-cache-ttl", "15m",
120-
"The TTL of an item in the cache. Valid time units are ns, us (or µs), ms, s, m, h.")
120+
"The TTL of an index in the cache. Valid time units are ns, us (or µs), ms, s, m, h.")
121121
flag.StringVar(&helmCachePurgeInterval, "helm-cache-purge-interval", "1m",
122122
"The interval at which the cache is purged. Valid time units are ns, us (or µs), ms, s, m, h.")
123123

0 commit comments

Comments
 (0)