Skip to content

Commit

Permalink
Merge pull request #94792 from postamar/cherry-picked-94774-on-222
Browse files Browse the repository at this point in the history
  • Loading branch information
Marius Posta authored Jan 6, 2023
2 parents 00ed514 + 64a402f commit d8e309b
Show file tree
Hide file tree
Showing 12 changed files with 687 additions and 22 deletions.
2 changes: 2 additions & 0 deletions pkg/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,7 @@ ALL_TESTS = [
"//pkg/spanconfig:spanconfig_test",
"//pkg/sql/backfill:backfill_test",
"//pkg/sql/cacheutil:cacheutil_test",
"//pkg/sql/catalog/bootstrap:bootstrap_test",
"//pkg/sql/catalog/catalogkeys:catalogkeys_test",
"//pkg/sql/catalog/catformat:catformat_test",
"//pkg/sql/catalog/catpb:catpb_disallowed_imports_test",
Expand Down Expand Up @@ -1336,6 +1337,7 @@ GO_TARGETS = [
"//pkg/sql/cacheutil:cacheutil",
"//pkg/sql/cacheutil:cacheutil_test",
"//pkg/sql/catalog/bootstrap:bootstrap",
"//pkg/sql/catalog/bootstrap:bootstrap_test",
"//pkg/sql/catalog/catalogkeys:catalogkeys",
"//pkg/sql/catalog/catalogkeys:catalogkeys_test",
"//pkg/sql/catalog/catformat:catformat",
Expand Down
11 changes: 5 additions & 6 deletions pkg/ccl/kvccl/kvtenantccl/tenant_upgrade_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ func TestTenantUpgrade(t *testing.T) {
cleanupPGUrl()
}
}
expectedInitialTenantVersion, _, _ := v0v1v2()
mkTenant := func(t *testing.T, id uint64) (tenantDB *gosql.DB, cleanup func()) {
settings := cluster.MakeTestingClusterSettingsWithVersions(
clusterversion.TestingBinaryVersion,
Expand All @@ -90,7 +91,7 @@ func TestTenantUpgrade(t *testing.T) {
)
// Initialize the version to the minimum it could be.
require.NoError(t, clusterversion.Initialize(ctx,
clusterversion.TestingBinaryMinSupportedVersion, &settings.SV))
expectedInitialTenantVersion, &settings.SV))
tenantArgs := base.TestTenantArgs{
TenantID: roachpb.MakeTenantID(id),
TestingKnobs: base.TestingKnobs{},
Expand All @@ -109,7 +110,7 @@ func TestTenantUpgrade(t *testing.T) {

// Ensure that the tenant works.
initialTenantRunner.CheckQueryResults(t, "SHOW CLUSTER SETTING version",
[][]string{{clusterversion.TestingBinaryMinSupportedVersion.String()}})
[][]string{{expectedInitialTenantVersion.String()}})
initialTenantRunner.Exec(t, "CREATE TABLE t (i INT PRIMARY KEY)")
initialTenantRunner.Exec(t, "INSERT INTO t VALUES (1), (2)")

Expand Down Expand Up @@ -172,11 +173,9 @@ func TestTenantUpgrade(t *testing.T) {

}

// Returns two versions v0, v1, v2 which correspond to adjacent releases. v0 will
// equal the TestingBinaryMinSupportedVersion to avoid rot in tests using this
// (as we retire old versions).
// Returns two versions v0, v1, v2 which correspond to adjacent releases.
func v0v1v2() (roachpb.Version, roachpb.Version, roachpb.Version) {
v0 := clusterversion.TestingBinaryMinSupportedVersion
v0 := clusterversion.ByKey(clusterversion.V22_1)
v1 := clusterversion.TestingBinaryVersion
v2 := clusterversion.TestingBinaryVersion
if v1.Internal > 2 {
Expand Down
1 change: 1 addition & 0 deletions pkg/cmd/roachtest/tests/multitenant_upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ func runMultiTenantUpgrade(ctx context.Context, t test.Test, c cluster.Cluster,
t.Status("verifying that the tenant 13 server works and is at the earlier version")

verifySQL(t, tenant13.pgURL,
mkStmt(`CREATE USER my_user`),
mkStmt(`CREATE TABLE foo (id INT PRIMARY KEY, v STRING)`),
mkStmt(`INSERT INTO foo VALUES($1, $2)`, 1, "bar"),
mkStmt(`SELECT * FROM foo LIMIT 1`).
Expand Down
24 changes: 22 additions & 2 deletions pkg/sql/catalog/bootstrap/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
load("//build/bazelutil/unused_checker:unused.bzl", "get_x_data")
load("@io_bazel_rules_go//go:def.bzl", "go_library")
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")

go_library(
name = "bootstrap",
srcs = ["metadata.go"],
srcs = [
"metadata.go",
"previous_release.go",
],
importpath = "github.com/cockroachdb/cockroach/pkg/sql/catalog/bootstrap",
visibility = ["//visibility:public"],
deps = [
Expand All @@ -23,4 +26,21 @@ go_library(
],
)

go_test(
name = "bootstrap_test",
srcs = ["bootstrap_test.go"],
args = ["-test.timeout=295s"],
data = glob(["testdata/**"]),
embed = [":bootstrap"],
deps = [
"//pkg/config/zonepb",
"//pkg/keys",
"//pkg/roachpb",
"//pkg/testutils",
"//pkg/util/leaktest",
"@com_github_cockroachdb_datadriven//:datadriven",
"@com_github_stretchr_testify//require",
],
)

get_x_data(name = "get_x_data")
105 changes: 105 additions & 0 deletions pkg/sql/catalog/bootstrap/bootstrap_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// Copyright 2022 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 bootstrap

import (
"crypto/sha256"
"encoding/hex"
"testing"

"github.com/cockroachdb/cockroach/pkg/config/zonepb"
"github.com/cockroachdb/cockroach/pkg/keys"
"github.com/cockroachdb/cockroach/pkg/roachpb"
"github.com/cockroachdb/cockroach/pkg/testutils"
"github.com/cockroachdb/cockroach/pkg/util/leaktest"
"github.com/cockroachdb/datadriven"
"github.com/stretchr/testify/require"
)

func TestInitialValuesToString(t *testing.T) {
defer leaktest.AfterTest(t)()
datadriven.Walk(t, testutils.TestDataPath(t), func(t *testing.T, path string) {
datadriven.RunTest(t, path, func(t *testing.T, d *datadriven.TestData) string {
codec := keys.SystemSQLCodec
switch d.Cmd {
case "system":
break

case "tenant":
const dummyTenantID = 12345
codec = keys.MakeSQLCodec(roachpb.MakeTenantID(dummyTenantID))

default:
t.Fatalf("unexpected command %q", d.Cmd)
}
var expectedHash string
d.ScanArgs(t, "hash", &expectedHash)
ms := MakeMetadataSchema(codec, zonepb.DefaultZoneConfigRef(), zonepb.DefaultSystemZoneConfigRef())
result := InitialValuesToString(ms)
h := sha256.Sum256([]byte(result))
if actualHash := hex.EncodeToString(h[:]); expectedHash != actualHash {
t.Errorf(`Unexpected hash value %s for %s.
If you're seeing this error message, this means that the bootstrapped system
schema has changed. Assuming that this is expected:
- If this occurred during development on the main branch, rewrite the expected
test output and the hash value and move on.
- If this occurred during development of a patch for a release branch, make
very sure that the underlying change really is expected and is backward-
compatible and is absolutely necessary. If that's the case, then there are
hardcoded literals in the main development branch as well as any subsequent
release branches that need to be updated also.`, actualHash, d.Cmd)
}
return result
})
})
}

func TestRoundTripInitialValuesStringRepresentation(t *testing.T) {
t.Run("system", func(t *testing.T) {
roundTripInitialValuesStringRepresentation(t, 0 /* tenantID */)
})
t.Run("tenant", func(t *testing.T) {
const dummyTenantID = 54321
roundTripInitialValuesStringRepresentation(t, dummyTenantID)
})
t.Run("tenants", func(t *testing.T) {
const dummyTenantID1, dummyTenantID2 = 54321, 12345
require.Equal(t,
InitialValuesToString(makeMetadataSchema(dummyTenantID1)),
InitialValuesToString(makeMetadataSchema(dummyTenantID2)),
)
})
}

func roundTripInitialValuesStringRepresentation(t *testing.T, tenantID uint64) {
ms := makeMetadataSchema(tenantID)
expectedKVs, expectedSplits := ms.GetInitialValues()
actualKVs, actualSplits, err := InitialValuesFromString(ms.codec, InitialValuesToString(ms))
require.NoError(t, err)
require.Len(t, actualKVs, len(expectedKVs))
require.Len(t, actualSplits, len(expectedSplits))
for i, actualKV := range actualKVs {
expectedKV := expectedKVs[i]
require.EqualValues(t, expectedKV, actualKV)
}
for i, actualSplit := range actualSplits {
expectedSplit := expectedSplits[i]
require.EqualValues(t, expectedSplit, actualSplit)
}
}

func makeMetadataSchema(tenantID uint64) MetadataSchema {
codec := keys.SystemSQLCodec
if tenantID > 0 {
codec = keys.MakeSQLCodec(roachpb.MakeTenantID(tenantID))
}
return MakeMetadataSchema(codec, zonepb.DefaultZoneConfigRef(), zonepb.DefaultSystemZoneConfigRef())
}
92 changes: 92 additions & 0 deletions pkg/sql/catalog/bootstrap/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,12 @@
package bootstrap

import (
"bytes"
"context"
"encoding/hex"
"encoding/json"
"sort"
"strings"

"github.com/cockroachdb/cockroach/pkg/config/zonepb"
"github.com/cockroachdb/cockroach/pkg/keys"
Expand Down Expand Up @@ -233,6 +237,94 @@ func (ms MetadataSchema) GetInitialValues() ([]roachpb.KeyValue, []roachpb.RKey)
return ret, splits
}

type initialValueStrings struct {
Key string `json:"key"`
Value string `json:"value,omitempty"`
}

// InitialValuesToString returns a string representation of the return values
// of MetadataSchema.GetInitialValues. The tenant prefix is stripped.
func InitialValuesToString(ms MetadataSchema) string {
kvs, splits := ms.GetInitialValues()
// Collect the records.
type record struct {
k, v []byte
}
records := make([]record, 0, len(kvs)+len(splits))
for _, kv := range kvs {
records = append(records, record{k: kv.Key, v: kv.Value.TagAndDataBytes()})
}
for _, s := range splits {
records = append(records, record{k: s})
}
// Strip the tenant prefix if there is one.
p := []byte(ms.codec.TenantPrefix())
for i, r := range records {
if !bytes.Equal(p, r.k[:len(p)]) {
panic("unexpected prefix")
}
records[i].k = r.k[len(p):]
}
// Build the string representation.
s := make([]initialValueStrings, len(records))
for i, r := range records {
s[i].Key = hex.EncodeToString(r.k)
s[i].Value = hex.EncodeToString(r.v)
}
// Sort the records by key.
sort.Slice(s, func(i, j int) bool {
return s[i].Key < s[j].Key
})
// Build the string representation.
var sb strings.Builder
for i, r := range s {
if i == 0 {
sb.WriteRune('[')
} else {
sb.WriteRune(',')
}
b, err := json.Marshal(r)
if err != nil {
panic(err)
}
sb.Write(b)
sb.WriteRune('\n')
}
sb.WriteRune(']')
return sb.String()
}

// InitialValuesFromString is the reciprocal to InitialValuesToString and
// appends the tenant prefix from the given codec.
func InitialValuesFromString(
codec keys.SQLCodec, str string,
) (kvs []roachpb.KeyValue, splits []roachpb.RKey, _ error) {
p := codec.TenantPrefix()
var s []initialValueStrings
if err := json.Unmarshal([]byte(str), &s); err != nil {
return nil, nil, err
}
for i, r := range s {
k, err := hex.DecodeString(r.Key)
if err != nil {
return nil, nil, errors.Errorf("failed to decode hex key %s for record #%d", r.Key, i+1)
}
v, err := hex.DecodeString(r.Value)
if err != nil {
return nil, nil, errors.Errorf("failed to decode hex value %s fo record #%d", r.Value, i+1)
}
k = append(p[:len(p):len(p)], k...)
if len(v) == 0 {
splits = append(splits, k)
} else {
kv := roachpb.KeyValue{Key: k}
kv.Value.SetTagAndData(v)
kvs = append(kvs, kv)
}
}
return kvs, splits, nil
}

// DescriptorIDs returns the descriptor IDs present in the metadata schema in
// sorted order.
func (ms MetadataSchema) DescriptorIDs() descpb.IDs {
Expand Down
Loading

0 comments on commit d8e309b

Please sign in to comment.