storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/pkg/bucket/bandwidth/monitor.go (about)

     1  /*
     2   * MinIO Cloud Storage, (C) 2020 MinIO, Inc.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package bandwidth
    18  
    19  import (
    20  	"context"
    21  	"sync"
    22  	"time"
    23  
    24  	"storj.io/minio/pkg/bandwidth"
    25  )
    26  
    27  // throttleBandwidth gets the throttle for bucket with the configured value
    28  func (m *Monitor) throttleBandwidth(ctx context.Context, bucket string, bandwidthBytesPerSecond int64, clusterBandwidth int64) *throttle {
    29  	m.lock.Lock()
    30  	defer m.lock.Unlock()
    31  	throttle, ok := m.bucketThrottle[bucket]
    32  	if !ok {
    33  		throttle = newThrottle(ctx, bandwidthBytesPerSecond, clusterBandwidth)
    34  		m.bucketThrottle[bucket] = throttle
    35  		return throttle
    36  	}
    37  	throttle.SetBandwidth(bandwidthBytesPerSecond, clusterBandwidth)
    38  	return throttle
    39  }
    40  
    41  // Monitor implements the monitoring for bandwidth measurements.
    42  type Monitor struct {
    43  	lock sync.Mutex // lock for all updates
    44  
    45  	activeBuckets map[string]*bucketMeasurement // Buckets with objects in flight
    46  
    47  	bucketMovingAvgTicker *time.Ticker // Ticker for calculating moving averages
    48  
    49  	bucketThrottle map[string]*throttle
    50  
    51  	doneCh <-chan struct{}
    52  }
    53  
    54  // NewMonitor returns a monitor with defaults.
    55  func NewMonitor(doneCh <-chan struct{}) *Monitor {
    56  	m := &Monitor{
    57  		activeBuckets:         make(map[string]*bucketMeasurement),
    58  		bucketMovingAvgTicker: time.NewTicker(2 * time.Second),
    59  		bucketThrottle:        make(map[string]*throttle),
    60  		doneCh:                doneCh,
    61  	}
    62  	go m.trackEWMA()
    63  	return m
    64  }
    65  
    66  // SelectionFunction for buckets
    67  type SelectionFunction func(bucket string) bool
    68  
    69  // SelectBuckets will select all the buckets passed in.
    70  func SelectBuckets(buckets ...string) SelectionFunction {
    71  	if len(buckets) == 0 {
    72  		return func(bucket string) bool {
    73  			return true
    74  		}
    75  	}
    76  	return func(bucket string) bool {
    77  		for _, b := range buckets {
    78  			if b == "" || b == bucket {
    79  				return true
    80  			}
    81  		}
    82  		return false
    83  	}
    84  }
    85  
    86  // GetReport gets the report for all bucket bandwidth details.
    87  func (m *Monitor) GetReport(selectBucket SelectionFunction) *bandwidth.Report {
    88  	m.lock.Lock()
    89  	defer m.lock.Unlock()
    90  	return m.getReport(selectBucket)
    91  }
    92  
    93  func (m *Monitor) getReport(selectBucket SelectionFunction) *bandwidth.Report {
    94  	report := &bandwidth.Report{
    95  		BucketStats: make(map[string]bandwidth.Details),
    96  	}
    97  	for bucket, bucketMeasurement := range m.activeBuckets {
    98  		if !selectBucket(bucket) {
    99  			continue
   100  		}
   101  		bucketThrottle, ok := m.bucketThrottle[bucket]
   102  		if !ok {
   103  			continue
   104  		}
   105  		report.BucketStats[bucket] = bandwidth.Details{
   106  			LimitInBytesPerSecond:            bucketThrottle.clusterBandwidth,
   107  			CurrentBandwidthInBytesPerSecond: bucketMeasurement.getExpMovingAvgBytesPerSecond(),
   108  		}
   109  	}
   110  	return report
   111  }
   112  
   113  func (m *Monitor) trackEWMA() {
   114  	for {
   115  		select {
   116  		case <-m.bucketMovingAvgTicker.C:
   117  			m.updateMovingAvg()
   118  		case <-m.doneCh:
   119  			return
   120  		}
   121  	}
   122  }
   123  
   124  func (m *Monitor) getBucketMeasurement(bucket string, initTime time.Time) *bucketMeasurement {
   125  	bucketTracker, ok := m.activeBuckets[bucket]
   126  	if !ok {
   127  		bucketTracker = newBucketMeasurement(initTime)
   128  		m.activeBuckets[bucket] = bucketTracker
   129  	}
   130  	return bucketTracker
   131  }
   132  
   133  func (m *Monitor) updateMovingAvg() {
   134  	m.lock.Lock()
   135  	defer m.lock.Unlock()
   136  	for _, bucketMeasurement := range m.activeBuckets {
   137  		bucketMeasurement.updateExponentialMovingAverage(time.Now())
   138  	}
   139  }
   140  
   141  // track returns the measurement object for bucket and object
   142  func (m *Monitor) track(bucket string, object string) *bucketMeasurement {
   143  	m.lock.Lock()
   144  	defer m.lock.Unlock()
   145  	return m.getBucketMeasurement(bucket, time.Now())
   146  }
   147  
   148  // DeleteBucket deletes monitoring the 'bucket'
   149  func (m *Monitor) DeleteBucket(bucket string) {
   150  	m.lock.Lock()
   151  	defer m.lock.Unlock()
   152  	delete(m.activeBuckets, bucket)
   153  	delete(m.bucketThrottle, bucket)
   154  }