Skip to content

Commit

Permalink
Merge #60257
Browse files Browse the repository at this point in the history
60257: sql: Allow for multi-region database restores r=otan,arulajmani,pbardea a=ajstorm

Until this commit, restores of multi-region databases failed due to the
fact that the multi-region enum's type ID was changing as part of the
restore.  This commit remaps the enum ID so that the restore succeeds.

The commit also rebuilds the database-level zone configuration.  The
table level zone configurations will be handled in a follow-on PR.

Release note (sql change): Adds support for restore of multi-region
databases.

Co-authored-by: Adam Storm <[email protected]>
  • Loading branch information
craig[bot] and ajstorm committed Feb 11, 2021
2 parents d0e0361 + 07f87f1 commit 57de93a
Show file tree
Hide file tree
Showing 4 changed files with 284 additions and 38 deletions.
55 changes: 55 additions & 0 deletions pkg/ccl/backupccl/restore_job.go
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,13 @@ func WriteDescriptors(
"validate table %d", errors.Safe(table.GetID()))
}
}

for _, db := range databases {
if err := db.Validate(); err != nil {
return errors.Wrapf(err,
"validate database %d", errors.Safe(db.GetID()))
}
}
return nil
}()
return errors.Wrapf(err, "restoring table desc and namespace entries")
Expand Down Expand Up @@ -1021,6 +1028,54 @@ func createImportingDescriptors(
p.ExecCfg().InternalExecutor, p.ExecCfg().DB, func(
ctx context.Context, txn *kv.Txn, descsCol *descs.Collection,
) error {
// A couple of pieces of cleanup are required for multi-region databases.
// First, we need to find all of the MULTIREGION_ENUMs types and remap the
// IDs stored in the corresponding database descriptors to match the type's
// new ID. Secondly, we need to rebuild the zone configuration for each
// multi-region database. We don't perform the zone configuration rebuild on
// cluster restores, as they will have the zone configurations restored as
// as the system tables are restored.
mrEnumsFound := make(map[descpb.ID]descpb.ID)
for _, t := range typesByID {
typeDesc := t.TypeDesc()
if typeDesc.GetKind() == descpb.TypeDescriptor_MULTIREGION_ENUM {
// Check to see if we've found more than one multi-region enum on any
// given database.
if id, ok := mrEnumsFound[typeDesc.ParentID]; ok {
return errors.AssertionFailedf(
"unexpectedly found more than one MULTIREGION_ENUM (IDs = %d, %d) "+
"on database %d during restore", id, typeDesc.ID, typeDesc.ParentID)
}
mrEnumsFound[typeDesc.ParentID] = typeDesc.ID

if db, ok := dbsByID[typeDesc.GetParentID()]; ok {
desc := db.DatabaseDesc()
if desc.RegionConfig == nil {
return errors.AssertionFailedf(
"found MULTIREGION_ENUM on non-multi-region database %s", desc.Name)
}

// Update the RegionEnumID to record the new multi-region enum ID.
desc.RegionConfig.RegionEnumID = t.GetID()

// If we're not in a cluster restore, rebuild the database-level zone
// configuration.
if details.DescriptorCoverage != tree.AllDescriptors {
log.Infof(ctx, "restoring zone configuration for database %d", desc.ID)
if err := sql.ApplyZoneConfigFromDatabaseRegionConfig(
ctx,
desc.GetID(),
*desc.RegionConfig,
txn,
p.ExecCfg(),
); err != nil {
return err
}
}
}
}
}

// Write the new descriptors which are set in the OFFLINE state.
if err := WriteDescriptors(
ctx, p.ExecCfg().Codec, txn, p.User(), descsCol, databases, writtenSchemas, tables, writtenTypes,
Expand Down
174 changes: 174 additions & 0 deletions pkg/ccl/logictestccl/testdata/logic_test/multi_region_backup
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
# LogicTest: multiregion-9node-3region-3azs

query TTTT
SHOW REGIONS
----
ap-southeast-2 {ap-az1,ap-az2,ap-az3} {} {}
ca-central-1 {ca-az1,ca-az2,ca-az3} {} {}
us-east-1 {us-az1,us-az2,us-az3} {} {}

statement ok
CREATE DATABASE mr_backup primary region "ca-central-1" regions "ap-southeast-2", "us-east-1"

statement ok
CREATE DATABASE mr_backup_2 primary region "ap-southeast-2" regions "ca-central-1", "us-east-1"

query TT
SHOW ZONE CONFIGURATION FOR DATABASE mr_backup
----
DATABASE mr_backup ALTER DATABASE mr_backup CONFIGURE ZONE USING
range_min_bytes = 134217728,
range_max_bytes = 536870912,
gc.ttlseconds = 90000,
num_replicas = 5,
num_voters = 3,
constraints = '{+region=ap-southeast-2: 1, +region=ca-central-1: 1, +region=us-east-1: 1}',
voter_constraints = '[+region=ca-central-1]',
lease_preferences = '[[+region=ca-central-1]]'


query TT
SHOW ZONE CONFIGURATION FOR DATABASE mr_backup_2
----
DATABASE mr_backup_2 ALTER DATABASE mr_backup_2 CONFIGURE ZONE USING
range_min_bytes = 134217728,
range_max_bytes = 536870912,
gc.ttlseconds = 90000,
num_replicas = 5,
num_voters = 3,
constraints = '{+region=ap-southeast-2: 1, +region=ca-central-1: 1, +region=us-east-1: 1}',
voter_constraints = '[+region=ap-southeast-2]',
lease_preferences = '[[+region=ap-southeast-2]]'

statement ok
ALTER DATABASE mr_backup CONFIGURE ZONE USING gc.ttlseconds = 1;
ALTER DATABASE mr_backup_2 CONFIGURE ZONE USING gc.ttlseconds = 1

statement ok
BACKUP DATABASE mr_backup TO 'nodelocal://self/mr_backup/';
BACKUP DATABASE mr_backup_2 TO 'nodelocal://self/mr_backup_2/';
BACKUP DATABASE mr_backup, mr_backup_2 TO 'nodelocal://self/mr_backup_combined/'

query T
select database_name from [show databases]
----
defaultdb
mr_backup
mr_backup_2
postgres
system
test

statement ok
DROP DATABASE mr_backup;
DROP DATABASE mr_backup_2

query T
select database_name from [show databases]
----
defaultdb
postgres
system
test

statement ok
RESTORE DATABASE mr_backup FROM 'nodelocal://self/mr_backup/'

query T
select database_name from [show databases]
----
defaultdb
mr_backup
postgres
system
test

query TT
SHOW ZONE CONFIGURATION FOR DATABASE mr_backup
----
DATABASE mr_backup ALTER DATABASE mr_backup CONFIGURE ZONE USING
range_min_bytes = 134217728,
range_max_bytes = 536870912,
gc.ttlseconds = 90000,
num_replicas = 5,
num_voters = 3,
constraints = '{+region=ap-southeast-2: 1, +region=ca-central-1: 1, +region=us-east-1: 1}',
voter_constraints = '[+region=ca-central-1]',
lease_preferences = '[[+region=ca-central-1]]'

statement ok
RESTORE DATABASE mr_backup_2 FROM 'nodelocal://self/mr_backup_2/'

query T
select database_name from [show databases]
----
defaultdb
mr_backup
mr_backup_2
postgres
system
test

query TT
SHOW ZONE CONFIGURATION FOR DATABASE mr_backup_2
----
DATABASE mr_backup_2 ALTER DATABASE mr_backup_2 CONFIGURE ZONE USING
range_min_bytes = 134217728,
range_max_bytes = 536870912,
gc.ttlseconds = 90000,
num_replicas = 5,
num_voters = 3,
constraints = '{+region=ap-southeast-2: 1, +region=ca-central-1: 1, +region=us-east-1: 1}',
voter_constraints = '[+region=ap-southeast-2]',
lease_preferences = '[[+region=ap-southeast-2]]'

statement ok
DROP DATABASE mr_backup;
DROP DATABASE mr_backup_2

query T
select database_name from [show databases]
----
defaultdb
postgres
system
test

statement ok
RESTORE DATABASE mr_backup, mr_backup_2 FROM 'nodelocal://self/mr_backup_combined/'

query T
select database_name from [show databases]
----
defaultdb
mr_backup
mr_backup_2
postgres
system
test

query TT
SHOW ZONE CONFIGURATION FOR DATABASE mr_backup
----
DATABASE mr_backup ALTER DATABASE mr_backup CONFIGURE ZONE USING
range_min_bytes = 134217728,
range_max_bytes = 536870912,
gc.ttlseconds = 90000,
num_replicas = 5,
num_voters = 3,
constraints = '{+region=ap-southeast-2: 1, +region=ca-central-1: 1, +region=us-east-1: 1}',
voter_constraints = '[+region=ca-central-1]',
lease_preferences = '[[+region=ca-central-1]]'

query TT
SHOW ZONE CONFIGURATION FOR DATABASE mr_backup_2
----
DATABASE mr_backup_2 ALTER DATABASE mr_backup_2 CONFIGURE ZONE USING
range_min_bytes = 134217728,
range_max_bytes = 536870912,
gc.ttlseconds = 90000,
num_replicas = 5,
num_voters = 3,
constraints = '{+region=ap-southeast-2: 1, +region=ca-central-1: 1, +region=us-east-1: 1}',
voter_constraints = '[+region=ap-southeast-2]',
lease_preferences = '[[+region=ap-southeast-2]]'
27 changes: 18 additions & 9 deletions pkg/sql/alter_database.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,10 +227,13 @@ func (n *alterDatabaseAddRegionNode) startExec(params runParams) error {
}

// Update the database's zone configuration.
if err := params.p.applyZoneConfigFromDatabaseRegionConfig(
if err := ApplyZoneConfigFromDatabaseRegionConfig(
params.ctx,
tree.Name(n.desc.Name),
*n.desc.RegionConfig); err != nil {
n.desc.ID,
*n.desc.RegionConfig,
params.p.txn,
params.p.execCfg,
); err != nil {
return err
}

Expand Down Expand Up @@ -538,10 +541,13 @@ func (n *alterDatabasePrimaryRegionNode) switchPrimaryRegion(params runParams) e
}

// Update the database's zone configuration.
if err := params.p.applyZoneConfigFromDatabaseRegionConfig(
if err := ApplyZoneConfigFromDatabaseRegionConfig(
params.ctx,
tree.Name(n.desc.Name),
*n.desc.RegionConfig); err != nil {
n.desc.ID,
*n.desc.RegionConfig,
params.p.txn,
params.p.execCfg,
); err != nil {
return err
}

Expand Down Expand Up @@ -757,10 +763,13 @@ func (n *alterDatabaseSurvivalGoalNode) startExec(params runParams) error {
}

// Update the database's zone configuration.
if err := params.p.applyZoneConfigFromDatabaseRegionConfig(
if err := ApplyZoneConfigFromDatabaseRegionConfig(
params.ctx,
tree.Name(n.desc.Name),
*n.desc.RegionConfig); err != nil {
n.desc.ID,
*n.desc.RegionConfig,
params.p.txn,
params.p.execCfg,
); err != nil {
return err
}

Expand Down
66 changes: 37 additions & 29 deletions pkg/sql/region_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -389,33 +389,33 @@ func zoneConfigForMultiRegionTable(
return ret, nil
}

// TODO(#59631): everything using this should instead use getZoneConfigRaw
// and writeZoneConfig instead of calling SQL for each query.
// This removes the requirement to only call this function after writeSchemaChange
// is called on creation of tables, and potentially removes the need for ReadingOwnWrites
// for some subcommands.
// Requires some logic to "inherit" from parents.
func (p *planner) applyZoneConfigForMultiRegion(
ctx context.Context, zs tree.ZoneSpecifier, zc *zonepb.ZoneConfig, desc string,
func applyZoneConfigForMultiRegion(
ctx context.Context,
zc *zonepb.ZoneConfig,
targetID descpb.ID,
table catalog.TableDescriptor,
txn *kv.Txn,
execConfig *ExecutorConfig,
) 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(&zs, zc)
if err != nil {
return err
}
if _, err := p.ExtendedEvalContext().ExecCfg.InternalExecutor.ExecEx(
// TODO (multiregion): Much like applyZoneConfigForMultiRegionTable we need to
// merge the zone config that we're writing with anything previously existing
// in there.
if _, err := writeZoneConfig(
ctx,
desc,
p.Txn(),
sessiondata.InternalExecutorOverride{
User: p.SessionData().User(),
},
sql,
txn,
targetID,
table,
zc,
execConfig,
true, /* hasNewSubzones */
); err != nil {
return err
}

return nil
}

Expand Down Expand Up @@ -612,21 +612,27 @@ func applyZoneConfigForMultiRegionTable(
return nil
}

func (p *planner) applyZoneConfigFromDatabaseRegionConfig(
ctx context.Context, dbName tree.Name, regionConfig descpb.DatabaseDescriptor_RegionConfig,
// ApplyZoneConfigFromDatabaseRegionConfig applies a zone configuration to the
// database using the information in the supplied RegionConfig.
func ApplyZoneConfigFromDatabaseRegionConfig(
ctx context.Context,
dbID descpb.ID,
regionConfig descpb.DatabaseDescriptor_RegionConfig,
txn *kv.Txn,
execConfig *ExecutorConfig,
) 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.
// Build a zone config based on the RegionConfig information.
dbZoneConfig, err := zoneConfigForMultiRegionDatabase(regionConfig)
if err != nil {
return err
}
return p.applyZoneConfigForMultiRegion(
return applyZoneConfigForMultiRegion(
ctx,
tree.ZoneSpecifier{Database: dbName},
dbZoneConfig,
"database-multiregion-set-zone-config",
dbID,
nil,
txn,
execConfig,
)
}

Expand Down Expand Up @@ -736,10 +742,12 @@ func (p *planner) initializeMultiRegionDatabase(ctx context.Context, desc *dbdes
}

// Create the database-level zone configuration.
if err := p.applyZoneConfigFromDatabaseRegionConfig(
if err := ApplyZoneConfigFromDatabaseRegionConfig(
ctx,
tree.Name(desc.Name),
*desc.RegionConfig); err != nil {
desc.ID,
*desc.RegionConfig,
p.txn,
p.execCfg); err != nil {
return err
}

Expand Down

0 comments on commit 57de93a

Please sign in to comment.