github.com/NVIDIA/aistore@v1.3.23-0.20240517131212-7df6609be51d/core/lcache.go (about)

     1  // Package core provides core metadata and in-cluster API
     2  /*
     3   * Copyright (c) 2018-2024, NVIDIA CORPORATION. All rights reserved.
     4   */
     5  package core
     6  
     7  import (
     8  	"sync"
     9  	"time"
    10  
    11  	"github.com/NVIDIA/aistore/cmn"
    12  	"github.com/NVIDIA/aistore/cmn/atomic"
    13  	"github.com/NVIDIA/aistore/cmn/cos"
    14  	"github.com/NVIDIA/aistore/cmn/nlog"
    15  	"github.com/NVIDIA/aistore/core/meta"
    16  	"github.com/NVIDIA/aistore/fs"
    17  	"github.com/NVIDIA/aistore/hk"
    18  	"github.com/NVIDIA/aistore/memsys"
    19  	"github.com/NVIDIA/aistore/sys"
    20  )
    21  
    22  const (
    23  	oomEvictAtime = time.Minute * 5  // OOM
    24  	mpeEvictAtime = time.Minute * 10 // extreme
    25  	mphEvictAtime = time.Minute * 20 // high
    26  	mpnEvictAtime = time.Hour        // normal
    27  
    28  	iniEvictAtime = mpnEvictAtime / 2 // initial
    29  	maxEvictAtime = mpnEvictAtime * 2 // maximum
    30  )
    31  
    32  type lchk struct {
    33  	cache *sync.Map
    34  	// runtime
    35  	now      time.Time
    36  	d        time.Duration
    37  	totalCnt int64
    38  	// stats
    39  	evictedCnt   int64
    40  	flushColdCnt int64
    41  	// single entry
    42  	running atomic.Bool
    43  }
    44  
    45  func regLomCacheWithHK() {
    46  	g.lchk.running.Store(false)
    47  	hk.Reg("lcache"+hk.NameSuffix, g.lchk.housekeep, iniEvictAtime)
    48  }
    49  
    50  //
    51  // evictions
    52  //
    53  
    54  func UncacheBck(b *meta.Bck) {
    55  	var (
    56  		caches = lomCaches()
    57  		n      = max(sys.NumCPU()/4, 4)
    58  		wg     = cos.NewLimitedWaitGroup(n, len(caches))
    59  	)
    60  	for _, lcache := range caches {
    61  		wg.Add(1)
    62  		go func(cache *sync.Map) {
    63  			cache.Range(func(hkey, value any) bool {
    64  				lmd := value.(*lmeta)
    65  				bck, _ := cmn.ParseUname(lmd.uname)
    66  				if bck.Equal((*cmn.Bck)(b)) {
    67  					cache.Delete(hkey)
    68  				}
    69  				return true
    70  			})
    71  			wg.Done()
    72  		}(lcache)
    73  	}
    74  	wg.Wait()
    75  }
    76  
    77  // NOTE: watch https://github.com/golang/go/pull/61702 for `sync.Map.Clear`, likely Go 22
    78  func UncacheMountpath(mi *fs.Mountpath) {
    79  	for idx := range cos.MultiSyncMapCount {
    80  		cache := mi.LomCache(idx)
    81  		cache.Range(func(hkey any, _ any) bool {
    82  			cache.Delete(hkey)
    83  			return true
    84  		})
    85  	}
    86  }
    87  
    88  //////////
    89  // lchk //
    90  //////////
    91  
    92  func (lchk *lchk) housekeep() (d time.Duration) {
    93  	var tag string
    94  	d, tag = lchk.mp()
    95  	if !lchk.running.CAS(false, true) {
    96  		if tag != "" {
    97  			nlog.Infof("running now: memory pressure %q, next sched %v", tag, d)
    98  		}
    99  		return
   100  	}
   101  	go lchk.evictAll(d /*evict older than*/)
   102  	return
   103  }
   104  
   105  const termDuration = time.Duration(-1)
   106  
   107  func (lchk *lchk) terminating() bool { return lchk.d == termDuration }
   108  
   109  func (*lchk) mp() (d time.Duration, tag string) {
   110  	p := g.pmm.Pressure()
   111  	switch p {
   112  	case memsys.OOM:
   113  		d = oomEvictAtime
   114  		tag = "OOM"
   115  	case memsys.PressureExtreme:
   116  		d = mpeEvictAtime
   117  		tag = "extreme"
   118  	case memsys.PressureHigh:
   119  		d = mphEvictAtime
   120  		tag = "high"
   121  	default:
   122  		d = mpnEvictAtime
   123  	}
   124  	return
   125  }
   126  
   127  func (lchk *lchk) evictAll(d time.Duration) {
   128  	lchk.now = time.Now()
   129  	lchk.d = d
   130  
   131  	lchk.evictedCnt, lchk.flushColdCnt, lchk.totalCnt = 0, 0, 0
   132  
   133  	// single-threaded: one cache at a time
   134  	caches := lomCaches()
   135  	if lchk.terminating() {
   136  		for _, cache := range caches {
   137  			lchk.cache = cache
   138  			cache.Range(lchk.fterm)
   139  		}
   140  		return
   141  	}
   142  
   143  	for _, cache := range caches {
   144  		lchk.cache = cache
   145  		cache.Range(lchk.frun)
   146  	}
   147  
   148  	if _, tag := lchk.mp(); tag != "" {
   149  		nlog.Infof("memory pressure %q, total %d, evicted %d", tag, lchk.totalCnt, lchk.evictedCnt)
   150  	}
   151  
   152  	// stats
   153  	g.tstats.Add(LcacheEvictedCount, lchk.evictedCnt)
   154  	g.tstats.Add(LcacheFlushColdCount, lchk.flushColdCnt)
   155  
   156  	lchk.running.Store(false)
   157  }
   158  
   159  func (lchk *lchk) fterm(_, value any) bool {
   160  	md := value.(*lmeta)
   161  	if md.Atime < 0 {
   162  		// prefetched, not yet accessed
   163  		lchk.flush(md, time.Unix(0, -md.Atime))
   164  		return true
   165  	}
   166  	if md.isDirty() || md.atimefs != uint64(md.Atime) {
   167  		lchk.flush(md, time.Unix(0, md.Atime))
   168  	}
   169  	return true
   170  }
   171  
   172  func (lchk *lchk) frun(hkey, value any) bool {
   173  	var (
   174  		md     = value.(*lmeta)
   175  		mdTime = md.Atime
   176  	)
   177  	lchk.totalCnt++
   178  
   179  	if mdTime < 0 {
   180  		// prefetched, not yet accessed
   181  		mdTime = -mdTime
   182  	}
   183  	atime := time.Unix(0, mdTime)
   184  	elapsed := lchk.now.Sub(atime)
   185  	if elapsed < lchk.d {
   186  		return true
   187  	}
   188  	if md.isDirty() {
   189  		if lchk.d > mphEvictAtime && elapsed < maxEvictAtime {
   190  			return true
   191  		}
   192  		lchk.flush(md, atime)
   193  	} else if md.atimefs != uint64(md.Atime) {
   194  		lchk.flush(md, atime)
   195  	}
   196  	lchk.cache.Delete(hkey)
   197  	lchk.evictedCnt++
   198  	return true
   199  }
   200  
   201  func (lchk *lchk) flush(md *lmeta, atime time.Time) {
   202  	lif := LIF{Uname: md.uname, BID: md.bckID}
   203  	lom, err := lif.LOM()
   204  	if err == nil {
   205  		lom.Lock(true)
   206  		lom.flushCold(md, atime)
   207  		lom.Unlock(true)
   208  		FreeLOM(lom)
   209  		lchk.flushColdCnt++
   210  	}
   211  }