Explorar el Código

track clusters and users in usage cache

Alexander Belanger hace 4 años
padre
commit
5c02fe3584
Se han modificado 2 ficheros con 54 adiciones y 39 borrados
  1. 6 0
      internal/models/usage.go
  2. 48 39
      internal/usage/usage.go

+ 6 - 0
internal/models/usage.go

@@ -51,6 +51,12 @@ type ProjectUsageCache struct {
 	// The memory usage, in bytes
 	ResourceMemory uint
 
+	// The number of clusters
+	Clusters uint
+
+	// The number of users
+	Users uint
+
 	// Whether the user is exceeding usage
 	Exceeded bool
 

+ 48 - 39
internal/usage/usage.go

@@ -32,6 +32,13 @@ func GetUsage(opts *GetUsageOpts) (
 		return nil, nil, nil, err
 	}
 
+	usageCache, err := opts.Repo.ProjectUsage().ReadProjectUsageCache(opts.Project.ID)
+	isCacheFound := true
+
+	if isCacheFound = !errors.Is(err, gorm.ErrRecordNotFound); err != nil && isCacheFound {
+		return nil, nil, nil, err
+	}
+
 	// query for the linked cluster counts
 	clusters, err := opts.Repo.Cluster().ListClustersByProjectID(opts.Project.ID)
 
@@ -54,71 +61,73 @@ func GetUsage(opts *GetUsageOpts) (
 		}
 	}
 
-	usageCache, err := opts.Repo.ProjectUsage().ReadProjectUsageCache(opts.Project.ID)
-	isCacheFound := true
-
-	if isCacheFound = !errors.Is(err, gorm.ErrRecordNotFound); err != nil && isCacheFound {
-		return nil, nil, nil, err
+	if !isCacheFound {
+		usageCache = &models.ProjectUsageCache{
+			ProjectID: opts.Project.ID,
+			Clusters:  uint(len(clusters)),
+			Users:     uint(len(countedRoles)),
+		}
 	}
 
-	// if the usage cache is 1 hour old, was not found, or usage is over limit,
-	// re-query for the usage
-	if !isCacheFound || usageCache.Is1HrOld() || usageCache.ResourceMemory > limit.ResourceMemory || usageCache.ResourceCPU > limit.ResourceCPU {
+	oldUsageCache := usageCache
+
+	usageCache.Clusters = uint(len(clusters))
+	usageCache.Users = uint(len(countedRoles))
+
+	// if the usage cache is 1 hour old, was not found, usage is currently over limit, or the clusters/users
+	// counts have changed, re-query for the usage
+	if true || !isCacheFound || usageCache.Is1HrOld() || isUsageExceeded(usageCache, limit) || isUsageChanged(oldUsageCache, usageCache) {
 		cpu, memory, err := getResourceUsage(opts, clusters)
 
 		if err != nil {
 			return nil, nil, nil, err
 		}
 
-		if !isCacheFound {
-			usageCache = &models.ProjectUsageCache{
-				ProjectID:      opts.Project.ID,
-				ResourceCPU:    cpu,
-				ResourceMemory: memory,
-			}
-		} else {
-			usageCache.ResourceCPU = cpu
-			usageCache.ResourceMemory = memory
-		}
+		usageCache.ResourceCPU = cpu
+		usageCache.ResourceMemory = memory
+	}
 
-		isExceeded := isUsageExceeded(usageCache, limit, uint(len(countedRoles)), uint(len(clusters)))
+	isExceeded := isUsageExceeded(usageCache, limit)
 
-		if !usageCache.Exceeded && isExceeded {
-			// update the usage cache with a time exceeded
-			currTime := time.Now()
-			usageCache.ExceededSince = &currTime
-		}
+	if !usageCache.Exceeded && isExceeded {
+		// update the usage cache with a time exceeded
+		currTime := time.Now()
+		usageCache.ExceededSince = &currTime
+	}
 
-		usageCache.Exceeded = isExceeded
+	usageCache.Exceeded = isExceeded
 
-		if !isCacheFound {
-			usageCache, err = opts.Repo.ProjectUsage().CreateProjectUsageCache(usageCache)
-		} else {
-			usageCache, err = opts.Repo.ProjectUsage().UpdateProjectUsageCache(usageCache)
-		}
+	if !isCacheFound {
+		usageCache, err = opts.Repo.ProjectUsage().CreateProjectUsageCache(usageCache)
+	} else if isUsageChanged(oldUsageCache, usageCache) {
+		usageCache, err = opts.Repo.ProjectUsage().UpdateProjectUsageCache(usageCache)
 	}
 
-	// we check whether it's currently exceeded based on the cache every time, since
-	// it's an inexpensive operation and involves no further DB lookups
-	usageCache.Exceeded = isUsageExceeded(usageCache, limit, uint(len(countedRoles)), uint(len(clusters)))
-
 	return &types.ProjectUsage{
 		ResourceCPU:    usageCache.ResourceCPU,
 		ResourceMemory: usageCache.ResourceMemory,
-		Clusters:       uint(len(clusters)),
-		Users:          uint(len(countedRoles)),
+		Clusters:       usageCache.Clusters,
+		Users:          usageCache.Users,
 	}, limit, usageCache, nil
 }
 
-func isUsageExceeded(usageCache *models.ProjectUsageCache, limit *types.ProjectUsage, numUsers, numClusters uint) bool {
+func isUsageExceeded(usageCache *models.ProjectUsageCache, limit *types.ProjectUsage) bool {
 	isCPUExceeded := limit.ResourceCPU != 0 && usageCache.ResourceCPU > limit.ResourceCPU
 	isMemExceeded := limit.ResourceMemory != 0 && usageCache.ResourceMemory > limit.ResourceMemory
-	isUsersExceeded := limit.Users != 0 && numUsers > limit.Users
-	isClustersExceeded := limit.Clusters != 0 && numClusters > limit.Clusters
+	isUsersExceeded := limit.Users != 0 && usageCache.Users > limit.Users
+	isClustersExceeded := limit.Clusters != 0 && usageCache.Clusters > limit.Clusters
 
 	return isCPUExceeded || isMemExceeded || isUsersExceeded || isClustersExceeded
 }
 
+func isUsageChanged(oldUsageCache, currUsageCache *models.ProjectUsageCache) bool {
+	return oldUsageCache.Exceeded != currUsageCache.Exceeded ||
+		oldUsageCache.Clusters != currUsageCache.Clusters ||
+		oldUsageCache.Users != currUsageCache.Users ||
+		oldUsageCache.ResourceCPU != currUsageCache.ResourceCPU ||
+		oldUsageCache.ResourceMemory != currUsageCache.ResourceMemory
+}
+
 // gets the total resource usage across all nodes in all clusters
 func getResourceUsage(opts *GetUsageOpts, clusters []*models.Cluster) (uint, uint, error) {
 	var totCPU, totMem uint = 0, 0