Update the docs with a section regarding the cache usage

Signed-off-by: Soule BA <soule@weave.works>
This commit is contained in:
Soule BA 2022-03-31 10:52:43 +02:00
parent 0f9302827c
commit 7ff96a8b0c
No known key found for this signature in database
GPG Key ID: 4D40965192802994
4 changed files with 78 additions and 14 deletions

View File

@ -463,10 +463,8 @@ func (r *HelmChartReconciler) buildFromHelmRepository(ctx context.Context, obj *
// Try to retrieve the repository index from the cache
if r.Cache != nil {
if index, found := r.Cache.Get(r.Storage.LocalPath(*repo.GetArtifact())); err == nil {
if found {
chartRepo.Index = index.(*helmrepo.IndexFile)
}
if index, found := r.Cache.Get(r.Storage.LocalPath(*repo.GetArtifact())); found {
chartRepo.Index = index.(*helmrepo.IndexFile)
}
}
@ -502,7 +500,7 @@ func (r *HelmChartReconciler) buildFromHelmRepository(ctx context.Context, obj *
// Using r.Storage.LocalPath(*repo.GetArtifact() is safe as the path is in the format /<helm-repository-name>/<chart-name>/<filename>.
err := r.Cache.Set(r.Storage.LocalPath(*repo.GetArtifact()), chartRepo.Index, r.TTL)
if err != nil {
r.eventLogf(ctx, obj, events.EventTypeTrace, sourcev1.CacheOperationFailedReason, "failed to cache index: %v", err)
r.eventLogf(ctx, obj, events.EventTypeTrace, sourcev1.CacheOperationFailedReason, "failed to cache index: %s", err)
}
}

View File

@ -390,6 +390,53 @@ Besides being reported in Events, the reconciliation errors are also logged by
the controller. The Flux CLI offer commands for filtering the logs for a
specific HelmChart, e.g. `flux logs --level=error --kind=HelmChart --name=<chart-name>`.
### Improving resource consumption by enabling the cache
When using a `HelmRepository` as Source for a `HelmChart`, the controller loads
the repository index in memory to find the latest version of the chart.
The controller can be configured to cache Helm repository indexes in memory.
The cache is used to avoid loading repository indexes for every `HelmChart`
reconciliation.
The following flags are provided to enable and configure the cache:
- `helm-cache-max-size`: The maximum size of the cache in number of indexes.
If `0`, then the cache is disabled.
- `helm-cache-ttl`: The TTL of an index in the cache.
- `helm-cache-purge-interval`: The interval at which the cache is purged of
expired items.
The caching strategy is to pull a repository index from the cache if it is
available, otherwise to load the index, retrieve and build the chart,
then cache the index. The cached index TTL is refreshed every time the
Helm repository index is loaded with the `helm-cache-ttl` value.
The cache is purged of expired items every `helm-cache-purge-interval`.
When the cache is full, no more items can be added to the cache, and the
source-controller will report a warning event instead.
In order to use the cache, set the related flags in the source-controller
Deployment config:
```yaml
spec:
containers:
- args:
- --watch-all-namespaces
- --log-level=info
- --log-encoding=json
- --enable-leader-election
- --storage-path=/data
- --storage-adv-addr=source-controller.$(RUNTIME_NAMESPACE).svc.cluster.local.
## Helm cache with up to 10 items, i.e. 10 indexes.
- --helm-cache-max-size=10
## TTL of an index is 1 hour.
- --helm-cache-ttl=1h
## Purge expired index every 10 minutes.
- --helm-cache-purge-interval=10m
```
## HelmChart Status
### Artifact

View File

@ -26,14 +26,16 @@ type Cache struct {
// Item is an item stored in the cache.
type Item struct {
Object interface{}
// Object is the item's value.
Object interface{}
// Expiration is the item's expiration time.
Expiration int64
}
type cache struct {
// Items holds the elements in the cache.
Items map[string]Item
// Maximum number of items the cache can hold.
// MaxItems is the maximum number of items the cache can hold.
MaxItems int
mu sync.RWMutex
janitor *janitor
@ -82,6 +84,9 @@ func (c *cache) Set(key string, value interface{}, expiration time.Duration) err
return fmt.Errorf("Cache is full")
}
// Add an item to the cache, existing items will not be overwritten.
// To overwrite existing items, use Set.
// If the cache is full, Add will return an error.
func (c *cache) Add(key string, value interface{}, expiration time.Duration) error {
c.mu.Lock()
_, found := c.Items[key]
@ -100,6 +105,8 @@ func (c *cache) Add(key string, value interface{}, expiration time.Duration) err
return fmt.Errorf("Cache is full")
}
// Get an item from the cache. Returns the item or nil, and a bool indicating
// whether the key was found.
func (c *cache) Get(key string) (interface{}, bool) {
c.mu.RLock()
item, found := c.Items[key]
@ -117,18 +124,23 @@ func (c *cache) Get(key string) (interface{}, bool) {
return item.Object, true
}
// Delete an item from the cache. Does nothing if the key is not in the cache.
func (c *cache) Delete(key string) {
c.mu.Lock()
delete(c.Items, key)
c.mu.Unlock()
}
// Clear all items from the cache.
// This reallocate the inderlying array holding the items,
// so that the memory used by the items is reclaimed.
func (c *cache) Clear() {
c.mu.Lock()
c.Items = make(map[string]Item)
c.mu.Unlock()
}
// HasExpired returns true if the item has expired.
func (c *cache) HasExpired(key string) bool {
c.mu.RLock()
item, ok := c.Items[key]
@ -146,6 +158,8 @@ func (c *cache) HasExpired(key string) bool {
return false
}
// SetExpiration sets the expiration for the given key.
// Does nothing if the key is not in the cache.
func (c *cache) SetExpiration(key string, expiration time.Duration) {
c.mu.Lock()
item, ok := c.Items[key]
@ -157,6 +171,9 @@ func (c *cache) SetExpiration(key string, expiration time.Duration) {
c.mu.Unlock()
}
// GetExpiration returns the expiration for the given key.
// Returns zero if the key is not in the cache or the item
// has already expired.
func (c *cache) GetExpiration(key string) time.Duration {
c.mu.RLock()
item, ok := c.Items[key]
@ -174,6 +191,7 @@ func (c *cache) GetExpiration(key string) time.Duration {
return time.Duration(item.Expiration - time.Now().UnixNano())
}
// DeleteExpired deletes all expired items from the cache.
func (c *cache) DeleteExpired() {
c.mu.Lock()
for k, v := range c.Items {
@ -185,12 +203,12 @@ func (c *cache) DeleteExpired() {
}
type janitor struct {
Interval time.Duration
interval time.Duration
stop chan bool
}
func (j *janitor) Run(c *cache) {
ticker := time.NewTicker(j.Interval)
func (j *janitor) run(c *cache) {
ticker := time.NewTicker(j.interval)
for {
select {
case <-ticker.C:
@ -206,12 +224,13 @@ func stopJanitor(c *Cache) {
c.janitor.stop <- true
}
// New creates a new cache with the given configuration.
func New(maxItems int, interval time.Duration) *Cache {
c := &cache{
Items: make(map[string]Item),
MaxItems: maxItems,
janitor: &janitor{
Interval: interval,
interval: interval,
stop: make(chan bool),
},
}
@ -219,7 +238,7 @@ func New(maxItems int, interval time.Duration) *Cache {
C := &Cache{c}
if interval > 0 {
go c.janitor.Run(c)
go c.janitor.run(c)
runtime.SetFinalizer(C, stopJanitor)
}

View File

@ -115,9 +115,9 @@ func main() {
flag.DurationVar(&requeueDependency, "requeue-dependency", 30*time.Second,
"The interval at which failing dependencies are reevaluated.")
flag.IntVar(&helmCacheMaxSize, "helm-cache-max-size", 0,
"The maximum size of the cache in number of items.")
"The maximum size of the cache in number of indexes.")
flag.StringVar(&helmCacheTTL, "helm-cache-ttl", "15m",
"The TTL of an item in the cache. Valid time units are ns, us (or µs), ms, s, m, h.")
"The TTL of an index in the cache. Valid time units are ns, us (or µs), ms, s, m, h.")
flag.StringVar(&helmCachePurgeInterval, "helm-cache-purge-interval", "1m",
"The interval at which the cache is purged. Valid time units are ns, us (or µs), ms, s, m, h.")