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 }