cache disk info to avoid repeated calls (#9682)

This value is requested on every upload when there are multiple zones.

Since this will result in an RPC call to every remote disk this scales 
quite badly in a distributed setup. Load every 1second interval.

2 servers, localhost only. In large distributed setups much bigger 
gains can be expected.

```
Operations: 21743 -> 22454
* Average: +3.28% (+0.0 MiB/s) throughput, +3.28% (+11.9) obj/s
* Fastest: +3.37% (+0.0 MiB/s) throughput, +3.37% (+13.0) obj/s
* 50% Median: +3.03% (+0.0 MiB/s) throughput, +3.03% (+11.2) obj/s
* Slowest: +8.03% (+0.0 MiB/s) throughput, +8.03% (+22.8) obj/s
```

For easy management of this a generic helper has been added.
This commit is contained in:
Klaus Post 2020-05-26 12:52:24 -07:00 committed by GitHub
parent de6c286258
commit 95814359bd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 73 additions and 0 deletions

View File

@ -631,3 +631,60 @@ func iamPolicyClaimNameOpenID() string {
func iamPolicyClaimNameSA() string {
return "sa-policy"
}
// timedValue contains a synchronized value that is considered valid
// for a specific amount of time.
// An Update function must be set to provide an updated value when needed.
type timedValue struct {
// Update must return an updated value.
// If an error is returned the cached value is not set.
// Only one caller will call this function at any time, others will be blocking.
// The returned value can no longer be modified once returned.
// Should be set before calling Get().
Update func() (interface{}, error)
// TTL for a cached value.
// If not set 1 second TTL is assumed.
// Should be set before calling Get().
TTL time.Duration
// Once can be used to initialize values for lazy initialization.
// Should be set before calling Get().
Once sync.Once
// Managed values.
value interface{}
lastUpdate time.Time
mu sync.Mutex
}
// Get will return a cached value or fetch a new one.
// If the Update function returns an error the value is forwarded as is and not cached.
func (t *timedValue) Get() (interface{}, error) {
t.mu.Lock()
defer t.mu.Unlock()
if t.TTL <= 0 {
t.TTL = time.Second
}
if t.value != nil {
if time.Since(t.lastUpdate) < t.TTL {
v := t.value
return v, nil
}
t.value = nil
}
v, err := t.Update()
if err != nil {
return v, err
}
t.value = v
t.lastUpdate = time.Now()
return v, nil
}
// Invalidate the value in the cache.
func (t *timedValue) Invalidate() {
t.mu.Lock()
t.value = nil
t.mu.Unlock()
}

View File

@ -95,6 +95,8 @@ type xlSets struct {
// Distribution algorithm of choice.
distributionAlgo string
disksStorageInfoCache timedValue
// Merge tree walk
pool *MergeWalkPool
poolSplunk *MergeWalkPool
@ -368,7 +370,21 @@ func (s *xlSets) NewNSLock(ctx context.Context, bucket string, objects ...string
}
// StorageInfo - combines output of StorageInfo across all erasure coded object sets.
// Caches values for 1 second.
func (s *xlSets) StorageInfo(ctx context.Context, local bool) StorageInfo {
s.disksStorageInfoCache.Once.Do(func() {
s.disksStorageInfoCache.TTL = time.Second
s.disksStorageInfoCache.Update = func() (interface{}, error) {
return s.storageInfo(ctx, local), nil
}
})
v, _ := s.disksStorageInfoCache.Get()
return v.(StorageInfo)
}
// storageInfo - combines output of StorageInfo across all erasure coded object sets.
// Use StorageInfo for a cached version.
func (s *xlSets) storageInfo(ctx context.Context, local bool) StorageInfo {
var storageInfo StorageInfo
storageInfos := make([]StorageInfo, len(s.sets))