github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/bench/setup.go (about) 1 // Copyright 2016 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 bench 12 13 import ( 14 "bytes" 15 "context" 16 gosql "database/sql" 17 "fmt" 18 "net/url" 19 "os/exec" 20 21 "github.com/cockroachdb/cockroach/pkg/testutils/sqlutils" 22 ) 23 24 const schema = ` 25 DROP TABLE IF EXISTS pgbench_accounts; 26 DROP TABLE IF EXISTS pgbench_branches; 27 DROP TABLE IF EXISTS pgbench_tellers; 28 DROP TABLE IF EXISTS pgbench_history; 29 30 CREATE TABLE pgbench_accounts ( 31 aid integer NOT NULL PRIMARY KEY, 32 bid integer, 33 abalance integer, 34 filler character(84) 35 ); 36 37 CREATE TABLE pgbench_branches ( 38 bid integer NOT NULL PRIMARY KEY, 39 bbalance integer, 40 filler character(88) 41 ); 42 43 CREATE TABLE pgbench_tellers ( 44 tid integer NOT NULL PRIMARY KEY, 45 bid integer, 46 tbalance integer, 47 filler character(84) 48 ); 49 50 CREATE TABLE pgbench_history ( 51 tid integer, 52 bid integer, 53 aid integer, 54 delta integer, 55 mtime timestamp, 56 filler character(22) 57 ); 58 ` 59 60 // CreateAndConnect connects and creates the requested DB (dropping 61 // if exists) then returns a new connection to the created DB. 62 func CreateAndConnect(pgURL url.URL, name string) (*gosql.DB, error) { 63 { 64 pgURL.Path = "" 65 db, err := gosql.Open("postgres", pgURL.String()) 66 if err != nil { 67 return nil, err 68 } 69 defer db.Close() 70 71 if _, err := db.Exec(fmt.Sprintf("DROP DATABASE IF EXISTS %s", name)); err != nil { 72 return nil, err 73 } 74 75 if _, err := db.Exec(fmt.Sprintf(`CREATE DATABASE %s`, name)); err != nil { 76 return nil, err 77 } 78 } 79 80 pgURL.Path = name 81 82 db, err := gosql.Open("postgres", pgURL.String()) 83 if err != nil { 84 return nil, err 85 } 86 return db, nil 87 } 88 89 // SetupExec creates and fills a DB and prepares a `pgbench` command 90 // to be run against it. 91 func SetupExec(pgURL url.URL, name string, accounts, transactions int) (*exec.Cmd, error) { 92 db, err := CreateAndConnect(pgURL, name) 93 if err != nil { 94 return nil, err 95 } 96 defer db.Close() 97 98 if err := SetupBenchDB(db, accounts, true /*quiet*/); err != nil { 99 return nil, err 100 } 101 102 return ExecPgbench(pgURL, name, transactions) 103 } 104 105 // SetupBenchDB sets up a db with the schema and initial data used by `pgbench`. 106 // The `-i` flag to `pgbench` is usually used to do this when testing postgres 107 // but the statements it generates use postgres-specific flags that cockroach does 108 // not support. The queries this script runs are based on a dump of a db created 109 // by `pgbench -i`, but sticking to the compatible subset that both cockroach and 110 // postgres support. 111 func SetupBenchDB(db sqlutils.DBHandle, accounts int, quiet bool) error { 112 ctx := context.TODO() 113 if _, err := db.ExecContext(ctx, schema); err != nil { 114 return err 115 } 116 return populateDB(ctx, db, accounts, quiet) 117 } 118 119 const tellers = 10 120 121 func populateDB(ctx context.Context, db sqlutils.DBHandle, accounts int, quiet bool) error { 122 branches := `INSERT INTO pgbench_branches (bid, bbalance, filler) VALUES (1, 7354, NULL)` 123 if r, err := db.ExecContext(ctx, branches); err != nil { 124 return err 125 } else if x, err := r.RowsAffected(); err != nil { 126 return err 127 } else if !quiet { 128 fmt.Printf("Inserted %d branch records\n", x) 129 } 130 131 // Various magic numbers came from `pg_dump` of a `pgbench` created DB. 132 tellers := `INSERT INTO pgbench_tellers VALUES (1, 1, 0, NULL), 133 (2, 1, 955, NULL), 134 (3, 1, -3338, NULL), 135 (4, 1, -24, NULL), 136 (5, 1, 2287, NULL), 137 (6, 1, 4129, NULL), 138 (7, 1, 0, NULL), 139 (8, 1, 0, NULL), 140 (9, 1, 0, NULL), 141 (10, 1, 3345, NULL) 142 ` 143 if r, err := db.ExecContext(ctx, tellers); err != nil { 144 return err 145 } else if x, err := r.RowsAffected(); err != nil { 146 return err 147 } else if !quiet { 148 fmt.Printf("Inserted %d teller records\n", x) 149 } 150 151 // Split account inserts into batches to avoid giant query. 152 done := 0 153 for { 154 batch := 5000 155 remaining := accounts - done 156 if remaining < 1 { 157 break 158 } 159 if batch > remaining { 160 batch = remaining 161 } 162 var placeholders bytes.Buffer 163 for i := 0; i < batch; i++ { 164 if i > 0 { 165 placeholders.WriteString(", ") 166 } 167 fmt.Fprintf(&placeholders, "(%d, 1, 0, ' ')", done+i) 168 } 169 stmt := fmt.Sprintf(`INSERT INTO pgbench_accounts VALUES %s`, placeholders.String()) 170 if r, err := db.ExecContext(ctx, stmt); err != nil { 171 return err 172 } else if x, err := r.RowsAffected(); err != nil { 173 return err 174 } else if !quiet { 175 fmt.Printf("Inserted %d account records\n", x) 176 } 177 done += batch 178 } 179 180 history := ` 181 INSERT INTO pgbench_history VALUES 182 (5, 1, 36833, 407, CURRENT_TIMESTAMP, NULL), 183 (3, 1, 43082, -3338, CURRENT_TIMESTAMP, NULL), 184 (2, 1, 49129, 2872, CURRENT_TIMESTAMP, NULL), 185 (6, 1, 81223, 1064, CURRENT_TIMESTAMP, NULL), 186 (6, 1, 28316, 3065, CURRENT_TIMESTAMP, NULL), 187 (4, 1, 10146, -24, CURRENT_TIMESTAMP, NULL), 188 (10, 1, 12019, 2265, CURRENT_TIMESTAMP, NULL), 189 (2, 1, 46717, -1917, CURRENT_TIMESTAMP, NULL), 190 (5, 1, 68648, 1880, CURRENT_TIMESTAMP, NULL), 191 (10, 1, 46989, 1080, CURRENT_TIMESTAMP, NULL);` 192 193 if r, err := db.ExecContext(ctx, history); err != nil { 194 return err 195 } else if x, err := r.RowsAffected(); err != nil { 196 return err 197 } else if !quiet { 198 fmt.Printf("Inserted %d history records\n", x) 199 } 200 201 return nil 202 }