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