// TestSender mocks out some of the txn coordinator sender's // functionality. It responds to PutRequests using testPutResp. func newTestSender(pre, post func(roachpb.BatchRequest) (*roachpb.BatchResponse, *roachpb.Error)) SenderFunc { txnKey := roachpb.Key("test-txn") txnID := uuid.NewV4() return func(_ context.Context, ba roachpb.BatchRequest) (*roachpb.BatchResponse, *roachpb.Error) { ba.UserPriority = 1 if ba.Txn != nil && ba.Txn.ID == nil { ba.Txn.Key = txnKey ba.Txn.ID = txnID } var br *roachpb.BatchResponse var pErr *roachpb.Error if pre != nil { br, pErr = pre(ba) } else { br = ba.CreateReply() } if pErr != nil { return nil, pErr } var writing bool status := roachpb.PENDING for i, req := range ba.Requests { args := req.GetInner() if _, ok := args.(*roachpb.PutRequest); ok { if !br.Responses[i].SetValue(util.CloneProto(testPutResp).(roachpb.Response)) { panic("failed to set put response") } } if roachpb.IsTransactionWrite(args) { writing = true } } if args, ok := ba.GetArg(roachpb.EndTransaction); ok { et := args.(*roachpb.EndTransactionRequest) writing = true if et.Commit { status = roachpb.COMMITTED } else { status = roachpb.ABORTED } } if ba.Txn != nil { txnClone := ba.Txn.Clone() br.Txn = &txnClone if pErr == nil { br.Txn.Writing = writing br.Txn.Status = status } } if post != nil { br, pErr = post(ba) } return br, pErr } }
// StartWithStopper is the same as Start, but allows passing a stopper // explicitly. func (ts *TestServer) StartWithStopper(stopper *stop.Stopper) error { if ts.Ctx == nil { ts.Ctx = NewTestContext() } if stopper == nil { stopper = stop.NewStopper() } // Change the replication requirements so we don't get log spam // about ranges not being replicated enough. // TODO(marc): set this in the zones table when we have an entry // for the default cluster-wide zone config and remove these // shenanigans about mutating the global default. oldDefaultZC := util.CloneProto(config.DefaultZoneConfig).(*config.ZoneConfig) config.DefaultZoneConfig.ReplicaAttrs = []roachpb.Attributes{{}} stopper.AddCloser(stop.CloserFn(func() { config.DefaultZoneConfig = oldDefaultZC })) var err error ts.Server, err = NewServer(ts.Ctx, stopper) if err != nil { return err } // Ensure we have the correct number of engines. Add in-memory ones where // needed. There must be at least one store/engine. if ts.StoresPerNode < 1 { ts.StoresPerNode = 1 } for i := len(ts.Ctx.Engines); i < ts.StoresPerNode; i++ { ts.Ctx.Engines = append(ts.Ctx.Engines, engine.NewInMem(roachpb.Attributes{}, 100<<20, ts.Server.stopper)) } if err := ts.Server.Start(); err != nil { return err } // If enabled, wait for initial splits to complete before returning control. // If initial splits do not complete, the server is stopped before // returning. if config.TestingTableSplitsDisabled() { return nil } if err := ts.WaitForInitialSplits(); err != nil { ts.Stop() return err } return nil }
func (ls *Stores) updateBootstrapInfo(bi *gossip.BootstrapInfo) error { if bi.Timestamp.Less(ls.biLatestTS) { return nil } // Update the latest timestamp and set cached version. ls.biLatestTS = bi.Timestamp ls.latestBI = util.CloneProto(bi).(*gossip.BootstrapInfo) // Update all stores. for _, s := range ls.storeMap { if err := engine.MVCCPutProto(s.engine, nil, keys.StoreGossipKey(), roachpb.ZeroTimestamp, nil, bi); err != nil { return err } } return nil }
func TestCloneProto(t *testing.T) { testCases := []struct { pb proto.Message shouldPanic bool }{ {&roachpb.StoreIdent{}, false}, {&roachpb.StoreIdent{ClusterID: uuid.MakeV4()}, true}, {&roachpb.TxnMeta{}, false}, {&roachpb.TxnMeta{ID: uuid.NewV4()}, true}, {&roachpb.Transaction{}, false}, {&config.ZoneConfig{RangeMinBytes: 123, RangeMaxBytes: 456}, false}, } for _, tc := range testCases { var clone proto.Message var panicObj interface{} func() { defer func() { panicObj = recover() }() clone = util.CloneProto(tc.pb) }() if tc.shouldPanic { if panicObj == nil { t.Errorf("%T: expected panic but didn't get one", tc.pb) } } else { if panicObj != nil { if panicStr := fmt.Sprint(panicObj); !strings.Contains(panicStr, "attempt to clone") { t.Errorf("%T: got unexpected panic %s", tc.pb, panicStr) } } } if panicObj == nil { realClone := proto.Clone(tc.pb) if !reflect.DeepEqual(clone, realClone) { t.Errorf("%T: clone did not equal original. expected:\n%+v\ngot:\n%+v", tc.pb, realClone, clone) } } } }
func TestSchemaChangeProcess(t *testing.T) { defer leaktest.AfterTest(t)() // The descriptor changes made must have an immediate effect // so disable leases on tables. defer csql.TestDisableTableLeases()() // Disable external processing of mutations. defer csql.TestDisableAsyncSchemaChangeExec()() server, sqlDB, kvDB := setup(t) defer cleanup(server, sqlDB) var id = csql.ID(keys.MaxReservedDescID + 2) var node = roachpb.NodeID(2) db := server.DB() leaseMgr := csql.NewLeaseManager(0, *db, hlc.NewClock(hlc.UnixNano)) changer := csql.NewSchemaChangerForTesting(id, 0, node, *db, leaseMgr) if _, err := sqlDB.Exec(` CREATE DATABASE t; CREATE TABLE t.test (k CHAR PRIMARY KEY, v CHAR, INDEX foo(v)); INSERT INTO t.test VALUES ('a', 'b'), ('c', 'd'); `); err != nil { t.Fatal(err) } // Read table descriptor for version. nameKey := csql.MakeNameMetadataKey(keys.MaxReservedDescID+1, "test") gr, pErr := kvDB.Get(nameKey) if pErr != nil { t.Fatal(pErr) } if !gr.Exists() { t.Fatalf("Name entry %q does not exist", nameKey) } descKey := csql.MakeDescMetadataKey(csql.ID(gr.ValueInt())) desc := &csql.Descriptor{} // Check that MaybeIncrementVersion doesn't increment the version // when the up_version bit is not set. if pErr := kvDB.GetProto(descKey, desc); pErr != nil { t.Fatal(pErr) } expectedVersion := desc.GetTable().Version if pErr := changer.MaybeIncrementVersion(); pErr != nil { t.Fatal(pErr) } if pErr := kvDB.GetProto(descKey, desc); pErr != nil { t.Fatal(pErr) } newVersion := desc.GetTable().Version if newVersion != expectedVersion { t.Fatalf("bad version; e = %d, v = %d", expectedVersion, newVersion) } isDone, err := changer.IsDone() if err != nil { t.Fatal(err) } if !isDone { t.Fatalf("table expected to not have an outstanding schema change: %v", desc.GetTable()) } // Check that MaybeIncrementVersion increments the version // correctly. expectedVersion++ desc.GetTable().UpVersion = true if pErr := kvDB.Put(descKey, desc); pErr != nil { t.Fatal(pErr) } isDone, err = changer.IsDone() if err != nil { t.Fatal(err) } if isDone { t.Fatalf("table expected to have an outstanding schema change: %v", desc.GetTable()) } if pErr := changer.MaybeIncrementVersion(); pErr != nil { t.Fatal(pErr) } if pErr := kvDB.GetProto(descKey, desc); pErr != nil { t.Fatal(pErr) } newVersion = desc.GetTable().Version if newVersion != expectedVersion { t.Fatalf("bad version; e = %d, v = %d", expectedVersion, newVersion) } isDone, err = changer.IsDone() if err != nil { t.Fatal(err) } if !isDone { t.Fatalf("table expected to not have an outstanding schema change: %v", desc.GetTable()) } // Check that RunStateMachineBeforeBackfill doesn't do anything // if there are no mutations queued. if err := changer.RunStateMachineBeforeBackfill(); err != nil { t.Fatal(err) } if pErr := kvDB.GetProto(descKey, desc); pErr != nil { t.Fatal(pErr) } newVersion = desc.GetTable().Version if newVersion != expectedVersion { t.Fatalf("bad version; e = %d, v = %d", expectedVersion, newVersion) } // Check that RunStateMachineBeforeBackfill functions properly. if pErr := kvDB.GetProto(descKey, desc); pErr != nil { t.Fatal(pErr) } table := desc.GetTable() expectedVersion = table.Version // Make a copy of the index for use in a mutation. index := util.CloneProto(&table.Indexes[0]).(*csql.IndexDescriptor) index.Name = "bar" index.ID = table.NextIndexID table.NextIndexID++ changer = csql.NewSchemaChangerForTesting(id, table.NextMutationID, node, *db, leaseMgr) table.Mutations = append(table.Mutations, csql.DescriptorMutation{ Descriptor_: &csql.DescriptorMutation_Index{Index: index}, Direction: csql.DescriptorMutation_ADD, State: csql.DescriptorMutation_DELETE_ONLY, MutationID: table.NextMutationID, }) table.NextMutationID++ // Run state machine in both directions. for _, direction := range []csql.DescriptorMutation_Direction{csql.DescriptorMutation_ADD, csql.DescriptorMutation_DROP} { table.Mutations[0].Direction = direction expectedVersion++ if pErr := kvDB.Put(descKey, desc); pErr != nil { t.Fatal(pErr) } // The expected end state. expectedState := csql.DescriptorMutation_WRITE_ONLY if direction == csql.DescriptorMutation_DROP { expectedState = csql.DescriptorMutation_DELETE_ONLY } // Run two times to ensure idempotency of operations. for i := 0; i < 2; i++ { if err := changer.RunStateMachineBeforeBackfill(); err != nil { t.Fatal(err) } if pErr := kvDB.GetProto(descKey, desc); pErr != nil { t.Fatal(pErr) } table = desc.GetTable() newVersion = table.Version if newVersion != expectedVersion { t.Fatalf("bad version; e = %d, v = %d", expectedVersion, newVersion) } state := table.Mutations[0].State if state != expectedState { t.Fatalf("bad state; e = %d, v = %d", expectedState, state) } } } // RunStateMachineBeforeBackfill() doesn't complete the schema change. isDone, err = changer.IsDone() if err != nil { t.Fatal(err) } if isDone { t.Fatalf("table expected to have an outstanding schema change: %v", desc.GetTable()) } }