Skip to content

Commit 24f2613

Browse files
antiguruclaude
andauthored
sql: graduate the bounded staleness isolation level (#38414)
### Motivation `bounded staleness <duration>` shipped in v26.29 behind `enable_bounded_staleness_isolation`, which has been defaulting on since. The level is documented as public preview and still carries the gating machinery that a preview needs. This graduates it: the flag goes away and the docs drop the preview annotation, so bounded staleness is a supported part of the `transaction_isolation` surface. ### Description * Removes `enable_bounded_staleness_isolation` from the feature-flag table. `check_transaction_isolation_feature_flag` now gates only `strong session serializable`, which is still flagged behind `enable_session_timelines`. The shared check and the session-default scrub in `command_handler` therefore stay in place; only the bounded staleness arm and the comment's example change. * An environment that persisted the removed parameter via `ALTER SYSTEM` is unaffected: an unknown system parameter in catalog storage is warned about and ignored at startup (`catalog/apply.rs`, `catalog/open.rs`). * Drops `{{< public-preview />}}` and the **Public preview.** cell from the isolation-level reference. The `{{< if-released "v26.29" >}}` guards are kept, since older Self-Managed versions still lack the level entirely. * Parallel workload no longer gates `BoundedStalenessReadAction` on the flag or flips the flag, and the flag leaves the LaunchDarkly consistency allowlist (it never had an LD flag, so it was listed under `KNOWN_MISSING_FROM_LD`). Not included: a release-note entry in `doc/user/content/releases/_index.md`. That file has no section for the in-flight version, so the GA note belongs with the release-notes commit for whichever release this lands in. ### Verification * `test/sqllogictest/bounded_staleness.slt` loses the feature-flag sections and keeps the role-default coverage: `ALTER ROLE ... SET` of the level, and a fresh session picking it up as its starting isolation. * `test/testdrive/bounded-staleness.td` no longer enables the flag; the happy-path coverage is otherwise unchanged. * The `mz-sql` unit test for the shared isolation gate now exercises `strong session serializable` (rejected with the flag off, accepted with it on) and asserts bounded staleness passes ungated. Release note: This release makes the bounded staleness isolation level generally available. --- _Generated by [Claude Code](https://claude.ai/code/session_01PfeCp5oXaZ9RayUSdD5aJb)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent 38447d1 commit 24f2613

8 files changed

Lines changed: 34 additions & 111 deletions

File tree

‎doc/user/content/reference/isolation-level.md‎

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ against Materialize:
3333
| --- | --- |
3434
| [**Strict Serializable**](#strict-serializable) | **Default.** Provides serializability and linearizability. |
3535
| [**Serializable**](#serializable) | Provides serializability but not linearizability. |
36-
| [**Bounded Staleness `<duration>`**](#bounded-staleness) | **Public preview.** Serves reads at a timestamp at most `<duration>` stale; never blocks, errors if the bound cannot be met. |
36+
| [**Bounded Staleness `<duration>`**](#bounded-staleness) | Serves reads at a timestamp at most `<duration>` stale; never blocks, errors if the bound cannot be met. |
3737
| Read Uncommitted, Read Committed, Repeatable Read | Accepted for compatibility; treated as Serializable. |
3838
{{< /if-released >}}
3939

@@ -156,8 +156,6 @@ made available to us (e.g., querying PostgreSQL for the replication slot's LSN).
156156
{{< if-released "v26.29" >}}
157157
## Bounded Staleness
158158

159-
{{< public-preview />}}
160-
161159
The Bounded Staleness isolation level lets you trade exact freshness for
162160
predictable latency. A query under bounded staleness is served at a timestamp
163161
that is at most `<duration>` stale—but never blocks waiting for input

‎misc/python/materialize/parallel_workload/action.py‎

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2679,15 +2679,10 @@ class BoundedStalenessReadAction(Action):
26792679
restored afterwards, since bounded staleness is read-only and would break
26802680
writes."""
26812681

2682-
def applicable(self, exe: Executor) -> bool:
2683-
return exe.db.flags.get("enable_bounded_staleness_isolation", "FALSE") == "TRUE"
2684-
26852682
def errors_to_ignore(self, exe: Executor) -> list[str]:
26862683
result = super().errors_to_ignore(exe)
26872684
result.extend(
26882685
[
2689-
# The flag was flipped off between applicable() and run().
2690-
"is not available",
26912686
# The freshness bound could not be met. Bounded staleness
26922687
# never blocks, it errors instead.
26932688
"not been materialized",
@@ -2988,9 +2983,6 @@ def __init__(
29882983
)
29892984
self.flags_with_values["enable_compute_error_distinct"] = BOOLEAN_FLAG_VALUES
29902985
self.flags_with_values["enable_alter_table_add_column"] = BOOLEAN_FLAG_VALUES
2991-
self.flags_with_values["enable_bounded_staleness_isolation"] = (
2992-
BOOLEAN_FLAG_VALUES
2993-
)
29942986
self.flags_with_values["enable_arrangement_dictionary_compression_alpha"] = (
29952987
BOOLEAN_FLAG_VALUES
29962988
)

‎src/adapter/src/coord/command_handler.rs‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1084,9 +1084,9 @@ impl Coordinator {
10841084

10851085
// If the resolved `transaction_isolation` default names a feature-flagged
10861086
// isolation level whose flag is now disabled (e.g. a role default set
1087-
// while `bounded staleness` was enabled, then the flag turned off), drop
1088-
// it so the session falls back to the built-in default rather than
1089-
// silently using a gated level.
1087+
// while `strong session serializable` was enabled, then the flag turned
1088+
// off), drop it so the session falls back to the built-in default rather
1089+
// than silently using a gated level.
10901090
if let Some(value) = session_defaults.get(TRANSACTION_ISOLATION_VAR_NAME) {
10911091
if check_transaction_isolation_feature_flag(
10921092
TRANSACTION_ISOLATION_VAR_NAME,

‎src/sql/src/session/vars.rs‎

Lines changed: 26 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1023,8 +1023,7 @@ fn compat_translate_name(name: &str) -> &str {
10231023
}
10241024

10251025
/// Enforces feature-flag gating for `transaction_isolation` levels that sit
1026-
/// behind a flag (`bounded staleness <duration>` and
1027-
/// `strong session serializable`).
1026+
/// behind a flag (`strong session serializable`).
10281027
///
10291028
/// Returns `Ok(())` for any other variable, and for an unparseable value
10301029
/// (parse errors surface on the actual set). This is shared by every path that
@@ -1045,9 +1044,6 @@ pub fn check_transaction_isolation_feature_flag(
10451044
};
10461045
match level {
10471046
IsolationLevel::StrongSessionSerializable => ENABLE_SESSION_TIMELINES.require(system_vars),
1048-
IsolationLevel::BoundedStaleness(_) => {
1049-
ENABLE_BOUNDED_STALENESS_ISOLATION.require(system_vars)
1050-
}
10511047
_ => Ok(()),
10521048
}
10531049
}
@@ -2613,46 +2609,49 @@ mod isolation_feature_flag_tests {
26132609
use super::*;
26142610

26152611
#[mz_ore::test]
2616-
fn gates_bounded_staleness_value() {
2612+
fn gates_strong_session_serializable_value() {
26172613
let mut system_vars = SystemVars::new();
26182614

2619-
// Default-on: the value passes the gate.
2620-
check_transaction_isolation_feature_flag(
2621-
TRANSACTION_ISOLATION_VAR_NAME,
2622-
VarInput::Flat("bounded staleness 5s"),
2623-
&system_vars,
2624-
)
2625-
.expect("flag on by default");
2626-
2627-
// Turn the flag off: the value is rejected regardless of the letter case
2628-
// of the variable name. This covers `SET`, `SET "TRANSACTION_ISOLATION"`,
2629-
// `ALTER ROLE ... SET`, and connection options, which all route through
2630-
// `SessionVars::set` and this shared check.
2631-
system_vars
2632-
.set("enable_bounded_staleness_isolation", VarInput::Flat("off"))
2633-
.expect("set flag");
2615+
// The flag defaults off: the value is rejected regardless of the letter
2616+
// case of the variable name. This covers `SET`,
2617+
// `SET "TRANSACTION_ISOLATION"`, `ALTER ROLE ... SET`, and connection
2618+
// options, which all route through `SessionVars::set` and this shared
2619+
// check.
26342620
for name in ["transaction_isolation", "TRANSACTION_ISOLATION"] {
26352621
let err = check_transaction_isolation_feature_flag(
26362622
name,
2637-
VarInput::Flat("bounded staleness 5s"),
2623+
VarInput::Flat("strong session serializable"),
26382624
&system_vars,
26392625
)
2640-
.expect_err("flag off rejects bounded staleness");
2626+
.expect_err("flag off rejects strong session serializable");
26412627
assert!(matches!(err, VarError::RequiresFeatureFlag { .. }));
26422628
}
26432629

2644-
// Non-gated levels are unaffected.
2630+
// Ungated levels pass regardless of the flag.
2631+
for level in ["serializable", "bounded staleness 5s"] {
2632+
check_transaction_isolation_feature_flag(
2633+
TRANSACTION_ISOLATION_VAR_NAME,
2634+
VarInput::Flat(level),
2635+
&system_vars,
2636+
)
2637+
.expect("ungated level always allowed");
2638+
}
2639+
2640+
// With the flag on, the gated value passes too.
2641+
system_vars
2642+
.set("enable_session_timelines", VarInput::Flat("on"))
2643+
.expect("set flag");
26452644
check_transaction_isolation_feature_flag(
26462645
TRANSACTION_ISOLATION_VAR_NAME,
2647-
VarInput::Flat("serializable"),
2646+
VarInput::Flat("strong session serializable"),
26482647
&system_vars,
26492648
)
2650-
.expect("serializable always allowed");
2649+
.expect("flag on");
26512650

26522651
// Unrelated variables are ignored, even with a gated-looking value.
26532652
check_transaction_isolation_feature_flag(
26542653
CLUSTER.name(),
2655-
VarInput::Flat("bounded staleness 5s"),
2654+
VarInput::Flat("strong session serializable"),
26562655
&system_vars,
26572656
)
26582657
.expect("unrelated var ignored");

‎src/sql/src/session/vars/definitions.rs‎

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2380,12 +2380,6 @@ feature_flags!(
23802380
default: true,
23812381
enable_for_item_parsing: false,
23822382
},
2383-
{
2384-
name: enable_bounded_staleness_isolation,
2385-
desc: "the `bounded staleness <duration>` transaction isolation level",
2386-
default: true,
2387-
enable_for_item_parsing: false,
2388-
},
23892383
);
23902384

23912385
impl From<&super::SystemVars> for OptimizerFeatures {

‎test/launchdarkly-flag-consistency/mzcompose.py‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,6 @@
239239
enable_background_alter_cluster
240240
enable_statement_arrival_logging
241241
enable_binary_date_bin
242-
enable_bounded_staleness_isolation
243242
enable_coalesce_case_transform
244243
enable_compute_half_join2
245244
enable_compute_render_fueled_as_specific_collection

‎test/sqllogictest/bounded_staleness.slt‎

Lines changed: 4 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,6 @@ mode cockroach
1313

1414
reset-server
1515

16-
simple conn=mz_system,user=mz_system
17-
ALTER SYSTEM SET enable_bounded_staleness_isolation = true;
18-
----
19-
COMPLETE 0
20-
2116
# --- parse and SHOW round-trip -----------------------------------------------
2217

2318
statement ok
@@ -229,70 +224,19 @@ RESET transaction_isolation
229224
statement ok
230225
DROP TABLE bs_copy
231226

232-
# --- feature-flag gating ------------------------------------------------------
233-
#
234-
# The flag defaults to `true` in public preview; explicitly turn it off here to
235-
# exercise the gating path.
236-
237-
simple conn=mz_system,user=mz_system
238-
ALTER SYSTEM SET enable_bounded_staleness_isolation = false;
239-
----
240-
COMPLETE 0
241-
242-
statement error the `bounded staleness <duration>` transaction isolation level is not available
243-
SET transaction_isolation TO 'bounded staleness 5s'
244-
245-
# The gate must not be bypassable via a quoted, upper-cased variable name.
246-
statement error the `bounded staleness <duration>` transaction isolation level is not available
247-
SET "TRANSACTION_ISOLATION" TO 'bounded staleness 5s'
248-
249-
# ... nor via `ALTER ROLE ... SET`.
250-
statement error the `bounded staleness <duration>` transaction isolation level is not available
251-
ALTER ROLE materialize SET transaction_isolation = 'bounded staleness 5s'
252-
253-
simple conn=mz_system,user=mz_system
254-
ALTER SYSTEM SET enable_bounded_staleness_isolation = true;
255-
----
256-
COMPLETE 0
257-
258-
statement ok
259-
SET transaction_isolation TO 'bounded staleness 5s'
260-
261-
statement ok
262-
RESET transaction_isolation
263-
264-
# --- role-default fallback when the flag is later disabled --------------------
227+
# --- role default -------------------------------------------------------------
265228
#
266-
# A role default set while the flag is on must not silently take effect once the
267-
# flag is turned off; fresh sessions fall back to the built-in default.
229+
# `ALTER ROLE ... SET` accepts the level, and a fresh session picks it up as its
230+
# starting isolation.
268231

269232
statement ok
270233
ALTER ROLE materialize SET transaction_isolation = 'bounded staleness 5s'
271234

272-
# A fresh session picks up the role default while the flag is on.
273-
simple conn=role_on,user=materialize
235+
simple conn=role_default,user=materialize
274236
SHOW transaction_isolation
275237
----
276238
bounded staleness 5s
277239
COMPLETE 1
278240

279-
simple conn=mz_system,user=mz_system
280-
ALTER SYSTEM SET enable_bounded_staleness_isolation = false;
281-
----
282-
COMPLETE 0
283-
284-
# With the flag off, a fresh session falls back to the built-in default rather
285-
# than applying the gated role default.
286-
simple conn=role_off,user=materialize
287-
SHOW transaction_isolation
288-
----
289-
strict serializable
290-
COMPLETE 1
291-
292-
simple conn=mz_system,user=mz_system
293-
ALTER SYSTEM SET enable_bounded_staleness_isolation = true;
294-
----
295-
COMPLETE 0
296-
297241
statement ok
298242
ALTER ROLE materialize RESET transaction_isolation

‎test/testdrive/bounded-staleness.td‎

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,6 @@
99

1010
# Bounded staleness isolation — happy-path coverage.
1111

12-
$ postgres-execute connection=postgres://mz_system:materialize@${testdrive.materialize-internal-sql-addr}
13-
ALTER SYSTEM SET enable_bounded_staleness_isolation = true
14-
1512
> CREATE TABLE bs_t (a int);
1613
> INSERT INTO bs_t VALUES (1), (2), (3);
1714

0 commit comments

Comments
 (0)