github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/workload/tpcc/delivery.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 gosql "database/sql" 16 "fmt" 17 "strings" 18 "sync/atomic" 19 20 "github.com/cockroachdb/cockroach-go/crdb" 21 "github.com/cockroachdb/cockroach/pkg/util/timeutil" 22 "github.com/cockroachdb/cockroach/pkg/workload" 23 "github.com/cockroachdb/errors" 24 "golang.org/x/exp/rand" 25 ) 26 27 // 2.7 The Delivery Transaction 28 29 // The Delivery business transaction consists of processing a batch of 10 new 30 // (not yet delivered) orders. Each order is processed (delivered) in full 31 // within the scope of a read-write database transaction. The number of orders 32 // delivered as a group (or batched) within the same database transaction is 33 // implementation specific. The business transaction, comprised of one or more 34 // (up to 10) database transactions, has a low frequency of execution and must 35 // complete within a relaxed response time requirement. 36 37 // The Delivery transaction is intended to be executed in deferred mode through 38 // a queuing mechanism, rather than interactively, with terminal response 39 // indicating transaction completion. The result of the deferred execution is 40 // recorded into a result file. 41 42 type delivery struct { 43 config *tpcc 44 mcp *workload.MultiConnPool 45 sr workload.SQLRunner 46 47 selectNewOrder workload.StmtHandle 48 sumAmount workload.StmtHandle 49 } 50 51 var _ tpccTx = &delivery{} 52 53 func createDelivery( 54 ctx context.Context, config *tpcc, mcp *workload.MultiConnPool, 55 ) (tpccTx, error) { 56 del := &delivery{ 57 config: config, 58 mcp: mcp, 59 } 60 61 del.selectNewOrder = del.sr.Define(` 62 SELECT no_o_id 63 FROM new_order 64 WHERE no_w_id = $1 AND no_d_id = $2 65 ORDER BY no_o_id ASC 66 LIMIT 1`, 67 ) 68 69 del.sumAmount = del.sr.Define(` 70 SELECT sum(ol_amount) FROM order_line 71 WHERE ol_w_id = $1 AND ol_d_id = $2 AND ol_o_id = $3`, 72 ) 73 74 if err := del.sr.Init(ctx, "delivery", mcp, config.connFlags); err != nil { 75 return nil, err 76 } 77 78 return del, nil 79 } 80 81 func (del *delivery) run(ctx context.Context, wID int) (interface{}, error) { 82 atomic.AddUint64(&del.config.auditor.deliveryTransactions, 1) 83 84 rng := rand.New(rand.NewSource(uint64(timeutil.Now().UnixNano()))) 85 86 oCarrierID := rng.Intn(10) + 1 87 olDeliveryD := timeutil.Now() 88 89 tx, err := del.mcp.Get().BeginEx(ctx, del.config.txOpts) 90 if err != nil { 91 return nil, err 92 } 93 err = crdb.ExecuteInTx( 94 ctx, (*workload.PgxTx)(tx), 95 func() error { 96 // 2.7.4.2. For each district: 97 dIDoIDPairs := make(map[int]int) 98 dIDolTotalPairs := make(map[int]float64) 99 for dID := 1; dID <= 10; dID++ { 100 var oID int 101 if err := del.selectNewOrder.QueryRowTx(ctx, tx, wID, dID).Scan(&oID); err != nil { 102 // If no matching order is found, the delivery of this order is skipped. 103 if !errors.Is(err, gosql.ErrNoRows) { 104 atomic.AddUint64(&del.config.auditor.skippedDelivieries, 1) 105 return err 106 } 107 continue 108 } 109 dIDoIDPairs[dID] = oID 110 111 var olTotal float64 112 if err := del.sumAmount.QueryRowTx( 113 ctx, tx, wID, dID, oID, 114 ).Scan(&olTotal); err != nil { 115 return err 116 } 117 dIDolTotalPairs[dID] = olTotal 118 } 119 dIDoIDPairsStr := makeInTuples(dIDoIDPairs) 120 121 rows, err := tx.QueryEx( 122 ctx, 123 fmt.Sprintf(` 124 UPDATE "order" 125 SET o_carrier_id = %d 126 WHERE o_w_id = %d AND (o_d_id, o_id) IN (%s) 127 RETURNING o_d_id, o_c_id`, 128 oCarrierID, wID, dIDoIDPairsStr, 129 ), 130 nil, /* options */ 131 ) 132 if err != nil { 133 return err 134 } 135 dIDcIDPairs := make(map[int]int) 136 for rows.Next() { 137 var dID, oCID int 138 if err := rows.Scan(&dID, &oCID); err != nil { 139 rows.Close() 140 return err 141 } 142 dIDcIDPairs[dID] = oCID 143 } 144 if err := rows.Err(); err != nil { 145 return err 146 } 147 rows.Close() 148 149 if err := checkSameKeys(dIDoIDPairs, dIDcIDPairs); err != nil { 150 return err 151 } 152 dIDcIDPairsStr := makeInTuples(dIDcIDPairs) 153 dIDToOlTotalStr := makeWhereCases(dIDolTotalPairs) 154 155 if _, err := tx.ExecEx( 156 ctx, 157 fmt.Sprintf(` 158 UPDATE customer 159 SET c_delivery_cnt = c_delivery_cnt + 1, 160 c_balance = c_balance + CASE c_d_id %s END 161 WHERE c_w_id = %d AND (c_d_id, c_id) IN (%s)`, 162 dIDToOlTotalStr, wID, dIDcIDPairsStr, 163 ), 164 nil, /* options */ 165 ); err != nil { 166 return err 167 } 168 if _, err := tx.ExecEx( 169 ctx, 170 fmt.Sprintf(` 171 DELETE FROM new_order 172 WHERE no_w_id = %d AND (no_d_id, no_o_id) IN (%s)`, 173 wID, dIDoIDPairsStr, 174 ), 175 nil, /* options */ 176 ); err != nil { 177 return err 178 } 179 180 _, err = tx.ExecEx( 181 ctx, 182 fmt.Sprintf(` 183 UPDATE order_line 184 SET ol_delivery_d = '%s' 185 WHERE ol_w_id = %d AND (ol_d_id, ol_o_id) IN (%s)`, 186 olDeliveryD.Format("2006-01-02 15:04:05"), wID, dIDoIDPairsStr, 187 ), 188 nil, /* options */ 189 ) 190 return err 191 }) 192 return nil, err 193 } 194 195 func makeInTuples(pairs map[int]int) string { 196 tupleStrs := make([]string, 0, len(pairs)) 197 for k, v := range pairs { 198 tupleStrs = append(tupleStrs, fmt.Sprintf("(%d, %d)", k, v)) 199 } 200 return strings.Join(tupleStrs, ", ") 201 } 202 203 func makeWhereCases(cases map[int]float64) string { 204 casesStrs := make([]string, 0, len(cases)) 205 for k, v := range cases { 206 casesStrs = append(casesStrs, fmt.Sprintf("WHEN %d THEN %f", k, v)) 207 } 208 return strings.Join(casesStrs, " ") 209 } 210 211 func checkSameKeys(a, b map[int]int) error { 212 if len(a) != len(b) { 213 return errors.Errorf("different number of keys") 214 } 215 for k := range a { 216 if _, ok := b[k]; !ok { 217 return errors.Errorf("missing key %v", k) 218 } 219 } 220 return nil 221 }