storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/pkg/disk/health.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  
    18  package disk
    19  
    20  import (
    21  	"context"
    22  	"fmt"
    23  	"os"
    24  	"time"
    25  
    26  	"github.com/dustin/go-humanize"
    27  	"github.com/montanaflynn/stats"
    28  )
    29  
    30  // Latency holds latency information for write operations to the drive
    31  type Latency struct {
    32  	Avg          float64 `json:"avg_secs,omitempty"`
    33  	Percentile50 float64 `json:"percentile50_secs,omitempty"`
    34  	Percentile90 float64 `json:"percentile90_secs,omitempty"`
    35  	Percentile99 float64 `json:"percentile99_secs,omitempty"`
    36  	Min          float64 `json:"min_secs,omitempty"`
    37  	Max          float64 `json:"max_secs,omitempty"`
    38  }
    39  
    40  // Throughput holds throughput information for write operations to the drive
    41  type Throughput struct {
    42  	Avg          float64 `json:"avg_bytes_per_sec,omitempty"`
    43  	Percentile50 float64 `json:"percentile50_bytes_per_sec,omitempty"`
    44  	Percentile90 float64 `json:"percentile90_bytes_per_sec,omitempty"`
    45  	Percentile99 float64 `json:"percentile99_bytes_per_sec,omitempty"`
    46  	Min          float64 `json:"min_bytes_per_sec,omitempty"`
    47  	Max          float64 `json:"max_bytes_per_sec,omitempty"`
    48  }
    49  
    50  // GetHealthInfo about the drive
    51  func GetHealthInfo(ctx context.Context, drive, fsPath string) (Latency, Throughput, error) {
    52  
    53  	// Create a file with O_DIRECT flag, choose default umask and also make sure
    54  	// we are exclusively writing to a new file using O_EXCL.
    55  	w, err := OpenFileDirectIO(fsPath, os.O_CREATE|os.O_WRONLY|os.O_EXCL, 0666)
    56  	if err != nil {
    57  		return Latency{}, Throughput{}, err
    58  	}
    59  
    60  	defer func() {
    61  		w.Close()
    62  		os.Remove(fsPath)
    63  	}()
    64  
    65  	blockSize := 4 * humanize.MiByte
    66  	fileSize := 256 * humanize.MiByte
    67  
    68  	latencies := make([]float64, fileSize/blockSize)
    69  	throughputs := make([]float64, fileSize/blockSize)
    70  
    71  	data := AlignedBlock(blockSize)
    72  
    73  	for i := 0; i < (fileSize / blockSize); i++ {
    74  		if ctx.Err() != nil {
    75  			return Latency{}, Throughput{}, ctx.Err()
    76  		}
    77  		startTime := time.Now()
    78  		if n, err := w.Write(data); err != nil {
    79  			return Latency{}, Throughput{}, err
    80  		} else if n != blockSize {
    81  			return Latency{}, Throughput{}, fmt.Errorf("Expected to write %d, but only wrote %d", blockSize, n)
    82  		}
    83  		latencyInSecs := time.Since(startTime).Seconds()
    84  		latencies[i] = latencyInSecs
    85  	}
    86  
    87  	// Sync every full writes fdatasync
    88  	Fdatasync(w)
    89  
    90  	for i := range latencies {
    91  		throughput := float64(blockSize) / latencies[i]
    92  		throughputs[i] = throughput
    93  	}
    94  
    95  	var avgLatency float64
    96  	var percentile50Latency float64
    97  	var percentile90Latency float64
    98  	var percentile99Latency float64
    99  	var minLatency float64
   100  	var maxLatency float64
   101  
   102  	var avgThroughput float64
   103  	var percentile50Throughput float64
   104  	var percentile90Throughput float64
   105  	var percentile99Throughput float64
   106  	var minThroughput float64
   107  	var maxThroughput float64
   108  
   109  	if avgLatency, err = stats.Mean(latencies); err != nil {
   110  		return Latency{}, Throughput{}, err
   111  	}
   112  	if percentile50Latency, err = stats.Percentile(latencies, 50); err != nil {
   113  		return Latency{}, Throughput{}, err
   114  	}
   115  	if percentile90Latency, err = stats.Percentile(latencies, 90); err != nil {
   116  		return Latency{}, Throughput{}, err
   117  	}
   118  	if percentile99Latency, err = stats.Percentile(latencies, 99); err != nil {
   119  		return Latency{}, Throughput{}, err
   120  	}
   121  	if maxLatency, err = stats.Max(latencies); err != nil {
   122  		return Latency{}, Throughput{}, err
   123  	}
   124  	if minLatency, err = stats.Min(latencies); err != nil {
   125  		return Latency{}, Throughput{}, err
   126  	}
   127  	l := Latency{
   128  		Avg:          avgLatency,
   129  		Percentile50: percentile50Latency,
   130  		Percentile90: percentile90Latency,
   131  		Percentile99: percentile99Latency,
   132  		Min:          minLatency,
   133  		Max:          maxLatency,
   134  	}
   135  
   136  	if avgThroughput, err = stats.Mean(throughputs); err != nil {
   137  		return Latency{}, Throughput{}, err
   138  	}
   139  	if percentile50Throughput, err = stats.Percentile(throughputs, 50); err != nil {
   140  		return Latency{}, Throughput{}, err
   141  	}
   142  	if percentile90Throughput, err = stats.Percentile(throughputs, 90); err != nil {
   143  		return Latency{}, Throughput{}, err
   144  	}
   145  	if percentile99Throughput, err = stats.Percentile(throughputs, 99); err != nil {
   146  		return Latency{}, Throughput{}, err
   147  	}
   148  	if maxThroughput, err = stats.Max(throughputs); err != nil {
   149  		return Latency{}, Throughput{}, err
   150  	}
   151  	if minThroughput, err = stats.Min(throughputs); err != nil {
   152  		return Latency{}, Throughput{}, err
   153  	}
   154  
   155  	t := Throughput{
   156  		Avg:          avgThroughput,
   157  		Percentile50: percentile50Throughput,
   158  		Percentile90: percentile90Throughput,
   159  		Percentile99: percentile99Throughput,
   160  		Min:          minThroughput,
   161  		Max:          maxThroughput,
   162  	}
   163  
   164  	return l, t, nil
   165  }