// TestRocksDBCompaction verifies that a garbage collector can be // installed on a RocksDB engine and will properly compact response // cache and transaction entries. func TestRocksDBCompaction(t *testing.T) { defer leaktest.AfterTest(t) stopper := stop.NewStopper() defer stopper.Stop() rocksdb := newMemRocksDB(roachpb.Attributes{Attrs: []string{"ssd"}}, testCacheSize, stopper) err := rocksdb.Open() if err != nil { t.Fatalf("could not create new in-memory rocksdb db instance: %v", err) } rocksdb.SetGCTimeouts(1, 2) cmdID := &roachpb.ClientCmdID{WallTime: 1, Random: 1} // Write two transaction values and two response cache values such // that exactly one of each should be GC'd based on our GC timeouts. kvs := []roachpb.KeyValue{ { Key: keys.ResponseCacheKey(1, cmdID), Value: roachpb.MakeValueFromBytes(encodePutResponse(makeTS(2, 0), t)), }, { Key: keys.ResponseCacheKey(2, cmdID), Value: roachpb.MakeValueFromBytes(encodePutResponse(makeTS(3, 0), t)), }, { Key: keys.TransactionKey(roachpb.Key("a"), roachpb.Key(uuid.NewUUID4())), Value: roachpb.MakeValueFromBytes(encodeTransaction(makeTS(1, 0), t)), }, { Key: keys.TransactionKey(roachpb.Key("b"), roachpb.Key(uuid.NewUUID4())), Value: roachpb.MakeValueFromBytes(encodeTransaction(makeTS(2, 0), t)), }, } for _, kv := range kvs { if err := MVCCPut(rocksdb, nil, kv.Key, roachpb.ZeroTimestamp, kv.Value, nil); err != nil { t.Fatal(err) } } // Compact range and scan remaining values to compare. rocksdb.CompactRange(nil, nil) actualKVs, _, err := MVCCScan(rocksdb, keyMin, keyMax, 0, roachpb.ZeroTimestamp, true, nil) if err != nil { t.Fatalf("could not run scan: %v", err) } var keys []roachpb.Key for _, kv := range actualKVs { keys = append(keys, kv.Key) } expKeys := []roachpb.Key{ kvs[1].Key, kvs[3].Key, } if !reflect.DeepEqual(expKeys, keys) { t.Errorf("expected keys %+v, got keys %+v", expKeys, keys) } }
func runMVCCConditionalPut(valueSize int, createFirst bool, b *testing.B) { rng, _ := randutil.NewPseudoRand() value := roachpb.MakeValueFromBytes(randutil.RandBytes(rng, valueSize)) keyBuf := append(make([]byte, 0, 64), []byte("key-")...) stopper := stop.NewStopper() defer stopper.Stop() rocksdb := NewInMem(roachpb.Attributes{}, testCacheSize, stopper) b.SetBytes(int64(valueSize)) var expected *roachpb.Value if createFirst { for i := 0; i < b.N; i++ { key := roachpb.Key(encoding.EncodeUvarintAscending(keyBuf[:4], uint64(i))) ts := makeTS(timeutil.Now().UnixNano(), 0) if err := MVCCPut(rocksdb, nil, key, ts, value, nil); err != nil { b.Fatalf("failed put: %s", err) } } expected = &value } b.ResetTimer() for i := 0; i < b.N; i++ { key := roachpb.Key(encoding.EncodeUvarintAscending(keyBuf[:4], uint64(i))) ts := makeTS(timeutil.Now().UnixNano(), 0) if err := MVCCConditionalPut(rocksdb, nil, key, ts, value, expected, nil); err != nil { b.Fatalf("failed put: %s", err) } } b.StopTimer() }
func BenchmarkMVCCPutDelete(b *testing.B) { const cacheSize = 1 << 30 // 1 GB stopper := stop.NewStopper() rocksdb := NewInMem(roachpb.Attributes{}, cacheSize, stopper) defer stopper.Stop() r := rand.New(rand.NewSource(int64(timeutil.Now().UnixNano()))) value := roachpb.MakeValueFromBytes(randutil.RandBytes(r, 10)) zeroTS := roachpb.ZeroTimestamp var blockNum int64 for i := 0; i < b.N; i++ { blockID := r.Int63() blockNum++ key := encoding.EncodeVarintAscending(nil, blockID) key = encoding.EncodeVarintAscending(key, blockNum) if err := MVCCPut(rocksdb, nil, key, zeroTS, value, nil /* txn */); err != nil { b.Fatal(err) } if err := MVCCDelete(rocksdb, nil, key, zeroTS, nil /* txn */); err != nil { b.Fatal(err) } } }
func runMVCCConditionalPut(emk engineMaker, valueSize int, createFirst bool, b *testing.B) { rng, _ := randutil.NewPseudoRand() value := roachpb.MakeValueFromBytes(randutil.RandBytes(rng, valueSize)) keyBuf := append(make([]byte, 0, 64), []byte("key-")...) eng, stopper := emk(b, fmt.Sprintf("cput_%d", valueSize)) defer stopper.Stop() b.SetBytes(int64(valueSize)) var expected *roachpb.Value if createFirst { for i := 0; i < b.N; i++ { key := roachpb.Key(encoding.EncodeUvarintAscending(keyBuf[:4], uint64(i))) ts := makeTS(timeutil.Now().UnixNano(), 0) if err := MVCCPut(context.Background(), eng, nil, key, ts, value, nil); err != nil { b.Fatalf("failed put: %s", err) } } expected = &value } b.ResetTimer() for i := 0; i < b.N; i++ { key := roachpb.Key(encoding.EncodeUvarintAscending(keyBuf[:4], uint64(i))) ts := makeTS(timeutil.Now().UnixNano(), 0) if err := MVCCConditionalPut(context.Background(), eng, nil, key, ts, value, expected, nil); err != nil { b.Fatalf("failed put: %s", err) } } b.StopTimer() }
// TestMultiRangeScanWithMaxResults tests that commands which access multiple // ranges with MaxResults parameter are carried out properly. func TestMultiRangeScanWithMaxResults(t *testing.T) { defer leaktest.AfterTest(t)() testCases := []struct { splitKeys []roachpb.Key keys []roachpb.Key }{ {[]roachpb.Key{roachpb.Key("m")}, []roachpb.Key{roachpb.Key("a"), roachpb.Key("z")}}, {[]roachpb.Key{roachpb.Key("h"), roachpb.Key("q")}, []roachpb.Key{roachpb.Key("b"), roachpb.Key("f"), roachpb.Key("k"), roachpb.Key("r"), roachpb.Key("w"), roachpb.Key("y")}}, } for i, tc := range testCases { s, _, _ := serverutils.StartServer(t, base.TestServerArgs{}) defer s.Stopper().Stop() ts := s.(*TestServer) retryOpts := base.DefaultRetryOptions() retryOpts.Closer = ts.stopper.ShouldDrain() ds := kv.NewDistSender(&kv.DistSenderContext{ Clock: s.Clock(), RPCContext: s.RPCContext(), RPCRetryOptions: &retryOpts, }, ts.Gossip()) tds := kv.NewTxnCoordSender(ds, ts.Clock(), ts.Ctx.Linearizable, tracing.NewTracer(), ts.stopper, kv.NewTxnMetrics(metric.NewRegistry())) for _, sk := range tc.splitKeys { if err := ts.node.ctx.DB.AdminSplit(sk); err != nil { t.Fatal(err) } } for _, k := range tc.keys { put := roachpb.NewPut(k, roachpb.MakeValueFromBytes(k)) if _, err := client.SendWrapped(tds, nil, put); err != nil { t.Fatal(err) } } // Try every possible ScanRequest startKey. for start := 0; start < len(tc.keys); start++ { // Try every possible maxResults, from 1 to beyond the size of key array. for maxResults := 1; maxResults <= len(tc.keys)-start+1; maxResults++ { scan := roachpb.NewScan(tc.keys[start], tc.keys[len(tc.keys)-1].Next(), int64(maxResults)) reply, err := client.SendWrapped(tds, nil, scan) if err != nil { t.Fatal(err) } rows := reply.(*roachpb.ScanResponse).Rows if start+maxResults <= len(tc.keys) && len(rows) != maxResults { t.Errorf("%d: start=%s: expected %d rows, but got %d", i, tc.keys[start], maxResults, len(rows)) } else if start+maxResults == len(tc.keys)+1 && len(rows) != maxResults-1 { t.Errorf("%d: expected %d rows, but got %d", i, maxResults-1, len(rows)) } } } } }
// setAppliedIndex persists a new applied index. func setAppliedIndex(eng engine.Engine, rangeID roachpb.RangeID, appliedIndex uint64) error { return engine.MVCCPut(eng, nil, /* stats */ keys.RaftAppliedIndexKey(rangeID), roachpb.ZeroTimestamp, roachpb.MakeValueFromBytes(encoding.EncodeUint64(nil, appliedIndex)), nil /* txn */) }
func BenchmarkMVCCPutDelete_RocksDB(b *testing.B) { const cacheSize = 1 << 30 // 1 GB rocksdb, stopper := setupMVCCInMemRocksDB(b, "put_delete") defer stopper.Stop() r := rand.New(rand.NewSource(int64(timeutil.Now().UnixNano()))) value := roachpb.MakeValueFromBytes(randutil.RandBytes(r, 10)) zeroTS := roachpb.ZeroTimestamp var blockNum int64 for i := 0; i < b.N; i++ { blockID := r.Int63() blockNum++ key := encoding.EncodeVarintAscending(nil, blockID) key = encoding.EncodeVarintAscending(key, blockNum) if err := MVCCPut(context.Background(), rocksdb, nil, key, zeroTS, value, nil /* txn */); err != nil { b.Fatal(err) } if err := MVCCDelete(context.Background(), rocksdb, nil, key, zeroTS, nil /* txn */); err != nil { b.Fatal(err) } } }
// createPutRequest returns a ready-made request using the // specified key, value & txn ID. func createPutRequest(key roachpb.Key, value []byte, txn *roachpb.Transaction) (*roachpb.PutRequest, roachpb.Header) { h := roachpb.Header{} h.Txn = txn return &roachpb.PutRequest{ Span: roachpb.Span{ Key: key, }, Value: roachpb.MakeValueFromBytes(value), }, h }
// TestRocksDBCompaction verifies that a garbage collector can be // installed on a RocksDB engine and will properly compact transaction // entries. func TestRocksDBCompaction(t *testing.T) { defer leaktest.AfterTest(t) stopper := stop.NewStopper() defer stopper.Stop() rocksdb := NewInMem(roachpb.Attributes{}, testCacheSize, stopper) rocksdb.SetGCTimeouts(1) // Write two transaction values such that exactly one should be GC'd based // on our GC timeouts. kvs := []roachpb.KeyValue{ { Key: keys.TransactionKey(roachpb.Key("a"), roachpb.Key(uuid.NewUUID4())), Value: roachpb.MakeValueFromBytes(encodeTransaction(makeTS(1, 0), t)), }, { Key: keys.TransactionKey(roachpb.Key("b"), roachpb.Key(uuid.NewUUID4())), Value: roachpb.MakeValueFromBytes(encodeTransaction(makeTS(2, 0), t)), }, } for _, kv := range kvs { if err := MVCCPut(rocksdb, nil, kv.Key, roachpb.ZeroTimestamp, kv.Value, nil); err != nil { t.Fatal(err) } } // Compact range and scan remaining values to compare. rocksdb.CompactRange(NilKey, NilKey) actualKVs, _, err := MVCCScan(rocksdb, keyMin, keyMax, 0, roachpb.ZeroTimestamp, true, nil) if err != nil { t.Fatalf("could not run scan: %v", err) } var keys []roachpb.Key for _, kv := range actualKVs { keys = append(keys, kv.Key) } expKeys := []roachpb.Key{ kvs[1].Key, } if !reflect.DeepEqual(expKeys, keys) { t.Errorf("expected keys %+v, got keys %+v", expKeys, keys) } }
// setupMVCCData writes up to numVersions values at each of numKeys // keys. The number of versions written for each key is chosen // randomly according to a uniform distribution. Each successive // version is written starting at 5ns and then in 5ns increments. This // allows scans at various times, starting at t=5ns, and continuing to // t=5ns*(numVersions+1). A version for each key will be read on every // such scan, but the dynamics of the scan will change depending on // the historical timestamp. Earlier timestamps mean scans which must // skip more historical versions; later timestamps mean scans which // skip fewer. // // The creation of the rocksdb database is time consuming, especially // for larger numbers of versions. The database is persisted between // runs and stored in the current directory as // "mvcc_scan_<versions>_<keys>_<valueBytes>". func setupMVCCScanData(numVersions, numKeys, valueBytes int, b *testing.B) (*RocksDB, *stop.Stopper) { loc := fmt.Sprintf("mvcc_scan_%d_%d_%d", numVersions, numKeys, valueBytes) exists := true if _, err := os.Stat(loc); os.IsNotExist(err) { exists = false } const cacheSize = 8 << 30 // 8 GB stopper := stop.NewStopper() rocksdb := NewRocksDB(roachpb.Attributes{}, loc, cacheSize, stopper) if err := rocksdb.Open(); err != nil { b.Fatalf("could not create new rocksdb db instance at %s: %v", loc, err) } if exists { return rocksdb, stopper } log.Infof("creating mvcc data: %s", loc) rng, _ := randutil.NewPseudoRand() keys := make([]roachpb.Key, numKeys) nvs := make([]int, numKeys) for t := 1; t <= numVersions; t++ { walltime := int64(5 * t) ts := makeTS(walltime, 0) batch := rocksdb.NewBatch() for i := 0; i < numKeys; i++ { if t == 1 { keys[i] = roachpb.Key(encoding.EncodeUvarint([]byte("key-"), uint64(i))) nvs[i] = rand.Intn(numVersions) + 1 } // Only write values if this iteration is less than the random // number of versions chosen for this key. if t <= nvs[i] { value := roachpb.MakeValueFromBytes(randutil.RandBytes(rng, valueBytes)) value.InitChecksum(keys[i]) if err := MVCCPut(batch, nil, keys[i], ts, value, nil); err != nil { b.Fatal(err) } } } if err := batch.Commit(); err != nil { b.Fatal(err) } batch.Close() } rocksdb.CompactRange(nil, nil) return rocksdb, stopper }
func runMVCCPut(valueSize int, b *testing.B) { rng, _ := randutil.NewPseudoRand() value := roachpb.MakeValueFromBytes(randutil.RandBytes(rng, valueSize)) keyBuf := append(make([]byte, 0, 64), []byte("key-")...) stopper := stop.NewStopper() defer stopper.Stop() rocksdb := NewInMem(roachpb.Attributes{Attrs: []string{"ssd"}}, testCacheSize, stopper) b.SetBytes(int64(valueSize)) b.ResetTimer() for i := 0; i < b.N; i++ { key := roachpb.Key(encoding.EncodeUvarint(keyBuf[0:4], uint64(i))) ts := makeTS(time.Now().UnixNano(), 0) if err := MVCCPut(rocksdb, nil, key, ts, value, nil); err != nil { b.Fatalf("failed put: %s", err) } } b.StopTimer() }
func runMVCCBatchPut(valueSize, batchSize int, b *testing.B) { defer tracing.Disable()() rng, _ := randutil.NewPseudoRand() value := roachpb.MakeValueFromBytes(randutil.RandBytes(rng, valueSize)) keyBuf := append(make([]byte, 0, 64), []byte("key-")...) stopper := stop.NewStopper() defer stopper.Stop() rocksdb := NewInMem(roachpb.Attributes{}, testCacheSize, stopper) b.SetBytes(int64(valueSize)) b.ResetTimer() for i := 0; i < b.N; i += batchSize { end := i + batchSize if end > b.N { end = b.N } batch := rocksdb.NewBatch() for j := i; j < end; j++ { key := roachpb.Key(encoding.EncodeUvarintAscending(keyBuf[:4], uint64(j))) ts := makeTS(time.Now().UnixNano(), 0) if err := MVCCPut(batch, nil, key, ts, value, nil); err != nil { b.Fatalf("failed put: %s", err) } } if err := batch.Commit(); err != nil { b.Fatal(err) } batch.Close() } b.StopTimer() }
func runMVCCBatchPut(emk engineMaker, valueSize, batchSize int, b *testing.B) { rng, _ := randutil.NewPseudoRand() value := roachpb.MakeValueFromBytes(randutil.RandBytes(rng, valueSize)) keyBuf := append(make([]byte, 0, 64), []byte("key-")...) stopper := stop.NewStopper() eng, stopper := emk(b, fmt.Sprintf("batch_put_%d_%d", valueSize, batchSize)) defer stopper.Stop() b.SetBytes(int64(valueSize)) b.ResetTimer() for i := 0; i < b.N; i += batchSize { end := i + batchSize if end > b.N { end = b.N } batch := eng.NewBatch() for j := i; j < end; j++ { key := roachpb.Key(encoding.EncodeUvarintAscending(keyBuf[:4], uint64(j))) ts := makeTS(timeutil.Now().UnixNano(), 0) if err := MVCCPut(context.Background(), batch, nil, key, ts, value, nil); err != nil { b.Fatalf("failed put: %s", err) } } if err := batch.Commit(); err != nil { b.Fatal(err) } batch.Close() } b.StopTimer() }
// setupMVCCData writes up to numVersions values at each of numKeys // keys. The number of versions written for each key is chosen // randomly according to a uniform distribution. Each successive // version is written starting at 5ns and then in 5ns increments. This // allows scans at various times, starting at t=5ns, and continuing to // t=5ns*(numVersions+1). A version for each key will be read on every // such scan, but the dynamics of the scan will change depending on // the historical timestamp. Earlier timestamps mean scans which must // skip more historical versions; later timestamps mean scans which // skip fewer. // // The creation of the rocksdb database is time consuming, especially // for larger numbers of versions. The database is persisted between // runs and stored in the current directory as // "mvcc_scan_<versions>_<keys>_<valueBytes>". func setupMVCCData(numVersions, numKeys, valueBytes int, b *testing.B) (*RocksDB, *stop.Stopper) { loc := fmt.Sprintf("mvcc_data_%d_%d_%d", numVersions, numKeys, valueBytes) exists := true if _, err := os.Stat(loc); os.IsNotExist(err) { exists = false } const cacheSize = 0 const memtableBudget = 512 << 20 // 512 MB stopper := stop.NewStopper() rocksdb := NewRocksDB(roachpb.Attributes{}, loc, cacheSize, memtableBudget, stopper) if err := rocksdb.Open(); err != nil { b.Fatalf("could not create new rocksdb db instance at %s: %v", loc, err) } if exists { readAllFiles(filepath.Join(loc, "*")) return rocksdb, stopper } log.Infof("creating mvcc data: %s", loc) // Generate the same data every time. rng := rand.New(rand.NewSource(1449168817)) keys := make([]roachpb.Key, numKeys) var order []int for i := 0; i < numKeys; i++ { keys[i] = roachpb.Key(encoding.EncodeUvarint([]byte("key-"), uint64(i))) keyVersions := rng.Intn(numVersions) + 1 for j := 0; j < keyVersions; j++ { order = append(order, i) } } // Randomize the order in which the keys are written. for i, n := 0, len(order)-2; i < n; i++ { j := i + rng.Intn(n-i) order[i], order[j] = order[j], order[i] } counts := make([]int, numKeys) batch := rocksdb.NewBatch() for i, idx := range order { // Output the keys in ~20 batches. If we used a single batch to output all // of the keys rocksdb would create a single sstable. We want multiple // sstables in order to exercise filtering of which sstables are examined // during iterator seeking. We fix the number of batches we output so that // optimizations which change the data size result in the same number of // sstables. if i > 0 && (i%(len(order)/20)) == 0 { if err := batch.Commit(); err != nil { b.Fatal(err) } batch.Close() batch = rocksdb.NewBatch() if err := rocksdb.Flush(); err != nil { b.Fatal(err) } } key := keys[idx] ts := makeTS(int64(counts[idx]+1)*5, 0) counts[idx]++ value := roachpb.MakeValueFromBytes(randutil.RandBytes(rng, valueBytes)) value.InitChecksum(key) if err := MVCCPut(batch, nil, key, ts, value, nil); err != nil { b.Fatal(err) } } if err := batch.Commit(); err != nil { b.Fatal(err) } batch.Close() if err := rocksdb.Flush(); err != nil { b.Fatal(err) } return rocksdb, stopper }
// TestMultiRangeScanDeleteRange tests that commands which access multiple // ranges are carried out properly. func TestMultiRangeScanDeleteRange(t *testing.T) { defer leaktest.AfterTest(t) s := StartTestServer(t) defer s.Stop() ds := kv.NewDistSender(&kv.DistSenderContext{Clock: s.Clock()}, s.Gossip()) tds := kv.NewTxnCoordSender(ds, s.Clock(), testContext.Linearizable, nil, s.stopper) if err := s.node.ctx.DB.AdminSplit("m"); err != nil { t.Fatal(err) } writes := []roachpb.Key{roachpb.Key("a"), roachpb.Key("z")} get := &roachpb.GetRequest{ Span: roachpb.Span{Key: writes[0]}, } get.EndKey = writes[len(writes)-1] if _, err := client.SendWrapped(tds, nil, get); err == nil { t.Errorf("able to call Get with a key range: %v", get) } var delTS roachpb.Timestamp for i, k := range writes { put := roachpb.NewPut(k, roachpb.MakeValueFromBytes(k)) reply, err := client.SendWrapped(tds, nil, put) if err != nil { t.Fatal(err) } scan := roachpb.NewScan(writes[0], writes[len(writes)-1].Next(), 0).(*roachpb.ScanRequest) // The Put ts may have been pushed by tsCache, // so make sure we see their values in our Scan. delTS = reply.(*roachpb.PutResponse).Timestamp reply, err = client.SendWrappedWith(tds, nil, roachpb.Header{Timestamp: delTS}, scan) if err != nil { t.Fatal(err) } sr := reply.(*roachpb.ScanResponse) if sr.Txn != nil { // This was the other way around at some point in the past. // Same below for Delete, etc. t.Errorf("expected no transaction in response header") } if rows := sr.Rows; len(rows) != i+1 { t.Fatalf("expected %d rows, but got %d", i+1, len(rows)) } } del := &roachpb.DeleteRangeRequest{ Span: roachpb.Span{ Key: writes[0], EndKey: roachpb.Key(writes[len(writes)-1]).Next(), }, } reply, err := client.SendWrappedWith(tds, nil, roachpb.Header{Timestamp: delTS}, del) if err != nil { t.Fatal(err) } dr := reply.(*roachpb.DeleteRangeResponse) if dr.Txn != nil { t.Errorf("expected no transaction in response header") } if n := dr.NumDeleted; n != int64(len(writes)) { t.Errorf("expected %d keys to be deleted, but got %d instead", len(writes), n) } scan := roachpb.NewScan(writes[0], writes[len(writes)-1].Next(), 0).(*roachpb.ScanRequest) txn := &roachpb.Transaction{Name: "MyTxn"} reply, err = client.SendWrappedWith(tds, nil, roachpb.Header{Txn: txn}, scan) if err != nil { t.Fatal(err) } sr := reply.(*roachpb.ScanResponse) if txn := sr.Txn; txn == nil || txn.Name != "MyTxn" { t.Errorf("wanted Txn to persist, but it changed to %v", txn) } if rows := sr.Rows; len(rows) > 0 { t.Fatalf("scan after delete returned rows: %v", rows) } }
// TestKVDBCoverage verifies that all methods may be invoked on the // key value database. func TestKVDBCoverage(t *testing.T) { defer leaktest.AfterTest(t) s := server.StartTestServer(t) defer s.Stop() db := createTestClient(t, s.Stopper(), s.ServingAddr()) key := roachpb.Key("a") value1 := []byte("value1") value2 := []byte("value2") value3 := []byte("value3") // Put first value at key. if err := db.Put(key, value1); err != nil { t.Fatal(err) } // Verify put. if gr, err := db.Get(key); err != nil { t.Fatal(err) } else if !gr.Exists() { t.Error("expected key to exist") } // Conditional put should succeed, changing value1 to value2. if err := db.CPut(key, value2, value1); err != nil { t.Fatal(err) } // Verify get by looking up conditional put value. if gr, err := db.Get(key); err != nil { t.Fatal(err) } else if !bytes.Equal(gr.ValueBytes(), value2) { t.Errorf("expected get to return %q; got %q", value2, gr.ValueBytes()) } // Increment. if ir, err := db.Inc("i", 10); err != nil { t.Fatal(err) } else if ir.ValueInt() != 10 { t.Errorf("expected increment new value of %d; got %d", 10, ir.ValueInt()) } // Delete conditional put value. if err := db.Del(key); err != nil { t.Fatal(err) } if gr, err := db.Get(key); err != nil { t.Fatal(err) } else if gr.Exists() { t.Error("expected key to not exist after delete") } // Put values in anticipation of scan & delete range. keyValues := []roachpb.KeyValue{ {Key: roachpb.Key("a"), Value: roachpb.MakeValueFromBytes(value1)}, {Key: roachpb.Key("b"), Value: roachpb.MakeValueFromBytes(value2)}, {Key: roachpb.Key("c"), Value: roachpb.MakeValueFromBytes(value3)}, } for _, kv := range keyValues { valueBytes, err := kv.Value.GetBytes() if err != nil { t.Fatal(err) } if err := db.Put(kv.Key, valueBytes); err != nil { t.Fatal(err) } } if rows, err := db.Scan("a", "d", 0); err != nil { t.Fatal(err) } else if len(rows) != len(keyValues) { t.Fatalf("expected %d rows in scan; got %d", len(keyValues), len(rows)) } else { for i, kv := range keyValues { valueBytes, err := kv.Value.GetBytes() if err != nil { t.Fatal(err) } if !bytes.Equal(rows[i].ValueBytes(), valueBytes) { t.Errorf("%d: key %q, values %q != %q", i, kv.Key, rows[i].ValueBytes(), valueBytes) } } } // Test reverse scan. if rows, err := db.ReverseScan("a", "d", 0); err != nil { t.Fatal(err) } else if len(rows) != len(keyValues) { t.Fatalf("expected %d rows in scan; got %d", len(keyValues), len(rows)) } else { for i, kv := range keyValues { valueBytes, err := kv.Value.GetBytes() if err != nil { t.Fatal(err) } if !bytes.Equal(rows[len(keyValues)-1-i].ValueBytes(), valueBytes) { t.Errorf("%d: key %q, values %q != %q", i, kv.Key, rows[len(keyValues)-i].ValueBytes(), valueBytes) } } } if err := db.DelRange("a", "c"); err != nil { t.Fatal(err) } }
// setLastIndex persists a new last index. func setLastIndex(eng engine.Engine, rangeID roachpb.RangeID, lastIndex uint64) error { return engine.MVCCPut(eng, nil, keys.RaftLastIndexKey(rangeID), roachpb.ZeroTimestamp, roachpb.MakeValueFromBytes(encoding.EncodeUint64(nil, lastIndex)), nil) }
func TestIndexKey(t *testing.T) { rng, _ := randutil.NewPseudoRand() var a DatumAlloc tests := []indexKeyTest{ {nil, nil, []parser.Datum{parser.NewDInt(10)}, []parser.Datum{parser.NewDInt(20)}, }, {[]ID{100}, nil, []parser.Datum{parser.NewDInt(10), parser.NewDInt(11)}, []parser.Datum{parser.NewDInt(20)}, }, {[]ID{100, 200}, nil, []parser.Datum{parser.NewDInt(10), parser.NewDInt(11), parser.NewDInt(12)}, []parser.Datum{parser.NewDInt(20)}, }, {nil, []ID{100}, []parser.Datum{parser.NewDInt(10)}, []parser.Datum{parser.NewDInt(20), parser.NewDInt(21)}, }, {[]ID{100}, []ID{100}, []parser.Datum{parser.NewDInt(10), parser.NewDInt(11)}, []parser.Datum{parser.NewDInt(20), parser.NewDInt(21)}, }, {[]ID{100}, []ID{200}, []parser.Datum{parser.NewDInt(10), parser.NewDInt(11)}, []parser.Datum{parser.NewDInt(20), parser.NewDInt(21)}, }, {[]ID{100, 200}, []ID{100, 300}, []parser.Datum{parser.NewDInt(10), parser.NewDInt(11), parser.NewDInt(12)}, []parser.Datum{parser.NewDInt(20), parser.NewDInt(21), parser.NewDInt(22)}, }, } for i := 0; i < 1000; i++ { var t indexKeyTest t.primaryInterleaves = make([]ID, rng.Intn(10)) for j := range t.primaryInterleaves { t.primaryInterleaves[j] = ID(1 + rng.Intn(10)) } valuesLen := randutil.RandIntInRange(rng, len(t.primaryInterleaves)+1, len(t.primaryInterleaves)+10) t.primaryValues = make([]parser.Datum, valuesLen) for j := range t.primaryValues { t.primaryValues[j] = RandDatum(rng, ColumnType_INT, true) } t.secondaryInterleaves = make([]ID, rng.Intn(10)) for j := range t.secondaryInterleaves { t.secondaryInterleaves[j] = ID(1 + rng.Intn(10)) } valuesLen = randutil.RandIntInRange(rng, len(t.secondaryInterleaves)+1, len(t.secondaryInterleaves)+10) t.secondaryValues = make([]parser.Datum, valuesLen) for j := range t.secondaryValues { t.secondaryValues[j] = RandDatum(rng, ColumnType_INT, true) } tests = append(tests, t) } for i, test := range tests { tableDesc, colMap := makeTableDescForTest(test) testValues := append(test.primaryValues, test.secondaryValues...) primaryKeyPrefix := MakeIndexKeyPrefix(&tableDesc, tableDesc.PrimaryIndex.ID) primaryKey, _, err := EncodeIndexKey( &tableDesc, &tableDesc.PrimaryIndex, colMap, testValues, primaryKeyPrefix) if err != nil { t.Fatal(err) } primaryValue := roachpb.MakeValueFromBytes(nil) primaryIndexKV := client.KeyValue{Key: primaryKey, Value: &primaryValue} secondaryIndexEntry, err := EncodeSecondaryIndex( &tableDesc, &tableDesc.Indexes[0], colMap, testValues) if err != nil { t.Fatal(err) } secondaryIndexKV := client.KeyValue{ Key: secondaryIndexEntry.Key, Value: &secondaryIndexEntry.Value, } checkEntry := func(index *IndexDescriptor, entry client.KeyValue) { values, err := decodeIndex(&a, &tableDesc, index, entry.Key) if err != nil { t.Fatal(err) } for j, value := range values { testValue := testValues[colMap[index.ColumnIDs[j]]] if value.Compare(testValue) != 0 { t.Fatalf("%d: value %d got %q but expected %q", i, j, value, testValue) } } indexID, _, err := DecodeIndexKeyPrefix(&a, &tableDesc, entry.Key) if err != nil { t.Fatal(err) } if indexID != index.ID { t.Errorf("%d", i) } extracted, err := ExtractIndexKey(&a, &tableDesc, entry) if err != nil { t.Fatal(err) } if !bytes.Equal(extracted, primaryKey) { t.Errorf("%d got %s <%x>, but expected %s <%x>", i, extracted, []byte(extracted), roachpb.Key(primaryKey), primaryKey) } } checkEntry(&tableDesc.PrimaryIndex, primaryIndexKV) checkEntry(&tableDesc.Indexes[0], secondaryIndexKV) } }
// TestMultiRangeScanDeleteRange tests that commands which access multiple // ranges are carried out properly. func TestMultiRangeScanDeleteRange(t *testing.T) { defer leaktest.AfterTest(t)() s, _, _ := serverutils.StartServer(t, base.TestServerArgs{}) defer s.Stopper().Stop() ts := s.(*TestServer) retryOpts := base.DefaultRetryOptions() retryOpts.Closer = ts.stopper.ShouldQuiesce() ds := kv.NewDistSender(&kv.DistSenderConfig{ Clock: s.Clock(), RPCContext: s.RPCContext(), RPCRetryOptions: &retryOpts, }, ts.Gossip()) ctx := tracing.WithTracer(context.Background(), tracing.NewTracer()) tds := kv.NewTxnCoordSender(ctx, ds, s.Clock(), ts.Ctx.Linearizable, ts.stopper, kv.MakeTxnMetrics()) if err := ts.node.ctx.DB.AdminSplit("m"); err != nil { t.Fatal(err) } writes := []roachpb.Key{roachpb.Key("a"), roachpb.Key("z")} get := &roachpb.GetRequest{ Span: roachpb.Span{Key: writes[0]}, } get.EndKey = writes[len(writes)-1] if _, err := client.SendWrapped(tds, nil, get); err == nil { t.Errorf("able to call Get with a key range: %v", get) } var delTS hlc.Timestamp for i, k := range writes { put := roachpb.NewPut(k, roachpb.MakeValueFromBytes(k)) reply, err := client.SendWrapped(tds, nil, put) if err != nil { t.Fatal(err) } scan := roachpb.NewScan(writes[0], writes[len(writes)-1].Next()) reply, err = client.SendWrapped(tds, nil, scan) if err != nil { t.Fatal(err) } sr := reply.(*roachpb.ScanResponse) if sr.Txn != nil { // This was the other way around at some point in the past. // Same below for Delete, etc. t.Errorf("expected no transaction in response header") } if rows := sr.Rows; len(rows) != i+1 { t.Fatalf("expected %d rows, but got %d", i+1, len(rows)) } } del := &roachpb.DeleteRangeRequest{ Span: roachpb.Span{ Key: writes[0], EndKey: roachpb.Key(writes[len(writes)-1]).Next(), }, ReturnKeys: true, } reply, err := client.SendWrappedWith(tds, nil, roachpb.Header{Timestamp: delTS}, del) if err != nil { t.Fatal(err) } dr := reply.(*roachpb.DeleteRangeResponse) if dr.Txn != nil { t.Errorf("expected no transaction in response header") } if !reflect.DeepEqual(dr.Keys, writes) { t.Errorf("expected %d keys to be deleted, but got %d instead", writes, dr.Keys) } scan := roachpb.NewScan(writes[0], writes[len(writes)-1].Next()) txn := &roachpb.Transaction{Name: "MyTxn"} reply, err = client.SendWrappedWith(tds, nil, roachpb.Header{Txn: txn}, scan) if err != nil { t.Fatal(err) } sr := reply.(*roachpb.ScanResponse) if txn := sr.Txn; txn == nil || txn.Name != "MyTxn" { t.Errorf("wanted Txn to persist, but it changed to %v", txn) } if rows := sr.Rows; len(rows) > 0 { t.Fatalf("scan after delete returned rows: %v", rows) } }
func kv(k, v []byte) roachpb.KeyValue { return roachpb.KeyValue{ Key: k, Value: roachpb.MakeValueFromBytes(v), } }
// TestUncertaintyObservedTimestampForwarding checks that when receiving an // uncertainty restart on a node, the next attempt to read (at the increased // timestamp) is free from uncertainty. See roachpb.Transaction for details. func TestUncertaintyMaxTimestampForwarding(t *testing.T) { defer leaktest.AfterTest(t)() s := createTestDB(t) disableOwnNodeCertain(s) defer s.Stop() // Large offset so that any value in the future is an uncertain read. // Also makes sure that the values we write in the future below don't // actually wind up in the past. s.Clock.SetMaxOffset(50 * time.Second) offsetNS := int64(100) keySlow := roachpb.Key("slow") keyFast := roachpb.Key("fast") valSlow := []byte("wols") valFast := []byte("tsaf") // Write keySlow at now+offset, keyFast at now+2*offset futureTS := s.Clock.Now() futureTS.WallTime += offsetNS val := roachpb.MakeValueFromBytes(valSlow) if err := engine.MVCCPut(s.Eng, nil, keySlow, futureTS, val, nil); err != nil { t.Fatal(err) } futureTS.WallTime += offsetNS val.SetBytes(valFast) if err := engine.MVCCPut(s.Eng, nil, keyFast, futureTS, val, nil); err != nil { t.Fatal(err) } i := 0 if tErr := s.DB.Txn(func(txn *client.Txn) *roachpb.Error { i++ // The first command serves to start a Txn, fixing the timestamps. // There will be a restart, but this is idempotent. if _, pErr := txn.Scan("t", roachpb.Key("t").Next(), 0); pErr != nil { t.Fatal(pErr) } // This is a bit of a hack for the sake of this test: By visiting the // node above, we've made a note of its clock, which allows us to // prevent the restart. But we want to catch the restart, so reset the // observed timestamps. txn.Proto.ResetObservedTimestamps() // The server's clock suddenly jumps ahead of keyFast's timestamp. s.Manual.Set(2*offsetNS + 1) // Now read slowKey first. It should read at 0, catch an uncertainty error, // and get keySlow's timestamp in that error, but upgrade it to the larger // node clock (which is ahead of keyFast as well). If the last part does // not happen, the read of keyFast should fail (i.e. read nothing). // There will be exactly one restart here. if gr, pErr := txn.Get(keySlow); pErr != nil { if i != 1 { t.Fatalf("unexpected transaction error: %s", pErr) } return pErr } else if !gr.Exists() || !bytes.Equal(gr.ValueBytes(), valSlow) { t.Fatalf("read of %q returned %v, wanted value %q", keySlow, gr.Value, valSlow) } // The node should already be certain, so we expect no restart here // and to read the correct key. if gr, pErr := txn.Get(keyFast); pErr != nil { t.Fatalf("second Get failed with %s", pErr) } else if !gr.Exists() || !bytes.Equal(gr.ValueBytes(), valFast) { t.Fatalf("read of %q returned %v, wanted value %q", keyFast, gr.Value, valFast) } return nil }); tErr != nil { t.Fatal(tErr) } }
// setupMVCCData writes up to numVersions values at each of numKeys // keys. The number of versions written for each key is chosen // randomly according to a uniform distribution. Each successive // version is written starting at 5ns and then in 5ns increments. This // allows scans at various times, starting at t=5ns, and continuing to // t=5ns*(numVersions+1). A version for each key will be read on every // such scan, but the dynamics of the scan will change depending on // the historical timestamp. Earlier timestamps mean scans which must // skip more historical versions; later timestamps mean scans which // skip fewer. // // The creation of the database is time consuming, especially for larger // numbers of versions. The database is persisted between runs and stored in // the current directory as "mvcc_scan_<versions>_<keys>_<valueBytes>" (which // is also returned). func setupMVCCData(emk engineMaker, numVersions, numKeys, valueBytes int, b *testing.B) (Engine, string, *stop.Stopper) { loc := fmt.Sprintf("mvcc_data_%d_%d_%d", numVersions, numKeys, valueBytes) exists := true if _, err := os.Stat(loc); os.IsNotExist(err) { exists = false } eng, stopper := emk(b, loc) if exists { readAllFiles(filepath.Join(loc, "*")) return eng, loc, stopper } log.Infof(context.Background(), "creating mvcc data: %s", loc) // Generate the same data every time. rng := rand.New(rand.NewSource(1449168817)) keys := make([]roachpb.Key, numKeys) var order []int for i := 0; i < numKeys; i++ { keys[i] = roachpb.Key(encoding.EncodeUvarintAscending([]byte("key-"), uint64(i))) keyVersions := rng.Intn(numVersions) + 1 for j := 0; j < keyVersions; j++ { order = append(order, i) } } // Randomize the order in which the keys are written. for i, n := 0, len(order); i < n-1; i++ { j := i + rng.Intn(n-i) order[i], order[j] = order[j], order[i] } counts := make([]int, numKeys) batch := eng.NewBatch() for i, idx := range order { // Output the keys in ~20 batches. If we used a single batch to output all // of the keys rocksdb would create a single sstable. We want multiple // sstables in order to exercise filtering of which sstables are examined // during iterator seeking. We fix the number of batches we output so that // optimizations which change the data size result in the same number of // sstables. if scaled := len(order) / 20; i > 0 && (i%scaled) == 0 { log.Infof(context.Background(), "committing (%d/~%d)", i/scaled, 20) if err := batch.Commit(); err != nil { b.Fatal(err) } batch.Close() batch = eng.NewBatch() if err := eng.Flush(); err != nil { b.Fatal(err) } } key := keys[idx] ts := makeTS(int64(counts[idx]+1)*5, 0) counts[idx]++ value := roachpb.MakeValueFromBytes(randutil.RandBytes(rng, valueBytes)) value.InitChecksum(key) if err := MVCCPut(context.Background(), batch, nil, key, ts, value, nil); err != nil { b.Fatal(err) } } if err := batch.Commit(); err != nil { b.Fatal(err) } batch.Close() if err := eng.Flush(); err != nil { b.Fatal(err) } return eng, loc, stopper }