lib/fs: Cache user lookups (#8496)
This commit is contained in:
@@ -7,8 +7,9 @@
|
||||
package fs
|
||||
|
||||
import (
|
||||
"os/user"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/syncthing/syncthing/lib/protocol"
|
||||
)
|
||||
@@ -16,7 +17,7 @@ import (
|
||||
// unixPlatformData is used on all platforms, because apart from being the
|
||||
// implementation for BasicFilesystem on Unixes it's also the implementation
|
||||
// in fakeFS.
|
||||
func unixPlatformData(fs Filesystem, name string) (protocol.PlatformData, error) {
|
||||
func unixPlatformData(fs Filesystem, name string, userCache *userCache, groupCache *groupCache) (protocol.PlatformData, error) {
|
||||
stat, err := fs.Lstat(name)
|
||||
if err != nil {
|
||||
return protocol.PlatformData{}, err
|
||||
@@ -24,8 +25,8 @@ func unixPlatformData(fs Filesystem, name string) (protocol.PlatformData, error)
|
||||
|
||||
ownerUID := stat.Owner()
|
||||
ownerName := ""
|
||||
if u, err := user.LookupId(strconv.Itoa(ownerUID)); err == nil && u.Username != "" {
|
||||
ownerName = u.Username
|
||||
if user := userCache.lookup(strconv.Itoa(ownerUID)); user != nil {
|
||||
ownerName = user.Username
|
||||
} else if ownerUID == 0 {
|
||||
// We couldn't look up a name, but UID zero should be "root". This
|
||||
// fixup works around the (unlikely) situation where the ownership
|
||||
@@ -38,8 +39,8 @@ func unixPlatformData(fs Filesystem, name string) (protocol.PlatformData, error)
|
||||
|
||||
groupID := stat.Group()
|
||||
groupName := ""
|
||||
if g, err := user.LookupGroupId(strconv.Itoa(groupID)); err == nil && g.Name != "" {
|
||||
groupName = g.Name
|
||||
if group := groupCache.lookup(strconv.Itoa(ownerUID)); group != nil {
|
||||
groupName = group.Name
|
||||
} else if groupID == 0 {
|
||||
groupName = "root"
|
||||
}
|
||||
@@ -53,3 +54,39 @@ func unixPlatformData(fs Filesystem, name string) (protocol.PlatformData, error)
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
type valueCache[K comparable, V any] struct {
|
||||
validity time.Duration
|
||||
fill func(K) (V, error)
|
||||
|
||||
mut sync.Mutex
|
||||
cache map[K]cacheEntry[V]
|
||||
}
|
||||
|
||||
type cacheEntry[V any] struct {
|
||||
value V
|
||||
when time.Time
|
||||
}
|
||||
|
||||
func newValueCache[K comparable, V any](validity time.Duration, fill func(K) (V, error)) *valueCache[K, V] {
|
||||
return &valueCache[K, V]{
|
||||
validity: validity,
|
||||
fill: fill,
|
||||
cache: make(map[K]cacheEntry[V]),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *valueCache[K, V]) lookup(key K) V {
|
||||
c.mut.Lock()
|
||||
defer c.mut.Unlock()
|
||||
if e, ok := c.cache[key]; ok && time.Since(e.when) < c.validity {
|
||||
return e.value
|
||||
}
|
||||
var e cacheEntry[V]
|
||||
if val, err := c.fill(key); err == nil {
|
||||
e.value = val
|
||||
}
|
||||
e.when = time.Now()
|
||||
c.cache[key] = e
|
||||
return e.value
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user