github.com/argoproj/argo-cd/v3@v3.2.1/util/cache/appstate/cache.go (about)

     1  package appstate
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"sort"
     7  	"time"
     8  
     9  	"github.com/spf13/cobra"
    10  
    11  	appv1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
    12  	cacheutil "github.com/argoproj/argo-cd/v3/util/cache"
    13  	"github.com/argoproj/argo-cd/v3/util/env"
    14  )
    15  
    16  var (
    17  	ErrCacheMiss  = cacheutil.ErrCacheMiss
    18  	treeShardSize = env.ParseInt64FromEnv("ARGOCD_APPLICATION_TREE_SHARD_SIZE", 0, 0, 1000)
    19  )
    20  
    21  const (
    22  	clusterInfoCacheExpiration = 10 * time.Minute
    23  )
    24  
    25  type Cache struct {
    26  	Cache                   *cacheutil.Cache
    27  	appStateCacheExpiration time.Duration
    28  }
    29  
    30  func NewCache(cache *cacheutil.Cache, appStateCacheExpiration time.Duration) *Cache {
    31  	return &Cache{cache, appStateCacheExpiration}
    32  }
    33  
    34  func AddCacheFlagsToCmd(cmd *cobra.Command, opts ...cacheutil.Options) func() (*Cache, error) {
    35  	var appStateCacheExpiration time.Duration
    36  
    37  	cmd.Flags().DurationVar(&appStateCacheExpiration, "app-state-cache-expiration", env.ParseDurationFromEnv("ARGOCD_APP_STATE_CACHE_EXPIRATION", 1*time.Hour, 0, 10*time.Hour), "Cache expiration for app state")
    38  
    39  	cacheFactory := cacheutil.AddCacheFlagsToCmd(cmd, opts...)
    40  
    41  	return func() (*Cache, error) {
    42  		cache, err := cacheFactory()
    43  		if err != nil {
    44  			return nil, err
    45  		}
    46  		return NewCache(cache, appStateCacheExpiration), nil
    47  	}
    48  }
    49  
    50  func (c *Cache) GetItem(key string, item any) error {
    51  	return c.Cache.GetItem(key, item)
    52  }
    53  
    54  func (c *Cache) SetItem(key string, item any, expiration time.Duration, deletion bool) error {
    55  	return c.Cache.SetItem(key, item, &cacheutil.CacheActionOpts{Expiration: expiration, Delete: deletion})
    56  }
    57  
    58  func appManagedResourcesKey(appName string) string {
    59  	return "app|managed-resources|" + appName
    60  }
    61  
    62  func (c *Cache) GetAppManagedResources(appName string, res *[]*appv1.ResourceDiff) error {
    63  	err := c.GetItem(appManagedResourcesKey(appName), &res)
    64  	return err
    65  }
    66  
    67  func (c *Cache) SetAppManagedResources(appName string, managedResources []*appv1.ResourceDiff) error {
    68  	sort.Slice(managedResources, func(i, j int) bool {
    69  		return managedResources[i].FullName() < managedResources[j].FullName()
    70  	})
    71  	return c.SetItem(appManagedResourcesKey(appName), managedResources, c.appStateCacheExpiration, managedResources == nil)
    72  }
    73  
    74  func appResourcesTreeKey(appName string, shard int64) string {
    75  	key := "app|resources-tree|" + appName
    76  	if shard > 0 {
    77  		key = fmt.Sprintf("%s|%d", key, shard)
    78  	}
    79  	return key
    80  }
    81  
    82  func clusterInfoKey(server string) string {
    83  	return "cluster|info|" + server
    84  }
    85  
    86  func (c *Cache) GetAppResourcesTree(appName string, res *appv1.ApplicationTree) error {
    87  	err := c.GetItem(appResourcesTreeKey(appName, 0), &res)
    88  	if res.ShardsCount > 1 {
    89  		for i := int64(1); i < res.ShardsCount; i++ {
    90  			var shard appv1.ApplicationTree
    91  			err = c.GetItem(appResourcesTreeKey(appName, i), &shard)
    92  			if err != nil {
    93  				return err
    94  			}
    95  			res.Merge(&shard)
    96  		}
    97  	}
    98  	return err
    99  }
   100  
   101  func (c *Cache) OnAppResourcesTreeChanged(ctx context.Context, appName string, callback func() error) error {
   102  	return c.Cache.OnUpdated(ctx, appManagedResourcesKey(appName), callback)
   103  }
   104  
   105  func (c *Cache) SetAppResourcesTree(appName string, resourcesTree *appv1.ApplicationTree) error {
   106  	if resourcesTree == nil {
   107  		if err := c.SetItem(appResourcesTreeKey(appName, 0), resourcesTree, c.appStateCacheExpiration, true); err != nil {
   108  			return err
   109  		}
   110  	} else {
   111  		// Splitting resource tree into shards reduces number of Redis SET calls and therefore amount of traffic sent
   112  		// from controller to Redis. Controller still stores each shard in cache but util/cache/twolevelclient.go
   113  		// forwards request to Redis only if shard actually changes.
   114  		for i, shard := range resourcesTree.GetShards(treeShardSize) {
   115  			if err := c.SetItem(appResourcesTreeKey(appName, int64(i)), shard, c.appStateCacheExpiration, false); err != nil {
   116  				return err
   117  			}
   118  		}
   119  	}
   120  
   121  	return c.Cache.NotifyUpdated(appManagedResourcesKey(appName))
   122  }
   123  
   124  func (c *Cache) SetClusterInfo(server string, info *appv1.ClusterInfo) error {
   125  	return c.SetItem(clusterInfoKey(server), info, clusterInfoCacheExpiration, info == nil)
   126  }
   127  
   128  func (c *Cache) GetClusterInfo(server string, res *appv1.ClusterInfo) error {
   129  	err := c.GetItem(clusterInfoKey(server), &res)
   130  	return err
   131  }