| 
									
										
										
										
											2021-11-18 04:10:57 +08:00
										 |  |  | // Copyright (c) 2015-2021 MinIO, Inc.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This file is part of MinIO Object Storage stack
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This program is free software: you can redistribute it and/or modify
 | 
					
						
							|  |  |  | // it under the terms of the GNU Affero General Public License as published by
 | 
					
						
							|  |  |  | // the Free Software Foundation, either version 3 of the License, or
 | 
					
						
							|  |  |  | // (at your option) any later version.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This program is distributed in the hope that it will be useful
 | 
					
						
							|  |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					
						
							|  |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					
						
							|  |  |  | // GNU Affero General Public License for more details.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // You should have received a copy of the GNU Affero General Public License
 | 
					
						
							|  |  |  | // along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-26 08:31:44 +08:00
										 |  |  | //go:generate msgp -file=$GOFILE -unexported
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-18 04:10:57 +08:00
										 |  |  | package cmd | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2022-07-06 05:45:49 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/minio/madmin-go" | 
					
						
							| 
									
										
										
										
											2021-11-18 04:10:57 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const ( | 
					
						
							|  |  |  | 	sizeLessThan1KiB = iota | 
					
						
							|  |  |  | 	sizeLessThan1MiB | 
					
						
							|  |  |  | 	sizeLessThan10MiB | 
					
						
							|  |  |  | 	sizeLessThan100MiB | 
					
						
							|  |  |  | 	sizeLessThan1GiB | 
					
						
							|  |  |  | 	sizeGreaterThan1GiB | 
					
						
							|  |  |  | 	// Add new entries here
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	sizeLastElemMarker | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // sizeToTag converts a size to a tag.
 | 
					
						
							|  |  |  | func sizeToTag(size int64) int { | 
					
						
							|  |  |  | 	switch { | 
					
						
							|  |  |  | 	case size < 1024: | 
					
						
							|  |  |  | 		return sizeLessThan1KiB | 
					
						
							|  |  |  | 	case size < 1024*1024: | 
					
						
							|  |  |  | 		return sizeLessThan1MiB | 
					
						
							|  |  |  | 	case size < 10*1024*1024: | 
					
						
							|  |  |  | 		return sizeLessThan10MiB | 
					
						
							|  |  |  | 	case size < 100*1024*1024: | 
					
						
							|  |  |  | 		return sizeLessThan100MiB | 
					
						
							|  |  |  | 	case size < 1024*1024*1024: | 
					
						
							|  |  |  | 		return sizeLessThan1GiB | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		return sizeGreaterThan1GiB | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func sizeTagToString(tag int) string { | 
					
						
							|  |  |  | 	switch tag { | 
					
						
							|  |  |  | 	case sizeLessThan1KiB: | 
					
						
							|  |  |  | 		return "LESS_THAN_1_KiB" | 
					
						
							|  |  |  | 	case sizeLessThan1MiB: | 
					
						
							|  |  |  | 		return "LESS_THAN_1_MiB" | 
					
						
							|  |  |  | 	case sizeLessThan10MiB: | 
					
						
							|  |  |  | 		return "LESS_THAN_10_MiB" | 
					
						
							|  |  |  | 	case sizeLessThan100MiB: | 
					
						
							|  |  |  | 		return "LESS_THAN_100_MiB" | 
					
						
							|  |  |  | 	case sizeLessThan1GiB: | 
					
						
							|  |  |  | 		return "LESS_THAN_1_GiB" | 
					
						
							|  |  |  | 	case sizeGreaterThan1GiB: | 
					
						
							|  |  |  | 		return "GREATER_THAN_1_GiB" | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		return "unknown" | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // AccElem holds information for calculating an average value
 | 
					
						
							|  |  |  | type AccElem struct { | 
					
						
							|  |  |  | 	Total int64 | 
					
						
							| 
									
										
										
										
											2022-07-06 05:45:49 +08:00
										 |  |  | 	Size  int64 | 
					
						
							| 
									
										
										
										
											2021-11-18 04:10:57 +08:00
										 |  |  | 	N     int64 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-26 08:31:44 +08:00
										 |  |  | // Add a duration to a single element.
 | 
					
						
							| 
									
										
										
										
											2021-11-18 04:10:57 +08:00
										 |  |  | func (a *AccElem) add(dur time.Duration) { | 
					
						
							| 
									
										
										
										
											2022-07-06 05:45:49 +08:00
										 |  |  | 	if dur < 0 { | 
					
						
							|  |  |  | 		dur = 0 | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-11-18 04:10:57 +08:00
										 |  |  | 	a.Total += int64(dur) | 
					
						
							|  |  |  | 	a.N++ | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-06 05:45:49 +08:00
										 |  |  | // Add a duration to a single element.
 | 
					
						
							|  |  |  | func (a *AccElem) addSize(dur time.Duration, sz int64) { | 
					
						
							|  |  |  | 	if dur < 0 { | 
					
						
							|  |  |  | 		dur = 0 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	a.Total += int64(dur) | 
					
						
							|  |  |  | 	a.Size += sz | 
					
						
							|  |  |  | 	a.N++ | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-26 08:31:44 +08:00
										 |  |  | // Merge b into a.
 | 
					
						
							| 
									
										
										
										
											2021-11-18 04:10:57 +08:00
										 |  |  | func (a *AccElem) merge(b AccElem) { | 
					
						
							|  |  |  | 	a.N += b.N | 
					
						
							|  |  |  | 	a.Total += b.Total | 
					
						
							| 
									
										
										
										
											2022-07-06 05:45:49 +08:00
										 |  |  | 	a.Size += b.Size | 
					
						
							| 
									
										
										
										
											2021-11-18 04:10:57 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-06 05:45:49 +08:00
										 |  |  | // Avg returns average time spent.
 | 
					
						
							|  |  |  | func (a AccElem) avg() time.Duration { | 
					
						
							| 
									
										
										
										
											2021-11-18 04:10:57 +08:00
										 |  |  | 	if a.N >= 1 && a.Total > 0 { | 
					
						
							| 
									
										
										
										
											2022-07-06 05:45:49 +08:00
										 |  |  | 		return time.Duration(a.Total / a.N) | 
					
						
							| 
									
										
										
										
											2021-11-18 04:10:57 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return 0 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-06 05:45:49 +08:00
										 |  |  | // asTimedAction returns the element as a madmin.TimedAction.
 | 
					
						
							|  |  |  | func (a AccElem) asTimedAction() madmin.TimedAction { | 
					
						
							|  |  |  | 	return madmin.TimedAction{AccTime: uint64(a.Total), Count: uint64(a.N), Bytes: uint64(a.Size)} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-26 08:31:44 +08:00
										 |  |  | // lastMinuteLatency keeps track of last minute latency.
 | 
					
						
							|  |  |  | type lastMinuteLatency struct { | 
					
						
							|  |  |  | 	Totals  [60]AccElem | 
					
						
							| 
									
										
										
										
											2021-11-18 04:10:57 +08:00
										 |  |  | 	LastSec int64 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-26 08:31:44 +08:00
										 |  |  | // Merge data of two lastMinuteLatency structure
 | 
					
						
							|  |  |  | func (l lastMinuteLatency) merge(o lastMinuteLatency) (merged lastMinuteLatency) { | 
					
						
							| 
									
										
										
										
											2021-12-18 07:33:13 +08:00
										 |  |  | 	if l.LastSec > o.LastSec { | 
					
						
							|  |  |  | 		o.forwardTo(l.LastSec) | 
					
						
							|  |  |  | 		merged.LastSec = l.LastSec | 
					
						
							| 
									
										
										
										
											2021-11-18 04:10:57 +08:00
										 |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2021-12-18 07:33:13 +08:00
										 |  |  | 		l.forwardTo(o.LastSec) | 
					
						
							|  |  |  | 		merged.LastSec = o.LastSec | 
					
						
							| 
									
										
										
										
											2021-11-18 04:10:57 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-18 07:33:13 +08:00
										 |  |  | 	for i := range merged.Totals { | 
					
						
							| 
									
										
										
										
											2022-01-26 08:31:44 +08:00
										 |  |  | 		merged.Totals[i] = AccElem{ | 
					
						
							|  |  |  | 			Total: l.Totals[i].Total + o.Totals[i].Total, | 
					
						
							|  |  |  | 			N:     l.Totals[i].N + o.Totals[i].N, | 
					
						
							| 
									
										
										
										
											2022-07-06 05:45:49 +08:00
										 |  |  | 			Size:  l.Totals[i].Size + o.Totals[i].Size, | 
					
						
							| 
									
										
										
										
											2021-11-18 04:10:57 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return merged | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-26 08:31:44 +08:00
										 |  |  | // Add  a new duration data
 | 
					
						
							|  |  |  | func (l *lastMinuteLatency) add(t time.Duration) { | 
					
						
							| 
									
										
										
										
											2021-11-18 04:10:57 +08:00
										 |  |  | 	sec := time.Now().Unix() | 
					
						
							|  |  |  | 	l.forwardTo(sec) | 
					
						
							|  |  |  | 	winIdx := sec % 60 | 
					
						
							| 
									
										
										
										
											2022-01-26 08:31:44 +08:00
										 |  |  | 	l.Totals[winIdx].add(t) | 
					
						
							| 
									
										
										
										
											2021-11-18 04:10:57 +08:00
										 |  |  | 	l.LastSec = sec | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-06 05:45:49 +08:00
										 |  |  | // Add  a new duration data
 | 
					
						
							|  |  |  | func (l *lastMinuteLatency) addSize(t time.Duration, sz int64) { | 
					
						
							|  |  |  | 	sec := time.Now().Unix() | 
					
						
							|  |  |  | 	l.forwardTo(sec) | 
					
						
							|  |  |  | 	winIdx := sec % 60 | 
					
						
							|  |  |  | 	l.Totals[winIdx].addSize(t, sz) | 
					
						
							|  |  |  | 	l.LastSec = sec | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-26 08:31:44 +08:00
										 |  |  | // Merge all recorded latencies of last minute into one
 | 
					
						
							| 
									
										
										
										
											2022-07-06 05:45:49 +08:00
										 |  |  | func (l *lastMinuteLatency) getTotal() AccElem { | 
					
						
							| 
									
										
										
										
											2022-01-26 08:31:44 +08:00
										 |  |  | 	var res AccElem | 
					
						
							| 
									
										
										
										
											2021-11-18 04:10:57 +08:00
										 |  |  | 	sec := time.Now().Unix() | 
					
						
							|  |  |  | 	l.forwardTo(sec) | 
					
						
							| 
									
										
										
										
											2022-01-26 08:31:44 +08:00
										 |  |  | 	for _, elem := range l.Totals[:] { | 
					
						
							|  |  |  | 		res.merge(elem) | 
					
						
							| 
									
										
										
										
											2021-11-18 04:10:57 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return res | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // forwardTo time t, clearing any entries in between.
 | 
					
						
							| 
									
										
										
										
											2022-01-26 08:31:44 +08:00
										 |  |  | func (l *lastMinuteLatency) forwardTo(t int64) { | 
					
						
							| 
									
										
										
										
											2021-11-18 04:10:57 +08:00
										 |  |  | 	if l.LastSec >= t { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if t-l.LastSec >= 60 { | 
					
						
							| 
									
										
										
										
											2022-01-26 08:31:44 +08:00
										 |  |  | 		l.Totals = [60]AccElem{} | 
					
						
							| 
									
										
										
										
											2021-11-18 04:10:57 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	for l.LastSec != t { | 
					
						
							|  |  |  | 		// Clear next element.
 | 
					
						
							|  |  |  | 		idx := (l.LastSec + 1) % 60 | 
					
						
							| 
									
										
										
										
											2022-01-26 08:31:44 +08:00
										 |  |  | 		l.Totals[idx] = AccElem{} | 
					
						
							| 
									
										
										
										
											2021-11-18 04:10:57 +08:00
										 |  |  | 		l.LastSec++ | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2022-01-26 08:31:44 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-06 05:45:49 +08:00
										 |  |  | // LastMinuteHistogram keeps track of last minute sizes added.
 | 
					
						
							|  |  |  | type LastMinuteHistogram [sizeLastElemMarker]lastMinuteLatency | 
					
						
							| 
									
										
										
										
											2022-01-26 08:31:44 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-06 05:45:49 +08:00
										 |  |  | // Merge safely merges two LastMinuteHistogram structures into one
 | 
					
						
							|  |  |  | func (l LastMinuteHistogram) Merge(o LastMinuteHistogram) (merged LastMinuteHistogram) { | 
					
						
							| 
									
										
										
										
											2022-01-26 08:31:44 +08:00
										 |  |  | 	for i := range l { | 
					
						
							|  |  |  | 		merged[i] = l[i].merge(o[i]) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return merged | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Add latency t from object with the specified size.
 | 
					
						
							| 
									
										
										
										
											2022-07-06 05:45:49 +08:00
										 |  |  | func (l *LastMinuteHistogram) Add(size int64, t time.Duration) { | 
					
						
							| 
									
										
										
										
											2022-01-26 08:31:44 +08:00
										 |  |  | 	l[sizeToTag(size)].add(t) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // GetAvgData will return the average for each bucket from the last time minute.
 | 
					
						
							|  |  |  | // The number of objects is also included.
 | 
					
						
							| 
									
										
										
										
											2022-07-06 05:45:49 +08:00
										 |  |  | func (l *LastMinuteHistogram) GetAvgData() [sizeLastElemMarker]AccElem { | 
					
						
							| 
									
										
										
										
											2022-01-26 08:31:44 +08:00
										 |  |  | 	var res [sizeLastElemMarker]AccElem | 
					
						
							|  |  |  | 	for i, elem := range l[:] { | 
					
						
							| 
									
										
										
										
											2022-07-06 05:45:49 +08:00
										 |  |  | 		res[i] = elem.getTotal() | 
					
						
							| 
									
										
										
										
											2022-01-26 08:31:44 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return res | 
					
						
							|  |  |  | } |