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 }