Skip to content

Commit

Permalink
revertccl: ALTER VIRTUAL CLUSTER RESET DATA
Browse files Browse the repository at this point in the history
This enables resetting a virtual cluster's data to a prior timestamp.

This is possible if the prior timestamp is still retained in the mvcc
history of the virtual cluster, the virtual cluster has stopped service,
and is run by a user with the MANAGEVIRTUALCLUSTER (or admin) privilege
in the system tenant.

Revisions of data in the system tenant newer than the target time to
which it is being reset are destroyed, reverting the tenant to the state
it was in as of the time reverted to. Destroyed revisions are not
recoverable; once a tenant has been reset to a timestamp, it cannot be
'un-reset' back to a higher timestamp.

Release note (cluster virtualization): Added a new 'flashback' command
to revert a virtual cluster to an earlier state using ALTER VIRTUAL
CLUSTER RESET DATA.

Epic: CRDB-34233.
  • Loading branch information
dt committed Jan 9, 2024
1 parent b38c1df commit 80f990a
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 3 deletions.
8 changes: 8 additions & 0 deletions pkg/ccl/revertccl/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "revertccl",
srcs = [
"alter_reset_tenant.go",
"revert.go",
"revert_tenant.go",
],
importpath = "github.com/cockroachdb/cockroach/pkg/ccl/revertccl",
visibility = ["//visibility:public"],
deps = [
"//pkg/ccl/utilccl",
"//pkg/keys",
"//pkg/kv",
"//pkg/kv/kvpb",
Expand All @@ -17,9 +19,15 @@ go_library(
"//pkg/roachpb",
"//pkg/settings",
"//pkg/sql",
"//pkg/sql/catalog/colinfo",
"//pkg/sql/clusterunique",
"//pkg/sql/exprutil",
"//pkg/sql/isql",
"//pkg/sql/pgwire/pgcode",
"//pkg/sql/pgwire/pgerror",
"//pkg/sql/sem/asof",
"//pkg/sql/sem/eval",
"//pkg/sql/sem/tree",
"//pkg/sql/sessionprotectedts",
"//pkg/util/hlc",
"//pkg/util/log",
Expand Down
84 changes: 84 additions & 0 deletions pkg/ccl/revertccl/alter_reset_tenant.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Copyright 2024 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 revertccl

import (
"context"

"github.com/cockroachdb/cockroach/pkg/ccl/utilccl"
"github.com/cockroachdb/cockroach/pkg/sql"
"github.com/cockroachdb/cockroach/pkg/sql/catalog/colinfo"
"github.com/cockroachdb/cockroach/pkg/sql/exprutil"
"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode"
"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror"
"github.com/cockroachdb/cockroach/pkg/sql/sem/asof"
"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
)

const (
alterTenantResetOp = "ALTER VIRTUAL CLUSTER RESET"
)

func alterTenantResetHook(
ctx context.Context, stmt tree.Statement, p sql.PlanHookState,
) (sql.PlanHookRowFn, colinfo.ResultColumns, []sql.PlanNode, bool, error) {
alterTenantStmt, ok := stmt.(*tree.AlterTenantReset)
if !ok {
return nil, nil, nil, false, nil
}
if !p.ExecCfg().Codec.ForSystemTenant() {
return nil, nil, nil, false, pgerror.Newf(pgcode.InsufficientPrivilege, "only the system tenant can alter tenant")
}

timestamp, err := asof.EvalSystemTimeExpr(ctx, &p.ExtendedEvalContext().Context, p.SemaCtx(), alterTenantStmt.Timestamp,
alterTenantResetOp, asof.ReplicationCutover)
if err != nil {
return nil, nil, nil, false, err
}

fn := func(ctx context.Context, _ []sql.PlanNode, resultsCh chan<- tree.Datums) error {
if err := sql.CanManageTenant(ctx, p); err != nil {
return err
}
if err := utilccl.CheckEnterpriseEnabled(p.ExecCfg().Settings, alterTenantResetOp); err != nil {
return err
}

tenInfo, err := p.LookupTenantInfo(ctx, alterTenantStmt.TenantSpec, alterTenantResetOp)
if err != nil {
return err
}
return RevertTenantToTimestamp(ctx, &p.ExtendedEvalContext().Context, tenInfo.Name, timestamp, p.ExtendedEvalContext().SessionID)
}
return fn, nil, nil, false, nil
}

func alterTenantResetHookTypeCheck(
ctx context.Context, stmt tree.Statement, p sql.PlanHookState,
) (bool, colinfo.ResultColumns, error) {
alterStmt, ok := stmt.(*tree.AlterTenantReset)
if !ok {
return false, nil, nil
}
if err := exprutil.TypeCheck(
ctx, alterTenantResetOp, p.SemaCtx(), exprutil.TenantSpec{TenantSpec: alterStmt.TenantSpec},
); err != nil {
return false, nil, err
}
if _, err := asof.TypeCheckSystemTimeExpr(
ctx, p.SemaCtx(), alterStmt.Timestamp, alterTenantResetOp,
); err != nil {
return false, nil, err
}
return true, nil, nil
}

func init() {
sql.AddPlanHook("alter virtual cluster reset", alterTenantResetHook, alterTenantResetHookTypeCheck)
}
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ func TestTenantStreamingCreationErrors(t *testing.T) {
})
t.Run("destination tenant revert timestamp must match resume timestamp", func(t *testing.T) {
sysSQL.Exec(t, "CREATE TENANT bat")
sysSQL.Exec(t, "SELECT crdb_internal.unsafe_revert_tenant_to_timestamp('bat', cluster_logical_timestamp())")
sysSQL.Exec(t, "ALTER VIRTUAL CLUSTER bat RESET DATA TO SYSTEM TIME cluster_logical_timestamp()")
sysSQL.ExpectErr(t, "doesn't match last revert timestamp",
"CREATE TENANT bat FROM REPLICATION OF source ON $1 WITH RESUME TIMESTAMP = cluster_logical_timestamp()", srcPgURL.String())
})
Expand Down Expand Up @@ -251,7 +251,7 @@ func TestTenantStreamingFailback(t *testing.T) {
sqlA.Exec(t, "ALTER VIRTUAL CLUSTER f STOP SERVICE")
waitUntilTenantServerStopped(t, serverA.SystemLayer(), "f")
t.Logf("starting replication g->f")
sqlA.Exec(t, fmt.Sprintf("SELECT crdb_internal.unsafe_revert_tenant_to_timestamp('f', %s)", ts1))
sqlA.Exec(t, "ALTER VIRTUAL CLUSTER f RESET DATA TO SYSTEM TIME $1::decimal", ts1)
sqlA.Exec(t, fmt.Sprintf("CREATE VIRTUAL CLUSTER f FROM REPLICATION OF g ON $1 WITH RESUME TIMESTAMP = '%s'", ts1), serverBURL.String())
_, consumerFJobID := replicationtestutils.GetStreamJobIds(t, ctx, sqlA, roachpb.TenantName("f"))
t.Logf("waiting for f@%s", ts2)
Expand Down Expand Up @@ -281,7 +281,7 @@ func TestTenantStreamingFailback(t *testing.T) {
sqlB.Exec(t, "ALTER VIRTUAL CLUSTER g STOP SERVICE")
waitUntilTenantServerStopped(t, serverB.SystemLayer(), "g")
t.Logf("starting replication f->g")
sqlB.Exec(t, fmt.Sprintf("SELECT crdb_internal.unsafe_revert_tenant_to_timestamp('g', %s)", ts3))
sqlB.Exec(t, "ALTER VIRTUAL CLUSTER g RESET DATA TO SYSTEM TIME $1::decimal", ts3)
sqlB.Exec(t, fmt.Sprintf("CREATE VIRTUAL CLUSTER g FROM REPLICATION OF f ON $1 WITH RESUME TIMESTAMP = '%s'", ts3), serverAURL.String())
_, consumerGJobID = replicationtestutils.GetStreamJobIds(t, ctx, sqlB, roachpb.TenantName("g"))
t.Logf("waiting for g@%s", ts3)
Expand Down
1 change: 1 addition & 0 deletions pkg/sql/opaque.go
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,7 @@ func init() {
&tree.AlterBackup{},
&tree.AlterBackupSchedule{},
&tree.AlterTenantReplication{},
&tree.AlterTenantReset{},
&tree.Backup{},
&tree.ShowBackup{},
&tree.Restore{},
Expand Down

0 comments on commit 80f990a

Please sign in to comment.