github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/libraries/doltcore/remotestorage/stats.go (about) 1 // Copyright 2019 Dolthub, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package remotestorage 16 17 import ( 18 "fmt" 19 "io" 20 "os" 21 "sync" 22 "time" 23 24 "github.com/HdrHistogram/hdrhistogram-go" 25 "github.com/fatih/color" 26 ) 27 28 var StatsFactory func() StatsRecorder = NullStatsRecorderFactory 29 var StatsFlusher func(StatsRecorder) = func(StatsRecorder) {} 30 31 func NullStatsRecorderFactory() StatsRecorder { 32 return NullStatsRecorder{} 33 } 34 35 func HistogramStatsRecorderFactory() StatsRecorder { 36 return NewHistorgramStatsRecorder() 37 } 38 39 func StatsFlusherToColorError(r StatsRecorder) { 40 r.WriteSummaryTo(color.Error) 41 } 42 43 func init() { 44 if _, ok := os.LookupEnv("DOLT_REMOTE_VERBOSE_DOWNLOAD_STATS"); ok { 45 StatsFactory = HistogramStatsRecorderFactory 46 StatsFlusher = StatsFlusherToColorError 47 } 48 } 49 50 type StatsRecorder interface { 51 RecordTimeToFirstByte(hedge, retry int, size uint64, d time.Duration) 52 RecordDownloadAttemptStart(hedge, retry int, offset, size uint64) 53 RecordDownloadComplete(hedge, retry int, size uint64, d time.Duration) 54 WriteSummaryTo(io.Writer) error 55 } 56 57 var _ StatsRecorder = NullStatsRecorder{} 58 59 type NullStatsRecorder struct { 60 } 61 62 func (NullStatsRecorder) RecordTimeToFirstByte(hedge, retry int, size uint64, d time.Duration) { 63 } 64 65 func (NullStatsRecorder) RecordDownloadAttemptStart(hedge, retry int, offset, size uint64) { 66 } 67 68 func (NullStatsRecorder) RecordDownloadComplete(hedge, retry int, size uint64, d time.Duration) { 69 } 70 71 func (NullStatsRecorder) WriteSummaryTo(io.Writer) error { 72 return nil 73 } 74 75 type HistogramStatsRecorder struct { 76 mu *sync.Mutex 77 sizes *hdrhistogram.Histogram 78 downloadTimeMillis *hdrhistogram.Histogram 79 firstByteMillis *hdrhistogram.Histogram 80 retryCount int 81 hedgeCount int 82 hedgeCompleteCount int 83 } 84 85 func NewHistorgramStatsRecorder() *HistogramStatsRecorder { 86 return &HistogramStatsRecorder{ 87 new(sync.Mutex), 88 hdrhistogram.New(128, 4294967296, 3), // 128 bytes - 4 GB 89 hdrhistogram.New(10, 3600000, 3), // 10 ms - 1 hr 90 hdrhistogram.New(10, 300000, 3), // 10 ms - 5 mins 91 0, 92 0, 93 0, 94 } 95 } 96 97 func (r *HistogramStatsRecorder) RecordTimeToFirstByte(hedge, retry int, size uint64, d time.Duration) { 98 r.mu.Lock() 99 defer r.mu.Unlock() 100 r.firstByteMillis.RecordValue(int64(d / time.Millisecond)) 101 } 102 103 func (r *HistogramStatsRecorder) RecordDownloadAttemptStart(hedge, retry int, offset, size uint64) { 104 if hedge == 1 && retry == 0 { 105 r.mu.Lock() 106 defer r.mu.Unlock() 107 r.sizes.RecordValue(int64(size)) 108 } 109 if retry > 0 { 110 r.mu.Lock() 111 defer r.mu.Unlock() 112 r.retryCount += 1 113 } else if hedge > 1 { 114 r.mu.Lock() 115 defer r.mu.Unlock() 116 r.hedgeCount += 1 117 } 118 } 119 120 func (r *HistogramStatsRecorder) RecordDownloadComplete(hedge, retry int, size uint64, d time.Duration) { 121 r.mu.Lock() 122 defer r.mu.Unlock() 123 r.downloadTimeMillis.RecordValue(int64(d / time.Millisecond)) 124 if hedge > 1 { 125 r.hedgeCompleteCount += 1 126 } 127 } 128 129 func (r *HistogramStatsRecorder) WriteSummaryTo(w io.Writer) error { 130 r.mu.Lock() 131 defer r.mu.Unlock() 132 _, err := fmt.Fprintf(w, "total downloads: %d, retries: %d, hedges: %d, completed hedges: %d\n", 133 r.sizes.TotalCount(), r.retryCount, r.hedgeCount, r.hedgeCompleteCount) 134 if err != nil { 135 return err 136 } 137 err = writeHistogram(w, "sizes (bytes)", r.sizes) 138 if err != nil { 139 return err 140 } 141 err = writeHistogram(w, "time to first byte (millis)", r.firstByteMillis) 142 if err != nil { 143 return err 144 } 145 err = writeHistogram(w, "download time (millis)", r.downloadTimeMillis) 146 if err != nil { 147 return err 148 } 149 return nil 150 } 151 152 func writeHistogram(w io.Writer, prefix string, h *hdrhistogram.Histogram) error { 153 _, err := fmt.Fprintf(w, "%s: p50: %d, p90: %d, p99: %d, avg: %.2f, max: %d\n", prefix, 154 h.ValueAtQuantile(50), h.ValueAtQuantile(90), h.ValueAtQuantile(99), h.Mean(), h.Max()) 155 return err 156 }