mirror of
https://github.com/rclone/rclone.git
synced 2024-12-22 23:22:08 +01:00
vfs: implement LRU-SP cache for more intelligent cache replacement - FIXME WIP
FIXME this needs docs, and maybe needs to be configurable. FIXME needs tests also See: https://forum.rclone.org/t/rclone-vfs-cache-maximum-size-per-file/30037 Fixes: #4110
This commit is contained in:
parent
705e8f2fe0
commit
6f53463c31
@ -618,9 +618,11 @@ func (c *Cache) purgeClean(quota int64) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Make a slice of clean cache files
|
// Make a slice of clean cache files
|
||||||
|
now := time.Now()
|
||||||
for _, item := range c.item {
|
for _, item := range c.item {
|
||||||
if !item.IsDirty() {
|
if !item.IsDirty() {
|
||||||
items = append(items, item)
|
items = append(items, item)
|
||||||
|
item.updateScore(now)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -709,9 +711,11 @@ func (c *Cache) purgeOverQuota(quota int64) {
|
|||||||
var items Items
|
var items Items
|
||||||
|
|
||||||
// Make a slice of unused files
|
// Make a slice of unused files
|
||||||
|
now := time.Now()
|
||||||
for _, item := range c.item {
|
for _, item := range c.item {
|
||||||
if !item.inUse() {
|
if !item.inUse() {
|
||||||
items = append(items, item)
|
items = append(items, item)
|
||||||
|
item.updateScore(now)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,6 +59,7 @@ type Item struct {
|
|||||||
mu sync.Mutex // protect the variables
|
mu sync.Mutex // protect the variables
|
||||||
cond sync.Cond // synchronize with cache cleaner
|
cond sync.Cond // synchronize with cache cleaner
|
||||||
name string // name in the VFS
|
name string // name in the VFS
|
||||||
|
score float64 // score for likelihood of reaping from cache (bigger is more likely)
|
||||||
opens int // number of times file is open
|
opens int // number of times file is open
|
||||||
downloaders *downloaders.Downloaders // a record of the downloaders in action - may be nil
|
downloaders *downloaders.Downloaders // a record of the downloaders in action - may be nil
|
||||||
o fs.Object // object we are caching - may be nil
|
o fs.Object // object we are caching - may be nil
|
||||||
@ -75,6 +76,7 @@ type Info struct {
|
|||||||
ModTime time.Time // last time file was modified
|
ModTime time.Time // last time file was modified
|
||||||
ATime time.Time // last time file was accessed
|
ATime time.Time // last time file was accessed
|
||||||
Size int64 // size of the file
|
Size int64 // size of the file
|
||||||
|
Opens int64 // number of times the file has been opened
|
||||||
Rs ranges.Ranges // which parts of the file are present
|
Rs ranges.Ranges // which parts of the file are present
|
||||||
Fingerprint string // fingerprint of remote object
|
Fingerprint string // fingerprint of remote object
|
||||||
Dirty bool // set if the backing file has been modified
|
Dirty bool // set if the backing file has been modified
|
||||||
@ -103,6 +105,8 @@ func (rr ResetResult) String() string {
|
|||||||
|
|
||||||
func (v Items) Len() int { return len(v) }
|
func (v Items) Len() int { return len(v) }
|
||||||
func (v Items) Swap(i, j int) { v[i], v[j] = v[j], v[i] }
|
func (v Items) Swap(i, j int) { v[i], v[j] = v[j], v[i] }
|
||||||
|
|
||||||
|
// Less implements the caching strategy of the VFS cache.
|
||||||
func (v Items) Less(i, j int) bool {
|
func (v Items) Less(i, j int) bool {
|
||||||
if i == j {
|
if i == j {
|
||||||
return false
|
return false
|
||||||
@ -114,7 +118,7 @@ func (v Items) Less(i, j int) bool {
|
|||||||
jItem.mu.Lock()
|
jItem.mu.Lock()
|
||||||
defer jItem.mu.Unlock()
|
defer jItem.mu.Unlock()
|
||||||
|
|
||||||
return iItem.info.ATime.Before(jItem.info.ATime)
|
return iItem.score > jItem.score
|
||||||
}
|
}
|
||||||
|
|
||||||
// clean the item after its cache file has been deleted
|
// clean the item after its cache file has been deleted
|
||||||
@ -179,6 +183,32 @@ func (item *Item) getATime() time.Time {
|
|||||||
return item.info.ATime
|
return item.info.ATime
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// update the score for the item and return it
|
||||||
|
//
|
||||||
|
// Bigger scores mean more likely to be reaped
|
||||||
|
func (item *Item) updateScore(now time.Time) float64 {
|
||||||
|
item.mu.Lock()
|
||||||
|
defer item.mu.Unlock()
|
||||||
|
accessedAgo := now.Sub(item.info.ATime).Seconds()
|
||||||
|
|
||||||
|
// For LRU cache, score is just how long ago it was accessed
|
||||||
|
// item.score = accessedAgo
|
||||||
|
|
||||||
|
// For LRU-SP cache score is size * accessedAgo / opens
|
||||||
|
opens := float64(item.opens)
|
||||||
|
if opens <= 1 {
|
||||||
|
opens = 1
|
||||||
|
}
|
||||||
|
size := float64(item.info.Rs.Size())
|
||||||
|
if size < 4096 {
|
||||||
|
size = 4096 // minimum size is 1 disk block ish
|
||||||
|
|
||||||
|
}
|
||||||
|
item.score = size * accessedAgo / opens
|
||||||
|
|
||||||
|
return item.score
|
||||||
|
}
|
||||||
|
|
||||||
// getDiskSize returns the size on disk (approximately) of the item
|
// getDiskSize returns the size on disk (approximately) of the item
|
||||||
//
|
//
|
||||||
// We return the sizes of the chunks we have fetched, however there is
|
// We return the sizes of the chunks we have fetched, however there is
|
||||||
@ -526,6 +556,7 @@ func (item *Item) open(o fs.Object) (err error) {
|
|||||||
defer item.mu.Unlock()
|
defer item.mu.Unlock()
|
||||||
|
|
||||||
item.info.ATime = time.Now()
|
item.info.ATime = time.Now()
|
||||||
|
item.info.Opens++
|
||||||
|
|
||||||
osPath, err := item.c.createItemDir(item.name) // No locking in Cache
|
osPath, err := item.c.createItemDir(item.name) // No locking in Cache
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
Loading…
Reference in New Issue
Block a user