k8s.io/test-infra@v0.0.0-20240520184403-27c6b4c223d8/greenhouse/eviction.go (about)

     1  /*
     2  Copyright 2018 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package main
    18  
    19  import (
    20  	"sort"
    21  	"time"
    22  
    23  	"github.com/sirupsen/logrus"
    24  	"k8s.io/test-infra/greenhouse/diskcache"
    25  	"k8s.io/test-infra/greenhouse/diskutil"
    26  )
    27  
    28  // monitorDiskAndEvict loops monitoring the disk, evicting cache entries
    29  // when the disk passes either minPercentBlocksFree until the disk is above
    30  // evictUntilPercentBlocksFree
    31  func monitorDiskAndEvict(
    32  	c *diskcache.Cache,
    33  	interval time.Duration,
    34  	minPercentBlocksFree, evictUntilPercentBlocksFree float64,
    35  ) {
    36  	diskRoot := c.DiskRoot()
    37  	// forever check if usage is past thresholds and evict
    38  	ticker := time.NewTicker(interval)
    39  	for ; true; <-ticker.C {
    40  		blocksFree, _, _, _, _, _, err := diskutil.GetDiskUsage(diskRoot)
    41  		if err != nil {
    42  			logrus.WithError(err).Error("Failed to get disk usage!")
    43  			continue
    44  		}
    45  		logger := logrus.WithFields(logrus.Fields{
    46  			"sync-loop":   "MonitorDiskAndEvict",
    47  			"blocks-free": blocksFree,
    48  		})
    49  		logger.Info("tick")
    50  		// if we are past the threshold, start evicting
    51  		if blocksFree < minPercentBlocksFree {
    52  			logger.Warn("Eviction triggered")
    53  			// get all cache entries and sort by lastaccess
    54  			// so we can pop entries until we have evicted enough
    55  			files := c.GetEntries()
    56  			sort.Slice(files, func(i, j int) bool {
    57  				return files[i].LastAccess.Before(files[j].LastAccess)
    58  			})
    59  			// evict until we pass the safe threshold so we don't thrash at the eviction trigger
    60  			for blocksFree < evictUntilPercentBlocksFree {
    61  				if len(files) < 1 {
    62  					logger.Fatal("Failed to find entries to evict!")
    63  				}
    64  				// pop entry and delete
    65  				var entry diskcache.EntryInfo
    66  				entry, files = files[0], files[1:]
    67  				err = c.Delete(c.PathToKey(entry.Path))
    68  				if err != nil {
    69  					logger.WithError(err).Errorf("Error deleting entry at path: %v", entry.Path)
    70  				} else {
    71  					promMetrics.FilesEvicted.Inc()
    72  					promMetrics.LastEvictedAccessAge.Set(time.Since(entry.LastAccess).Hours())
    73  				}
    74  				// get new disk usage
    75  				blocksFree, _, _, _, _, _, err = diskutil.GetDiskUsage(diskRoot)
    76  				logger = logrus.WithFields(logrus.Fields{
    77  					"sync-loop":   "MonitorDiskAndEvict",
    78  					"blocks-free": blocksFree,
    79  				})
    80  				if err != nil {
    81  					logrus.WithError(err).Error("Failed to get disk usage!")
    82  					continue
    83  				}
    84  			}
    85  			logger.Info("Done evicting")
    86  		}
    87  	}
    88  }