Skip to content

Commit 0a9de0c

Browse files
committed
sql: introduce canary_window storage parameter
This commit introduces a new table-level storage parameter `canary_window` that can be specified in CREATE TABLE or ALTER TABLE statements. The canary window specifies how long newly collected statistics will remain in "canary" state before being promoted to stable, enabling gradual rollout of new statistics to mitigate performance regressions. When set to a non-zero duration, this parameter enables canary statistics rollout for the table. During the canary window, the cluster setting sql.stats.canary_fraction (introduced in the next commit) determines what percentage of queries use the new canary statistics versus the previous stable statistics, providing a buffer period for observation and intervention. The window is capped by 48 hours to avoid an outrageously long canary window. Note that this commit adds only syntax support and storage for the parameter. The actual canary statistics selection logic will be implemented in subsequent commits. Release note (sql change): A new table storage parameter `canary_window` has been introduced to enable gradual rollout of newly collected table statistics. It takes a duration string as the value, with maximum allowed duration 48 hours. When set with a non-negative duration, the new statistics remain in a "canary" state for the specified duration before being promoted to stable. This allows for controlled exposure and intervention opportunities before statistics are fully deployed across all queries.
1 parent 35ec42c commit 0a9de0c

File tree

19 files changed

+184
-18
lines changed

19 files changed

+184
-18
lines changed

pkg/ccl/logictestccl/tests/3node-tenant/generated_test.go

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/ccl/logictestccl/tests/local-read-committed/generated_test.go

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/ccl/logictestccl/tests/local-repeatable-read/generated_test.go

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/cli/testdata/doctor/test_recreate_zipdir-json

Lines changed: 6 additions & 6 deletions
Large diffs are not rendered by default.

pkg/sql/catalog/descpb/structured.proto

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1378,7 +1378,15 @@ message TableDescriptor {
13781378
optional uint32 rbr_using_constraint = 70 [(gogoproto.nullable) = false,
13791379
(gogoproto.customname) = "RBRUsingConstraint", (gogoproto.casttype) = "ConstraintID"];
13801380

1381-
// Next ID: 71
1381+
// CanaryWindowSize specifies the duration for which newly collected statistics
1382+
// remain in "canary" state before being promoted to stable. During this
1383+
// window, the cluster setting sql.stats.canary_fraction determines what
1384+
// percentage of queries use the new canary statistics vs. the previous stable
1385+
// statistics. This provides a buffer period for observation and intervention
1386+
// before new statistics are fully deployed to all queries throughout the
1387+
// cluster.
1388+
optional int64 canary_window_size = 71 [(gogoproto.nullable) = false, (gogoproto.casttype)="time.Duration"];
1389+
// Next ID: 72
13821390
}
13831391

13841392
// ExternalRowData indicates that the row data for this object is stored outside

pkg/sql/catalog/tabledesc/structured.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2604,6 +2604,9 @@ func (desc *wrapper) GetStorageParams(spaceBetweenEqual bool) ([]string, error)
26042604
if desc.IsSchemaLocked() {
26052605
appendStorageParam(`schema_locked`, `true`)
26062606
}
2607+
if desc.CanaryWindowSize != 0 {
2608+
appendStorageParam(`canary_window`, fmt.Sprintf(`'%s'`, desc.CanaryWindowSize.String()))
2609+
}
26072610
if usingFK := desc.GetRegionalByRowUsingConstraint(); usingFK != descpb.ConstraintID(0) {
26082611
// NOTE: when validating the descriptor, we check that the referenced
26092612
// constraint exists, so this should never fail.

pkg/sql/catalog/tabledesc/ttl.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ func ValidateRowLevelTTL(ttl *catpb.RowLevelTTL) error {
5555
}
5656
}
5757
if ttl.RowStatsPollInterval != 0 {
58-
if err := ValidateTTLRowStatsPollInterval("ttl_row_stats_poll_interval", ttl.RowStatsPollInterval); err != nil {
58+
if err := ValidateNotNegativeInterval("ttl_row_stats_poll_interval", ttl.RowStatsPollInterval); err != nil {
5959
return err
6060
}
6161
}
@@ -154,9 +154,8 @@ func ValidateTTLCronExpr(key string, str string) error {
154154
return nil
155155
}
156156

157-
// ValidateTTLRowStatsPollInterval validates the automatic statistics field
158-
// of TTL.
159-
func ValidateTTLRowStatsPollInterval(key string, val time.Duration) error {
157+
// ValidateNotNegativeInterval validates the provided interval is not negative.
158+
func ValidateNotNegativeInterval(key string, val time.Duration) error {
160159
if val < 0 {
161160
return pgerror.Newf(
162161
pgcode.InvalidParameterValue,

pkg/sql/catalog/tabledesc/validate_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ var validationMap = []struct {
148148
"RowLevelSecurityEnabled": {status: thisFieldReferencesNoObjects},
149149
"RowLevelSecurityForced": {status: thisFieldReferencesNoObjects},
150150
"RBRUsingConstraint": {status: iSolemnlySwearThisFieldIsValidated},
151+
"CanaryWindowSize": {status: thisFieldReferencesNoObjects},
151152
},
152153
},
153154
{
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# LogicTest: !local-prepared
2+
3+
statement ok
4+
DROP TABLE IF EXISTS canary_table;
5+
6+
# To avoid test flake.
7+
statement ok
8+
SET create_table_with_schema_locked=false
9+
10+
statement ok
11+
CREATE TABLE canary_table (x int primary key, y int, FAMILY (x, y)) WITH (canary_window = '20s');
12+
13+
query TT
14+
SHOW CREATE TABLE canary_table
15+
----
16+
canary_table CREATE TABLE public.canary_table (
17+
x INT8 NOT NULL,
18+
y INT8 NULL,
19+
CONSTRAINT canary_table_pkey PRIMARY KEY (x ASC),
20+
FAMILY fam_0_x_y (x, y)
21+
) WITH (canary_window = '20s');
22+
23+
let $table_creation_ts
24+
SELECT now()
25+
26+
statement ok
27+
ALTER TABLE canary_table SET (canary_window = '30s');
28+
29+
# Test the gating with a value that exceeds the maximum allowed canary window.
30+
statement error canary window size 72h0m0s exceeds maximum allowed value of 48 hours
31+
ALTER TABLE canary_table SET (canary_window = '259200s');
32+
33+
query TT
34+
SHOW CREATE TABLE canary_table
35+
----
36+
canary_table CREATE TABLE public.canary_table (
37+
x INT8 NOT NULL,
38+
y INT8 NULL,
39+
CONSTRAINT canary_table_pkey PRIMARY KEY (x ASC),
40+
FAMILY fam_0_x_y (x, y)
41+
) WITH (canary_window = '30s');
42+
43+
query T
44+
SELECT create_statement FROM crdb_internal.create_statements AS OF SYSTEM TIME '$table_creation_ts' WHERE descriptor_name = 'canary_table';
45+
----
46+
CREATE TABLE public.canary_table (
47+
x INT8 NOT NULL,
48+
y INT8 NULL,
49+
CONSTRAINT canary_table_pkey PRIMARY KEY (x ASC),
50+
FAMILY fam_0_x_y (x, y)
51+
) WITH (canary_window = '20s')

pkg/sql/logictest/tests/fakedist-disk/generated_test.go

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)