-
Notifications
You must be signed in to change notification settings - Fork 3.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
spanconfig: introduce the spanconfig.SQLTranslator
This patch introduces the spanconfig.SQLTranslator which translates zone configurations to span configurations. The zone config and span config protos look similar, but they differ from each other in a couple of ways: - zone configurations correspond to {descriptor, zone} IDs whereas span configurations correspond to keyspans. - zone configurations have a notion of inheritance which span configurations do not. Instead, they're fully hydrated and flattened out. When the SQLTranslator is given a {zone,descriptor} ID it generates span configurations for all objects under the given ID in the zone configuration hierarchy. The SQLTranslator fills in any missing fields by following up the inheritance chain to fully hydrate span configs. It also accounts for subzones (and subzone spans) when constructing (span, span config) tuples. Split out from #69661. Release note: None
- Loading branch information
1 parent
f612208
commit 6cc313a
Showing
32 changed files
with
1,503 additions
and
30 deletions.
There are no files selected for viewing
Validating CODEOWNERS rules …
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
load("@io_bazel_rules_go//go:def.bzl", "go_test") | ||
|
||
go_test( | ||
name = "spanconfigccl_test", | ||
srcs = [ | ||
"datadriven_test.go", | ||
"main_test.go", | ||
], | ||
data = glob(["testdata/**"]), | ||
deps = [ | ||
"//pkg/base", | ||
"//pkg/ccl/partitionccl", | ||
"//pkg/ccl/utilccl", | ||
"//pkg/config/zonepb", | ||
"//pkg/roachpb:with-mocks", | ||
"//pkg/security", | ||
"//pkg/security/securitytest", | ||
"//pkg/server", | ||
"//pkg/spanconfig", | ||
"//pkg/sql", | ||
"//pkg/sql/catalog/catalogkv", | ||
"//pkg/sql/catalog/descpb", | ||
"//pkg/testutils/serverutils", | ||
"//pkg/testutils/sqlutils", | ||
"//pkg/testutils/testcluster", | ||
"//pkg/util/leaktest", | ||
"//pkg/util/log", | ||
"//pkg/util/randutil", | ||
"@com_github_cockroachdb_datadriven//:datadriven", | ||
"@com_github_stretchr_testify//require", | ||
], | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,219 @@ | ||
// Copyright 2021 The Cockroach Authors. | ||
// | ||
// Licensed as a CockroachDB Enterprise file under the Cockroach Community | ||
// License (the "License"); you may not use this file except in compliance with | ||
// the License. You may obtain a copy of the License at | ||
// | ||
// https://github.com/cockroachdb/cockroach/blob/master/licenses/CCL.txt | ||
|
||
package spanconfigccl_test | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"reflect" | ||
"strings" | ||
"testing" | ||
|
||
"github.com/cockroachdb/cockroach/pkg/base" | ||
_ "github.com/cockroachdb/cockroach/pkg/ccl/partitionccl" | ||
"github.com/cockroachdb/cockroach/pkg/config/zonepb" | ||
"github.com/cockroachdb/cockroach/pkg/roachpb" | ||
"github.com/cockroachdb/cockroach/pkg/spanconfig" | ||
"github.com/cockroachdb/cockroach/pkg/sql" | ||
"github.com/cockroachdb/cockroach/pkg/sql/catalog/catalogkv" | ||
"github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb" | ||
"github.com/cockroachdb/cockroach/pkg/testutils/sqlutils" | ||
"github.com/cockroachdb/cockroach/pkg/testutils/testcluster" | ||
"github.com/cockroachdb/cockroach/pkg/util/leaktest" | ||
"github.com/cockroachdb/cockroach/pkg/util/log" | ||
"github.com/cockroachdb/datadriven" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
// TestSQLTranslatorDataDriven is a data-driven test for the | ||
// spanconfigsqltranslator.SQLTranslator. It allows users to set up zone config | ||
// hierarchies and validate their translation to SpanConfigs is as expected. | ||
// Only fields that are different from the (default) RANGE DEFAULT are printed | ||
// in the test output for readability. | ||
// | ||
// It offers the following commands: | ||
// | ||
// "exec-sql": executes the input SQL query. | ||
// | ||
// "query-sql": executes the input SQL query and prints the results. | ||
// | ||
// "translate [database=<string>] [table=<string>] [named-zone=<string>] | ||
// [id=<int>]: | ||
// translates the SQL zone config state to the span config state starting from | ||
// the referenced object (named zone, database, database + table, or id) as the | ||
// root. Also see spanconfig.SQLTranslator.Translate(). | ||
// | ||
// "full-translate": performs a full translation of the SQL zone config state | ||
// to the implied span config state. Also see | ||
// spanconfig.SQLTranslator.FullTranslate(). | ||
// TODO(arul): Add a secondary tenant configuration for this test as well. | ||
func TestSQLTranslatorDataDriven(t *testing.T) { | ||
defer leaktest.AfterTest(t)() | ||
defer log.Scope(t).Close(t) | ||
|
||
ctx := context.Background() | ||
datadriven.Walk(t, "testdata/", func(t *testing.T, path string) { | ||
tc := testcluster.StartTestCluster(t, 1, base.TestClusterArgs{ | ||
ServerArgs: base.TestServerArgs{ | ||
EnableSpanConfigs: true, | ||
Knobs: base.TestingKnobs{ | ||
SpanConfig: &spanconfig.TestingKnobs{ | ||
ManagerDisableJobCreation: true, | ||
}, | ||
}, | ||
}, | ||
}) | ||
defer tc.Stopper().Stop(ctx) | ||
sqlDB := tc.ServerConn(0 /* idx */) | ||
|
||
datadriven.RunTest(t, path, func(t *testing.T, d *datadriven.TestData) string { | ||
switch d.Cmd { | ||
case "exec-sql": | ||
_, err := sqlDB.Exec(d.Input) | ||
if err != nil { | ||
return err.Error() | ||
} | ||
case "query-sql": | ||
rows, err := sqlDB.Query(d.Input) | ||
if err != nil { | ||
return err.Error() | ||
} | ||
output, err := sqlutils.RowsToDataDrivenOutput(rows) | ||
require.NoError(t, err) | ||
return output | ||
case "translate": | ||
// Parse the args to get the object ID we're interested in translating. | ||
objID := descpb.InvalidID | ||
if d.HasArg(namedZone) { | ||
var zone string | ||
d.ScanArgs(t, namedZone, &zone) | ||
if namedZoneID, found := zonepb.NamedZones[zonepb.NamedZone(zone)]; found { | ||
objID = descpb.ID(namedZoneID) | ||
} else { | ||
t.Fatalf("unkown named zone: %s", zone) | ||
} | ||
} else if d.HasArg(database) { | ||
var dbName string | ||
d.ScanArgs(t, database, &dbName) | ||
if d.HasArg(table) { | ||
var tbName string | ||
d.ScanArgs(t, table, &tbName) | ||
tableDesc := catalogkv.TestingGetTableDescriptor( | ||
tc.Server(0).DB(), | ||
tc.Server(0).ExecutorConfig().(sql.ExecutorConfig).Codec, | ||
dbName, | ||
tbName, | ||
) | ||
objID = tableDesc.GetID() | ||
} else { | ||
dbDesc := catalogkv.TestingGetDatabaseDescriptor( | ||
tc.Server(0).DB(), | ||
tc.Server(0).ExecutorConfig().(sql.ExecutorConfig).Codec, | ||
dbName, | ||
) | ||
objID = dbDesc.GetID() | ||
} | ||
} else if d.HasArg(id) { | ||
var scanID int | ||
d.ScanArgs(t, id, &scanID) | ||
objID = descpb.ID(scanID) | ||
} else { | ||
t.Fatal("insufficient args provided to translate") | ||
} | ||
sqlTranslator := tc.Server(0).SpanConfigSQLTranslator().(spanconfig.SQLTranslator) | ||
entries, err := sqlTranslator.Translate(ctx, descpb.IDs{objID}) | ||
if err != nil { | ||
return err.Error() | ||
} | ||
require.NoError(t, err) | ||
return datadrivenTranslationResult(entries) | ||
case "full-translate": | ||
sqlTranslator := tc.Server(0).SpanConfigSQLTranslator().(spanconfig.SQLTranslator) | ||
entries, _, err := sqlTranslator.FullTranslate(ctx) | ||
require.NoError(t, err) | ||
return datadrivenTranslationResult(entries) | ||
default: | ||
t.Fatalf("unknown command: %s", d.Cmd) | ||
} | ||
return "" | ||
}) | ||
}) | ||
} | ||
|
||
// Constants for data-driven args. | ||
const ( | ||
id = "id" | ||
namedZone = "named-zone" | ||
table = "table" | ||
database = "database" | ||
) | ||
|
||
// datadrivenTranslationResult constructs the datadriven output for a given | ||
// slice of (translated) span config entries. | ||
func datadrivenTranslationResult(entries []roachpb.SpanConfigEntry) string { | ||
var output strings.Builder | ||
for _, entry := range entries { | ||
res := diffEntryAgainstRangeDefault(entry) | ||
output.WriteString(res) | ||
output.WriteByte('\n') | ||
} | ||
return output.String() | ||
} | ||
|
||
// diffEntryAgainstRangeDefault computes the difference between the given config | ||
// and RANGE DEFAULT. It then constructs a (span<->mismatching field(s)) string | ||
// and returns it. If there config is same as RANGE DEFAULT, a (span, DEFAULT) | ||
// string is returned instead. | ||
func diffEntryAgainstRangeDefault(entry roachpb.SpanConfigEntry) string { | ||
var res strings.Builder | ||
defaultSpanConfig := zonepb.DefaultZoneConfig().AsSpanConfig() | ||
var diffs []string | ||
|
||
if entry.Config.RangeMaxBytes != defaultSpanConfig.RangeMaxBytes { | ||
diffs = append(diffs, fmt.Sprintf("range_max_bytes=%d", entry.Config.RangeMaxBytes)) | ||
} | ||
if entry.Config.RangeMinBytes != defaultSpanConfig.RangeMinBytes { | ||
diffs = append(diffs, fmt.Sprintf("range_min_bytes=%d", entry.Config.RangeMinBytes)) | ||
} | ||
if entry.Config.GCPolicy.TTLSeconds != defaultSpanConfig.GCPolicy.TTLSeconds { | ||
diffs = append(diffs, fmt.Sprintf("ttl_seconds=%d", entry.Config.GCPolicy.TTLSeconds)) | ||
} | ||
if entry.Config.GlobalReads != defaultSpanConfig.GlobalReads { | ||
diffs = append(diffs, fmt.Sprintf("global_reads=%v", entry.Config.GlobalReads)) | ||
} | ||
if entry.Config.NumReplicas != defaultSpanConfig.NumReplicas { | ||
diffs = append(diffs, fmt.Sprintf("num_replicas=%d", entry.Config.NumReplicas)) | ||
} | ||
if entry.Config.NumVoters != defaultSpanConfig.NumVoters { | ||
diffs = append(diffs, fmt.Sprintf("num_voters=%d", entry.Config.NumVoters)) | ||
} | ||
if !reflect.DeepEqual(entry.Config.Constraints, defaultSpanConfig.Constraints) { | ||
diffs = append(diffs, fmt.Sprintf("constraints=%v", entry.Config.Constraints)) | ||
} | ||
if !reflect.DeepEqual(entry.Config.VoterConstraints, defaultSpanConfig.VoterConstraints) { | ||
diffs = append(diffs, fmt.Sprintf("voter_constraints=%v", entry.Config.VoterConstraints)) | ||
} | ||
if !reflect.DeepEqual(entry.Config.LeasePreferences, defaultSpanConfig.LeasePreferences) { | ||
diffs = append(diffs, fmt.Sprintf("lease_preferences=%v", entry.Config.VoterConstraints)) | ||
} | ||
|
||
if len(diffs) != 0 { | ||
for i, diff := range diffs { | ||
if i == 0 { | ||
res.WriteString(fmt.Sprintf("%-30s %s", entry.Span.String(), diff)) | ||
} else { | ||
res.WriteByte(' ') | ||
res.WriteString(diff) | ||
} | ||
} | ||
} else { | ||
res.WriteString(fmt.Sprintf("%-30s %s", entry.Span.String(), "DEFAULT")) | ||
} | ||
return res.String() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
// Copyright 2021 The Cockroach Authors. | ||
// | ||
// Licensed as a CockroachDB Enterprise file under the Cockroach Community | ||
// License (the "License"); you may not use this file except in compliance with | ||
// the License. You may obtain a copy of the License at | ||
// | ||
// https://github.com/cockroachdb/cockroach/blob/master/licenses/CCL.txt | ||
|
||
package spanconfigccl_test | ||
|
||
import ( | ||
"os" | ||
"testing" | ||
|
||
"github.com/cockroachdb/cockroach/pkg/ccl/utilccl" | ||
"github.com/cockroachdb/cockroach/pkg/security" | ||
"github.com/cockroachdb/cockroach/pkg/security/securitytest" | ||
"github.com/cockroachdb/cockroach/pkg/server" | ||
"github.com/cockroachdb/cockroach/pkg/testutils/serverutils" | ||
"github.com/cockroachdb/cockroach/pkg/testutils/testcluster" | ||
"github.com/cockroachdb/cockroach/pkg/util/randutil" | ||
) | ||
|
||
//go:generate ../../util/leaktest/add-leaktest.sh *_test.go | ||
|
||
func TestMain(m *testing.M) { | ||
defer utilccl.TestingEnableEnterprise()() | ||
security.SetAssetLoader(securitytest.EmbeddedAssets) | ||
randutil.SeedForTests() | ||
serverutils.InitTestServerFactory(server.TestServerFactory) | ||
serverutils.InitTestClusterFactory(testcluster.TestClusterFactory) | ||
os.Exit(m.Run()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
# Create a database with some tables, types, and schemas. Check that span | ||
# configurations are as we expect. | ||
|
||
exec-sql | ||
CREATE DATABASE db; | ||
CREATE TABLE db.t1(); | ||
CREATE TYPE db.typ AS ENUM(); | ||
CREATE SCHEMA db.sc; | ||
CREATE TABLE db.t2(); | ||
---- | ||
|
||
query-sql | ||
SELECT id FROM system.namespace WHERE name='t1' | ||
---- | ||
53 | ||
|
||
query-sql | ||
SELECT id FROM system.namespace WHERE name='t2' | ||
---- | ||
57 | ||
|
||
# We only expect there to be span config entries for tables t1 and t2. | ||
translate database=db | ||
---- | ||
/Table/5{3-4} DEFAULT | ||
/Table/5{7-8} DEFAULT | ||
|
||
# Alter zone config fields on the database and one of the tables to ensure | ||
# things are cascading. | ||
exec-sql | ||
ALTER DATABASE db CONFIGURE ZONE USING num_replicas=7; | ||
ALTER TABLE db.t1 CONFIGURE ZONE USING num_voters=5; | ||
---- | ||
|
||
translate database=db | ||
---- | ||
/Table/5{3-4} num_replicas=7 num_voters=5 | ||
/Table/5{7-8} num_replicas=7 | ||
|
||
# Translating the tables in the database individually should result in the same | ||
# config as above. | ||
|
||
translate database=db table=t1 | ||
---- | ||
/Table/5{3-4} num_replicas=7 num_voters=5 | ||
|
||
translate database=db table=t2 | ||
---- | ||
/Table/5{7-8} num_replicas=7 |
Oops, something went wrong.