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 }