github.com/argoproj/argo-cd/v2@v2.10.9/controller/sharding/cache.go (about)

     1  package sharding
     2  
     3  import (
     4  	"sync"
     5  
     6  	"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
     7  	"github.com/argoproj/argo-cd/v2/util/db"
     8  	log "github.com/sirupsen/logrus"
     9  )
    10  
    11  type ClusterShardingCache interface {
    12  	Init(clusters *v1alpha1.ClusterList)
    13  	Add(c *v1alpha1.Cluster)
    14  	Delete(clusterServer string)
    15  	Update(oldCluster *v1alpha1.Cluster, newCluster *v1alpha1.Cluster)
    16  	IsManagedCluster(c *v1alpha1.Cluster) bool
    17  	GetDistribution() map[string]int
    18  }
    19  
    20  type ClusterSharding struct {
    21  	Shard           int
    22  	Replicas        int
    23  	Shards          map[string]int
    24  	Clusters        map[string]*v1alpha1.Cluster
    25  	lock            sync.RWMutex
    26  	getClusterShard DistributionFunction
    27  }
    28  
    29  func NewClusterSharding(_ db.ArgoDB, shard, replicas int, shardingAlgorithm string) ClusterShardingCache {
    30  	log.Debugf("Processing clusters from shard %d: Using filter function:  %s", shard, shardingAlgorithm)
    31  	clusterSharding := &ClusterSharding{
    32  		Shard:    shard,
    33  		Replicas: replicas,
    34  		Shards:   make(map[string]int),
    35  		Clusters: make(map[string]*v1alpha1.Cluster),
    36  	}
    37  	distributionFunction := NoShardingDistributionFunction()
    38  	if replicas > 1 {
    39  		log.Debugf("Processing clusters from shard %d: Using filter function:  %s", shard, shardingAlgorithm)
    40  		distributionFunction = GetDistributionFunction(clusterSharding.GetClusterAccessor(), shardingAlgorithm, replicas)
    41  	} else {
    42  		log.Info("Processing all cluster shards")
    43  	}
    44  	clusterSharding.getClusterShard = distributionFunction
    45  	return clusterSharding
    46  }
    47  
    48  // IsManagedCluster returns wheter or not the cluster should be processed by a given shard.
    49  func (s *ClusterSharding) IsManagedCluster(c *v1alpha1.Cluster) bool {
    50  	s.lock.RLock()
    51  	defer s.lock.RUnlock()
    52  	if c == nil { // nil cluster (in-cluster) is always managed by current clusterShard
    53  		return true
    54  	}
    55  	clusterShard := 0
    56  	if shard, ok := s.Shards[c.Server]; ok {
    57  		clusterShard = shard
    58  	} else {
    59  		log.Warnf("The cluster %s has no assigned shard.", c.Server)
    60  	}
    61  	log.Debugf("Checking if cluster %s with clusterShard %d should be processed by shard %d", c.Server, clusterShard, s.Shard)
    62  	return clusterShard == s.Shard
    63  }
    64  
    65  func (sharding *ClusterSharding) Init(clusters *v1alpha1.ClusterList) {
    66  	sharding.lock.Lock()
    67  	defer sharding.lock.Unlock()
    68  	newClusters := make(map[string]*v1alpha1.Cluster, len(clusters.Items))
    69  	for _, c := range clusters.Items {
    70  		cluster := c
    71  		newClusters[c.Server] = &cluster
    72  	}
    73  	sharding.Clusters = newClusters
    74  	sharding.updateDistribution()
    75  }
    76  
    77  func (sharding *ClusterSharding) Add(c *v1alpha1.Cluster) {
    78  	sharding.lock.Lock()
    79  	defer sharding.lock.Unlock()
    80  
    81  	old, ok := sharding.Clusters[c.Server]
    82  	sharding.Clusters[c.Server] = c
    83  	if !ok || hasShardingUpdates(old, c) {
    84  		sharding.updateDistribution()
    85  	} else {
    86  		log.Debugf("Skipping sharding distribution update. Cluster already added")
    87  	}
    88  }
    89  
    90  func (sharding *ClusterSharding) Delete(clusterServer string) {
    91  	sharding.lock.Lock()
    92  	defer sharding.lock.Unlock()
    93  	if _, ok := sharding.Clusters[clusterServer]; ok {
    94  		delete(sharding.Clusters, clusterServer)
    95  		delete(sharding.Shards, clusterServer)
    96  		sharding.updateDistribution()
    97  	}
    98  }
    99  
   100  func (sharding *ClusterSharding) Update(oldCluster *v1alpha1.Cluster, newCluster *v1alpha1.Cluster) {
   101  	sharding.lock.Lock()
   102  	defer sharding.lock.Unlock()
   103  
   104  	if _, ok := sharding.Clusters[oldCluster.Server]; ok && oldCluster.Server != newCluster.Server {
   105  		delete(sharding.Clusters, oldCluster.Server)
   106  		delete(sharding.Shards, oldCluster.Server)
   107  	}
   108  	sharding.Clusters[newCluster.Server] = newCluster
   109  	if hasShardingUpdates(oldCluster, newCluster) {
   110  		sharding.updateDistribution()
   111  	} else {
   112  		log.Debugf("Skipping sharding distribution update. No relevant changes")
   113  	}
   114  }
   115  
   116  func (sharding *ClusterSharding) GetDistribution() map[string]int {
   117  	sharding.lock.RLock()
   118  	defer sharding.lock.RUnlock()
   119  	shards := sharding.Shards
   120  
   121  	distribution := make(map[string]int, len(shards))
   122  	for k, v := range shards {
   123  		distribution[k] = v
   124  	}
   125  	return distribution
   126  }
   127  
   128  func (sharding *ClusterSharding) updateDistribution() {
   129  	for k, c := range sharding.Clusters {
   130  		shard := 0
   131  		if c.Shard != nil {
   132  			requestedShard := int(*c.Shard)
   133  			if requestedShard < sharding.Replicas {
   134  				shard = requestedShard
   135  			} else {
   136  				log.Warnf("Specified cluster shard (%d) for cluster: %s is greater than the number of available shard (%d). Using shard 0.", requestedShard, c.Server, sharding.Replicas)
   137  			}
   138  		} else {
   139  			shard = sharding.getClusterShard(c)
   140  		}
   141  
   142  		existingShard, ok := sharding.Shards[k]
   143  		if ok && existingShard != shard {
   144  			log.Infof("Cluster %s has changed shard from %d to %d", k, existingShard, shard)
   145  		} else if !ok {
   146  			log.Infof("Cluster %s has been assigned to shard %d", k, shard)
   147  		} else {
   148  			log.Debugf("Cluster %s has not changed shard", k)
   149  		}
   150  		sharding.Shards[k] = shard
   151  	}
   152  }
   153  
   154  // hasShardingUpdates returns true if the sharding distribution has explicitly changed
   155  func hasShardingUpdates(old, new *v1alpha1.Cluster) bool {
   156  	if old == nil || new == nil {
   157  		return false
   158  	}
   159  
   160  	// returns true if the cluster id has changed because some sharding algorithms depend on it.
   161  	if old.ID != new.ID {
   162  		return true
   163  	}
   164  
   165  	if old.Server != new.Server {
   166  		return true
   167  	}
   168  
   169  	// return false if the shard field has not been modified
   170  	if old.Shard == nil && new.Shard == nil {
   171  		return false
   172  	}
   173  	return old.Shard == nil || new.Shard == nil || int64(*old.Shard) != int64(*new.Shard)
   174  }
   175  
   176  func (d *ClusterSharding) GetClusterAccessor() clusterAccessor {
   177  	return func() []*v1alpha1.Cluster {
   178  		// no need to lock, as this is only called from the updateDistribution function
   179  		clusters := make([]*v1alpha1.Cluster, 0, len(d.Clusters))
   180  		for _, c := range d.Clusters {
   181  			clusters = append(clusters, c)
   182  		}
   183  		return clusters
   184  	}
   185  }