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