forked from cockroachdb/cockroach
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
settingswatcher: add version guard utility
Add the settingswatcher.VersionGuard utility. VersionGuard makes it possible to check a cluster version in the context of a transaction. Until a version gate is reached, VersionGuard will look up the version set in the settings table. Once a version is reached, the guard can skip the KV read because versions never go backwards. The version guard will be used to migrate the sqlinstance, sql_liveness, lease system tables to a regional by row compatible index. The in-memory IsActive check is insufficient for the migration, because the setting can change in between when the transaction starts and when the transaction commits. Part of cockroachdb#94843
- Loading branch information
1 parent
cd80204
commit a62c9c8
Showing
5 changed files
with
206 additions
and
0 deletions.
There are no files selected for viewing
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,62 @@ | ||
// Copyright 2023 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 settingswatcher | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/cockroachdb/cockroach/pkg/clusterversion" | ||
"github.com/cockroachdb/cockroach/pkg/kv" | ||
) | ||
|
||
// VersionGuard is a utility for checking the cluster version in a transaction. | ||
// VersionGuard is optimized to avoid the extra kv read overhead once the | ||
// cluster is finalized. | ||
// | ||
// Example Usage: | ||
// | ||
// guard, err := watcher.MakeVersionGuard(ctx, txn, version.MaxVersionGateToCheck) | ||
// if err != nil { | ||
// return err // unable to read version | ||
// } | ||
// if guard.IsActive(version.SomeVersionLessThanMax) { | ||
// ... | ||
// } else if guard.IsActive(version.MaxVersionGateToCheck) { | ||
// ... | ||
// } | ||
type VersionGuard struct { | ||
maxGateIsActive bool | ||
txnVersion clusterversion.ClusterVersion | ||
} | ||
|
||
// MakeVersionGuard constructs a version guard for the transaction. | ||
func (s *SettingsWatcher) MakeVersionGuard( | ||
ctx context.Context, txn *kv.Txn, maxGate clusterversion.Key, | ||
) (VersionGuard, error) { | ||
if s.settings.Version.IsActive(ctx, maxGate) { | ||
return VersionGuard{ | ||
maxGateIsActive: true, | ||
}, nil | ||
} | ||
txnVersion, err := s.GetClusterVersionFromStorage(ctx, txn) | ||
return VersionGuard{ | ||
txnVersion: txnVersion, | ||
}, err | ||
} | ||
|
||
// IsActive returns true if the transaction should treat the version guard as | ||
// active. | ||
func (v *VersionGuard) IsActive(version clusterversion.Key) bool { | ||
if v.maxGateIsActive { | ||
return true | ||
} | ||
return v.txnVersion.IsActive(version) | ||
} |
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,127 @@ | ||
// Copyright 2023 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 settingswatcher_test | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
|
||
"github.com/cockroachdb/cockroach/pkg/base" | ||
"github.com/cockroachdb/cockroach/pkg/clusterversion" | ||
"github.com/cockroachdb/cockroach/pkg/kv" | ||
"github.com/cockroachdb/cockroach/pkg/server/settingswatcher" | ||
"github.com/cockroachdb/cockroach/pkg/settings/cluster" | ||
"github.com/cockroachdb/cockroach/pkg/testutils/serverutils" | ||
"github.com/cockroachdb/cockroach/pkg/testutils/sqlutils" | ||
"github.com/cockroachdb/cockroach/pkg/util/leaktest" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestVersionGuard(t *testing.T) { | ||
defer leaktest.AfterTest(t)() | ||
ctx := context.Background() | ||
s, sqlDB, kvDB := serverutils.StartServer(t, base.TestServerArgs{}) | ||
defer s.Stopper().Stop(ctx) | ||
tDB := sqlutils.MakeSQLRunner(sqlDB) | ||
|
||
type testCase struct { | ||
name string | ||
storageVersion clusterversion.Key | ||
settingsVersion clusterversion.Key | ||
checkVersions map[clusterversion.Key]bool | ||
} | ||
|
||
initialVersion := clusterversion.V22_2 | ||
maxVersion := clusterversion.V23_1 | ||
tests := []testCase{ | ||
{ | ||
name: "unfinalized", | ||
storageVersion: initialVersion, | ||
settingsVersion: initialVersion, | ||
checkVersions: map[clusterversion.Key]bool{ | ||
initialVersion: true, | ||
clusterversion.V23_1Start: false, | ||
maxVersion: false, | ||
}, | ||
}, | ||
{ | ||
name: "mid-finalize", | ||
storageVersion: clusterversion.V23_1Start, | ||
settingsVersion: initialVersion, | ||
checkVersions: map[clusterversion.Key]bool{ | ||
initialVersion: true, | ||
clusterversion.V23_1Start: true, | ||
maxVersion: false, | ||
}, | ||
}, | ||
{ | ||
name: "finalized", | ||
storageVersion: maxVersion, | ||
settingsVersion: maxVersion, | ||
checkVersions: map[clusterversion.Key]bool{ | ||
initialVersion: true, | ||
clusterversion.V23_1Start: true, | ||
maxVersion: true, | ||
}, | ||
}, | ||
{ | ||
// Once the version guard's max version is active, it no longer | ||
// consults the stored value. This allows us to remove the overhead | ||
// of the version guard after finalization completes. A storage | ||
// version behind the settings version should not exist in | ||
// production. | ||
name: "verify-optimization", | ||
storageVersion: initialVersion, | ||
settingsVersion: maxVersion, | ||
checkVersions: map[clusterversion.Key]bool{ | ||
initialVersion: true, | ||
maxVersion: true, | ||
clusterversion.V23_1Start: true, | ||
}, | ||
}, | ||
} | ||
|
||
for _, test := range tests { | ||
t.Run(test.name, func(t *testing.T) { | ||
settings := cluster.MakeTestingClusterSettingsWithVersions( | ||
clusterversion.ByKey(maxVersion), | ||
clusterversion.ByKey(initialVersion), | ||
false, | ||
) | ||
require.NoError(t, clusterversion.Initialize(ctx, clusterversion.ByKey(test.settingsVersion), &settings.SV)) | ||
settingVersion := clusterversion.ClusterVersion{Version: clusterversion.ByKey(test.settingsVersion)} | ||
require.NoError(t, settings.Version.SetActiveVersion(ctx, settingVersion)) | ||
|
||
storageVersion := clusterversion.ClusterVersion{Version: clusterversion.ByKey(test.storageVersion)} | ||
marshaledVersion, err := storageVersion.Marshal() | ||
|
||
require.NoError(t, err) | ||
tDB.Exec(t, ` | ||
UPDATE system.settings | ||
SET value = $1 | ||
WHERE name = 'version'`, marshaledVersion) | ||
|
||
watcher := settingswatcher.New(nil, s.Codec(), settings, nil, nil, nil) | ||
require.NoError(t, kvDB.Txn(ctx, func(ctx context.Context, txn *kv.Txn) error { | ||
guard, err := watcher.MakeVersionGuard(ctx, txn, maxVersion) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
for version, expect := range test.checkVersions { | ||
require.Equal(t, expect, guard.IsActive(version), "expect guard.IsActive(%v) to be %t", version, expect) | ||
} | ||
|
||
return nil | ||
})) | ||
}) | ||
} | ||
} |
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