func TestObjectIDForKey(t *testing.T) { defer leaktest.AfterTest(t)() testCases := []struct { key roachpb.RKey success bool id uint32 }{ // Before the structured span. {roachpb.RKeyMin, false, 0}, // Boundaries of structured span. {roachpb.RKeyMax, false, 0}, // Valid, even if there are things after the ID. {testutils.MakeKey(keys.MakeTablePrefix(42), roachpb.RKey("\xff")), true, 42}, {keys.MakeTablePrefix(0), true, 0}, {keys.MakeTablePrefix(999), true, 999}, } for tcNum, tc := range testCases { id, success := config.ObjectIDForKey(tc.key) if success != tc.success { t.Errorf("#%d: expected success=%t", tcNum, tc.success) continue } if id != tc.id { t.Errorf("#%d: expected id=%d, got %d", tcNum, tc.id, id) } } }
func sqlKV(tableID uint32, indexID, descriptorID uint64) roachpb.KeyValue { k := keys.MakeTablePrefix(tableID) k = encoding.EncodeUvarintAscending(k, indexID) k = encoding.EncodeUvarintAscending(k, descriptorID) k = encoding.EncodeUvarintAscending(k, 12345) // Column ID, but could be anything. return kv(k, nil) }
func TestDropIndexInterleaved(t *testing.T) { defer leaktest.AfterTest(t)() const chunkSize = 200 params, _ := createTestServerParams() params.Knobs = base.TestingKnobs{ SQLSchemaChanger: &sql.SchemaChangerTestingKnobs{ BackfillChunkSize: chunkSize, }, } s, sqlDB, kvDB := serverutils.StartServer(t, params) defer s.Stopper().Stop() numRows := 2*chunkSize + 1 createKVInterleavedTable(t, sqlDB, numRows) tableDesc := sqlbase.GetTableDescriptor(kvDB, "t", "kv") tablePrefix := roachpb.Key(keys.MakeTablePrefix(uint32(tableDesc.ID))) checkKeyCount(t, kvDB, tablePrefix, 3*numRows) if _, err := sqlDB.Exec(`DROP INDEX t.intlv@intlv_idx`); err != nil { t.Fatal(err) } checkKeyCount(t, kvDB, tablePrefix, 2*numRows) // Ensure that index is not active. tableDesc = sqlbase.GetTableDescriptor(kvDB, "t", "intlv") if _, _, err := tableDesc.FindIndexByName("intlv_idx"); err == nil { t.Fatalf("table descriptor still contains index after index is dropped") } }
func (z *zeroSum) monkey(tableID uint32, d time.Duration) { r := newRand() zipf := z.accountDistribution(r) for { time.Sleep(time.Duration(rand.Float64() * float64(d))) key := keys.MakeTablePrefix(tableID) key = encoding.EncodeVarintAscending(key, int64(zipf.Uint64())) key = keys.MakeRowSentinelKey(key) switch r.Intn(2) { case 0: if err := z.Split(z.RandNode(r.Intn), key); err != nil { if strings.Contains(err.Error(), "range is already split at key") || strings.Contains(err.Error(), storage.ErrMsgConflictUpdatingRangeDesc) { continue } z.maybeLogError(err) } else { atomic.AddUint64(&z.stats.splits, 1) } case 1: if transferred, err := z.TransferLease(z.RandNode(r.Intn), r, key); err != nil { z.maybeLogError(err) } else if transferred { atomic.AddUint64(&z.stats.transfers, 1) } } } }
// TestDropTableInterleaved tests dropping a table that is interleaved within // another table. func TestDropTableInterleaved(t *testing.T) { defer leaktest.AfterTest(t)() params, _ := createTestServerParams() s, sqlDB, kvDB := serverutils.StartServer(t, params) defer s.Stopper().Stop() numRows := 2*sql.TableTruncateChunkSize + 1 createKVInterleavedTable(t, sqlDB, numRows) tableDesc := sqlbase.GetTableDescriptor(kvDB, "t", "kv") tablePrefix := roachpb.Key(keys.MakeTablePrefix(uint32(tableDesc.ID))) checkKeyCount(t, kvDB, tablePrefix, 3*numRows) if _, err := sqlDB.Exec(`DROP TABLE t.intlv`); err != nil { t.Fatal(err) } checkKeyCount(t, kvDB, tablePrefix, numRows) // Test that deleted table cannot be used. This prevents regressions where // name -> descriptor ID caches might make this statement erronously work. if _, err := sqlDB.Exec(`SELECT * FROM t.intlv`); !testutils.IsError( err, `table "t.intlv" does not exist`, ) { t.Fatalf("different error than expected: %v", err) } }
func TestSplitAtTableBoundary(t *testing.T) { defer leaktest.AfterTest(t)() testClusterArgs := base.TestClusterArgs{ ReplicationMode: base.ReplicationAuto, } tc := testcluster.StartTestCluster(t, 3, testClusterArgs) defer tc.Stopper().Stop() runner := sqlutils.MakeSQLRunner(t, tc.Conns[0]) runner.Exec(`CREATE DATABASE test`) runner.Exec(`CREATE TABLE test.t (k SERIAL PRIMARY KEY, v INT)`) const tableIDQuery = ` SELECT tables.id FROM system.namespace tables JOIN system.namespace dbs ON dbs.id = tables.parentid WHERE dbs.name = $1 AND tables.name = $2 ` var tableID uint32 runner.QueryRow(tableIDQuery, "test", "t").Scan(&tableID) tableStartKey := keys.MakeTablePrefix(tableID) // Wait for new table to split. testutils.SucceedsSoon(t, func() error { desc, err := tc.LookupRange(keys.MakeRowSentinelKey(tableStartKey)) if err != nil { t.Fatal(err) } if !desc.StartKey.Equal(tableStartKey) { log.Infof(context.TODO(), "waiting on split results") return errors.Errorf("expected range start key %s; got %s", tableStartKey, desc.StartKey) } return nil }) }
func restoreTableDesc( ctx context.Context, txn *client.Txn, database sqlbase.DatabaseDescriptor, table sqlbase.TableDescriptor, ) error { // Run getDescriptorID again to make sure the database hasn't been dropped // while we were importing. var err error if table.ParentID, err = getDescriptorID(txn, tableKey{name: database.Name}); err != nil { return err } tableIDKey := tableKey{parentID: table.ParentID, name: table.Name}.Key() tableDescKey := sqlbase.MakeDescMetadataKey(table.ID) // Check for an existing table. var existingDesc sqlbase.Descriptor existingIDKV, err := txn.Get(tableIDKey) if err != nil { return err } if existingIDKV.Value != nil { existingID, err := existingIDKV.Value.GetInt() if err != nil { return err } existingDescKV, err := txn.Get(sqlbase.MakeDescMetadataKey(sqlbase.ID(existingID))) if err != nil { return err } if err := existingDescKV.Value.GetProto(&existingDesc); err != nil { return err } } // Write the new descriptors. First the ID -> TableDescriptor for the new // table, then flip (or initialize) the name -> ID entry so any new queries // will use the new one. If there was an existing table, it can now be // cleaned up. b := txn.NewBatch() b.CPut(tableDescKey, sqlbase.WrapDescriptor(&table), nil) if existingTable := existingDesc.GetTable(); existingTable == nil { b.CPut(tableIDKey, table.ID, nil) } else { existingIDKV.Value.ClearChecksum() b.CPut(tableIDKey, table.ID, existingIDKV.Value) // TODO(dan): This doesn't work for interleaved tables. Fix it when we // fix the empty range interleaved table TODO below. existingDataPrefix := roachpb.Key(keys.MakeTablePrefix(uint32(existingTable.ID))) b.DelRange(existingDataPrefix, existingDataPrefix.PrefixEnd(), false) zoneKey, _, descKey := GetKeysForTableDescriptor(existingTable) // Delete the desc and zone entries. Leave the name because the new // table is using it. b.Del(descKey) b.Del(zoneKey) } return txn.Run(b) }
// checkTableSize checks that the number of key:value pairs stored // in the table equals e. func (mt mutationTest) checkTableSize(e int) { // Check that there are no hidden values tablePrefix := keys.MakeTablePrefix(uint32(mt.tableDesc.ID)) tableStartKey := roachpb.Key(tablePrefix) tableEndKey := tableStartKey.PrefixEnd() if kvs, err := mt.kvDB.Scan(context.TODO(), tableStartKey, tableEndKey, 0); err != nil { mt.Error(err) } else if len(kvs) != e { mt.Errorf("expected %d key value pairs, but got %d", e, len(kvs)) } }
// MakeNameMetadataKey returns the key for the name. Pass name == "" in order // to generate the prefix key to use to scan over all of the names for the // specified parentID. func MakeNameMetadataKey(parentID ID, name string) roachpb.Key { normName := parser.ReNormalizeName(name) k := keys.MakeTablePrefix(uint32(NamespaceTable.ID)) k = encoding.EncodeUvarintAscending(k, uint64(NamespaceTable.PrimaryIndex.ID)) k = encoding.EncodeUvarintAscending(k, uint64(parentID)) if name != "" { k = encoding.EncodeBytesAscending(k, []byte(normName)) k = keys.MakeFamilyKey(k, uint32(NamespaceTable.Columns[2].ID)) } return k }
func allSQLDescriptors(txn *client.Txn) ([]sqlbase.Descriptor, error) { startKey := roachpb.Key(keys.MakeTablePrefix(keys.DescriptorTableID)) endKey := startKey.PrefixEnd() // TODO(dan): Iterate with some batch size. rows, err := txn.Scan(startKey, endKey, 0) if err != nil { return nil, errors.Wrap(err, "unable to scan SQL descriptors") } sqlDescs := make([]sqlbase.Descriptor, len(rows)) for i, row := range rows { if err := row.ValueProto(&sqlDescs[i]); err != nil { return nil, errors.Wrapf(err, "%s: unable to unmarshal SQL descriptor", row.Key) } } return sqlDescs, nil }
// GetTableSpan gets the key span for a SQL table, including any indices. func (ie InternalExecutor) GetTableSpan( user string, txn *client.Txn, dbName, tableName string, ) (roachpb.Span, error) { // Lookup the table ID. p := makeInternalPlanner("get-table-span", txn, user, ie.LeaseManager.memMetrics) defer finishInternalPlanner(p) p.leaseMgr = ie.LeaseManager tn := parser.TableName{DatabaseName: parser.Name(dbName), TableName: parser.Name(tableName)} tableID, err := getTableID(p, &tn) if err != nil { return roachpb.Span{}, err } // Determine table data span. tablePrefix := keys.MakeTablePrefix(uint32(tableID)) tableStartKey := roachpb.Key(tablePrefix) tableEndKey := tableStartKey.PrefixEnd() return roachpb.Span{Key: tableStartKey, EndKey: tableEndKey}, nil }
// MakeAllDescsMetadataKey returns the key for all descriptors. func MakeAllDescsMetadataKey() roachpb.Key { k := keys.MakeTablePrefix(uint32(DescriptorTable.ID)) return encoding.EncodeUvarintAscending(k, uint64(DescriptorTable.PrimaryIndex.ID)) }
// TestSplitOnTableBoundaries verifies that ranges get split // as new tables get created. func TestSplitOnTableBoundaries(t *testing.T) { defer leaktest.AfterTest(t)() params, _ := createTestServerParams() // We want fast scan. params.ScanInterval = time.Millisecond params.ScanMaxIdleTime = time.Millisecond s, sqlDB, kvDB := serverutils.StartServer(t, params) defer s.Stopper().Stop() expectedInitialRanges := server.ExpectedInitialRangeCount() if _, err := sqlDB.Exec(`CREATE DATABASE test`); err != nil { t.Fatal(err) } // We split up to the largest allocated descriptor ID, be it a table // or a database. util.SucceedsSoon(t, func() error { num, err := getNumRanges(kvDB) if err != nil { return err } if e := expectedInitialRanges + 1; num != e { return errors.Errorf("expected %d splits, found %d", e, num) } return nil }) // Verify the actual splits. objectID := uint32(keys.MaxReservedDescID + 1) splits := []roachpb.RKey{keys.MakeTablePrefix(objectID), roachpb.RKeyMax} ranges, err := getRangeKeys(kvDB) if err != nil { t.Fatal(err) } if a, e := ranges[expectedInitialRanges-1:], splits; !rangesMatchSplits(a, e) { t.Fatalf("Found ranges: %v\nexpected: %v", a, e) } // Let's create a table. if _, err := sqlDB.Exec(`CREATE TABLE test.test (k INT PRIMARY KEY, v INT)`); err != nil { t.Fatal(err) } util.SucceedsSoon(t, func() error { num, err := getNumRanges(kvDB) if err != nil { return err } if e := expectedInitialRanges + 2; num != e { return errors.Errorf("expected %d splits, found %d", e, num) } return nil }) // Verify the actual splits. splits = []roachpb.RKey{keys.MakeTablePrefix(objectID), keys.MakeTablePrefix(objectID + 1), roachpb.RKeyMax} ranges, err = getRangeKeys(kvDB) if err != nil { t.Fatal(err) } if a, e := ranges[expectedInitialRanges-1:], splits; !rangesMatchSplits(a, e) { t.Fatalf("Found ranges: %v\nexpected: %v", a, e) } }
// Test schema changes are retried and complete properly. This also checks // that a mutation checkpoint reduces the number of chunks operated on during // a retry. func TestSchemaChangeRetry(t *testing.T) { defer leaktest.AfterTest(t)() params, _ := createTestServerParams() attempts := 0 seenSpan := roachpb.Span{} params.Knobs = base.TestingKnobs{ SQLSchemaChanger: &csql.SchemaChangerTestingKnobs{ RunBeforeBackfillChunk: func(sp roachpb.Span) error { attempts++ // Fail somewhere in the middle. if attempts == 3 { return context.DeadlineExceeded } if seenSpan.Key != nil { // Check that the keys are never reevaluated if seenSpan.Key.Compare(sp.Key) >= 0 { t.Errorf("reprocessing span %s, already seen span %s", sp, seenSpan) } if !seenSpan.EndKey.Equal(sp.EndKey) { t.Errorf("different EndKey: span %s, already seen span %s", sp, seenSpan) } } seenSpan = sp return nil }, // Disable asynchronous schema change execution to allow // synchronous path to run schema changes. AsyncExecNotification: asyncSchemaChangerDisabled, WriteCheckpointInterval: time.Nanosecond, }, } s, sqlDB, kvDB := serverutils.StartServer(t, params) defer s.Stopper().Stop() if _, err := sqlDB.Exec(` CREATE DATABASE t; CREATE TABLE t.test (k INT PRIMARY KEY, v INT); `); err != nil { t.Fatal(err) } // Bulk insert. maxValue := 5000 if err := bulkInsertIntoTable(sqlDB, maxValue); err != nil { t.Fatal(err) } // Add an index and check that it succeeds. if _, err := sqlDB.Exec("CREATE UNIQUE INDEX foo ON t.test (v)"); err != nil { t.Fatal(err) } // The schema change succeeded. Verify that the index foo over v is // consistent. rows, err := sqlDB.Query(`SELECT v from t.test@foo`) if err != nil { t.Fatal(err) } count := 0 for ; rows.Next(); count++ { var val int if err := rows.Scan(&val); err != nil { t.Errorf("row %d scan failed: %s", count, err) continue } if count != val { t.Errorf("e = %d, v = %d", count, val) } } if err := rows.Err(); err != nil { t.Fatal(err) } if eCount := maxValue + 1; eCount != count { t.Fatalf("read the wrong number of rows: e = %d, v = %d", eCount, count) } tableDesc := sqlbase.GetTableDescriptor(kvDB, "t", "test") tablePrefix := roachpb.Key(keys.MakeTablePrefix(uint32(tableDesc.ID))) tableEnd := tablePrefix.PrefixEnd() numKeysPerRow := 2 if kvs, err := kvDB.Scan(context.TODO(), tablePrefix, tableEnd, 0); err != nil { t.Fatal(err) } else if e := numKeysPerRow * (maxValue + 1); len(kvs) != e { t.Fatalf("expected %d key value pairs, but got %d", e, len(kvs)) } // Add a column and check that it works. attempts = 0 seenSpan = roachpb.Span{} if _, err := sqlDB.Exec("ALTER TABLE t.test ADD COLUMN x DECIMAL DEFAULT (DECIMAL '1.4')"); err != nil { t.Fatal(err) } rows, err = sqlDB.Query(`SELECT x from t.test`) if err != nil { t.Fatal(err) } count = 0 for ; rows.Next(); count++ { var val float64 if err := rows.Scan(&val); err != nil { t.Errorf("row %d scan failed: %s", count, err) continue } if e := 1.4; e != val { t.Errorf("e = %f, v = %f", e, val) } } if err := rows.Err(); err != nil { t.Fatal(err) } if eCount := maxValue + 1; eCount != count { t.Fatalf("read the wrong number of rows: e = %d, v = %d", eCount, count) } numKeysPerRow++ if kvs, err := kvDB.Scan(context.TODO(), tablePrefix, tableEnd, 0); err != nil { t.Fatal(err) } else if e := numKeysPerRow * (maxValue + 1); len(kvs) != e { t.Fatalf("expected %d key value pairs, but got %d", e, len(kvs)) } // Delete a column and check that it works. attempts = 0 seenSpan = roachpb.Span{} if _, err := sqlDB.Exec("ALTER TABLE t.test DROP x"); err != nil { t.Fatal(err) } numKeysPerRow-- if kvs, err := kvDB.Scan(context.TODO(), tablePrefix, tableEnd, 0); err != nil { t.Fatal(err) } else if e := numKeysPerRow * (maxValue + 1); len(kvs) != e { t.Fatalf("expected %d key value pairs, but got %d", e, len(kvs)) } }
// Test aborting a schema change backfill transaction and check that the // backfill is completed correctly. The backfill transaction is aborted at a // time when it thinks it has processed all the rows of the table. Later, // before the transaction is retried, the table is populated with more rows // that a backfill chunk, requiring the backfill to forget that it is at the // end of its processing and needs to continue on to process two more chunks // of data. func TestAbortSchemaChangeBackfill(t *testing.T) { defer leaktest.AfterTest(t)() var backfillNotification, commandsDone chan struct{} var dontAbortBackfill uint32 params, _ := createTestServerParams() const maxValue = 100 backfillCount := int64(0) retriedBackfill := int64(0) var retriedSpan roachpb.Span // Disable asynchronous schema change execution to allow synchronous path // to trigger start of backfill notification. params.Knobs = base.TestingKnobs{ SQLExecutor: &csql.ExecutorTestingKnobs{ // Fix the priority to guarantee that a high priority transaction // pushes a lower priority one. FixTxnPriority: true, }, SQLSchemaChanger: &csql.SchemaChangerTestingKnobs{ RunBeforeBackfillChunk: func(sp roachpb.Span) error { switch atomic.LoadInt64(&backfillCount) { case 0: // Keep track of the span provided with the first backfill // attempt. retriedSpan = sp case 1: // Ensure that the second backfill attempt provides the // same span as the first. if sp.Equal(retriedSpan) { atomic.AddInt64(&retriedBackfill, 1) } } return nil }, RunAfterBackfillChunk: func() { atomic.AddInt64(&backfillCount, 1) if atomic.SwapUint32(&dontAbortBackfill, 1) == 1 { return } // Close channel to notify that the backfill has been // completed but hasn't yet committed. close(backfillNotification) // Receive signal that the commands that push the backfill // transaction have completed; The backfill will attempt // to commit and will abort. <-commandsDone }, AsyncExecNotification: asyncSchemaChangerDisabled, BackfillChunkSize: maxValue, }, } server, sqlDB, kvDB := serverutils.StartServer(t, params) defer server.Stopper().Stop() if _, err := sqlDB.Exec(` CREATE DATABASE t; CREATE TABLE t.test (k INT PRIMARY KEY, v INT); `); err != nil { t.Fatal(err) } // Bulk insert enough rows to exceed the chunk size. inserts := make([]string, maxValue+1) for i := 0; i < maxValue+1; i++ { inserts[i] = fmt.Sprintf(`(%d, %d)`, i, i) } if _, err := sqlDB.Exec(`INSERT INTO t.test VALUES ` + strings.Join(inserts, ",")); err != nil { t.Fatal(err) } // The two drop cases (column and index) do not need to be tested here // because the INSERT down below will not insert an entry for a dropped // column or index, however, it's still nice to have them just in case // INSERT gets messed up. testCases := []struct { sql string // Each schema change adds/drops a schema element that affects the // number of keys representing a table row. expectedNumKeysPerRow int }{ {"ALTER TABLE t.test ADD COLUMN x DECIMAL DEFAULT (DECIMAL '1.4')", 2}, {"ALTER TABLE t.test DROP x", 1}, {"CREATE UNIQUE INDEX foo ON t.test (v)", 2}, {"DROP INDEX t.test@foo", 1}, } for i, testCase := range testCases { t.Run(testCase.sql, func(t *testing.T) { // Delete two rows so that the table size is smaller than a backfill // chunk. The two values will be added later to make the table larger // than a backfill chunk after the schema change backfill is aborted. for i := 0; i < 2; i++ { if _, err := sqlDB.Exec(`DELETE FROM t.test WHERE k = $1`, i); err != nil { t.Fatal(err) } } backfillNotification = make(chan struct{}) commandsDone = make(chan struct{}) atomic.StoreUint32(&dontAbortBackfill, 0) // Run the column schema change in a separate goroutine. var wg sync.WaitGroup wg.Add(1) go func() { // Start schema change that eventually runs a backfill. if _, err := sqlDB.Exec(testCase.sql); err != nil { t.Error(err) } wg.Done() }() // Wait until the schema change backfill has finished writing its // intents. <-backfillNotification // Delete a row that will push the backfill transaction. if _, err := sqlDB.Exec(` BEGIN TRANSACTION PRIORITY HIGH; DELETE FROM t.test WHERE k = 2; COMMIT; `); err != nil { t.Fatal(err) } // Add missing rows so that the table exceeds the size of a // backfill chunk. for i := 0; i < 3; i++ { if _, err := sqlDB.Exec(`INSERT INTO t.test VALUES($1, $2)`, i, i); err != nil { t.Fatal(err) } } // Release backfill so that it can try to commit and in the // process discover that it was aborted. close(commandsDone) wg.Wait() // for schema change to complete // Backfill retry happened. if count, e := atomic.SwapInt64(&retriedBackfill, 0), int64(1); count != e { t.Fatalf("expected = %d, found = %d", e, count) } // 1 failed + 2 retried backfill chunks. expectNumBackfills := int64(3) if i == len(testCases)-1 { // The DROP INDEX case: The above INSERTs do not add any index // entries for the inserted rows, so the index remains smaller // than a backfill chunk and is dropped in a single retried // backfill chunk. expectNumBackfills = 2 } if count := atomic.SwapInt64(&backfillCount, 0); count != expectNumBackfills { t.Fatalf("expected = %d, found = %d", expectNumBackfills, count) } // Verify the number of keys left behind in the table to validate // schema change operations. tableDesc := sqlbase.GetTableDescriptor(kvDB, "t", "test") tablePrefix := roachpb.Key(keys.MakeTablePrefix(uint32(tableDesc.ID))) tableEnd := tablePrefix.PrefixEnd() if kvs, err := kvDB.Scan(context.TODO(), tablePrefix, tableEnd, 0); err != nil { t.Fatal(err) } else if e := testCase.expectedNumKeysPerRow * (maxValue + 1); len(kvs) != e { t.Fatalf("expected %d key value pairs, but got %d", e, len(kvs)) } }) } }
// Test schema change backfills are not affected by various operations // that run simultaneously. func TestRaceWithBackfill(t *testing.T) { defer leaktest.AfterTest(t)() var backfillNotification chan bool params, _ := createTestServerParams() // Disable asynchronous schema change execution to allow synchronous path // to trigger start of backfill notification. params.Knobs = base.TestingKnobs{ SQLSchemaChanger: &csql.SchemaChangerTestingKnobs{ RunBeforeBackfillChunk: func(sp roachpb.Span) error { if backfillNotification != nil { // Close channel to notify that the backfill has started. close(backfillNotification) backfillNotification = nil } return nil }, AsyncExecNotification: asyncSchemaChangerDisabled, }, } server, sqlDB, kvDB := serverutils.StartServer(t, params) defer server.Stopper().Stop() if _, err := sqlDB.Exec(` CREATE DATABASE t; CREATE TABLE t.test (k INT PRIMARY KEY, v INT, pi DECIMAL DEFAULT (DECIMAL '3.14')); CREATE UNIQUE INDEX vidx ON t.test (v); `); err != nil { t.Fatal(err) } // Bulk insert. maxValue := 4000 if err := bulkInsertIntoTable(sqlDB, maxValue); err != nil { t.Fatal(err) } // Read table descriptor for version. tableDesc := sqlbase.GetTableDescriptor(kvDB, "t", "test") tablePrefix := roachpb.Key(keys.MakeTablePrefix(uint32(tableDesc.ID))) tableEnd := tablePrefix.PrefixEnd() // number of keys == 3 * number of rows; 2 column families and 1 index entry // for each row. if kvs, err := kvDB.Scan(context.TODO(), tablePrefix, tableEnd, 0); err != nil { t.Fatal(err) } else if e := 3 * (maxValue + 1); len(kvs) != e { t.Fatalf("expected %d key value pairs, but got %d", e, len(kvs)) } // Run some schema changes with operations. // Add column. backfillNotification = make(chan bool) runSchemaChangeWithOperations( t, sqlDB, kvDB, "ALTER TABLE t.test ADD COLUMN x DECIMAL DEFAULT (DECIMAL '1.4')", maxValue, 4, backfillNotification) // Drop column. backfillNotification = make(chan bool) runSchemaChangeWithOperations( t, sqlDB, kvDB, "ALTER TABLE t.test DROP pi", maxValue, 3, backfillNotification) // Add index. backfillNotification = make(chan bool) runSchemaChangeWithOperations( t, sqlDB, kvDB, "CREATE UNIQUE INDEX foo ON t.test (v)", maxValue, 4, backfillNotification) // Drop index. backfillNotification = make(chan bool) runSchemaChangeWithOperations( t, sqlDB, kvDB, "DROP INDEX t.test@vidx", maxValue, 3, backfillNotification) // Verify that the index foo over v is consistent, and that column x has // been backfilled properly. rows, err := sqlDB.Query(`SELECT v, x from t.test@foo`) if err != nil { t.Fatal(err) } count := 0 for ; rows.Next(); count++ { var val int var x float64 if err := rows.Scan(&val, &x); err != nil { t.Errorf("row %d scan failed: %s", count, err) continue } if count != val { t.Errorf("e = %d, v = %d", count, val) } if 1.4 != x { t.Errorf("e = %f, v = %f", 1.4, x) } } if err := rows.Err(); err != nil { t.Fatal(err) } eCount := maxValue + 1 if eCount != count { t.Fatalf("read the wrong number of rows: e = %d, v = %d", eCount, count) } // Verify that a table delete in the middle of a backfill works properly. // The backfill will terminate in the middle, and the delete will // successfully delete all the table data. // // This test could be made its own test but is placed here to speed up the // testing. notification := make(chan bool) backfillNotification = notification // Run the schema change in a separate goroutine. var wg sync.WaitGroup wg.Add(1) go func() { // Start schema change that eventually runs a backfill. if _, err := sqlDB.Exec("CREATE UNIQUE INDEX bar ON t.test (v)"); err != nil { t.Error(err) } wg.Done() }() // Wait until the schema change backfill starts. <-notification // Wait for a short bit to ensure that the backfill has likely progressed // and written some data, but not long enough that the backfill has // completed. time.Sleep(10 * time.Millisecond) if _, err := sqlDB.Exec("DROP TABLE t.test"); err != nil { t.Fatal(err) } // Wait until the schema change is done. wg.Wait() // Ensure that the table data has been deleted. if kvs, err := kvDB.Scan(context.TODO(), tablePrefix, tableEnd, 0); err != nil { t.Fatal(err) } else if len(kvs) != 0 { t.Fatalf("expected %d key value pairs, but got %d", 0, len(kvs)) } }
// GetLargestObjectID returns the largest object ID found in the config which is // less than or equal to maxID. If maxID is 0, returns the largest ID in the // config. func (s SystemConfig) GetLargestObjectID(maxID uint32) (uint32, error) { testingLock.Lock() hook := testingLargestIDHook testingLock.Unlock() if hook != nil { return hook(maxID), nil } // Search for the descriptor table entries within the SystemConfig. highBound := roachpb.Key(keys.MakeTablePrefix(keys.DescriptorTableID + 1)) highIndex := sort.Search(len(s.Values), func(i int) bool { return bytes.Compare(s.Values[i].Key, highBound) >= 0 }) lowBound := roachpb.Key(keys.MakeTablePrefix(keys.DescriptorTableID)) lowIndex := sort.Search(len(s.Values), func(i int) bool { return bytes.Compare(s.Values[i].Key, lowBound) >= 0 }) if highIndex == lowIndex { return 0, fmt.Errorf("descriptor table not found in system config of %d values", len(s.Values)) } // No maximum specified; maximum ID is the last entry in the descriptor // table. if maxID == 0 { id, err := decodeDescMetadataID(s.Values[highIndex-1].Key) if err != nil { return 0, err } return uint32(id), nil } // Maximum specified: need to search the descriptor table. Binary search // through all descriptor table values to find the first descriptor with ID // >= maxID. searchSlice := s.Values[lowIndex:highIndex] var err error maxIdx := sort.Search(len(searchSlice), func(i int) bool { var id uint64 id, err = decodeDescMetadataID(searchSlice[i].Key) if err != nil { return false } return uint32(id) >= maxID }) if err != nil { return 0, err } // If we found an index within the list, maxIdx might point to a descriptor // with exactly maxID. if maxIdx < len(searchSlice) { id, err := decodeDescMetadataID(searchSlice[maxIdx].Key) if err != nil { return 0, err } if uint32(id) == maxID { return uint32(id), nil } } if maxIdx == 0 { return 0, fmt.Errorf("no descriptors present with ID < %d", maxID) } // Return ID of the immediately preceding descriptor. id, err := decodeDescMetadataID(searchSlice[maxIdx-1].Key) if err != nil { return 0, err } return uint32(id), nil }
func TestDropDatabase(t *testing.T) { defer leaktest.AfterTest(t)() params, _ := createTestServerParams() s, sqlDB, kvDB := serverutils.StartServer(t, params) defer s.Stopper().Stop() ctx := context.TODO() // Fix the column families so the key counts below don't change if the // family heuristics are updated. if _, err := sqlDB.Exec(` CREATE DATABASE t; CREATE TABLE t.kv (k CHAR PRIMARY KEY, v CHAR, FAMILY (k), FAMILY (v)); INSERT INTO t.kv VALUES ('c', 'e'), ('a', 'c'), ('b', 'd'); `); err != nil { t.Fatal(err) } dbNameKey := sqlbase.MakeNameMetadataKey(keys.RootNamespaceID, "t") r, err := kvDB.Get(ctx, dbNameKey) if err != nil { t.Fatal(err) } if !r.Exists() { t.Fatalf(`database "t" does not exist`) } dbDescKey := sqlbase.MakeDescMetadataKey(sqlbase.ID(r.ValueInt())) desc := &sqlbase.Descriptor{} if err := kvDB.GetProto(ctx, dbDescKey, desc); err != nil { t.Fatal(err) } dbDesc := desc.GetDatabase() tbNameKey := sqlbase.MakeNameMetadataKey(dbDesc.ID, "kv") gr, err := kvDB.Get(ctx, tbNameKey) if err != nil { t.Fatal(err) } if !gr.Exists() { t.Fatalf(`table "kv" does not exist`) } tbDescKey := sqlbase.MakeDescMetadataKey(sqlbase.ID(gr.ValueInt())) if err := kvDB.GetProto(ctx, tbDescKey, desc); err != nil { t.Fatal(err) } tbDesc := desc.GetTable() // Add a zone config for both the table and database. cfg := config.DefaultZoneConfig() buf, err := protoutil.Marshal(&cfg) if err != nil { t.Fatal(err) } if _, err := sqlDB.Exec(`INSERT INTO system.zones VALUES ($1, $2)`, tbDesc.ID, buf); err != nil { t.Fatal(err) } if _, err := sqlDB.Exec(`INSERT INTO system.zones VALUES ($1, $2)`, dbDesc.ID, buf); err != nil { t.Fatal(err) } tbZoneKey := sqlbase.MakeZoneKey(tbDesc.ID) dbZoneKey := sqlbase.MakeZoneKey(dbDesc.ID) if gr, err := kvDB.Get(ctx, tbZoneKey); err != nil { t.Fatal(err) } else if !gr.Exists() { t.Fatalf("table zone config entry not found") } if gr, err := kvDB.Get(ctx, dbZoneKey); err != nil { t.Fatal(err) } else if !gr.Exists() { t.Fatalf("database zone config entry not found") } tablePrefix := keys.MakeTablePrefix(uint32(tbDesc.ID)) tableStartKey := roachpb.Key(tablePrefix) tableEndKey := tableStartKey.PrefixEnd() if kvs, err := kvDB.Scan(ctx, tableStartKey, tableEndKey, 0); err != nil { t.Fatal(err) } else if l := 6; len(kvs) != l { t.Fatalf("expected %d key value pairs, but got %d", l, len(kvs)) } if _, err := sqlDB.Exec(`DROP DATABASE t`); err != nil { t.Fatal(err) } if kvs, err := kvDB.Scan(ctx, tableStartKey, tableEndKey, 0); err != nil { t.Fatal(err) } else if l := 0; len(kvs) != l { t.Fatalf("expected %d key value pairs, but got %d", l, len(kvs)) } if gr, err := kvDB.Get(ctx, tbDescKey); err != nil { t.Fatal(err) } else if gr.Exists() { t.Fatalf("table descriptor still exists after database is dropped: %q", tbDescKey) } if gr, err := kvDB.Get(ctx, tbNameKey); err != nil { t.Fatal(err) } else if gr.Exists() { t.Fatalf("table descriptor key still exists after database is dropped") } if gr, err := kvDB.Get(ctx, dbDescKey); err != nil { t.Fatal(err) } else if gr.Exists() { t.Fatalf("database descriptor still exists after database is dropped") } if gr, err := kvDB.Get(ctx, dbNameKey); err != nil { t.Fatal(err) } else if gr.Exists() { t.Fatalf("database descriptor key still exists after database is dropped") } if gr, err := kvDB.Get(ctx, tbZoneKey); err != nil { t.Fatal(err) } else if gr.Exists() { t.Fatalf("table zone config entry still exists after the database is dropped") } if gr, err := kvDB.Get(ctx, dbZoneKey); err != nil { t.Fatal(err) } else if gr.Exists() { t.Fatalf("database zone config entry still exists after the database is dropped") } }
// Test schema change purge failure doesn't leave DB in a bad state. func TestSchemaChangePurgeFailure(t *testing.T) { defer leaktest.AfterTest(t)() params, _ := createTestServerParams() const chunkSize = 200 // Disable the async schema changer. var enableAsyncSchemaChanges uint32 attempts := 0 // attempt 1: write the first chunk of the index. // attempt 2: write the second chunk and hit a unique constraint // violation; purge the schema change. // attempt 3: return an error while purging the schema change. expectedAttempts := 3 params.Knobs = base.TestingKnobs{ SQLSchemaChanger: &csql.SchemaChangerTestingKnobs{ RunBeforeBackfillChunk: func(sp roachpb.Span) error { attempts++ // Return a deadline exceeded error during the third attempt // which attempts to clean up the schema change. if attempts == expectedAttempts { return context.DeadlineExceeded } return nil }, AsyncExecNotification: func() error { if enable := atomic.LoadUint32(&enableAsyncSchemaChanges); enable == 0 { return errors.New("async schema changes are disabled") } return nil }, // Speed up evaluation of async schema changes so that it // processes a purged schema change quickly. AsyncExecQuickly: true, BackfillChunkSize: chunkSize, }, } server, sqlDB, kvDB := serverutils.StartServer(t, params) defer server.Stopper().Stop() if _, err := sqlDB.Exec(` CREATE DATABASE t; CREATE TABLE t.test (k INT PRIMARY KEY, v INT); `); err != nil { t.Fatal(err) } // Bulk insert. const maxValue = chunkSize + 1 if err := bulkInsertIntoTable(sqlDB, maxValue); err != nil { t.Fatal(err) } // Add a row with a duplicate value for v if _, err := sqlDB.Exec( `INSERT INTO t.test VALUES ($1, $2)`, maxValue+1, maxValue, ); err != nil { t.Fatal(err) } // A schema change that violates integrity constraints. if _, err := sqlDB.Exec( "CREATE UNIQUE INDEX foo ON t.test (v)", ); !testutils.IsError(err, "violates unique constraint") { t.Fatal(err) } // The deadline exceeded error in the schema change purge results in no // retry attempts of the purge. if attempts != expectedAttempts { t.Fatalf("%d retries, despite allowing only (schema change + reverse) = %d", attempts, expectedAttempts) } // The index doesn't exist if _, err := sqlDB.Query( `SELECT v from t.test@foo`, ); !testutils.IsError(err, "index .* not found") { t.Fatal(err) } // Read table descriptor. tableDesc := sqlbase.GetTableDescriptor(kvDB, "t", "test") // There is still a mutation hanging off of it. if e := 1; len(tableDesc.Mutations) != e { t.Fatalf("the table has %d instead of %d mutations", len(tableDesc.Mutations), e) } // The mutation is for a DROP. if tableDesc.Mutations[0].Direction != sqlbase.DescriptorMutation_DROP { t.Fatalf("the table has mutation %v instead of a DROP", tableDesc.Mutations[0]) } // There is still some garbage index data that needs to be purged. All the // rows from k = 0 to k = maxValue have index values. The k = maxValue + 1 // row with the conflict doesn't contain an index value. numGarbageValues := chunkSize tablePrefix := roachpb.Key(keys.MakeTablePrefix(uint32(tableDesc.ID))) tableEnd := tablePrefix.PrefixEnd() if kvs, err := kvDB.Scan(context.TODO(), tablePrefix, tableEnd, 0); err != nil { t.Fatal(err) } else if e := 1*(maxValue+2) + numGarbageValues; len(kvs) != e { t.Fatalf("expected %d key value pairs, but got %d", e, len(kvs)) } // Enable async schema change processing to ensure that it cleans up the // above garbage left behind. atomic.StoreUint32(&enableAsyncSchemaChanges, 1) testutils.SucceedsSoon(t, func() error { tableDesc = sqlbase.GetTableDescriptor(kvDB, "t", "test") if len(tableDesc.Mutations) > 0 { return errors.Errorf("%d mutations remaining", len(tableDesc.Mutations)) } return nil }) // No garbage left behind. numGarbageValues = 0 if kvs, err := kvDB.Scan(context.TODO(), tablePrefix, tableEnd, 0); err != nil { t.Fatal(err) } else if e := 1*(maxValue+2) + numGarbageValues; len(kvs) != e { t.Fatalf("expected %d key value pairs, but got %d", e, len(kvs)) } // A new attempt cleans up a chunk of data. if attempts != expectedAttempts+1 { t.Fatalf("%d chunk ops, despite allowing only (schema change + reverse) = %d", attempts, expectedAttempts) } }
// TestAmbiguousCommitDueToLeadershipChange verifies that an ambiguous // commit error is returned from sql.Exec in situations where an // EndTransaction is part of a batch and the disposition of the batch // request is unknown after a network failure or timeout. The goal // here is to prevent spurious transaction retries after the initial // transaction actually succeeded. In cases where there's an // auto-generated primary key, this can result in silent // duplications. In cases where the primary key is specified in // advance, it can result in violated uniqueness constraints, or // duplicate key violations. See #6053, #7604, and #10023. func TestAmbiguousCommitDueToLeadershipChange(t *testing.T) { defer leaktest.AfterTest(t)() t.Skip("#10341") // Create a command filter which prevents EndTransaction from // returning a response. params := base.TestServerArgs{} committed := make(chan struct{}) wait := make(chan struct{}) var tableStartKey atomic.Value var responseCount int32 // Prevent the first conditional put on table 51 from returning to // waiting client in order to simulate a lost update or slow network // link. params.Knobs.Store = &storage.StoreTestingKnobs{ TestingResponseFilter: func(ba roachpb.BatchRequest, br *roachpb.BatchResponse) *roachpb.Error { req, ok := ba.GetArg(roachpb.ConditionalPut) tsk := tableStartKey.Load() if tsk == nil { return nil } if !ok || !bytes.HasPrefix(req.Header().Key, tsk.([]byte)) { return nil } // If this is the first write to the table, wait to respond to the // client in order to simulate a retry. if atomic.AddInt32(&responseCount, 1) == 1 { close(committed) <-wait } return nil }, } testClusterArgs := base.TestClusterArgs{ ReplicationMode: base.ReplicationAuto, ServerArgs: params, } const numReplicas = 3 tc := testcluster.StartTestCluster(t, numReplicas, testClusterArgs) defer tc.Stopper().Stop() sqlDB := sqlutils.MakeSQLRunner(t, tc.Conns[0]) sqlDB.Exec(`CREATE DATABASE test`) sqlDB.Exec(`CREATE TABLE test.t (k SERIAL PRIMARY KEY, v INT)`) tableID := sqlutils.QueryTableID(t, tc.Conns[0], "test", "t") tableStartKey.Store(keys.MakeTablePrefix(tableID)) // Wait for new table to split. util.SucceedsSoon(t, func() error { startKey := tableStartKey.Load().([]byte) desc, err := tc.LookupRange(keys.MakeRowSentinelKey(startKey)) if err != nil { t.Fatal(err) } if !desc.StartKey.Equal(startKey) { return errors.Errorf("expected range start key %s; got %s", startKey, desc.StartKey) } return nil }) // Lookup the lease. tableRangeDesc, err := tc.LookupRange(keys.MakeRowSentinelKey(tableStartKey.Load().([]byte))) if err != nil { t.Fatal(err) } leaseHolder, err := tc.FindRangeLeaseHolder( &tableRangeDesc, &testcluster.ReplicationTarget{ NodeID: tc.Servers[0].GetNode().Descriptor.NodeID, StoreID: tc.Servers[0].GetFirstStoreID(), }) if err != nil { t.Fatal(err) } // In a goroutine, send an insert which will commit but not return // from the leader (due to the command filter we installed on node 0). sqlErrCh := make(chan error, 1) go func() { // Use a connection other than through the node which is the current // leaseholder to ensure that we use GRPC instead of the local server. // If we use a local server, the hanging response we simulate takes // up the dist sender thread of execution because local requests are // executed synchronously. sqlConn := tc.Conns[leaseHolder.NodeID%numReplicas] _, err := sqlConn.Exec(`INSERT INTO test.t (v) VALUES (1)`) sqlErrCh <- err close(wait) }() // Wait until the insert has committed. <-committed // Find a node other than the current lease holder to transfer the lease to. for i, s := range tc.Servers { if leaseHolder.StoreID != s.GetFirstStoreID() { if err := tc.TransferRangeLease(&tableRangeDesc, tc.Target(i)); err != nil { t.Fatal(err) } break } } // Wait for the error from the pending SQL insert. if err := <-sqlErrCh; !testutils.IsError(err, "result is ambiguous") { t.Errorf("expected ambiguous commit error; got %v", err) } // Verify a single row exists in the table. var rowCount int sqlDB.QueryRow(`SELECT COUNT(*) FROM test.t`).Scan(&rowCount) if rowCount != 1 { t.Errorf("expected 1 row but found %d", rowCount) } }
func TestManualReplication(t *testing.T) { defer leaktest.AfterTest(t)() tc := StartTestCluster(t, 3, base.TestClusterArgs{ ReplicationMode: base.ReplicationManual, ServerArgs: base.TestServerArgs{ UseDatabase: "t", }, }) defer tc.Stopper().Stop() s0 := sqlutils.MakeSQLRunner(t, tc.Conns[0]) s1 := sqlutils.MakeSQLRunner(t, tc.Conns[1]) s2 := sqlutils.MakeSQLRunner(t, tc.Conns[2]) s0.Exec(`CREATE DATABASE t`) s0.Exec(`CREATE TABLE test (k INT PRIMARY KEY, v INT)`) s0.Exec(`INSERT INTO test VALUES (5, 1), (4, 2), (1, 2)`) if r := s1.Query(`SELECT * FROM test WHERE k = 5`); !r.Next() { t.Fatal("no rows") } s2.ExecRowsAffected(3, `DELETE FROM test`) // Split the table to a new range. kvDB := tc.Servers[0].DB() tableDesc := sqlbase.GetTableDescriptor(kvDB, "t", "test") tableStartKey := keys.MakeRowSentinelKey(keys.MakeTablePrefix(uint32(tableDesc.ID))) leftRangeDesc, tableRangeDesc, err := tc.SplitRange(tableStartKey) if err != nil { t.Fatal(err) } log.Infof(context.Background(), "After split got ranges: %+v and %+v.", leftRangeDesc, tableRangeDesc) if len(tableRangeDesc.Replicas) == 0 { t.Fatalf( "expected replica on node 1, got no replicas: %+v", tableRangeDesc.Replicas) } if tableRangeDesc.Replicas[0].NodeID != 1 { t.Fatalf( "expected replica on node 1, got replicas: %+v", tableRangeDesc.Replicas) } // Replicate the table's range to all the nodes. tableRangeDesc, err = tc.AddReplicas( tableRangeDesc.StartKey.AsRawKey(), tc.Target(1), tc.Target(2), ) if err != nil { t.Fatal(err) } if len(tableRangeDesc.Replicas) != 3 { t.Fatalf("expected 3 replicas, got %+v", tableRangeDesc.Replicas) } for i := 0; i < 3; i++ { if _, ok := tableRangeDesc.GetReplicaDescriptor( tc.Servers[i].GetFirstStoreID()); !ok { t.Fatalf("expected replica on store %d, got %+v", tc.Servers[i].GetFirstStoreID(), tableRangeDesc.Replicas) } } // Transfer the lease to node 1. leaseHolder, err := tc.FindRangeLeaseHolder( tableRangeDesc, &ReplicationTarget{ NodeID: tc.Servers[0].GetNode().Descriptor.NodeID, StoreID: tc.Servers[0].GetFirstStoreID(), }) if err != nil { t.Fatal(err) } if leaseHolder.StoreID != tc.Servers[0].GetFirstStoreID() { t.Fatalf("expected initial lease on server idx 0, but is on node: %+v", leaseHolder) } err = tc.TransferRangeLease(tableRangeDesc, tc.Target(1)) if err != nil { t.Fatal(err) } // Check that the lease holder has changed. We'll use the old lease holder as // the hint, since it's guaranteed that the old lease holder has applied the // new lease. leaseHolder, err = tc.FindRangeLeaseHolder( tableRangeDesc, &ReplicationTarget{ NodeID: tc.Servers[0].GetNode().Descriptor.NodeID, StoreID: tc.Servers[0].GetFirstStoreID(), }) if err != nil { t.Fatal(err) } if leaseHolder.StoreID != tc.Servers[1].GetFirstStoreID() { t.Fatalf("expected lease on server idx 1 (node: %d store: %d), but is on node: %+v", tc.Servers[1].GetNode().Descriptor.NodeID, tc.Servers[1].GetFirstStoreID(), leaseHolder) } }
// MakeZoneKey returns the key for 'id's entry in the system.zones table. func MakeZoneKey(id ID) roachpb.Key { k := keys.MakeTablePrefix(uint32(ZonesTable.ID)) k = encoding.EncodeUvarintAscending(k, uint64(ZonesTable.PrimaryIndex.ID)) k = encoding.EncodeUvarintAscending(k, uint64(id)) return keys.MakeFamilyKey(k, uint32(ZonesTable.Columns[1].ID)) }
// TestSplitQueueShouldQueue verifies shouldQueue method correctly // combines splits in zone configs with the size of the range. func TestSplitQueueShouldQueue(t *testing.T) { defer leaktest.AfterTest(t)() tc := testContext{} tc.Start(t) defer tc.Stop() // Set zone configs. config.TestingSetZoneConfig(2000, config.ZoneConfig{RangeMaxBytes: 32 << 20}) config.TestingSetZoneConfig(2002, config.ZoneConfig{RangeMaxBytes: 32 << 20}) // Despite faking the zone configs, we still need to have a gossip entry. if err := tc.gossip.AddInfoProto(gossip.KeySystemConfig, &config.SystemConfig{}, 0); err != nil { t.Fatal(err) } testCases := []struct { start, end roachpb.RKey bytes int64 shouldQ bool priority float64 }{ // No intersection, no bytes. {roachpb.RKeyMin, roachpb.RKey("/"), 0, false, 0}, // Intersection in zone, no bytes. {keys.MakeTablePrefix(2001), roachpb.RKeyMax, 0, true, 1}, // Already split at largest ID. {keys.MakeTablePrefix(2002), roachpb.RKeyMax, 0, false, 0}, // Multiple intersections, no bytes. {roachpb.RKeyMin, roachpb.RKeyMax, 0, true, 1}, // No intersection, max bytes. {roachpb.RKeyMin, roachpb.RKey("/"), 64 << 20, false, 0}, // No intersection, max bytes+1. {roachpb.RKeyMin, roachpb.RKey("/"), 64<<20 + 1, true, 1}, // No intersection, max bytes * 2. {roachpb.RKeyMin, roachpb.RKey("/"), 64 << 21, true, 2}, // Intersection, max bytes +1. {keys.MakeTablePrefix(2000), roachpb.RKeyMax, 32<<20 + 1, true, 2}, // Split needed at table boundary, but no zone config. {keys.MakeTablePrefix(2001), roachpb.RKeyMax, 32<<20 + 1, true, 1}, } splitQ := newSplitQueue(tc.store, nil, tc.gossip) cfg, ok := tc.gossip.GetSystemConfig() if !ok { t.Fatal("config not set") } for i, test := range testCases { func() { // Hold lock throughout to reduce chance of random commands leading // to inconsistent state. tc.rng.mu.Lock() defer tc.rng.mu.Unlock() ms := enginepb.MVCCStats{KeyBytes: test.bytes} if err := setMVCCStats(context.Background(), tc.rng.store.Engine(), tc.rng.RangeID, ms); err != nil { t.Fatal(err) } tc.rng.mu.state.Stats = ms }() copy := *tc.rng.Desc() copy.StartKey = test.start copy.EndKey = test.end if err := tc.rng.setDesc(©); err != nil { t.Fatal(err) } shouldQ, priority := splitQ.shouldQueue(context.TODO(), hlc.ZeroTimestamp, tc.rng, cfg) if shouldQ != test.shouldQ { t.Errorf("%d: should queue expected %t; got %t", i, test.shouldQ, shouldQ) } if math.Abs(priority-test.priority) > 0.00001 { t.Errorf("%d: priority expected %f; got %f", i, test.priority, priority) } } }
func TestDatabaseDescriptor(t *testing.T) { defer leaktest.AfterTest(t)() params, _ := createTestServerParams() s, sqlDB, kvDB := serverutils.StartServer(t, params) defer s.Stopper().Stop() ctx := context.TODO() expectedCounter := int64(keys.MaxReservedDescID + 1) // Test values before creating the database. // descriptor ID counter. if ir, err := kvDB.Get(ctx, keys.DescIDGenerator); err != nil { t.Fatal(err) } else if actual := ir.ValueInt(); actual != expectedCounter { t.Fatalf("expected descriptor ID == %d, got %d", expectedCounter, actual) } // Database name. nameKey := sqlbase.MakeNameMetadataKey(keys.RootNamespaceID, "test") if gr, err := kvDB.Get(ctx, nameKey); err != nil { t.Fatal(err) } else if gr.Exists() { t.Fatal("expected non-existing key") } // Write a descriptor key that will interfere with database creation. dbDescKey := sqlbase.MakeDescMetadataKey(sqlbase.ID(expectedCounter)) dbDesc := &sqlbase.Descriptor{ Union: &sqlbase.Descriptor_Database{ Database: &sqlbase.DatabaseDescriptor{ Name: "sentinel", ID: sqlbase.ID(expectedCounter), Privileges: &sqlbase.PrivilegeDescriptor{}, }, }, } if err := kvDB.CPut(ctx, dbDescKey, dbDesc, nil); err != nil { t.Fatal(err) } // Database creation should fail, and nothing should have been written. if _, err := sqlDB.Exec(`CREATE DATABASE test`); !testutils.IsError(err, "unexpected value") { t.Fatalf("unexpected error %v", err) } if ir, err := kvDB.Get(ctx, keys.DescIDGenerator); err != nil { t.Fatal(err) } else if actual := ir.ValueInt(); actual != expectedCounter { t.Fatalf("expected descriptor ID == %d, got %d", expectedCounter, actual) } start := roachpb.Key(keys.MakeTablePrefix(uint32(keys.NamespaceTableID))) if kvs, err := kvDB.Scan(ctx, start, start.PrefixEnd(), 0); err != nil { t.Fatal(err) } else { if a, e := len(kvs), server.GetBootstrapSchema().SystemDescriptorCount(); a != e { t.Fatalf("expected %d keys to have been written, found %d keys", e, a) } } // Remove the junk; allow database creation to proceed. if err := kvDB.Del(ctx, dbDescKey); err != nil { t.Fatal(err) } if _, err := sqlDB.Exec(`CREATE DATABASE test`); err != nil { t.Fatal(err) } expectedCounter++ // Check keys again. // descriptor ID counter. if ir, err := kvDB.Get(ctx, keys.DescIDGenerator); err != nil { t.Fatal(err) } else if actual := ir.ValueInt(); actual != expectedCounter { t.Fatalf("expected descriptor ID == %d, got %d", expectedCounter, actual) } // Database name. if gr, err := kvDB.Get(ctx, nameKey); err != nil { t.Fatal(err) } else if !gr.Exists() { t.Fatal("key is missing") } // database descriptor. if gr, err := kvDB.Get(ctx, dbDescKey); err != nil { t.Fatal(err) } else if !gr.Exists() { t.Fatal("key is missing") } // Now try to create it again. We should fail, but not increment the counter. if _, err := sqlDB.Exec(`CREATE DATABASE test`); err == nil { t.Fatal("failure expected") } // Check keys again. // descriptor ID counter. if ir, err := kvDB.Get(ctx, keys.DescIDGenerator); err != nil { t.Fatal(err) } else if actual := ir.ValueInt(); actual != expectedCounter { t.Fatalf("expected descriptor ID == %d, got %d", expectedCounter, actual) } // Database name. if gr, err := kvDB.Get(ctx, nameKey); err != nil { t.Fatal(err) } else if !gr.Exists() { t.Fatal("key is missing") } // database descriptor. if gr, err := kvDB.Get(ctx, dbDescKey); err != nil { t.Fatal(err) } else if !gr.Exists() { t.Fatal("key is missing") } }
// ComputeSplitKeys takes a start and end key and returns an array of keys // at which to split the span [start, end). // The only required splits are at each user table prefix. func (s SystemConfig) ComputeSplitKeys(startKey, endKey roachpb.RKey) []roachpb.RKey { tableStart := roachpb.RKey(keys.SystemConfigTableDataMax) if !tableStart.Less(endKey) { // This range is before the user tables span: no required splits. return nil } startID, ok := ObjectIDForKey(startKey) if !ok || startID <= keys.MaxSystemConfigDescID { // The start key is either: // - not part of the structured data span // - part of the system span // In either case, start looking for splits at the first ID usable // by the user data span. startID = keys.MaxSystemConfigDescID + 1 } else { // The start key is either already a split key, or after the split // key for its ID. We can skip straight to the next one. startID++ } // Build key prefixes for sequential table IDs until we reach endKey. Note // that there are two disjoint sets of sequential keys: non-system reserved // tables have sequential IDs, as do user tables, but the two ranges contain a // gap. var splitKeys []roachpb.RKey var key roachpb.RKey // appendSplitKeys generates all possible split keys between the given range // of IDs and adds them to splitKeys. appendSplitKeys := func(startID, endID uint32) { // endID could be smaller than startID if we don't have user tables. for id := startID; id <= endID; id++ { key = keys.MakeRowSentinelKey(keys.MakeTablePrefix(id)) // Skip if this ID matches the startKey passed to ComputeSplitKeys. if !startKey.Less(key) { continue } // Handle the case where EndKey is already a table prefix. if !key.Less(endKey) { break } splitKeys = append(splitKeys, key) } } // If the startKey falls within the non-system reserved range, compute those // keys first. if startID <= keys.MaxReservedDescID { endID, err := s.GetLargestObjectID(keys.MaxReservedDescID) if err != nil { log.Errorf(context.TODO(), "unable to determine largest reserved object ID from system config: %s", err) return nil } appendSplitKeys(startID, endID) startID = keys.MaxReservedDescID + 1 } // Append keys in the user space. endID, err := s.GetLargestObjectID(0) if err != nil { log.Errorf(context.TODO(), "unable to determine largest object ID from system config: %s", err) return nil } appendSplitKeys(startID, endID) return splitKeys }
func TestDropTable(t *testing.T) { defer leaktest.AfterTest(t)() params, _ := createTestServerParams() s, sqlDB, kvDB := serverutils.StartServer(t, params) defer s.Stopper().Stop() ctx := context.TODO() numRows := 2*sql.TableTruncateChunkSize + 1 createKVTable(t, sqlDB, numRows) tableDesc := sqlbase.GetTableDescriptor(kvDB, "t", "kv") nameKey := sqlbase.MakeNameMetadataKey(keys.MaxReservedDescID+1, "kv") gr, err := kvDB.Get(ctx, nameKey) if err != nil { t.Fatal(err) } if !gr.Exists() { t.Fatalf("Name entry %q does not exist", nameKey) } descKey := sqlbase.MakeDescMetadataKey(sqlbase.ID(gr.ValueInt())) // Add a zone config for the table. cfg := config.DefaultZoneConfig() buf, err := protoutil.Marshal(&cfg) if err != nil { t.Fatal(err) } if _, err := sqlDB.Exec(`INSERT INTO system.zones VALUES ($1, $2)`, tableDesc.ID, buf); err != nil { t.Fatal(err) } zoneKey := sqlbase.MakeZoneKey(tableDesc.ID) if gr, err := kvDB.Get(ctx, zoneKey); err != nil { t.Fatal(err) } else if !gr.Exists() { t.Fatalf("zone config entry not found") } tablePrefix := roachpb.Key(keys.MakeTablePrefix(uint32(tableDesc.ID))) checkKeyCount(t, kvDB, tablePrefix, 3*numRows) if _, err := sqlDB.Exec(`DROP TABLE t.kv`); err != nil { t.Fatal(err) } checkKeyCount(t, kvDB, tablePrefix, 0) // Test that deleted table cannot be used. This prevents regressions where // name -> descriptor ID caches might make this statement erronously work. if _, err := sqlDB.Exec(`SELECT * FROM t.kv`); !testutils.IsError(err, `table "t.kv" does not exist`) { t.Fatalf("different error than expected: %v", err) } if gr, err := kvDB.Get(ctx, descKey); err != nil { t.Fatal(err) } else if gr.Exists() { t.Fatalf("table descriptor still exists after the table is dropped") } if gr, err := kvDB.Get(ctx, nameKey); err != nil { t.Fatal(err) } else if gr.Exists() { t.Fatalf("table namekey still exists after the table is dropped") } if gr, err := kvDB.Get(ctx, zoneKey); err != nil { t.Fatal(err) } else if gr.Exists() { t.Fatalf("zone config entry still exists after the table is dropped") } }
// TestSchemaChangeReverseMutations tests that schema changes get reversed // correctly when one of them violates a constraint. func TestSchemaChangeReverseMutations(t *testing.T) { defer leaktest.AfterTest(t)() params, _ := createTestServerParams() const chunkSize = 200 // Disable synchronous schema change processing so that the mutations get // processed asynchronously. var enableAsyncSchemaChanges uint32 params.Knobs = base.TestingKnobs{ SQLSchemaChanger: &csql.SchemaChangerTestingKnobs{ SyncFilter: func(tscc csql.TestingSchemaChangerCollection) { tscc.ClearSchemaChangers() }, AsyncExecNotification: func() error { if enable := atomic.LoadUint32(&enableAsyncSchemaChanges); enable == 0 { return errors.New("async schema changes are disabled") } return nil }, AsyncExecQuickly: true, BackfillChunkSize: chunkSize, }, } s, sqlDB, kvDB := serverutils.StartServer(t, params) defer s.Stopper().Stop() // Create a k-v table. if _, err := sqlDB.Exec(` CREATE DATABASE t; CREATE TABLE t.test (k INT PRIMARY KEY, v INT); `); err != nil { t.Fatal(err) } // Add some data const maxValue = chunkSize + 1 if err := bulkInsertIntoTable(sqlDB, maxValue); err != nil { t.Fatal(err) } // Create a column that is not NULL. This schema change doesn't return an // error only because we've turned off the synchronous execution path; it // will eventually fail when run by the asynchronous path. if _, err := sqlDB.Exec(`ALTER TABLE t.test ADD a INT NOT NULL, ADD c INT`); err != nil { t.Fatal(err) } // Add an index over a column that will be purged. This index will // eventually not get added. if _, err := sqlDB.Exec(`CREATE UNIQUE INDEX idx_a ON t.test (a)`); err != nil { t.Fatal(err) } // The purge of column 'a' doesn't influence these schema changes. // Drop column 'v' moves along just fine. The constraint 'foo' will not be // enforced because c is not added. if _, err := sqlDB.Exec( `ALTER TABLE t.test DROP v, ADD CONSTRAINT foo UNIQUE (c)`, ); err != nil { t.Fatal(err) } // Add unique column 'b' moves along creating column b and the index on // it. if _, err := sqlDB.Exec(`ALTER TABLE t.test ADD b INT UNIQUE`); err != nil { t.Fatal(err) } tableDesc := sqlbase.GetTableDescriptor(kvDB, "t", "test") if e := 7; e != len(tableDesc.Mutations) { t.Fatalf("e = %d, v = %d", e, len(tableDesc.Mutations)) } // Enable async schema change processing. atomic.StoreUint32(&enableAsyncSchemaChanges, 1) // Wait until all the mutations have been processed. var rows *gosql.Rows expectedCols := []string{"k", "b"} testutils.SucceedsSoon(t, func() error { // Read table descriptor. tableDesc = sqlbase.GetTableDescriptor(kvDB, "t", "test") if len(tableDesc.Mutations) > 0 { return errors.Errorf("%d mutations remaining", len(tableDesc.Mutations)) } // Verify that t.test has the expected data. Read the table data while // ensuring that the correct table lease is in use. var err error rows, err = sqlDB.Query(`SELECT * from t.test`) if err != nil { t.Fatal(err) } cols, err := rows.Columns() if err != nil { t.Fatal(err) } // Ensure that sql is using the correct table lease. if len(cols) != len(expectedCols) { return errors.Errorf("incorrect columns: %v, expected: %v", cols, expectedCols) } if cols[0] != expectedCols[0] || cols[1] != expectedCols[1] { t.Fatalf("incorrect columns: %v", cols) } return nil }) // rows contains the data; verify that it's the right data. vals := make([]interface{}, len(expectedCols)) for i := range vals { vals[i] = new(interface{}) } var count int64 for ; rows.Next(); count++ { if err := rows.Scan(vals...); err != nil { t.Errorf("row %d scan failed: %s", count, err) continue } for j, v := range vals { if j == 0 { if val := *v.(*interface{}); val != nil { switch k := val.(type) { case int64: if count != k { t.Errorf("k = %d, expected %d", k, count) } default: t.Errorf("error input of type %T", k) } } else { t.Error("received nil value for column 'k'") } } else { if val := *v.(*interface{}); val != nil { t.Error("received non NULL value for column 'b'") } } } } if err := rows.Err(); err != nil { t.Fatal(err) } if eCount := int64(maxValue + 1); eCount != count { t.Fatalf("read the wrong number of rows: e = %d, v = %d", eCount, count) } // Check that the index on b eventually goes live even though a schema // change in front of it in the queue got purged. rows, err := sqlDB.Query(`SELECT * from t.test@test_b_key`) if err != nil { t.Fatal(err) } count = 0 for ; rows.Next(); count++ { } if err := rows.Err(); err != nil { t.Fatal(err) } if eCount := int64(maxValue + 1); eCount != count { t.Fatalf("read the wrong number of rows: e = %d, v = %d", eCount, count) } // Check that the index on c gets purged. if _, err = sqlDB.Query(`SELECT * from t.test@foo`); err == nil { t.Fatal("SELECT over index 'foo' works") } // Check that the number of k-v pairs is accurate. tablePrefix := roachpb.Key(keys.MakeTablePrefix(uint32(tableDesc.ID))) tableEnd := tablePrefix.PrefixEnd() if kvs, err := kvDB.Scan(context.TODO(), tablePrefix, tableEnd, 0); err != nil { t.Fatal(err) } else if e := 2 * (maxValue + 1); len(kvs) != e { t.Fatalf("expected %d key value pairs, but got %d", e, len(kvs)) } }
// Run a particular schema change and run some OLTP operations in parallel, as // soon as the schema change starts executing its backfill. func runSchemaChangeWithOperations( t *testing.T, sqlDB *gosql.DB, kvDB *client.DB, schemaChange string, maxValue int, keyMultiple int, backfillNotification chan bool, ) { tableDesc := sqlbase.GetTableDescriptor(kvDB, "t", "test") // Run the schema change in a separate goroutine. var wg sync.WaitGroup wg.Add(1) go func() { start := timeutil.Now() // Start schema change that eventually runs a backfill. if _, err := sqlDB.Exec(schemaChange); err != nil { t.Error(err) } t.Logf("schema change %s took %v", schemaChange, timeutil.Since(start)) wg.Done() }() // Wait until the schema change backfill starts. <-backfillNotification // Run a variety of operations during the backfill. // Grabbing a schema change lease on the table will fail, disallowing // another schema change from being simultaneously executed. sc := csql.NewSchemaChangerForTesting(tableDesc.ID, 0, 0, *kvDB, nil) if l, err := sc.AcquireLease(); err == nil { t.Fatalf("schema change lease acquisition on table %d succeeded: %v", tableDesc.ID, l) } // Update some rows. var updatedKeys []int for i := 0; i < 10; i++ { k := rand.Intn(maxValue) v := maxValue + i + 1 if _, err := sqlDB.Exec(`UPDATE t.test SET v = $1 WHERE k = $2`, v, k); err != nil { t.Error(err) } updatedKeys = append(updatedKeys, k) } // Reupdate updated values back to what they were before. for _, k := range updatedKeys { if _, err := sqlDB.Exec(`UPDATE t.test SET v = $1 WHERE k = $2`, maxValue-k, k); err != nil { t.Error(err) } } // Delete some rows. deleteStartKey := rand.Intn(maxValue - 10) for i := 0; i < 10; i++ { if _, err := sqlDB.Exec(`DELETE FROM t.test WHERE k = $1`, deleteStartKey+i); err != nil { t.Error(err) } } // Reinsert deleted rows. for i := 0; i < 10; i++ { k := deleteStartKey + i if _, err := sqlDB.Exec(`INSERT INTO t.test VALUES($1, $2)`, k, maxValue-k); err != nil { t.Error(err) } } // Insert some new rows. numInserts := 10 for i := 0; i < numInserts; i++ { k := maxValue + i + 1 if _, err := sqlDB.Exec(`INSERT INTO t.test VALUES($1, $1)`, k); err != nil { t.Error(err) } } wg.Wait() // for schema change to complete. // Verify the number of keys left behind in the table to validate schema // change operations. tablePrefix := roachpb.Key(keys.MakeTablePrefix(uint32(tableDesc.ID))) tableEnd := tablePrefix.PrefixEnd() if kvs, err := kvDB.Scan(context.TODO(), tablePrefix, tableEnd, 0); err != nil { t.Fatal(err) } else if e := keyMultiple * (maxValue + numInserts + 1); len(kvs) != e { for _, kv := range kvs { t.Errorf("key %s, value %s", kv.Key, kv.Value) } t.Fatalf("expected %d key value pairs, but got %d", e, len(kvs)) } // Delete the rows inserted. for i := 0; i < numInserts; i++ { if _, err := sqlDB.Exec(`DELETE FROM t.test WHERE k = $1`, maxValue+i+1); err != nil { t.Error(err) } } }
func TestReplicateQueueRebalance(t *testing.T) { defer leaktest.AfterTest(t)() // Set the gossip stores interval lower to speed up rebalancing. With the // default of 5s we have to wait ~5s for the rebalancing to start. defer func(v time.Duration) { gossip.GossipStoresInterval = v }(gossip.GossipStoresInterval) gossip.GossipStoresInterval = 100 * time.Millisecond // TODO(peter): Remove when lease rebalancing is the default. defer func(v bool) { storage.EnableLeaseRebalancing = v }(storage.EnableLeaseRebalancing) storage.EnableLeaseRebalancing = true const numNodes = 5 tc := testcluster.StartTestCluster(t, numNodes, base.TestClusterArgs{ReplicationMode: base.ReplicationAuto}, ) defer tc.Stopper().Stop() const newRanges = 5 for i := 0; i < newRanges; i++ { tableID := keys.MaxReservedDescID + i + 1 splitKey := keys.MakeRowSentinelKey(keys.MakeTablePrefix(uint32(tableID))) for { if _, _, err := tc.SplitRange(splitKey); err != nil { if testutils.IsError(err, "split at key .* failed: conflict updating range descriptors") || testutils.IsError(err, "range is already split at key") { continue } t.Fatal(err) } break } } countReplicas := func() []int { counts := make([]int, len(tc.Servers)) for _, s := range tc.Servers { err := s.Stores().VisitStores(func(s *storage.Store) error { counts[s.StoreID()-1] += s.ReplicaCount() return nil }) if err != nil { t.Fatal(err) } } return counts } numRanges := newRanges + server.ExpectedInitialRangeCount() numReplicas := numRanges * 3 const minThreshold = 0.9 minReplicas := int(math.Floor(minThreshold * (float64(numReplicas) / numNodes))) util.SucceedsSoon(t, func() error { counts := countReplicas() for _, c := range counts { if c < minReplicas { err := errors.Errorf("not balanced: %d", counts) log.Info(context.Background(), err) return err } } return nil }) }
// TestGetZoneConfig exercises config.GetZoneConfig and the sql hook for it. func TestGetZoneConfig(t *testing.T) { defer leaktest.AfterTest(t)() params, _ := createTestServerParams() srv, sqlDB, _ := serverutils.StartServer(t, params) defer srv.Stopper().Stop() s := srv.(*server.TestServer) expectedCounter := uint32(keys.MaxReservedDescID) defaultZoneConfig := config.DefaultZoneConfig() defaultZoneConfig.RangeMinBytes = 1 << 20 defaultZoneConfig.RangeMaxBytes = 1 << 20 defaultZoneConfig.GC.TTLSeconds = 60 { buf, err := protoutil.Marshal(&defaultZoneConfig) if err != nil { t.Fatal(err) } objID := keys.RootNamespaceID if _, err = sqlDB.Exec(`UPDATE system.zones SET config = $2 WHERE id = $1`, objID, buf); err != nil { t.Fatalf("problem writing zone %+v: %s", defaultZoneConfig, err) } } // Naming scheme for database and tables: // db1 has tables tb11 and tb12 // db2 has tables tb21 and tb22 expectedCounter++ db1 := expectedCounter if _, err := sqlDB.Exec(`CREATE DATABASE db1`); err != nil { t.Fatal(err) } expectedCounter++ db2 := expectedCounter if _, err := sqlDB.Exec(`CREATE DATABASE db2`); err != nil { t.Fatal(err) } expectedCounter++ tb11 := expectedCounter if _, err := sqlDB.Exec(`CREATE TABLE db1.tb1 (k INT PRIMARY KEY, v INT)`); err != nil { t.Fatal(err) } expectedCounter++ tb12 := expectedCounter if _, err := sqlDB.Exec(`CREATE TABLE db1.tb2 (k INT PRIMARY KEY, v INT)`); err != nil { t.Fatal(err) } expectedCounter++ tb21 := expectedCounter if _, err := sqlDB.Exec(`CREATE TABLE db2.tb1 (k INT PRIMARY KEY, v INT)`); err != nil { t.Fatal(err) } expectedCounter++ tb22 := expectedCounter if _, err := sqlDB.Exec(`CREATE TABLE db2.tb2 (k INT PRIMARY KEY, v INT)`); err != nil { t.Fatal(err) } { cfg := forceNewConfig(t, s) // We have no custom zone configs. testCases := []struct { key roachpb.RKey zoneCfg config.ZoneConfig }{ {roachpb.RKeyMin, defaultZoneConfig}, {keys.MakeTablePrefix(0), defaultZoneConfig}, {keys.MakeTablePrefix(1), defaultZoneConfig}, {keys.MakeTablePrefix(keys.MaxReservedDescID), defaultZoneConfig}, {keys.MakeTablePrefix(db1), defaultZoneConfig}, {keys.MakeTablePrefix(db2), defaultZoneConfig}, {keys.MakeTablePrefix(tb11), defaultZoneConfig}, {keys.MakeTablePrefix(tb12), defaultZoneConfig}, {keys.MakeTablePrefix(tb21), defaultZoneConfig}, {keys.MakeTablePrefix(tb22), defaultZoneConfig}, } for tcNum, tc := range testCases { zoneCfg, err := cfg.GetZoneConfigForKey(tc.key) if err != nil { t.Fatalf("#%d: err=%s", tcNum, err) } if !proto.Equal(&zoneCfg, &tc.zoneCfg) { t.Errorf("#%d: bad zone config.\nexpected: %+v\ngot: %+v", tcNum, tc.zoneCfg, zoneCfg) } } } // Now set some zone configs. We don't have a nice way of using table // names for this, so we do raw puts. // Here is the list of dbs/tables and whether they have a custom zone config: // db1: true // tb1: true // tb2: false // db1: false // tb1: true // tb2: false db1Cfg := config.ZoneConfig{ NumReplicas: 1, Constraints: config.Constraints{Constraints: []config.Constraint{{Value: "db1"}}}, } tb11Cfg := config.ZoneConfig{ NumReplicas: 1, Constraints: config.Constraints{Constraints: []config.Constraint{{Value: "db1.tb1"}}}, } tb21Cfg := config.ZoneConfig{ NumReplicas: 1, Constraints: config.Constraints{Constraints: []config.Constraint{{Value: "db2.tb1"}}}, } for objID, objZone := range map[uint32]config.ZoneConfig{ db1: db1Cfg, tb11: tb11Cfg, tb21: tb21Cfg, } { buf, err := protoutil.Marshal(&objZone) if err != nil { t.Fatal(err) } if _, err = sqlDB.Exec(`INSERT INTO system.zones VALUES ($1, $2)`, objID, buf); err != nil { t.Fatalf("problem writing zone %+v: %s", objZone, err) } } { cfg := forceNewConfig(t, s) testCases := []struct { key roachpb.RKey zoneCfg config.ZoneConfig }{ {roachpb.RKeyMin, defaultZoneConfig}, {keys.MakeTablePrefix(0), defaultZoneConfig}, {keys.MakeTablePrefix(1), defaultZoneConfig}, {keys.MakeTablePrefix(keys.MaxReservedDescID), defaultZoneConfig}, {keys.MakeTablePrefix(db1), db1Cfg}, {keys.MakeTablePrefix(db2), defaultZoneConfig}, {keys.MakeTablePrefix(tb11), tb11Cfg}, {keys.MakeTablePrefix(tb12), db1Cfg}, {keys.MakeTablePrefix(tb21), tb21Cfg}, {keys.MakeTablePrefix(tb22), defaultZoneConfig}, } for tcNum, tc := range testCases { zoneCfg, err := cfg.GetZoneConfigForKey(tc.key) if err != nil { t.Fatalf("#%d: err=%s", tcNum, err) } if !proto.Equal(&zoneCfg, &tc.zoneCfg) { t.Errorf("#%d: bad zone config.\nexpected: %+v\ngot: %+v", tcNum, tc.zoneCfg, zoneCfg) } } } }