github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/tests/kv_test.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 tests_test
    12  
    13  import (
    14  	"bytes"
    15  	"context"
    16  	gosql "database/sql"
    17  	"fmt"
    18  	"math/rand"
    19  	"reflect"
    20  	"runtime"
    21  	"strings"
    22  	"testing"
    23  
    24  	"github.com/cockroachdb/cockroach/pkg/base"
    25  	kv2 "github.com/cockroachdb/cockroach/pkg/kv"
    26  	"github.com/cockroachdb/cockroach/pkg/testutils/serverutils"
    27  	"github.com/cockroachdb/errors"
    28  )
    29  
    30  type kvInterface interface {
    31  	Insert(rows, run int) error
    32  	Update(rows, run int) error
    33  	Delete(rows, run int) error
    34  	Scan(rows, run int) error
    35  
    36  	prep(rows int, initData bool) error
    37  	done()
    38  }
    39  
    40  // kvNative uses the native client package to implement kvInterface.
    41  type kvNative struct {
    42  	db     *kv2.DB
    43  	epoch  int
    44  	prefix string
    45  	doneFn func()
    46  }
    47  
    48  func newKVNative(b *testing.B) kvInterface {
    49  	s, _, db := serverutils.StartServer(b, base.TestServerArgs{})
    50  
    51  	// Note that using the local client.DB isn't a strictly fair
    52  	// comparison with SQL as we want these client requests to be sent
    53  	// over the network.
    54  	return &kvNative{
    55  		db: db,
    56  		doneFn: func() {
    57  			s.Stopper().Stop(context.Background())
    58  		},
    59  	}
    60  }
    61  
    62  func (kv *kvNative) Insert(rows, run int) error {
    63  	firstRow := rows * run
    64  	lastRow := rows * (run + 1)
    65  	err := kv.db.Txn(context.Background(), func(ctx context.Context, txn *kv2.Txn) error {
    66  		b := txn.NewBatch()
    67  		for i := firstRow; i < lastRow; i++ {
    68  			b.Put(fmt.Sprintf("%s%08d", kv.prefix, i), i)
    69  		}
    70  		return txn.CommitInBatch(ctx, b)
    71  	})
    72  	return err
    73  }
    74  
    75  func (kv *kvNative) Update(rows, run int) error {
    76  	perm := rand.Perm(rows)
    77  	err := kv.db.Txn(context.Background(), func(ctx context.Context, txn *kv2.Txn) error {
    78  		// Read all values in a batch.
    79  		b := txn.NewBatch()
    80  		for i := 0; i < rows; i++ {
    81  			b.Get(fmt.Sprintf("%s%08d", kv.prefix, perm[i]))
    82  		}
    83  		if err := txn.Run(ctx, b); err != nil {
    84  			return err
    85  		}
    86  		// Now add one to each value and add as puts to write batch.
    87  		wb := txn.NewBatch()
    88  		for i, result := range b.Results {
    89  			v := result.Rows[0].ValueInt()
    90  			wb.Put(fmt.Sprintf("%s%08d", kv.prefix, perm[i]), v+1)
    91  		}
    92  		return txn.CommitInBatch(ctx, wb)
    93  	})
    94  	return err
    95  }
    96  
    97  func (kv *kvNative) Delete(rows, run int) error {
    98  	firstRow := rows * run
    99  	lastRow := rows * (run + 1)
   100  	err := kv.db.Txn(context.Background(), func(ctx context.Context, txn *kv2.Txn) error {
   101  		b := txn.NewBatch()
   102  		for i := firstRow; i < lastRow; i++ {
   103  			b.Del(fmt.Sprintf("%s%08d", kv.prefix, i))
   104  		}
   105  		return txn.CommitInBatch(ctx, b)
   106  	})
   107  	return err
   108  }
   109  
   110  func (kv *kvNative) Scan(rows, run int) error {
   111  	var kvs []kv2.KeyValue
   112  	err := kv.db.Txn(context.Background(), func(ctx context.Context, txn *kv2.Txn) error {
   113  		var err error
   114  		kvs, err = txn.Scan(ctx, fmt.Sprintf("%s%08d", kv.prefix, 0), fmt.Sprintf("%s%08d", kv.prefix, rows), int64(rows))
   115  		return err
   116  	})
   117  	if len(kvs) != rows {
   118  		return errors.Errorf("expected %d rows; got %d", rows, len(kvs))
   119  	}
   120  	return err
   121  }
   122  
   123  func (kv *kvNative) prep(rows int, initData bool) error {
   124  	kv.epoch++
   125  	kv.prefix = fmt.Sprintf("%d/", kv.epoch)
   126  	if !initData {
   127  		return nil
   128  	}
   129  	err := kv.db.Txn(context.Background(), func(ctx context.Context, txn *kv2.Txn) error {
   130  		b := txn.NewBatch()
   131  		for i := 0; i < rows; i++ {
   132  			b.Put(fmt.Sprintf("%s%08d", kv.prefix, i), i)
   133  		}
   134  		return txn.CommitInBatch(ctx, b)
   135  	})
   136  	return err
   137  }
   138  
   139  func (kv *kvNative) done() {
   140  	kv.doneFn()
   141  }
   142  
   143  // kvSQL is a SQL-based implementation of the KV interface.
   144  type kvSQL struct {
   145  	db     *gosql.DB
   146  	buf    bytes.Buffer
   147  	doneFn func()
   148  }
   149  
   150  func newKVSQL(b *testing.B) kvInterface {
   151  	s, db, _ := serverutils.StartServer(b, base.TestServerArgs{UseDatabase: "bench"})
   152  
   153  	if _, err := db.Exec(`CREATE DATABASE IF NOT EXISTS bench`); err != nil {
   154  		b.Fatal(err)
   155  	}
   156  
   157  	kv := &kvSQL{}
   158  	kv.db = db
   159  	kv.doneFn = func() {
   160  		s.Stopper().Stop(context.Background())
   161  	}
   162  	return kv
   163  }
   164  
   165  func (kv *kvSQL) Insert(rows, run int) error {
   166  	firstRow := rows * run
   167  	defer kv.buf.Reset()
   168  	kv.buf.WriteString(`INSERT INTO bench.kv VALUES `)
   169  	for i := 0; i < rows; i++ {
   170  		if i > 0 {
   171  			kv.buf.WriteString(", ")
   172  		}
   173  		fmt.Fprintf(&kv.buf, "('%08d', %d)", i+firstRow, i)
   174  	}
   175  	_, err := kv.db.Exec(kv.buf.String())
   176  	return err
   177  }
   178  
   179  func (kv *kvSQL) Update(rows, run int) error {
   180  	perm := rand.Perm(rows)
   181  	defer kv.buf.Reset()
   182  	kv.buf.WriteString(`UPDATE bench.kv SET v = v + 1 WHERE k IN (`)
   183  	for j := 0; j < rows; j++ {
   184  		if j > 0 {
   185  			kv.buf.WriteString(", ")
   186  		}
   187  		fmt.Fprintf(&kv.buf, `'%08d'`, perm[j])
   188  	}
   189  	kv.buf.WriteString(`)`)
   190  	_, err := kv.db.Exec(kv.buf.String())
   191  	return err
   192  }
   193  
   194  func (kv *kvSQL) Delete(rows, run int) error {
   195  	firstRow := rows * run
   196  	defer kv.buf.Reset()
   197  	kv.buf.WriteString(`DELETE FROM bench.kv WHERE k IN (`)
   198  	for j := 0; j < rows; j++ {
   199  		if j > 0 {
   200  			kv.buf.WriteString(", ")
   201  		}
   202  		fmt.Fprintf(&kv.buf, `'%08d'`, j+firstRow)
   203  	}
   204  	kv.buf.WriteString(`)`)
   205  	_, err := kv.db.Exec(kv.buf.String())
   206  	return err
   207  }
   208  
   209  func (kv *kvSQL) Scan(count, run int) error {
   210  	rows, err := kv.db.Query(fmt.Sprintf("SELECT * FROM bench.kv LIMIT %d", count))
   211  	if err != nil {
   212  		return err
   213  	}
   214  	n := 0
   215  	for rows.Next() {
   216  		n++
   217  	}
   218  	rows.Close()
   219  	if err := rows.Err(); err != nil {
   220  		return err
   221  	}
   222  	if n != count {
   223  		return errors.Errorf("unexpected result count: %d (expected %d)", n, count)
   224  	}
   225  	return nil
   226  }
   227  
   228  func (kv *kvSQL) prep(rows int, initData bool) error {
   229  	if _, err := kv.db.Exec(`DROP TABLE IF EXISTS bench.kv`); err != nil {
   230  		return err
   231  	}
   232  	schema := `
   233  CREATE TABLE IF NOT EXISTS bench.kv (
   234    k STRING PRIMARY KEY,
   235    v INT,
   236    FAMILY (k, v)
   237  )
   238  `
   239  	if _, err := kv.db.Exec(schema); err != nil {
   240  		return err
   241  	}
   242  	if !initData {
   243  		return nil
   244  	}
   245  	defer kv.buf.Reset()
   246  	kv.buf.WriteString(`INSERT INTO bench.kv VALUES `)
   247  	for i := 0; i < rows; i++ {
   248  		if i > 0 {
   249  			kv.buf.WriteString(", ")
   250  		}
   251  		fmt.Fprintf(&kv.buf, "('%08d', %d)", i, i)
   252  	}
   253  	_, err := kv.db.Exec(kv.buf.String())
   254  	return err
   255  }
   256  
   257  func (kv *kvSQL) done() {
   258  	kv.doneFn()
   259  }
   260  
   261  func BenchmarkKV(b *testing.B) {
   262  	for i, opFn := range []func(kvInterface, int, int) error{
   263  		kvInterface.Insert,
   264  		kvInterface.Update,
   265  		kvInterface.Delete,
   266  		kvInterface.Scan,
   267  	} {
   268  		opName := runtime.FuncForPC(reflect.ValueOf(opFn).Pointer()).Name()
   269  		opName = strings.TrimPrefix(opName, "github.com/cockroachdb/cockroach/pkg/sql/tests_test.kvInterface.")
   270  		b.Run(opName, func(b *testing.B) {
   271  			for _, kvFn := range []func(*testing.B) kvInterface{
   272  				newKVNative,
   273  				newKVSQL,
   274  			} {
   275  				kvTyp := runtime.FuncForPC(reflect.ValueOf(kvFn).Pointer()).Name()
   276  				kvTyp = strings.TrimPrefix(kvTyp, "github.com/cockroachdb/cockroach/pkg/sql/tests_test.newKV")
   277  				b.Run(kvTyp, func(b *testing.B) {
   278  					for _, rows := range []int{1, 10, 100, 1000, 10000} {
   279  						b.Run(fmt.Sprintf("rows=%d", rows), func(b *testing.B) {
   280  							kv := kvFn(b)
   281  							defer kv.done()
   282  
   283  							if err := kv.prep(rows, i != 0 /* Insert */ && i != 2 /* Delete */); err != nil {
   284  								b.Fatal(err)
   285  							}
   286  							b.ResetTimer()
   287  							for i := 0; i < b.N; i++ {
   288  								if err := opFn(kv, rows, i); err != nil {
   289  									b.Fatal(err)
   290  								}
   291  							}
   292  							b.StopTimer()
   293  						})
   294  					}
   295  				})
   296  			}
   297  		})
   298  	}
   299  }