github.com/ncw/rclone@v1.48.1-0.20190724201158-a35aa1360e3e/fs/cache/cache.go (about)

     1  // Package cache implements the Fs cache
     2  package cache
     3  
     4  import (
     5  	"sync"
     6  	"time"
     7  
     8  	"github.com/ncw/rclone/fs"
     9  )
    10  
    11  var (
    12  	fsCacheMu           sync.Mutex
    13  	fsCache             = map[string]*cacheEntry{}
    14  	fsNewFs             = fs.NewFs // for tests
    15  	expireRunning       = false
    16  	cacheExpireDuration = 300 * time.Second // expire the cache entry when it is older than this
    17  	cacheExpireInterval = 60 * time.Second  // interval to run the cache expire
    18  )
    19  
    20  type cacheEntry struct {
    21  	f        fs.Fs     // cached f
    22  	err      error     // nil or fs.ErrorIsFile
    23  	fsString string    // remote string
    24  	lastUsed time.Time // time used for expiry
    25  }
    26  
    27  // Get gets a fs.Fs named fsString either from the cache or creates it afresh
    28  func Get(fsString string) (f fs.Fs, err error) {
    29  	fsCacheMu.Lock()
    30  	entry, ok := fsCache[fsString]
    31  	if !ok {
    32  		fsCacheMu.Unlock() // Unlock in case Get is called recursively
    33  		f, err = fsNewFs(fsString)
    34  		if err != nil && err != fs.ErrorIsFile {
    35  			return f, err
    36  		}
    37  		entry = &cacheEntry{
    38  			f:        f,
    39  			fsString: fsString,
    40  			err:      err,
    41  		}
    42  		fsCacheMu.Lock()
    43  		fsCache[fsString] = entry
    44  	}
    45  	defer fsCacheMu.Unlock()
    46  	entry.lastUsed = time.Now()
    47  	if !expireRunning {
    48  		time.AfterFunc(cacheExpireInterval, cacheExpire)
    49  		expireRunning = true
    50  	}
    51  	return entry.f, entry.err
    52  }
    53  
    54  // Put puts an fs.Fs named fsString into the cache
    55  func Put(fsString string, f fs.Fs) {
    56  	fsCacheMu.Lock()
    57  	defer fsCacheMu.Unlock()
    58  	fsCache[fsString] = &cacheEntry{
    59  		f:        f,
    60  		fsString: fsString,
    61  		lastUsed: time.Now(),
    62  	}
    63  	if !expireRunning {
    64  		time.AfterFunc(cacheExpireInterval, cacheExpire)
    65  		expireRunning = true
    66  	}
    67  }
    68  
    69  // cacheExpire expires any entries that haven't been used recently
    70  func cacheExpire() {
    71  	fsCacheMu.Lock()
    72  	defer fsCacheMu.Unlock()
    73  	now := time.Now()
    74  	for fsString, entry := range fsCache {
    75  		if now.Sub(entry.lastUsed) > cacheExpireDuration {
    76  			delete(fsCache, fsString)
    77  		}
    78  	}
    79  	if len(fsCache) != 0 {
    80  		time.AfterFunc(cacheExpireInterval, cacheExpire)
    81  		expireRunning = true
    82  	} else {
    83  		expireRunning = false
    84  	}
    85  }
    86  
    87  // Clear removes everything from the cahce
    88  func Clear() {
    89  	fsCacheMu.Lock()
    90  	for k := range fsCache {
    91  		delete(fsCache, k)
    92  	}
    93  	fsCacheMu.Unlock()
    94  }