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  }