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  }