// TestTimestampCacheReadVsWrite verifies that the timestamp cache // can differentiate between read and write timestamp. func TestTimestampCacheReadVsWrite(t *testing.T) { defer leaktest.AfterTest(t) manual := hlc.NewManualClock(0) clock := hlc.NewClock(manual.UnixNano) tc := NewTimestampCache(clock) // Add read-only non-txn entry at current time. ts1 := clock.Now() tc.Add(proto.Key("a"), proto.Key("b"), ts1, nil, true) // Add two successive txn entries; one read-only and one read-write. txn1ID := util.NewUUID4() txn2ID := util.NewUUID4() ts2 := clock.Now() tc.Add(proto.Key("a"), nil, ts2, txn1ID, true) ts3 := clock.Now() tc.Add(proto.Key("a"), nil, ts3, txn2ID, false) // Fetching with no transaction gets latest values. if rTS, wTS := tc.GetMax(proto.Key("a"), nil, nil); !rTS.Equal(ts2) || !wTS.Equal(ts3) { t.Errorf("expected %s %s; got %s %s", ts2, ts3, rTS, wTS) } // Fetching with txn ID "1" gets original for read and most recent for write. if rTS, wTS := tc.GetMax(proto.Key("a"), nil, txn1ID); !rTS.Equal(ts1) || !wTS.Equal(ts3) { t.Errorf("expected %s %s; got %s %s", ts1, ts3, rTS, wTS) } // Fetching with txn ID "2" gets ts2 for read and low water mark for write. if rTS, wTS := tc.GetMax(proto.Key("a"), nil, txn2ID); !rTS.Equal(ts2) || !wTS.Equal(tc.lowWater) { t.Errorf("expected %s %s; got %s %s", ts2, tc.lowWater, rTS, wTS) } }
// TestTimestampCacheWithTxnID verifies that timestamps matching // the specified txn ID are ignored. func TestTimestampCacheWithTxnID(t *testing.T) { defer leaktest.AfterTest(t) manual := hlc.NewManualClock(0) clock := hlc.NewClock(manual.UnixNano) tc := NewTimestampCache(clock) // Add two successive txn entries. txn1ID := util.NewUUID4() txn2ID := util.NewUUID4() ts1 := clock.Now() tc.Add(proto.Key("a"), proto.Key("c"), ts1, txn1ID, true) ts2 := clock.Now() // This entry will remove "a"-"b" from the cache. tc.Add(proto.Key("b"), proto.Key("d"), ts2, txn2ID, true) // Fetching with no transaction gets latest value. if ts, _ := tc.GetMax(proto.Key("b"), nil, nil); !ts.Equal(ts2) { t.Errorf("expected %s; got %s", ts2, ts) } // Fetching with txn ID "1" gets most recent. if ts, _ := tc.GetMax(proto.Key("b"), nil, txn1ID); !ts.Equal(ts2) { t.Errorf("expected %s; got %s", ts2, ts) } // Fetching with txn ID "2" skips most recent. if ts, _ := tc.GetMax(proto.Key("b"), nil, txn2ID); !ts.Equal(ts1) { t.Errorf("expected %s; got %s", ts1, ts) } }
// TestTimestampCacheReplacements verifies that a newer entry // in the timestamp cache which completely "covers" an older // entry will replace it. func TestTimestampCacheReplacements(t *testing.T) { defer leaktest.AfterTest(t) manual := hlc.NewManualClock(0) clock := hlc.NewClock(manual.UnixNano) tc := NewTimestampCache(clock) txn1ID := util.NewUUID4() txn2ID := util.NewUUID4() ts1 := clock.Now() tc.Add(proto.Key("a"), nil, ts1, nil, true) if ts, _ := tc.GetMax(proto.Key("a"), nil, nil); !ts.Equal(ts1) { t.Errorf("expected %s; got %s", ts1, ts) } // Write overlapping value with txn1 and verify with txn1--we should get // low water mark, not ts1. ts2 := clock.Now() tc.Add(proto.Key("a"), nil, ts2, txn1ID, true) if ts, _ := tc.GetMax(proto.Key("a"), nil, txn1ID); !ts.Equal(tc.lowWater) { t.Errorf("expected low water (empty) time; got %s", ts) } // Write range which overlaps "a" with txn2 and verify with txn2--we should // get low water mark, not ts2. ts3 := clock.Now() tc.Add(proto.Key("a"), proto.Key("c"), ts3, txn2ID, true) if ts, _ := tc.GetMax(proto.Key("a"), nil, txn2ID); !ts.Equal(tc.lowWater) { t.Errorf("expected low water (empty) time; got %s", ts) } // Also, verify txn1 sees ts3. if ts, _ := tc.GetMax(proto.Key("a"), nil, txn1ID); !ts.Equal(ts3) { t.Errorf("expected %s; got %s", ts3, ts) } // Now, write to "b" with a higher timestamp and no txn. Should be // visible to all txns. ts4 := clock.Now() tc.Add(proto.Key("b"), nil, ts4, nil, true) if ts, _ := tc.GetMax(proto.Key("b"), nil, nil); !ts.Equal(ts4) { t.Errorf("expected %s; got %s", ts4, ts) } if ts, _ := tc.GetMax(proto.Key("b"), nil, txn1ID); !ts.Equal(ts4) { t.Errorf("expected %s; got %s", ts4, ts) } // Finally, write an earlier version of "a"; should simply get // tossed and we should see ts4 still. tc.Add(proto.Key("b"), nil, ts1, nil, true) if ts, _ := tc.GetMax(proto.Key("b"), nil, nil); !ts.Equal(ts4) { t.Errorf("expected %s; got %s", ts4, ts) } }
func newTestSender(handler func(proto.Call)) SenderFunc { txnKey := proto.Key("test-txn") txnID := []byte(util.NewUUID4()) return func(_ context.Context, call proto.Call) { header := call.Args.Header() header.UserPriority = gogoproto.Int32(-1) if header.Txn != nil && len(header.Txn.ID) == 0 { header.Txn.Key = txnKey header.Txn.ID = txnID } call.Reply.Reset() switch call.Args.(type) { case *proto.PutRequest: gogoproto.Merge(call.Reply, testPutResp) default: // Do nothing. } call.Reply.Header().Txn = gogoproto.Clone(call.Args.Header().Txn).(*proto.Transaction) if handler != nil { handler(call) } } }
func TestTxnIDEqual(t *testing.T) { txn1, txn2 := util.NewUUID4(), util.NewUUID4() txn1Copy := append([]byte(nil), txn1...) testCases := []struct { a, b []byte expEqual bool }{ {txn1, txn1, true}, {txn1, txn2, false}, {txn1, txn1Copy, true}, } for i, test := range testCases { if eq := TxnIDEqual(test.a, test.b); eq != test.expEqual { t.Errorf("%d: expected %q == %q: %t; got %t", i, test.a, test.b, test.expEqual, eq) } } }
func TestKeyAddress(t *testing.T) { defer leaktest.AfterTest(t) testCases := []struct { key, expAddress proto.Key }{ {proto.Key{}, proto.KeyMin}, {proto.Key("123"), proto.Key("123")}, {MakeKey(ConfigAccountingPrefix, proto.Key("foo")), proto.Key("\x00acctfoo")}, {RangeDescriptorKey(proto.Key("foo")), proto.Key("foo")}, {TransactionKey(proto.Key("baz"), proto.Key(util.NewUUID4())), proto.Key("baz")}, {TransactionKey(proto.KeyMax, proto.Key(util.NewUUID4())), proto.KeyMax}, {MakeNameMetadataKey(0, "foo"), proto.Key("\x00name-\bfoo")}, {MakeDescMetadataKey(123), proto.Key("\x00desc-\t{")}, {nil, nil}, } for i, test := range testCases { result := KeyAddress(test.key) if !result.Equal(test.expAddress) { t.Errorf("%d: expected address for key %q doesn't match %q", i, test.key, test.expAddress) } } }
// NewTransaction creates a new transaction. The transaction key is // composed using the specified baseKey (for locality with data // affected by the transaction) and a random ID to guarantee // uniqueness. The specified user-level priority is combined with a // randomly chosen value to yield a final priority, used to settle // write conflicts in a way that avoids starvation of long-running // transactions (see Range.InternalPushTxn). func NewTransaction(name string, baseKey Key, userPriority int32, isolation IsolationType, now Timestamp, maxOffset int64) *Transaction { // Compute priority by adjusting based on userPriority factor. priority := MakePriority(nil, userPriority) // Compute timestamp and max timestamp. max := now max.WallTime += maxOffset return &Transaction{ Name: name, Key: baseKey, ID: util.NewUUID4(), Priority: priority, Isolation: isolation, Timestamp: now, OrigTimestamp: now, MaxTimestamp: max, } }
// runInit initializes the engine based on the first // store. The bootstrap engine may not be an in-memory type. func runInit(cmd *cobra.Command, args []string) { // Default user for servers. Context.User = security.NodeUser // First initialize the Context as it is used in other places. err := Context.Init("init") if err != nil { log.Errorf("failed to initialize context: %s", err) return } // Generate a new UUID for cluster ID and bootstrap the cluster. clusterID := util.NewUUID4().String() stopper := util.NewStopper() if _, err := server.BootstrapCluster(clusterID, Context.Engines, stopper); err != nil { log.Errorf("unable to bootstrap cluster: %s", err) return } stopper.Stop() log.Infof("cockroach cluster %s has been initialized", clusterID) }
// TestStoreVerifyKeys checks that key length is enforced and // that end keys must sort >= start. func TestStoreVerifyKeys(t *testing.T) { defer leaktest.AfterTest(t) store, _, stopper := createTestStore(t) defer stopper.Stop() tooLongKey := proto.Key(strings.Repeat("x", proto.KeyMaxLength+1)) // Start with a too-long key on a get. gArgs, gReply := getArgs(tooLongKey, 1, store.StoreID()) if err := store.ExecuteCmd(context.Background(), proto.Call{Args: gArgs, Reply: gReply}); err == nil { t.Fatal("expected error for key too long") } // Try a start key == KeyMax. gArgs.Key = proto.KeyMax if err := store.ExecuteCmd(context.Background(), proto.Call{Args: gArgs, Reply: gReply}); err == nil { t.Fatal("expected error for start key == KeyMax") } // Try a get with an end key specified (get requires only a start key and should fail). gArgs.EndKey = proto.KeyMax if err := store.ExecuteCmd(context.Background(), proto.Call{Args: gArgs, Reply: gReply}); err == nil { t.Fatal("expected error for end key specified on a non-range-based operation") } // Try a scan with too-long EndKey. sArgs, sReply := scanArgs(proto.KeyMin, tooLongKey, 1, store.StoreID()) if err := store.ExecuteCmd(context.Background(), proto.Call{Args: sArgs, Reply: sReply}); err == nil { t.Fatal("expected error for end key too long") } // Try a scan with end key < start key. sArgs.Key = []byte("b") sArgs.EndKey = []byte("a") if err := store.ExecuteCmd(context.Background(), proto.Call{Args: sArgs, Reply: sReply}); err == nil { t.Fatal("expected error for end key < start") } // Try a scan with start key == end key. sArgs.Key = []byte("a") sArgs.EndKey = sArgs.Key if err := store.ExecuteCmd(context.Background(), proto.Call{Args: sArgs, Reply: sReply}); err == nil { t.Fatal("expected error for start == end key") } // Try a put to meta2 key which would otherwise exceed maximum key // length, but is accepted because of the meta prefix. meta2KeyMax := keys.MakeKey(keys.Meta2Prefix, proto.KeyMax) pArgs, pReply := putArgs(meta2KeyMax, []byte("value"), 1, store.StoreID()) if err := store.ExecuteCmd(context.Background(), proto.Call{Args: pArgs, Reply: pReply}); err != nil { t.Fatalf("unexpected error on put to meta2 value: %s", err) } // Try to put a range descriptor record for a start key which is // maximum length. key := append([]byte{}, proto.KeyMax...) key[len(key)-1] = 0x01 pArgs, pReply = putArgs(keys.RangeDescriptorKey(key), []byte("value"), 1, store.StoreID()) if err := store.ExecuteCmd(context.Background(), proto.Call{Args: pArgs, Reply: pReply}); err != nil { t.Fatalf("unexpected error on put to range descriptor for KeyMax value: %s", err) } // Try a put to txn record for a meta2 key (note that this doesn't // actually happen in practice, as txn records are not put directly, // but are instead manipulated only through txn methods). pArgs, pReply = putArgs(keys.TransactionKey(meta2KeyMax, []byte(util.NewUUID4())), []byte("value"), 1, store.StoreID()) if err := store.ExecuteCmd(context.Background(), proto.Call{Args: pArgs, Reply: pReply}); err != nil { t.Fatalf("unexpected error on put to txn meta2 value: %s", err) } }