github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/workload/tpcc/stock_level.go (about)

     1  // Copyright 2017 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  	"context"
    15  
    16  	"github.com/cockroachdb/cockroach-go/crdb"
    17  	"github.com/cockroachdb/cockroach/pkg/util/timeutil"
    18  	"github.com/cockroachdb/cockroach/pkg/workload"
    19  	"golang.org/x/exp/rand"
    20  )
    21  
    22  // 2.8 The Stock-Level Transaction
    23  //
    24  // The Stock-Level business transaction determines the number of recently sold
    25  // items that have a stock level below a specified threshold. It represents a
    26  // heavy read-only database transaction with a low frequency of execution, a
    27  // relaxed response time requirement, and relaxed consistency requirements.
    28  
    29  // 2.8.2.3 states:
    30  // Full serializability and repeatable reads are not required for the
    31  // Stock-Level business transaction. All data read must be committed and no
    32  // older than the most recently committed data prior to the time this business
    33  // transaction was initiated. All other ACID properties must be maintained.
    34  // TODO(jordan): can we take advantage of this?
    35  
    36  type stockLevelData struct {
    37  	// This data must all be returned by the transaction. See 2.8.3.4.
    38  	dID       int
    39  	threshold int
    40  	lowStock  int
    41  }
    42  
    43  type stockLevel struct {
    44  	config *tpcc
    45  	mcp    *workload.MultiConnPool
    46  	sr     workload.SQLRunner
    47  
    48  	selectDNextOID    workload.StmtHandle
    49  	countRecentlySold workload.StmtHandle
    50  }
    51  
    52  var _ tpccTx = &stockLevel{}
    53  
    54  func createStockLevel(
    55  	ctx context.Context, config *tpcc, mcp *workload.MultiConnPool,
    56  ) (tpccTx, error) {
    57  	s := &stockLevel{
    58  		config: config,
    59  		mcp:    mcp,
    60  	}
    61  
    62  	s.selectDNextOID = s.sr.Define(`
    63  		SELECT d_next_o_id
    64  		FROM district
    65  		WHERE d_w_id = $1 AND d_id = $2`,
    66  	)
    67  
    68  	// Count the number of recently sold items that have a stock level below
    69  	// the threshold.
    70  	// TODO(radu): we use count(DISTINCT s_i_id) because DISTINCT inside
    71  	// aggregates was not supported by the optimizer. This can be cleaned up.
    72  	s.countRecentlySold = s.sr.Define(`
    73  		SELECT count(*) FROM (
    74  			SELECT DISTINCT s_i_id
    75  			FROM order_line
    76  			JOIN stock
    77  			ON s_i_id=ol_i_id AND s_w_id=ol_w_id
    78  			WHERE ol_w_id = $1
    79  				AND ol_d_id = $2
    80  				AND ol_o_id BETWEEN $3 - 20 AND $3 - 1
    81  				AND s_quantity < $4
    82  		)`,
    83  	)
    84  
    85  	if err := s.sr.Init(ctx, "stock-level", mcp, config.connFlags); err != nil {
    86  		return nil, err
    87  	}
    88  
    89  	return s, nil
    90  }
    91  
    92  func (s *stockLevel) run(ctx context.Context, wID int) (interface{}, error) {
    93  	rng := rand.New(rand.NewSource(uint64(timeutil.Now().UnixNano())))
    94  
    95  	// 2.8.1.2: The threshold of minimum quantity in stock is selected at random
    96  	// within [10..20].
    97  	d := stockLevelData{
    98  		threshold: int(randInt(rng, 10, 20)),
    99  		dID:       rng.Intn(10) + 1,
   100  	}
   101  
   102  	tx, err := s.mcp.Get().BeginEx(ctx, s.config.txOpts)
   103  	if err != nil {
   104  		return nil, err
   105  	}
   106  	if err := crdb.ExecuteInTx(
   107  		ctx, (*workload.PgxTx)(tx),
   108  		func() error {
   109  			var dNextOID int
   110  			if err := s.selectDNextOID.QueryRowTx(
   111  				ctx, tx, wID, d.dID,
   112  			).Scan(&dNextOID); err != nil {
   113  				return err
   114  			}
   115  
   116  			// Count the number of recently sold items that have a stock level below
   117  			// the threshold.
   118  			return s.countRecentlySold.QueryRowTx(
   119  				ctx, tx, wID, d.dID, dNextOID, d.threshold,
   120  			).Scan(&d.lowStock)
   121  		}); err != nil {
   122  		return nil, err
   123  	}
   124  	return d, nil
   125  }