storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/pkg/bucket/bandwidth/measurement.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  	"sync"
    21  	"sync/atomic"
    22  	"time"
    23  )
    24  
    25  const (
    26  	// betaBucket is the weight used to calculate exponential moving average
    27  	betaBucket = 0.1 // Number of averages considered = 1/(1-betaObject)
    28  )
    29  
    30  // bucketMeasurement captures the bandwidth details for one bucket
    31  type bucketMeasurement struct {
    32  	lock                 sync.Mutex
    33  	bytesSinceLastWindow uint64    // Total bytes since last window was processed
    34  	startTime            time.Time // Start time for window
    35  	expMovingAvg         float64   // Previously calculate sliding window
    36  }
    37  
    38  // newBucketMeasurement creates a new instance of the measurement with the initial start time.
    39  func newBucketMeasurement(initTime time.Time) *bucketMeasurement {
    40  	return &bucketMeasurement{
    41  		startTime: initTime,
    42  	}
    43  }
    44  
    45  // incrementBytes add bytes reported for a bucket.
    46  func (m *bucketMeasurement) incrementBytes(bytes uint64) {
    47  	atomic.AddUint64(&m.bytesSinceLastWindow, bytes)
    48  }
    49  
    50  // updateExponentialMovingAverage processes the measurements captured so far.
    51  func (m *bucketMeasurement) updateExponentialMovingAverage(endTime time.Time) {
    52  	// Calculate aggregate avg bandwidth and exp window avg
    53  	m.lock.Lock()
    54  	defer func() {
    55  		m.startTime = endTime
    56  		m.lock.Unlock()
    57  	}()
    58  
    59  	if endTime.Before(m.startTime) {
    60  		return
    61  	}
    62  
    63  	duration := endTime.Sub(m.startTime)
    64  
    65  	bytesSinceLastWindow := atomic.SwapUint64(&m.bytesSinceLastWindow, 0)
    66  
    67  	if m.expMovingAvg == 0 {
    68  		// Should address initial calculation and should be fine for resuming from 0
    69  		m.expMovingAvg = float64(bytesSinceLastWindow) / duration.Seconds()
    70  		return
    71  	}
    72  
    73  	increment := float64(bytesSinceLastWindow) / duration.Seconds()
    74  	m.expMovingAvg = exponentialMovingAverage(betaBucket, m.expMovingAvg, increment)
    75  }
    76  
    77  // exponentialMovingAverage calculates the exponential moving average
    78  func exponentialMovingAverage(beta, previousAvg, incrementAvg float64) float64 {
    79  	return (1-beta)*incrementAvg + beta*previousAvg
    80  }
    81  
    82  // getExpMovingAvgBytesPerSecond returns the exponential moving average for the bucket in bytes
    83  func (m *bucketMeasurement) getExpMovingAvgBytesPerSecond() float64 {
    84  	m.lock.Lock()
    85  	defer m.lock.Unlock()
    86  	return m.expMovingAvg
    87  }