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.
master
Klaus Post 5 years ago committed by GitHub
parent de6c286258
commit 95814359bd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 57
      cmd/utils.go
  2. 16
      cmd/xl-sets.go

@ -631,3 +631,60 @@ func iamPolicyClaimNameOpenID() string {
func iamPolicyClaimNameSA() string { func iamPolicyClaimNameSA() string {
return "sa-policy" 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()
}

@ -95,6 +95,8 @@ type xlSets struct {
// Distribution algorithm of choice. // Distribution algorithm of choice.
distributionAlgo string distributionAlgo string
disksStorageInfoCache timedValue
// Merge tree walk // Merge tree walk
pool *MergeWalkPool pool *MergeWalkPool
poolSplunk *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. // 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 { 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 var storageInfo StorageInfo
storageInfos := make([]StorageInfo, len(s.sets)) storageInfos := make([]StorageInfo, len(s.sets))

Loading…
Cancel
Save