Skip to main content

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(&COPY_TO_S3_PARQUET_ROW_GROUP_FILE_RATIO)
825        .add(&COPY_TO_S3_ARROW_BUILDER_BUFFER_RATIO)
826        .add(&COPY_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}