github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/workload/tpcc/result.go (about) 1 // Copyright 2019 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package tpcc 12 13 import ( 14 "time" 15 16 "github.com/cockroachdb/cockroach/pkg/workload/histogram" 17 "github.com/cockroachdb/errors" 18 "github.com/codahale/hdrhistogram" 19 ) 20 21 // SpecWarehouseFactor is the default maximum per-warehouse newOrder 22 // throughput per second used to compute the maximum throughput value for a 23 // given warehouse count. This value is provided in the TPC-C spec. 24 const SpecWarehouseFactor = 12.86 25 26 // DeckWarehouseFactor is the warehouse factor to be used with the workload deck 27 // implemented in this package. This value differs from the spec's provided 28 // value as the deck mechanism used by workload differs from the default 29 // workload but is an acceptable alternative. This is the default value. 30 // 31 // The 12.605 is computed from the operation mix and the number of secs 32 // it takes to cycle through a deck: 33 // 34 // 10*(18+12) + 10*(3+12) + 1*(2+10) + 1*(2+5) + 1*(2+5) = 476 35 // 36 // 10 workers per warehouse times 10 newOrder ops per deck results in: 37 // 38 // (10*10)/(476/60) = 12.605... 39 // 40 const DeckWarehouseFactor = 12.605 41 42 // PassingEfficiency is a percentage of the theoretical maximum tpmC required 43 // to pass TPC-C. 44 const PassingEfficiency = 85.0 45 46 // These values represent the maximum allowable 90th-%ile latency for the 47 // given queries as specified in section 5.2.5.7 of the TPC-C spec. 48 var passing90ThPercentile = map[string]time.Duration{ 49 "newOrder": 5 * time.Second, 50 "payment": 5 * time.Second, 51 "orderStatus": 5 * time.Second, 52 "delivery": 5 * time.Second, 53 "stockLevel": 20 * time.Second, 54 } 55 56 // Result represents the outcome of a TPCC run. 57 type Result struct { 58 59 // ActiveWarehouses is the number of warehouses used in the TPC-C run. 60 ActiveWarehouses int 61 62 // Cumulative maps from query name to the cumulative response time 63 // histogram for the TPC-C run. 64 Cumulative map[string]*hdrhistogram.Histogram 65 66 // Elapsed is the amount of time captured by the Cumulative. 67 Elapsed time.Duration 68 69 // WarehouseFactor is the maximal number of newOrder transactions per second 70 // per Warehouse. If zero it defaults to DeckWarehouseFactor which is derived 71 // from this workload. The value is used to compute the efficiency of the run. 72 WarehouseFactor float64 73 } 74 75 // MergeResults returns a result value constructed by merging the arguments. 76 // It should only be used if the number of ActiveWarehouses matches and will 77 // panic if they differ. Elapsed is computed as the max of all elapsed values 78 // and makes the assumption that the distributions roughly overlap in time. 79 // This should be used when merging from multiple load generators during the 80 // same test run. 81 func MergeResults(results ...*Result) *Result { 82 if len(results) == 0 { 83 return nil 84 } 85 base := &Result{ 86 ActiveWarehouses: results[0].ActiveWarehouses, 87 Cumulative: make(map[string]*hdrhistogram.Histogram, 88 len(results[0].Cumulative)), 89 WarehouseFactor: results[0].WarehouseFactor, 90 } 91 for _, r := range results { 92 if r.Elapsed > base.Elapsed { 93 base.Elapsed = r.Elapsed 94 } 95 if r.ActiveWarehouses != base.ActiveWarehouses { 96 panic(errors.Errorf("cannot merge histograms with different "+ 97 "ActiveWarehouses values: got both %v and %v", 98 r.ActiveWarehouses, base.ActiveWarehouses)) 99 } 100 for q, h := range r.Cumulative { 101 if cur, exists := base.Cumulative[q]; exists { 102 cur.Merge(h) 103 } else { 104 base.Cumulative[q] = histogram.Copy(h) 105 } 106 } 107 } 108 return base 109 } 110 111 // NewResult constructs a new Result. 112 func NewResult( 113 activeWarehouses int, 114 warehouseFactor float64, 115 elapsed time.Duration, 116 cumulative map[string]*hdrhistogram.Histogram, 117 ) *Result { 118 return &Result{ 119 ActiveWarehouses: activeWarehouses, 120 WarehouseFactor: warehouseFactor, 121 Elapsed: elapsed, 122 Cumulative: cumulative, 123 } 124 } 125 126 // NewResultWithSnapshots creates a new result from a deserialized set of 127 // histogram snapshots. 128 func NewResultWithSnapshots( 129 activeWarehouses int, warehouseFactor float64, snapshots map[string][]histogram.SnapshotTick, 130 ) *Result { 131 var start time.Time 132 var end time.Time 133 ret := make(map[string]*hdrhistogram.Histogram, len(snapshots)) 134 for n, snaps := range snapshots { 135 var cur *hdrhistogram.Histogram 136 for _, s := range snaps { 137 h := hdrhistogram.Import(s.Hist) 138 if cur == nil { 139 cur = h 140 } else { 141 cur.Merge(h) 142 } 143 if start.IsZero() || s.Now.Before(start) { 144 start = s.Now 145 } 146 if sEnd := s.Now.Add(s.Elapsed); end.IsZero() || sEnd.After(end) { 147 end = sEnd 148 } 149 } 150 ret[n] = cur 151 } 152 return NewResult(activeWarehouses, warehouseFactor, end.Sub(start), ret) 153 } 154 155 // TpmC returns a tpmC value with a warehouse factor of 12.86. 156 // TpmC will panic if r does not contain a "newOrder" histogram in Cumulative. 157 func (r *Result) TpmC() float64 { 158 return float64(r.Cumulative["newOrder"].TotalCount()) / (r.Elapsed.Seconds() / 60) 159 } 160 161 // Efficiency returns the efficiency of a TPC-C run. 162 // It relies on the WarehouseFactor which defaults to DeckWarehouseFactor. 163 func (r *Result) Efficiency() float64 { 164 tpmC := r.TpmC() 165 warehouseFactor := r.WarehouseFactor 166 if warehouseFactor == 0 { 167 warehouseFactor = DeckWarehouseFactor 168 } 169 return (100 * tpmC) / (warehouseFactor * float64(r.ActiveWarehouses)) 170 } 171 172 // FailureError returns nil if the Result is passing or an error describing 173 // the failure if the result is failing. 174 func (r *Result) FailureError() error { 175 if _, newOrderExists := r.Cumulative["newOrder"]; !newOrderExists { 176 return errors.Errorf("no newOrder data exists") 177 } 178 179 // Collect all failing criteria errors into errs so that the returned error 180 // contains information about all of the failures. 181 var err error 182 if eff := r.Efficiency(); eff < PassingEfficiency { 183 err = errors.CombineErrors(err, 184 errors.Errorf("efficiency value of %v is below ppassing threshold of %v", 185 eff, PassingEfficiency)) 186 } 187 for query, max90th := range passing90ThPercentile { 188 h, exists := r.Cumulative[query] 189 if !exists { 190 return errors.Errorf("no %v data exists", query) 191 } 192 if v := time.Duration(h.ValueAtQuantile(.9)); v > max90th { 193 err = errors.CombineErrors(err, 194 errors.Errorf("90th percentile latency for %v at %v exceeds passing threshold of %v", 195 query, v, max90th)) 196 } 197 } 198 return err 199 }