mz_compute_types/dyncfgs.rs
1// Copyright Materialize, Inc. and contributors. All rights reserved.
2//
3// Use of this software is governed by the Business Source License
4// included in the LICENSE file.
5//
6// As of the Change Date specified in that file, in accordance with
7// the Business Source License, use of this software will be governed
8// by the Apache License, Version 2.0.
9
10//! Dyncfgs used by the compute layer.
11
12use std::time::Duration;
13
14use mz_dyncfg::{Config, ConfigSet, ParameterScope};
15
16/// Whether rendering should use `half_join2` rather than DD's `half_join` for delta joins.
17///
18/// `half_join2` avoids quadratic behavior in certain join patterns. This flag exists as an escape
19/// hatch to revert to the old implementation if issues arise.
20pub const ENABLE_HALF_JOIN2: Config<bool> = Config::new(
21 "enable_compute_half_join2",
22 true,
23 "Whether compute should use `half_join2` rather than DD's `half_join` to render delta joins.",
24 ParameterScope::Environment,
25);
26
27/// Whether rendering should collapse error multiplicities to one where it arranges errors.
28///
29/// Error semantics depend only on whether an error is present, so its multiplicity carries no
30/// information a consumer reads. Left uncollapsed, a shared collection contributes its errors once
31/// per plan path that reads it, and because those factors apply again at each level of sharing they
32/// compound multiplicatively until the `Diff` overflows.
33///
34/// Governs sharing within a dataflow only. Sharing across objects is bounded unconditionally, by
35/// normalizing at every boundary another dataflow can read, so what this flag decides is never
36/// durable state and two replicas rendering under different values still write the same thing.
37/// Environment-scoped for now because nothing needs finer granularity, not because finer would be
38/// unsafe.
39pub const ENABLE_ERROR_DISTINCT: Config<bool> = Config::new(
40 "enable_compute_error_distinct",
41 true,
42 "Whether compute rendering should collapse error multiplicities to one where it arranges \
43 errors.",
44 ParameterScope::Environment,
45);
46
47/// Use the column-paged merge batcher code path at arrange sites. When
48/// `true`, arrange operators use `Col2ValPagedBatcher` (in
49/// `mz_timely_util::columnar`) and `RowRowColPagedBuilder` (in
50/// `mz_row_spine`), the columnar-native batcher that the pager can spill
51/// (gated by [`ENABLE_COLUMN_PAGED_BATCHER_SPILL`]). Read at operator
52/// construction time. Flips take effect on dataflows created after the
53/// change.
54///
55/// Takes precedence over [`ENABLE_COLUMNAR_MERGE_BATCHER`]: both select
56/// columnar chains, and this one additionally routes them through the pager.
57/// With both `false` the arrange sites use the columnation
58/// `Col2ValBatcher` / `RowRowBuilder` path. See
59/// `mz_compute::extensions::arrange::ArrangementBatcher` for the resolution.
60///
61/// Disabled by default while the new path is stabilizing.
62/// `DifferentialJoinHydration*` feature-benchmark scenarios opt in
63/// explicitly so the spill path is measured.
64pub const ENABLE_COLUMN_PAGED_BATCHER: Config<bool> = Config::new(
65 "enable_column_paged_batcher",
66 false,
67 "Use the columnar-native paged merge batcher at arrange sites. Takes precedence over \
68 enable_columnar_merge_batcher; with both false, arranges use the columnation \
69 `Col2ValBatcher` / `RowRowBuilder` path.",
70 ParameterScope::Replica,
71);
72
73/// Use the resident columnar merge batcher at arrange sites. When `true`,
74/// arrange operators use `Col2ValColBatcher` (in `mz_timely_util::columnar`)
75/// and `RowRowColPagedBuilder` (in `mz_row_spine`): the same `Column` chains
76/// and the same builder as the paged arm, merged by `ColumnMerger` with no
77/// pager and no spill budget. When `false` (the default), the arrange sites
78/// use the columnation `Col2ValBatcher` / `RowRowBuilder` path. Read at
79/// operator construction time. Flips take effect on dataflows created after
80/// the change.
81///
82/// This is the columnation-versus-columnar axis on its own, so the two paths
83/// can be compared without the pager in the measurement. It is ignored while
84/// [`ENABLE_COLUMN_PAGED_BATCHER`] is `true`.
85pub const ENABLE_COLUMNAR_MERGE_BATCHER: Config<bool> = Config::new(
86 "enable_columnar_merge_batcher",
87 false,
88 "Use the resident columnar merge batcher at arrange sites, instead of the columnation \
89 one. Ignored when enable_column_paged_batcher is true.",
90 ParameterScope::Replica,
91);
92
93/// Store the accumulable reduce's accumulators in columnar form.
94///
95/// The accumulable reduce keeps one accumulator per aggregate in the diff of its
96/// input arrangement. When `true`, that arrangement holds its diffs in a columnar
97/// container, which lays the accumulators out by variant so each pays only for its
98/// own fields. When `false` (the default), the diffs live in a columnation stack at
99/// the width of the largest variant. Read at operator construction time, so flips
100/// take effect on dataflows created after the change.
101pub const ENABLE_COLUMNAR_ACCUMULABLE_DIFF: Config<bool> = Config::new(
102 "enable_columnar_accumulable_diff",
103 false,
104 "Store the accumulable reduce's accumulators in a columnar arrangement diff, laid out by \
105 variant, instead of a columnation stack of fixed-width accumulator enums.",
106 ParameterScope::Replica,
107);
108
109/// Allow the column-paged batcher's pager to evict chunks under memory
110/// pressure. Only meaningful when [`ENABLE_COLUMN_PAGED_BATCHER`] is `true`.
111/// With the spill flag off the pager keeps every chunk resident regardless of
112/// budget.
113///
114/// This flag (or the storage-side `enable_upsert_paged_spill`) also gates
115/// installation of the process buffer pool (`mz_ore::pool`): the first
116/// configuration tick with either gate on reserves the pool's virtual
117/// address space and spawns its spill threads. Turning the gates back off
118/// stops retuning but does not tear the installed pool down.
119///
120/// Off by default, even when the batcher path itself is on, so the
121/// no-pressure case stays a pure resident operation. Tune the budget via
122/// [`COLUMN_PAGED_BATCHER_BUDGET_FRACTION`].
123pub const ENABLE_COLUMN_PAGED_BATCHER_SPILL: Config<bool> = Config::new(
124 "enable_column_paged_batcher_spill",
125 false,
126 "Allow the column-paged batcher's pager to evict chunks under memory pressure. Only \
127 meaningful when `enable_column_paged_batcher = true`.",
128 ParameterScope::Replica,
129);
130
131/// The youngest chunk generation whose spilled bodies are compressed.
132///
133/// A chunk at generational depth `d` is rewritten with frequency
134/// proportional to `2^-d` under geometric merging, so compressing shallow
135/// generations buys pool bytes back for only a short stay at a guaranteed
136/// near-term codec round-trip. Generations below the floor spill under the
137/// identity codec: fully budgeted and swap-backed, with encode and decode
138/// reduced to copies. The default exempts only fresh (depth 0) chunks. A
139/// chunk that outlives a merge untouched ages a generation regardless, and
140/// an identity-coded body at or past the floor is re-spilled compressed at
141/// its next survival, so key-disjoint input cannot hold its backlog
142/// uncompressed indefinitely. Lowering this at runtime therefore migrates
143/// bodies that already spilled, rather than applying only to new ones.
144/// `0` compresses every spilled body.
145pub const COLUMN_CHUNK_COMPRESS_MIN_DEPTH: Config<u32> = Config::new(
146 "column_chunk_compress_min_depth",
147 1,
148 "The youngest chunk generation whose spilled bodies are lz4-compressed in the buffer \
149 pool; younger generations store uncompressed. 0 compresses every spilled body.",
150 ParameterScope::Replica,
151);
152
153/// Resident-bytes budget fraction for chunk spilling. Two consumers read
154/// it: the column pager's tiered policy multiplies it against the
155/// announced memory limit, and the buffer pool (`mz_ore::pool`)
156/// multiplies it against physical RAM, since a resident budget must
157/// derive from memory that can be resident and the announced limit
158/// includes swap on swap-provisioned nodes.
159///
160/// `0.05` (5%) is a reasonable starting point: large enough that the
161/// per-call ColumnBuilder ship-threshold (~2 MiB) fits multiple chunks
162/// per worker, small enough that the merge-batcher's transient state
163/// doesn't crowd out the spine. Set lower to spill more aggressively
164/// under pressure. The computed budget is floored at 128 MiB so the
165/// no-pressure case doesn't page per chunk. Ignored when
166/// `enable_column_paged_batcher_spill` is `false`.
167pub const COLUMN_PAGED_BATCHER_BUDGET_FRACTION: Config<f64> = Config::new(
168 "column_paged_batcher_budget_fraction",
169 0.05,
170 "Budget fraction for chunk spilling: the buffer pool multiplies it against physical \
171 RAM and the column pager's tiered policy against the announced memory limit. \
172 Total pool budget = max(ram * fraction, 128 MiB).",
173 ParameterScope::Replica,
174);
175
176/// Number of buffer-pool spill threads performing eviction I/O (lz4
177/// compression plus the synchronous-reclaim `MADV_PAGEOUT`) off the threads
178/// that trip the budget. Zero evicts inline on the calling thread, which
179/// measurably convoys workers behind eviction I/O at hydration eviction
180/// rates. Thread spawning is once per process: raising the value later has
181/// no effect beyond re-enabling, and lowering it to zero falls back to
182/// inline eviction while spawned threads idle.
183pub const COLUMN_PAGED_BATCHER_SPILL_WORKER_COUNT: Config<usize> = Config::new(
184 "column_paged_batcher_spill_worker_count",
185 2,
186 "Buffer-pool spill threads for off-worker eviction I/O; 0 evicts inline on the caller.",
187 ParameterScope::Replica,
188);
189
190/// Compress chunks the column-paged batcher spills, using lz4. Only
191/// meaningful when [`ENABLE_COLUMN_PAGED_BATCHER_SPILL`] is `true`; the codec
192/// is applied on the pageout path and reversed on page-in. Trades CPU for a
193/// smaller on-storage (and, for the swap backend, resident) footprint.
194///
195/// Off by default so the spill path's cost stays a pure copy until compression
196/// is shown to pay for itself on the target workload.
197pub const COLUMN_PAGED_BATCHER_LZ4: Config<bool> = Config::new(
198 "column_paged_batcher_lz4",
199 false,
200 "Compress column-paged batcher chunks with lz4 on the spill path. Only meaningful when \
201 `enable_column_paged_batcher_spill = true`.",
202 ParameterScope::Replica,
203);
204
205/// Proactively evict the column-paged batcher's lz4-compressed spill chunks
206/// from RSS via `MADV_PAGEOUT` when spilling to the swap backend. Only
207/// meaningful when [`COLUMN_PAGED_BATCHER_LZ4`] is `true` and the active
208/// backend is swap (no scratch directory): on that path the compressed bytes
209/// stay resident in the process address space and currently receive no madvise
210/// at all, so the kernel reclaims them only lazily under LRU pressure.
211/// `MADV_PAGEOUT` instead swaps them out eagerly at spill time, holding RSS at
212/// the budget rather than letting it drift up to the pressure cliff. A later
213/// page-in re-faults the pages — cheap because lz4 shrank the byte volume,
214/// which is what makes eager eviction pay off on this path.
215///
216/// Off by default: the eager-reclaim syscall is the one kernel interaction the
217/// pager design singled out as risky, so it stays gated until proven on the
218/// target workload.
219pub const COLUMN_PAGED_BATCHER_SWAP_PAGEOUT: Config<bool> = Config::new(
220 "column_paged_batcher_swap_pageout",
221 false,
222 "Eagerly evict the column-paged batcher's lz4-compressed swap-backend spill chunks from RSS \
223 via `MADV_PAGEOUT` (they otherwise receive no madvise and are reclaimed only lazily). Only \
224 meaningful when `column_paged_batcher_lz4 = true` and the swap backend is active.",
225 ParameterScope::Replica,
226);
227
228/// Eagerly compress unbacked buffer-pool chunks to `BackedResident` on idle
229/// spill threads (write-behind). The chunk stays readable in its slot while
230/// a compressed extent accumulates on the swap device, so budget-driven
231/// eviction becomes a pure page release instead of a compression. Trades
232/// background CPU (compression of chunks that may die before pressure
233/// reaches them) for near-free pressure response.
234pub const COLUMN_PAGED_BATCHER_EAGER_BACKING: Config<bool> = Config::new(
235 "column_paged_batcher_eager_backing",
236 false,
237 "Eagerly compress buffer-pool chunks to compressed-but-resident on idle spill threads, so \
238 budget-driven eviction is a pure page release. Only meaningful with spill workers.",
239 ParameterScope::Replica,
240);
241
242/// Ceiling on the buffer pool's total RSS, as a fraction of *physical RAM*
243/// (never the announced limit, which includes swap on swap-provisioned
244/// nodes). The compressed-but-resident extent tier is the headroom above the
245/// slot budget and warm cap: chunks evicted from the budget stay in RAM
246/// compressed (~5.6x denser; reads decompress without faulting) until this
247/// ceiling forces the oldest extents out to the swap device via
248/// `MADV_PAGEOUT`. Zero collapses the tier: extents page out as soon as
249/// they are written.
250///
251/// The default pairs with the 0.05 budget default to leave ~20% of RAM for
252/// the compressed tier — the same share zswap's default compressed pool
253/// takes, and roughly RAM-sized logical coverage at the measured ~5.6x
254/// ratio — while keeping three quarters of RAM for everything else in the
255/// process.
256pub const COLUMN_PAGED_BATCHER_POOL_RSS_TARGET_FRACTION: Config<f64> = Config::new(
257 "column_paged_batcher_pool_rss_target_fraction",
258 0.25,
259 "Ceiling on the buffer pool's total RSS as a fraction of physical RAM; the headroom above \
260 the slot budget holds compressed-but-resident extents. Zero pages extents out immediately.",
261 ParameterScope::Replica,
262);
263
264/// Whether rendering should use `mz_join_core` rather than DD's `JoinCore::join_core`.
265pub const ENABLE_MZ_JOIN_CORE: Config<bool> = Config::new(
266 "enable_mz_join_core",
267 true,
268 "Whether compute should use `mz_join_core` rather than DD's `JoinCore::join_core` to render \
269 linear joins.",
270 ParameterScope::Environment,
271);
272
273/// Use sync Timely operators with Tokio tasks for the MV sink.
274pub const ENABLE_SYNC_MV_SINK: Config<bool> = Config::new(
275 "enable_compute_sync_mv_sink",
276 false,
277 "Use sync Timely operators with Tokio tasks for the MV sink.",
278 ParameterScope::Environment,
279);
280
281/// Whether rendering should use the new MV sink correction buffer implementation.
282pub const ENABLE_CORRECTION_V2: Config<bool> = Config::new(
283 "enable_compute_correction_v2",
284 true,
285 "Whether compute should use the new MV sink correction buffer implementation.",
286 ParameterScope::Environment,
287);
288
289/// The size factor of subsequent chains in the correction V2 buffer.
290pub const CORRECTION_V2_CHAIN_PROPORTIONALITY: Config<f64> = Config::new(
291 "compute_correction_v2_chain_proportionality",
292 3.0,
293 "The size factor of subsequent chains in the correction V2 buffer.",
294 ParameterScope::Replica,
295);
296
297/// The byte size of chunks in the correction V2 buffer.
298pub const CORRECTION_V2_CHUNK_SIZE: Config<usize> = Config::new(
299 "compute_correction_v2_chunk_size",
300 8 * 1024,
301 "The byte size of chunks in the correction V2 buffer.",
302 ParameterScope::Replica,
303);
304
305/// Whether to enable temporal bucketing in compute.
306pub const ENABLE_COMPUTE_TEMPORAL_BUCKETING: Config<bool> = Config::new(
307 "enable_compute_temporal_bucketing",
308 false,
309 "Whether to enable temporal bucketing in compute.",
310 ParameterScope::Environment,
311);
312
313/// The summary to apply to the frontier in temporal bucketing in compute.
314pub const TEMPORAL_BUCKETING_SUMMARY: Config<Duration> = Config::new(
315 "compute_temporal_bucketing_summary",
316 Duration::from_secs(2),
317 "The summary to apply to frontiers in temporal bucketing in compute.",
318 ParameterScope::Environment,
319);
320
321/// The yielding behavior with which linear joins should be rendered.
322pub const LINEAR_JOIN_YIELDING: Config<&str> = Config::new(
323 "linear_join_yielding",
324 "work:1000000,time:100",
325 "The yielding behavior compute rendering should apply for linear join operators. Either \
326 'work:<amount>' or 'time:<milliseconds>' or 'work:<amount>,time:<milliseconds>'. Note \
327 that omitting one of 'work' or 'time' will entirely disable join yielding by time or \
328 work, respectively, rather than falling back to some default.",
329 ParameterScope::Replica,
330);
331
332/// Enable lgalloc.
333pub const ENABLE_LGALLOC: Config<bool> = Config::new(
334 "enable_lgalloc",
335 true,
336 "Enable lgalloc.",
337 ParameterScope::Replica,
338);
339
340/// Enable lgalloc's eager memory return/reclamation feature.
341pub const ENABLE_LGALLOC_EAGER_RECLAMATION: Config<bool> = Config::new(
342 "enable_lgalloc_eager_reclamation",
343 true,
344 "Enable lgalloc's eager return behavior.",
345 ParameterScope::Replica,
346);
347
348/// The interval at which the background thread wakes.
349pub const LGALLOC_BACKGROUND_INTERVAL: Config<Duration> = Config::new(
350 "lgalloc_background_interval",
351 Duration::from_secs(1),
352 "Scheduling interval for lgalloc's background worker.",
353 ParameterScope::Replica,
354);
355
356/// Enable lgalloc's eager memory return/reclamation feature.
357pub const LGALLOC_FILE_GROWTH_DAMPENER: Config<usize> = Config::new(
358 "lgalloc_file_growth_dampener",
359 2,
360 "Lgalloc's file growth dampener parameter.",
361 ParameterScope::Replica,
362);
363
364/// Enable lgalloc's eager memory return/reclamation feature.
365pub const LGALLOC_LOCAL_BUFFER_BYTES: Config<usize> = Config::new(
366 "lgalloc_local_buffer_bytes",
367 64 << 20,
368 "Lgalloc's local buffer bytes parameter.",
369 ParameterScope::Replica,
370);
371
372/// The bytes to reclaim (slow path) per size class, for each background thread activation.
373pub const LGALLOC_SLOW_CLEAR_BYTES: Config<usize> = Config::new(
374 "lgalloc_slow_clear_bytes",
375 128 << 20,
376 "Clear byte size per size class for every invocation",
377 ParameterScope::Replica,
378);
379
380/// Interval to run the memory limiter. A zero duration disables the limiter.
381pub const MEMORY_LIMITER_INTERVAL: Config<Duration> = Config::new(
382 "memory_limiter_interval",
383 Duration::from_secs(10),
384 "Interval to run the memory limiter. A zero duration disables the limiter.",
385 ParameterScope::Replica,
386);
387
388/// Bias to the memory limiter usage factor.
389pub const MEMORY_LIMITER_USAGE_BIAS: Config<f64> = Config::new(
390 "memory_limiter_usage_bias",
391 1.,
392 "Multiplicative bias to the memory limiter's limit.",
393 ParameterScope::Replica,
394);
395
396/// Burst factor to memory limit.
397pub const MEMORY_LIMITER_BURST_FACTOR: Config<f64> = Config::new(
398 "memory_limiter_burst_factor",
399 0.,
400 "Multiplicative burst factor to the memory limiter's limit.",
401 ParameterScope::Replica,
402);
403
404/// Enable lgalloc for columnation.
405pub const ENABLE_COLUMNATION_LGALLOC: Config<bool> = Config::new(
406 "enable_columnation_lgalloc",
407 true,
408 "Enable allocating regions from lgalloc.",
409 ParameterScope::Replica,
410);
411
412/// The interval at which the compute server performs maintenance tasks.
413pub const COMPUTE_SERVER_MAINTENANCE_INTERVAL: Config<Duration> = Config::new(
414 "compute_server_maintenance_interval",
415 Duration::from_millis(10),
416 "The interval at which the compute server performs maintenance tasks. Zero enables maintenance on every iteration.",
417 ParameterScope::Replica,
418);
419
420/// Maximum number of in-flight bytes emitted by persist_sources feeding dataflows.
421pub const DATAFLOW_MAX_INFLIGHT_BYTES: Config<Option<usize>> = Config::new(
422 "compute_dataflow_max_inflight_bytes",
423 None,
424 "The maximum number of in-flight bytes emitted by persist_sources feeding \
425 compute dataflows in non-cc clusters.",
426 ParameterScope::Replica,
427);
428
429/// The "physical backpressure" of `compute_dataflow_max_inflight_bytes_cc` has
430/// been replaced in cc replicas by persist lgalloc and we intend to remove it
431/// once everything has switched to cc. In the meantime, this is a CYA to turn
432/// it back on if absolutely necessary.
433pub const DATAFLOW_MAX_INFLIGHT_BYTES_CC: Config<Option<usize>> = Config::new(
434 "compute_dataflow_max_inflight_bytes_cc",
435 None,
436 "The maximum number of in-flight bytes emitted by persist_sources feeding \
437 compute dataflows in cc clusters.",
438 ParameterScope::Replica,
439);
440
441/// The term `n` in the growth rate `1 + 1/(n + 1)` for `ConsolidatingVec`.
442/// The smallest value `0` corresponds to the greatest allowed growth, of doubling.
443pub const CONSOLIDATING_VEC_GROWTH_DAMPENER: Config<usize> = Config::new(
444 "consolidating_vec_growth_dampener",
445 1,
446 "Dampener in growth rate for consolidating vector size",
447 ParameterScope::Replica,
448);
449
450/// The number of dataflows that may hydrate concurrently.
451///
452/// Enforced in `environmentd`, by the controller's per-replica hydration
453/// interceptor withholding `Schedule` commands, rather than by the replica. The
454/// interceptor resolves it from the configuration commands it observes, which
455/// are already specialized for its replica, so the limit still follows the
456/// replica's scoped override.
457pub const HYDRATION_CONCURRENCY: Config<usize> = Config::new(
458 "compute_hydration_concurrency",
459 4,
460 "Controls how many compute dataflows may hydrate concurrently.",
461 ParameterScope::Replica,
462);
463
464/// See `src/storage-operators/src/s3_oneshot_sink/parquet.rs` for more details.
465pub const COPY_TO_S3_PARQUET_ROW_GROUP_FILE_RATIO: Config<usize> = Config::new(
466 "copy_to_s3_parquet_row_group_file_ratio",
467 20,
468 "The ratio (defined as a percentage) of row-group size to max-file-size. \
469 Must be <= 100.",
470 ParameterScope::Environment,
471);
472
473/// See `src/storage-operators/src/s3_oneshot_sink/parquet.rs` for more details.
474pub const COPY_TO_S3_ARROW_BUILDER_BUFFER_RATIO: Config<usize> = Config::new(
475 "copy_to_s3_arrow_builder_buffer_ratio",
476 150,
477 "The ratio (defined as a percentage) of arrow-builder size to row-group size. \
478 Must be >= 100.",
479 ParameterScope::Environment,
480);
481
482/// The size of each part in the multi-part upload to use when uploading files to S3.
483pub const COPY_TO_S3_MULTIPART_PART_SIZE_BYTES: Config<usize> = Config::new(
484 "copy_to_s3_multipart_part_size_bytes",
485 1024 * 1024 * 8,
486 "The size of each part in a multipart upload to S3.",
487 ParameterScope::Environment,
488);
489
490/// Main switch to enable or disable replica expiration.
491///
492/// Changes affect existing replicas only after restart.
493///
494/// The env-wide kill switch for the feature, read in `environmentd` when
495/// specializing `CreateInstance` for a replica. [`COMPUTE_REPLICA_EXPIRATION_OFFSET`]
496/// is the replica-scoped half of the pair.
497pub const ENABLE_COMPUTE_REPLICA_EXPIRATION: Config<bool> = Config::new(
498 "enable_compute_replica_expiration",
499 true,
500 "Main switch to disable replica expiration.",
501 ParameterScope::Environment,
502);
503
504/// The maximum lifetime of a replica configured as an offset to the replica start time.
505/// Used in temporal filters to drop diffs generated at timestamps beyond the expiration time.
506///
507/// A zero duration implies no expiration. Changing this value does not affect existing replicas,
508/// even when they are restarted.
509pub const COMPUTE_REPLICA_EXPIRATION_OFFSET: Config<Duration> = Config::new(
510 "compute_replica_expiration_offset",
511 Duration::ZERO,
512 "The expiration time offset for replicas. Zero disables expiration.",
513 ParameterScope::Replica,
514);
515
516/// When enabled, applies the column demands from a MapFilterProject onto the RelationDesc used to
517/// read out of Persist. This allows Persist to prune unneeded columns as a performance
518/// optimization.
519pub const COMPUTE_APPLY_COLUMN_DEMANDS: Config<bool> = Config::new(
520 "compute_apply_column_demands",
521 true,
522 "When enabled, passes applys column demands to the RelationDesc used to read out of Persist.",
523 ParameterScope::Environment,
524);
525
526/// The amount of output the flat-map operator produces before yielding. Set to a high value to
527/// avoid yielding, or to a low value to yield frequently.
528pub const COMPUTE_FLAT_MAP_FUEL: Config<usize> = Config::new(
529 "compute_flat_map_fuel",
530 1_000_000,
531 "The amount of output the flat-map operator produces before yielding.",
532 ParameterScope::Replica,
533);
534
535/// Whether to apply logical backpressure in compute dataflows.
536pub const ENABLE_COMPUTE_LOGICAL_BACKPRESSURE: Config<bool> = Config::new(
537 "enable_compute_logical_backpressure",
538 false,
539 "When enabled, compute dataflows will apply logical backpressure.",
540 ParameterScope::Replica,
541);
542
543/// Maximal number of capabilities retained by the logical backpressure operator.
544///
545/// Selecting this value is subtle. If it's too small, it'll diminish the effectiveness of the
546/// logical backpressure operators. If it's too big, we can slow down hydration and cause state
547/// in the operator's implementation to build up.
548///
549/// The default value represents a compromise between these two extremes. We retain some metrics
550/// for 30 days, and the metrics update every minute. The default is exactly this number.
551pub const COMPUTE_LOGICAL_BACKPRESSURE_MAX_RETAINED_CAPABILITIES: Config<Option<usize>> =
552 Config::new(
553 "compute_logical_backpressure_max_retained_capabilities",
554 Some(30 * 24 * 60),
555 "The maximum number of capabilities retained by the logical backpressure operator.",
556 ParameterScope::Replica,
557 );
558
559/// The slack to round observed timestamps up to.
560///
561/// The default corresponds to Mz's default tick interval, but does not need to do so. Ideally,
562/// it is not smaller than the tick interval, but it can be larger.
563pub const COMPUTE_LOGICAL_BACKPRESSURE_INFLIGHT_SLACK: Config<Duration> = Config::new(
564 "compute_logical_backpressure_inflight_slack",
565 Duration::from_secs(1),
566 "Round observed timestamps to slack.",
567 ParameterScope::Replica,
568);
569
570/// Enable per-column dictionary compression for row containers in arrangements.
571///
572/// The `_alpha` suffix is load-bearing: this feature is not yet considered
573/// production-ready, and the name is meant to make that unmissable at the
574/// `ALTER SYSTEM SET` call site rather than relying on out-of-band warnings.
575///
576/// Disposition: added 2026-06-09; solicit feedback for one month and remove in
577/// the absence of a positive response.
578pub const ENABLE_ARRANGEMENT_DICTIONARY_COMPRESSION_ALPHA: Config<bool> = Config::new(
579 "enable_arrangement_dictionary_compression_alpha",
580 true,
581 "Enable arrangement dictionary compression (alpha; not yet production-ready).",
582 ParameterScope::Replica,
583);
584
585/// Whether to enable the peek response stash, for sending back large peek
586/// responses. The response stash will only be used for results that exceed
587/// `compute_peek_response_stash_threshold_bytes`.
588pub const ENABLE_PEEK_RESPONSE_STASH: Config<bool> = Config::new(
589 "enable_compute_peek_response_stash",
590 true,
591 "Whether to enable the peek response stash, for sending back large peek responses. Will only be used for results that exceed compute_peek_response_stash_threshold_bytes.",
592 ParameterScope::Environment,
593);
594
595/// The threshold for peek response size above which we should use the peek
596/// response stash. Only used if the peek response stash is enabled _and_ if the
597/// query is "streamable" (roughly: doesn't have an ORDER BY).
598pub const PEEK_RESPONSE_STASH_THRESHOLD_BYTES: Config<usize> = Config::new(
599 "compute_peek_response_stash_threshold_bytes",
600 1024 * 10, /* 10KB */
601 "The threshold above which to use the peek response stash, for sending back large peek responses.",
602 ParameterScope::Environment,
603);
604
605/// The size at which a peek bound for the stash hands its accumulated rows to the upload, once
606/// the first batch at [`PEEK_RESPONSE_STASH_THRESHOLD_BYTES`] has decided that the answer is not
607/// an inline one.
608///
609/// Kept apart from the threshold because they answer different questions: the threshold sizes
610/// what an inline answer may hold, this sizes one hand-over. Each hand-over costs a round trip
611/// through the blocking pool, and a scan retains up to this much between them.
612pub const PEEK_RESPONSE_STASH_BATCH_BYTES: Config<usize> = Config::new(
613 "compute_peek_response_stash_batch_bytes",
614 1024 * 1024,
615 "The size in bytes at which a peek bound for the peek response stash hands its rows to the upload, after the first batch at the stash threshold.",
616 ParameterScope::Replica,
617);
618
619/// The target number of maximum runs in the batches written to the stash.
620///
621/// Setting this reasonably low will make it so batches get consolidated/sorted
622/// concurrently with data being written. Which will in turn make it so that we
623/// have to do less work when reading/consolidating those batches in
624/// `environmentd`.
625pub const PEEK_RESPONSE_STASH_BATCH_MAX_RUNS: Config<usize> = Config::new(
626 "compute_peek_response_stash_batch_max_runs",
627 // The lowest possible setting, do as much work as possible on the
628 // `clusterd` side.
629 2,
630 "The target number of maximum runs in the batches written to the stash.",
631 ParameterScope::Environment,
632);
633
634/// The target size for batches of rows we read out of the peek stash.
635pub const PEEK_RESPONSE_STASH_READ_BATCH_SIZE_BYTES: Config<usize> = Config::new(
636 "compute_peek_response_stash_read_batch_size_bytes",
637 1024 * 1024 * 100, /* 100mb */
638 "The target size for batches of rows we read out of the peek stash.",
639 ParameterScope::Environment,
640);
641
642/// The memory budget for consolidating stashed peek responses in
643/// `environmentd`.
644pub const PEEK_RESPONSE_STASH_READ_MEMORY_BUDGET_BYTES: Config<usize> = Config::new(
645 "compute_peek_response_stash_read_memory_budget_bytes",
646 1024 * 1024 * 64, /* 64mb */
647 "The memory budget for consolidating stashed peek responses in environmentd.",
648 ParameterScope::Environment,
649);
650
651/// Whether compute should stop peeks that iterate over too many rows.
652pub const ENABLE_PEEK_ROW_ITERATION_LIMIT: Config<bool> = Config::new(
653 "enable_compute_peek_row_iteration_limit",
654 false,
655 "Whether compute should stop peeks that exceed compute_peek_row_iteration_limit.",
656 ParameterScope::Environment,
657);
658
659/// The maximum number of rows a peek may iterate over on each worker.
660///
661/// The count spans a peek's whole walk of its arrangement, rows written to the peek stash
662/// included, because a peek walks its arrangement once and the count travels with that walk.
663pub const PEEK_ROW_ITERATION_LIMIT: Config<usize> = Config::new(
664 "compute_peek_row_iteration_limit",
665 1000,
666 "The maximum number of rows a peek may iterate over on each worker when enable_compute_peek_row_iteration_limit is enabled. The count spans the peek's whole walk, rows written to the peek stash included.",
667 ParameterScope::Environment,
668);
669
670/// Whether a fast-path index peek may move its walk off the timely worker for latency.
671///
672/// Off, a peek walks on the worker that owns it until it answers, delaying every other message
673/// that worker serves. On, a peek that outruns [`INDEX_PEEK_INLINE_BUDGET`] finishes away from it.
674///
675/// This gates latency offload only. A peek whose rows outgrow an inline answer is offloaded either
676/// way, because the driver that writes to the peek stash is the offloaded one. Off means an ordinary
677/// peek runs where it used to, not that none leaves the worker.
678pub const ENABLE_INDEX_PEEK_OFFLOAD: Config<bool> = Config::new(
679 "enable_compute_index_peek_offload",
680 false,
681 "Whether a fast-path index peek may move its walk off the timely worker.",
682 ParameterScope::Replica,
683);
684
685/// How far one peek may walk on the worker before it is offloaded, in consumed cursor positions.
686///
687/// A peek that exceeds it has been measured expensive, not predicted to be, so a point lookup over
688/// a skewed hot key offloads without a special case.
689///
690/// Counted in cursor positions, not rows returned: the result iterator steps the cursor without
691/// returning anything whenever the MFP rejects a row, so a selective filter over a large
692/// arrangement would never spend a row-counted budget. No wall-clock component, since under memory
693/// pressure a time bound anti-correlates with progress, one major fault consuming a whole slice.
694///
695/// Zero walks one position, not none: a peek granted no fuel would suspend having walked nowhere
696/// and be offloaded for it.
697pub const INDEX_PEEK_INLINE_BUDGET: Config<usize> = Config::new(
698 "compute_index_peek_inline_budget",
699 1024,
700 "How far one index peek may walk on the timely worker, in consumed cursor positions, before it is offloaded.",
701 ParameterScope::Replica,
702);
703
704/// What all peeks together may spend in one worker activation, in consumed cursor positions.
705///
706/// Every activation visits every pending peek, so a per-peek budget with no aggregate lets N
707/// pending peeks cost N times [`INDEX_PEEK_INLINE_BUDGET`] in one pass, unbounded in N. Peeks that
708/// get no turn are served first on the next activation.
709///
710/// Raising the ratio to the inline budget drains a burst in fewer activations, lowering it caps
711/// how long one activation withholds the worker.
712pub const INDEX_PEEK_ACTIVATION_BUDGET: Config<usize> = Config::new(
713 "compute_index_peek_activation_budget",
714 8 * 1024,
715 "What all index peeks together may spend in one timely worker activation, in consumed cursor positions.",
716 ParameterScope::Replica,
717);
718
719/// How often an offloaded scan checks for cancellation and re-reads its configuration, in
720/// consumed cursor positions.
721///
722/// At a plausible 100ns to 1us per position this bounds cancellation latency to single-digit
723/// milliseconds. Larger than [`INDEX_PEEK_INLINE_BUDGET`] because an offloaded scan is off the
724/// worker's critical path, so its slices answer to cancellation latency, not to the worker's
725/// availability. A check is a few loads and no hand-off, so a finer granularity costs little.
726///
727/// An upper bound, not a period: a walk bound for the peek stash suspends once its accumulation
728/// crosses `peek_response_stash_threshold_bytes`, by far the smaller trigger at that threshold's
729/// default, and unspent fuel is not carried over.
730pub const INDEX_PEEK_YIELD_GRANULARITY: Config<usize> = Config::new(
731 "compute_index_peek_yield_granularity",
732 10000,
733 "How often an offloaded index peek scan checks for cancellation, in consumed cursor positions.",
734 ParameterScope::Replica,
735);
736
737/// How many offloaded index peek scans may run at once, as a fraction of the timely workers a
738/// compute runtime runs.
739///
740/// A fraction so the bound scales with the replica instead of being retuned per size. `1.0` admits
741/// one scan per worker, `0.5` one per two workers, and the bound is never below one scan. One per
742/// worker is the default because a peek walking on its worker occupies one core, so peek CPU is
743/// already capped at one core per worker today.
744///
745/// Per compute runtime, not global: a process running a maintenance and an interactive runtime
746/// admits it once per runtime.
747///
748/// This bounds running scans only. Scans that do not fit queue, costing a queue entry holding a
749/// suspended scan instead of a thread. Queue depth is a signal to alert on, not a second bound,
750/// since capping it would mean failing peeks.
751pub const INDEX_PEEK_PERMIT_FRACTION: Config<f64> = Config::new(
752 "compute_index_peek_permit_fraction",
753 1.0,
754 "How many offloaded index peek scans may run at once in one compute runtime, as a fraction of \
755 the timely workers it runs. Never below one scan.",
756 ParameterScope::Replica,
757);
758
759/// The collection interval for the Prometheus metrics introspection source.
760///
761/// Set to zero to disable scraping and retract any existing data.
762pub const COMPUTE_PROMETHEUS_INTROSPECTION_SCRAPE_INTERVAL: Config<Duration> = Config::new(
763 "compute_prometheus_introspection_scrape_interval",
764 Duration::from_secs(10),
765 "The collection interval for the Prometheus metrics introspection source. Set to zero to disable.",
766 ParameterScope::Replica,
767);
768
769/// If set, skip fetching or processing the snapshot data for subscribes when possible.
770///
771/// Read twice. At plan time in `environmentd` it gates whether snapshot elision runs at all, and
772/// at render time on the replica it gates whether an elided snapshot is honored. The replica-side
773/// read only ever puts a snapshot back, never takes one away, so the two reads disagreeing costs
774/// work rather than correctness.
775///
776/// Environment-scoped because the plan-time read has no replica in scope. Making it
777/// cluster-coherent instead would need plan-time resolution of cluster overrides for
778/// `OptimizerConfig` fields that are not `OptimizerFeatures`, which is the only place cluster
779/// overrides are resolved today.
780pub const SUBSCRIBE_SNAPSHOT_OPTIMIZATION: Config<bool> = Config::new(
781 "compute_subscribe_snapshot_optimization",
782 true,
783 "If set, skip fetching or processing the snapshot data for subscribes when possible.",
784 ParameterScope::Environment,
785);
786
787/// Temporary flag to de-risk the rollout of a release-blocker fix.
788///
789/// TODO: Remove after one, or a couple, releases.
790pub const MV_SINK_ADVANCE_PERSIST_FRONTIERS: Config<bool> = Config::new(
791 "compute_mv_sink_advance_persist_frontiers",
792 true,
793 "Whether the MV sink's write operator advances its internal persist frontiers to the as_of.",
794 ParameterScope::Environment,
795);
796
797/// Adds the full set of all compute `Config`s.
798pub fn all_dyncfgs(configs: ConfigSet) -> ConfigSet {
799 configs
800 .add(&ENABLE_HALF_JOIN2)
801 .add(&ENABLE_ERROR_DISTINCT)
802 .add(&ENABLE_MZ_JOIN_CORE)
803 .add(&ENABLE_SYNC_MV_SINK)
804 .add(&ENABLE_CORRECTION_V2)
805 .add(&CORRECTION_V2_CHAIN_PROPORTIONALITY)
806 .add(&CORRECTION_V2_CHUNK_SIZE)
807 .add(&ENABLE_COMPUTE_TEMPORAL_BUCKETING)
808 .add(&TEMPORAL_BUCKETING_SUMMARY)
809 .add(&LINEAR_JOIN_YIELDING)
810 .add(&ENABLE_LGALLOC)
811 .add(&LGALLOC_BACKGROUND_INTERVAL)
812 .add(&LGALLOC_FILE_GROWTH_DAMPENER)
813 .add(&LGALLOC_LOCAL_BUFFER_BYTES)
814 .add(&LGALLOC_SLOW_CLEAR_BYTES)
815 .add(&MEMORY_LIMITER_INTERVAL)
816 .add(&MEMORY_LIMITER_USAGE_BIAS)
817 .add(&MEMORY_LIMITER_BURST_FACTOR)
818 .add(&ENABLE_LGALLOC_EAGER_RECLAMATION)
819 .add(&ENABLE_COLUMNATION_LGALLOC)
820 .add(&COMPUTE_SERVER_MAINTENANCE_INTERVAL)
821 .add(&DATAFLOW_MAX_INFLIGHT_BYTES)
822 .add(&DATAFLOW_MAX_INFLIGHT_BYTES_CC)
823 .add(&HYDRATION_CONCURRENCY)
824 .add(©_TO_S3_PARQUET_ROW_GROUP_FILE_RATIO)
825 .add(©_TO_S3_ARROW_BUILDER_BUFFER_RATIO)
826 .add(©_TO_S3_MULTIPART_PART_SIZE_BYTES)
827 .add(&ENABLE_COMPUTE_REPLICA_EXPIRATION)
828 .add(&COMPUTE_REPLICA_EXPIRATION_OFFSET)
829 .add(&COMPUTE_APPLY_COLUMN_DEMANDS)
830 .add(&COMPUTE_FLAT_MAP_FUEL)
831 .add(&CONSOLIDATING_VEC_GROWTH_DAMPENER)
832 .add(&ENABLE_COMPUTE_LOGICAL_BACKPRESSURE)
833 .add(&COMPUTE_LOGICAL_BACKPRESSURE_MAX_RETAINED_CAPABILITIES)
834 .add(&COMPUTE_LOGICAL_BACKPRESSURE_INFLIGHT_SLACK)
835 .add(&ENABLE_ARRANGEMENT_DICTIONARY_COMPRESSION_ALPHA)
836 .add(&ENABLE_PEEK_RESPONSE_STASH)
837 .add(&PEEK_RESPONSE_STASH_THRESHOLD_BYTES)
838 .add(&PEEK_RESPONSE_STASH_BATCH_BYTES)
839 .add(&PEEK_RESPONSE_STASH_BATCH_MAX_RUNS)
840 .add(&PEEK_RESPONSE_STASH_READ_BATCH_SIZE_BYTES)
841 .add(&PEEK_RESPONSE_STASH_READ_MEMORY_BUDGET_BYTES)
842 .add(&ENABLE_PEEK_ROW_ITERATION_LIMIT)
843 .add(&PEEK_ROW_ITERATION_LIMIT)
844 .add(&ENABLE_INDEX_PEEK_OFFLOAD)
845 .add(&INDEX_PEEK_INLINE_BUDGET)
846 .add(&INDEX_PEEK_ACTIVATION_BUDGET)
847 .add(&INDEX_PEEK_YIELD_GRANULARITY)
848 .add(&INDEX_PEEK_PERMIT_FRACTION)
849 .add(&COMPUTE_PROMETHEUS_INTROSPECTION_SCRAPE_INTERVAL)
850 .add(&SUBSCRIBE_SNAPSHOT_OPTIMIZATION)
851 .add(&MV_SINK_ADVANCE_PERSIST_FRONTIERS)
852 .add(&ENABLE_COLUMN_PAGED_BATCHER)
853 .add(&ENABLE_COLUMNAR_MERGE_BATCHER)
854 .add(&ENABLE_COLUMNAR_ACCUMULABLE_DIFF)
855 .add(&ENABLE_COLUMN_PAGED_BATCHER_SPILL)
856 .add(&COLUMN_PAGED_BATCHER_BUDGET_FRACTION)
857 .add(&COLUMN_PAGED_BATCHER_LZ4)
858 .add(&COLUMN_PAGED_BATCHER_SWAP_PAGEOUT)
859 .add(&COLUMN_PAGED_BATCHER_SPILL_WORKER_COUNT)
860 .add(&COLUMN_PAGED_BATCHER_EAGER_BACKING)
861 .add(&COLUMN_PAGED_BATCHER_POOL_RSS_TARGET_FRACTION)
862 .add(&COLUMN_CHUNK_COMPRESS_MIN_DEPTH)
863}