Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
57244: sql: configure zone config for CREATE DATABASE r=ajstorm a=otan

Release note (sql change): CREATE DATABASE ... WITH [PRIMARY] REGIONS
now modifies the zone configurations with the settings as set by the
multiregion configuration.

Co-authored-by: Oliver Tan <[email protected]>
  • Loading branch information
craig[bot] and otan committed Dec 4, 2020
2 parents 9cd5016 + 670bf59 commit 3c740d0
Show file tree
Hide file tree
Showing 6 changed files with 277 additions and 4 deletions.
1 change: 1 addition & 0 deletions pkg/sql/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,7 @@ go_test(
"planner_test.go",
"privileged_accessor_test.go",
"rand_test.go",
"region_util_test.go",
"rename_test.go",
"revert_test.go",
"run_control_test.go",
Expand Down
3 changes: 2 additions & 1 deletion pkg/sql/create_database.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,5 +131,6 @@ func (*createDatabaseNode) Close(context.Context) {}

// ReadingOwnWrites implements the planNodeReadingOwnWrites Interface. This is
// required because we create a type descriptor for multi-region databases,
// which must be read during validation.
// which must be read during validation. We also call CONFIGURE ZONE which
// perms multiple KV operations on descriptors and expects to see its own writes.
func (*createDatabaseNode) ReadingOwnWrites() {}
10 changes: 7 additions & 3 deletions pkg/sql/descriptor.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,17 +114,21 @@ func (p *planner) createDatabase(
for _, region := range regionConfig.Regions {
regionLabels = append(regionLabels, tree.EnumValue(region))
}
err := p.createEnumWithID(
// TODO(#multiregion): See github issue:
// https://github.com/cockroachdb/cockroach/issues/56877.
if err := p.createEnumWithID(
p.RunParams(ctx),
desc.RegionConfig.RegionEnumID,
regionLabels,
desc,
tree.NewQualifiedTypeName(dbName, tree.PublicSchema, tree.RegionEnum),
enumTypeMultiRegion,
)
if err != nil {
); err != nil {
return nil, false, err
}
if err := p.applyZoneConfigFromRegionConfigForDatabase(ctx, database.Name, *regionConfig); err != nil {
return nil, true, err
}
}

// TODO(solon): This check should be removed and a public schema should
Expand Down
44 changes: 44 additions & 0 deletions pkg/sql/logictest/testdata/logic_test/multiregion
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,50 @@ SHOW SURVIVAL GOAL FROM DATABASE region_test_db
----
region_test_db zone

query TT
SHOW ZONE CONFIGURATION FOR DATABASE region_test_db
----
DATABASE region_test_db ALTER DATABASE region_test_db CONFIGURE ZONE USING
range_min_bytes = 134217728,
range_max_bytes = 536870912,
gc.ttlseconds = 90000,
num_replicas = 3,
constraints = '{+region=test1: 1}',
lease_preferences = '[[+region=test1]]'

query TT
SHOW ZONE CONFIGURATION FOR DATABASE multi_region_test_db
----
DATABASE multi_region_test_db ALTER DATABASE multi_region_test_db CONFIGURE ZONE USING
range_min_bytes = 134217728,
range_max_bytes = 536870912,
gc.ttlseconds = 90000,
num_replicas = 3,
constraints = '{+region=test1: 1, +region=test2: 1, +region=test3: 1}',
lease_preferences = '[[+region=test2]]'

query TT
SHOW ZONE CONFIGURATION FOR DATABASE multi_region_test_explicit_primary_region_db
----
DATABASE multi_region_test_explicit_primary_region_db ALTER DATABASE multi_region_test_explicit_primary_region_db CONFIGURE ZONE USING
range_min_bytes = 134217728,
range_max_bytes = 536870912,
gc.ttlseconds = 90000,
num_replicas = 3,
constraints = '{+region=test1: 1, +region=test2: 1, +region=test3: 1}',
lease_preferences = '[[+region=test1]]'

query TT
SHOW ZONE CONFIGURATION FOR DATABASE multi_region_test_survive_zone_failure_db
----
DATABASE multi_region_test_survive_zone_failure_db ALTER DATABASE multi_region_test_survive_zone_failure_db CONFIGURE ZONE USING
range_min_bytes = 134217728,
range_max_bytes = 536870912,
gc.ttlseconds = 90000,
num_replicas = 3,
constraints = '{+region=test1: 1, +region=test2: 1, +region=test3: 1}',
lease_preferences = '[[+region=test3]]'

statement error PRIMARY REGION must be specified if REGIONS are specified
CREATE DATABASE no_primary_region_db REGIONS "test1"

Expand Down
71 changes: 71 additions & 0 deletions pkg/sql/region_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,16 @@
package sql

import (
"context"
"sort"
"strings"

"github.com/cockroachdb/cockroach/pkg/config/zonepb"
"github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb"
"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode"
"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror"
"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
"github.com/cockroachdb/cockroach/pkg/sql/sessiondata"
"github.com/cockroachdb/errors"
)

Expand Down Expand Up @@ -74,3 +78,70 @@ func checkLiveClusterRegion(liveClusterRegions liveClusterRegions, region descpb
}
return nil
}

func makeRequiredZoneConstraintForRegion(r descpb.Region) zonepb.Constraint {
return zonepb.Constraint{
Type: zonepb.Constraint_REQUIRED,
Key: "region",
Value: string(r),
}
}

// genZoneConfigFromRegionConfigForDatabase generates a desired ZoneConfig based
// on the region config.
// TODO(aayushshah15): properly configure this for region survivability and leaseholder
// preferences when new zone configuration parameters merge.
func genZoneConfigFromRegionConfigForDatabase(
regionConfig descpb.DatabaseDescriptor_RegionConfig,
) *zonepb.ZoneConfig {
numReplicas := int32(len(regionConfig.Regions))
if numReplicas < 3 {
numReplicas = 3
}
conjunctions := []zonepb.ConstraintsConjunction{}
for _, region := range regionConfig.Regions {
conjunctions = append(
conjunctions,
zonepb.ConstraintsConjunction{
NumReplicas: 1,
Constraints: []zonepb.Constraint{makeRequiredZoneConstraintForRegion(region)},
},
)
}
return &zonepb.ZoneConfig{
NumReplicas: &numReplicas,
LeasePreferences: []zonepb.LeasePreference{
{Constraints: []zonepb.Constraint{makeRequiredZoneConstraintForRegion(regionConfig.PrimaryRegion)}},
},
Constraints: conjunctions,
}
}

func (p *planner) applyZoneConfigFromRegionConfigForDatabase(
ctx context.Context, dbName tree.Name, regionConfig descpb.DatabaseDescriptor_RegionConfig,
) error {
// Convert the partially filled zone config to re-run as a SQL command.
// This avoid us having to modularize planNode logic from set_zone_config
// and the optimizer.
sql, err := zoneConfigToSQL(
&tree.ZoneSpecifier{
Database: dbName,
},
genZoneConfigFromRegionConfigForDatabase(regionConfig),
)
if err != nil {
return err
}
if _, err := p.ExtendedEvalContext().ExecCfg.InternalExecutor.ExecEx(
ctx,
"apply-database-multiregion-set-zone-config",
p.Txn(),
sessiondata.InternalExecutorOverride{
User: p.SessionData().User(),
},
sql,
); err != nil {
return err
}
return nil
}
152 changes: 152 additions & 0 deletions pkg/sql/region_util_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
// Copyright 2020 The Cockroach Authors.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.

package sql

import (
"testing"

"github.com/cockroachdb/cockroach/pkg/config/zonepb"
"github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb"
"github.com/cockroachdb/cockroach/pkg/util/leaktest"
"github.com/gogo/protobuf/proto"
"github.com/stretchr/testify/require"
)

func TestGenZoneConfigFromRegionConfigForDatabase(t *testing.T) {
defer leaktest.AfterTest(t)()

testCases := []struct {
desc string
regionConfig descpb.DatabaseDescriptor_RegionConfig
expected *zonepb.ZoneConfig
}{
{
desc: "one region, zone survival",
regionConfig: descpb.DatabaseDescriptor_RegionConfig{
Regions: []descpb.Region{"region_a"},
PrimaryRegion: "region_a",
SurvivalGoal: descpb.SurvivalGoal_ZONE_FAILURE,
},
expected: &zonepb.ZoneConfig{
NumReplicas: proto.Int32(3),
LeasePreferences: []zonepb.LeasePreference{
{Constraints: []zonepb.Constraint{{Type: zonepb.Constraint_REQUIRED, Key: "region", Value: "region_a"}}},
},
Constraints: []zonepb.ConstraintsConjunction{
{NumReplicas: 1, Constraints: []zonepb.Constraint{{Type: zonepb.Constraint_REQUIRED, Key: "region", Value: "region_a"}}},
},
},
},
{
desc: "two regions, zone survival",
regionConfig: descpb.DatabaseDescriptor_RegionConfig{
Regions: []descpb.Region{"region_b", "region_a"},
PrimaryRegion: "region_a",
SurvivalGoal: descpb.SurvivalGoal_ZONE_FAILURE,
},
expected: &zonepb.ZoneConfig{
NumReplicas: proto.Int32(3),
LeasePreferences: []zonepb.LeasePreference{
{Constraints: []zonepb.Constraint{{Type: zonepb.Constraint_REQUIRED, Key: "region", Value: "region_a"}}},
},
Constraints: []zonepb.ConstraintsConjunction{
{NumReplicas: 1, Constraints: []zonepb.Constraint{{Type: zonepb.Constraint_REQUIRED, Key: "region", Value: "region_b"}}},
{NumReplicas: 1, Constraints: []zonepb.Constraint{{Type: zonepb.Constraint_REQUIRED, Key: "region", Value: "region_a"}}},
},
},
},
{
desc: "three regions, zone survival",
regionConfig: descpb.DatabaseDescriptor_RegionConfig{
Regions: []descpb.Region{"region_b", "region_c", "region_a"},
PrimaryRegion: "region_b",
SurvivalGoal: descpb.SurvivalGoal_ZONE_FAILURE,
},
expected: &zonepb.ZoneConfig{
NumReplicas: proto.Int32(3),
LeasePreferences: []zonepb.LeasePreference{
{Constraints: []zonepb.Constraint{{Type: zonepb.Constraint_REQUIRED, Key: "region", Value: "region_b"}}},
},
Constraints: []zonepb.ConstraintsConjunction{
{NumReplicas: 1, Constraints: []zonepb.Constraint{{Type: zonepb.Constraint_REQUIRED, Key: "region", Value: "region_b"}}},
{NumReplicas: 1, Constraints: []zonepb.Constraint{{Type: zonepb.Constraint_REQUIRED, Key: "region", Value: "region_c"}}},
{NumReplicas: 1, Constraints: []zonepb.Constraint{{Type: zonepb.Constraint_REQUIRED, Key: "region", Value: "region_a"}}},
},
},
},
{
desc: "three regions, region survival",
regionConfig: descpb.DatabaseDescriptor_RegionConfig{
Regions: []descpb.Region{"region_b", "region_c", "region_a"},
PrimaryRegion: "region_b",
SurvivalGoal: descpb.SurvivalGoal_REGION_FAILURE,
},
expected: &zonepb.ZoneConfig{
NumReplicas: proto.Int32(3),
LeasePreferences: []zonepb.LeasePreference{
{Constraints: []zonepb.Constraint{{Type: zonepb.Constraint_REQUIRED, Key: "region", Value: "region_b"}}},
},
Constraints: []zonepb.ConstraintsConjunction{
{NumReplicas: 1, Constraints: []zonepb.Constraint{{Type: zonepb.Constraint_REQUIRED, Key: "region", Value: "region_b"}}},
{NumReplicas: 1, Constraints: []zonepb.Constraint{{Type: zonepb.Constraint_REQUIRED, Key: "region", Value: "region_c"}}},
{NumReplicas: 1, Constraints: []zonepb.Constraint{{Type: zonepb.Constraint_REQUIRED, Key: "region", Value: "region_a"}}},
},
},
},
{
desc: "four regions, zone survival",
regionConfig: descpb.DatabaseDescriptor_RegionConfig{
Regions: []descpb.Region{"region_b", "region_c", "region_a", "region_d"},
PrimaryRegion: "region_b",
SurvivalGoal: descpb.SurvivalGoal_ZONE_FAILURE,
},
expected: &zonepb.ZoneConfig{
NumReplicas: proto.Int32(4),
LeasePreferences: []zonepb.LeasePreference{
{Constraints: []zonepb.Constraint{{Type: zonepb.Constraint_REQUIRED, Key: "region", Value: "region_b"}}},
},
Constraints: []zonepb.ConstraintsConjunction{
{NumReplicas: 1, Constraints: []zonepb.Constraint{{Type: zonepb.Constraint_REQUIRED, Key: "region", Value: "region_b"}}},
{NumReplicas: 1, Constraints: []zonepb.Constraint{{Type: zonepb.Constraint_REQUIRED, Key: "region", Value: "region_c"}}},
{NumReplicas: 1, Constraints: []zonepb.Constraint{{Type: zonepb.Constraint_REQUIRED, Key: "region", Value: "region_a"}}},
{NumReplicas: 1, Constraints: []zonepb.Constraint{{Type: zonepb.Constraint_REQUIRED, Key: "region", Value: "region_d"}}},
},
},
},
{
desc: "four regions, region survival",
regionConfig: descpb.DatabaseDescriptor_RegionConfig{
Regions: []descpb.Region{"region_b", "region_c", "region_a", "region_d"},
PrimaryRegion: "region_b",
SurvivalGoal: descpb.SurvivalGoal_REGION_FAILURE,
},
expected: &zonepb.ZoneConfig{
NumReplicas: proto.Int32(4),
LeasePreferences: []zonepb.LeasePreference{
{Constraints: []zonepb.Constraint{{Type: zonepb.Constraint_REQUIRED, Key: "region", Value: "region_b"}}},
},
Constraints: []zonepb.ConstraintsConjunction{
{NumReplicas: 1, Constraints: []zonepb.Constraint{{Type: zonepb.Constraint_REQUIRED, Key: "region", Value: "region_b"}}},
{NumReplicas: 1, Constraints: []zonepb.Constraint{{Type: zonepb.Constraint_REQUIRED, Key: "region", Value: "region_c"}}},
{NumReplicas: 1, Constraints: []zonepb.Constraint{{Type: zonepb.Constraint_REQUIRED, Key: "region", Value: "region_a"}}},
{NumReplicas: 1, Constraints: []zonepb.Constraint{{Type: zonepb.Constraint_REQUIRED, Key: "region", Value: "region_d"}}},
},
},
},
}

for _, tc := range testCases {
t.Run(tc.desc, func(t *testing.T) {
res := genZoneConfigFromRegionConfigForDatabase(tc.regionConfig)
require.Equal(t, tc.expected, res)
})
}
}

0 comments on commit 3c740d0

Please sign in to comment.