1#![allow(missing_docs)]
11
12use std::sync::Arc;
22use std::sync::atomic::AtomicBool;
23use std::time::{Duration, SystemTime, UNIX_EPOCH};
24
25use mz_build_info::BuildInfo;
26use mz_dyncfg::{Config, ConfigDefault, ConfigSet, ConfigUpdates, ParameterScope};
27use mz_ore::instrument;
28use mz_ore::now::NowFn;
29use mz_persist::cfg::BlobKnobs;
30use mz_persist::retry::Retry;
31use mz_persist::s3::{BLOB_HTTP_POOL_MAX_IN_FLIGHT_PER_CLIENT, BlobHttpPool};
32use mz_postgres_client::PostgresClientKnobs;
33use proptest_derive::Arbitrary;
34use semver::Version;
35use serde::{Deserialize, Serialize};
36use tokio::sync::watch;
37
38use crate::async_runtime;
39use crate::internal::machine::{
40 NEXT_LISTEN_BATCH_RETRYER_CLAMP, NEXT_LISTEN_BATCH_RETRYER_INITIAL_BACKOFF,
41 NEXT_LISTEN_BATCH_RETRYER_MULTIPLIER,
42};
43use crate::internal::state::ROLLUP_THRESHOLD;
44use crate::operators::STORAGE_SOURCE_DECODE_FUEL;
45use crate::read::READER_LEASE_DURATION;
46
47const SELF_MANAGED_VERSIONS: &[Version; 2] = &[
49 Version::new(0, 130, 0),
51 Version::new(0, 147, 0),
53];
54
55#[derive(Debug, Clone)]
107pub struct PersistConfig {
108 pub build_version: Version,
110 pub hostname: String,
113 pub is_cc_active: bool,
115 pub announce_memory_limit: Option<usize>,
117 pub now: NowFn,
119 pub configs: Arc<ConfigSet>,
124 configs_synced_once: Arc<watch::Sender<bool>>,
127 pub compaction_enabled: bool,
129 pub compaction_process_requests: Arc<AtomicBool>,
131 pub compaction_concurrency_limit: usize,
134 pub compaction_queue_size: usize,
137 pub compaction_yield_after_n_updates: usize,
140 pub writer_lease_duration: Duration,
143 pub critical_downgrade_interval: Duration,
145 pub isolated_runtime_worker_threads: usize,
148}
149
150impl std::ops::Deref for PersistConfig {
152 type Target = ConfigSet;
153 fn deref(&self) -> &Self::Target {
154 &self.configs
155 }
156}
157
158impl PersistConfig {
159 pub fn new_default_configs(build_info: &BuildInfo, now: NowFn) -> Self {
162 Self::new(build_info, now, all_dyncfgs(ConfigSet::default()))
163 }
164
165 pub fn new(build_info: &BuildInfo, now: NowFn, configs: ConfigSet) -> Self {
168 let compaction_disabled = mz_ore::env::is_var_truthy("MZ_PERSIST_COMPACTION_DISABLED");
170
171 let (configs_synced_once, _) = watch::channel(false);
173
174 Self {
175 build_version: build_info.semver_version(),
176 is_cc_active: false,
177 announce_memory_limit: None,
178 now,
179 configs: Arc::new(configs),
180 configs_synced_once: Arc::new(configs_synced_once),
181 compaction_enabled: !compaction_disabled,
182 compaction_process_requests: Arc::new(AtomicBool::new(true)),
183 compaction_concurrency_limit: 5,
184 compaction_queue_size: 20,
185 compaction_yield_after_n_updates: 100_000,
186 writer_lease_duration: 60 * Duration::from_secs(60),
187 critical_downgrade_interval: Duration::from_secs(30),
188 isolated_runtime_worker_threads: num_cpus::get(),
189 hostname: {
194 use std::fmt::Write;
195 let mut name = std::env::var("HOSTNAME").unwrap_or_else(|_| "unknown".to_owned());
196 write!(&mut name, " {}", build_info.version)
197 .expect("writing to string should not fail");
198 name
199 },
200 }
201 }
202
203 pub(crate) fn set_config<T: ConfigDefault>(&self, cfg: &Config<T>, val: T) {
204 let mut updates = ConfigUpdates::default();
205 updates.add(cfg, val);
206 updates.apply(self)
207 }
208
209 pub fn apply_from(&self, updates: &ConfigUpdates) {
215 updates.apply(&self.configs);
216 self.configs_synced_once.send_replace(true);
217 }
218
219 #[instrument(level = "info")]
228 pub async fn configs_synced_once(&self) {
229 self.configs_synced_once
230 .subscribe()
231 .wait_for(|synced| *synced)
232 .await
233 .expect("we have a borrow on sender so it cannot drop");
234 }
235
236 pub fn storage_source_decode_fuel(&self) -> usize {
239 STORAGE_SOURCE_DECODE_FUEL.get(self)
240 }
241
242 pub fn set_reader_lease_duration(&self, val: Duration) {
244 self.set_config(&READER_LEASE_DURATION, val);
245 }
246
247 pub fn set_rollup_threshold(&self, val: usize) {
249 self.set_config(&ROLLUP_THRESHOLD, val);
250 }
251
252 pub fn set_next_listen_batch_retryer(&self, val: RetryParameters) {
255 self.set_config(
256 &NEXT_LISTEN_BATCH_RETRYER_INITIAL_BACKOFF,
257 val.initial_backoff,
258 );
259 self.set_config(&NEXT_LISTEN_BATCH_RETRYER_MULTIPLIER, val.multiplier);
260 self.set_config(&NEXT_LISTEN_BATCH_RETRYER_CLAMP, val.clamp);
261 }
262
263 pub fn disable_compaction(&self) {
264 tracing::info!("Disabling Persist Compaction");
265 self.compaction_process_requests
266 .store(false, std::sync::atomic::Ordering::Relaxed);
267 }
268
269 pub fn enable_compaction(&self) {
270 tracing::info!("Enabling Persist Compaction");
271 self.compaction_process_requests
272 .store(true, std::sync::atomic::Ordering::Relaxed);
273 }
274
275 pub fn new_for_tests() -> Self {
277 use mz_build_info::DUMMY_BUILD_INFO;
278 use mz_ore::now::SYSTEM_TIME;
279
280 let mut cfg = Self::new_default_configs(&DUMMY_BUILD_INFO, SYSTEM_TIME.clone());
281 cfg.hostname = "tests".into();
282 cfg.isolated_runtime_worker_threads = async_runtime::TEST_THREADS;
283 cfg
284 }
285}
286
287#[allow(non_upper_case_globals)]
288pub(crate) const MiB: usize = 1024 * 1024;
289
290pub fn all_dyncfgs(configs: ConfigSet) -> ConfigSet {
305 mz_persist::cfg::all_dyn_configs(configs)
306 .add(&crate::batch::BATCH_DELETE_ENABLED)
307 .add(&crate::batch::BLOB_TARGET_SIZE)
308 .add(&crate::batch::INLINE_WRITES_TOTAL_MAX_BYTES)
309 .add(&crate::batch::INLINE_WRITES_SINGLE_MAX_BYTES)
310 .add(&crate::batch::ENCODING_ENABLE_DICTIONARY)
311 .add(&crate::batch::ENCODING_COMPRESSION_FORMAT)
312 .add(&crate::batch::STRUCTURED_KEY_LOWER_LEN)
313 .add(&crate::batch::MAX_RUN_LEN)
314 .add(&crate::batch::MAX_RUNS)
315 .add(&BLOB_OPERATION_TIMEOUT)
316 .add(&BLOB_OPERATION_ATTEMPT_TIMEOUT)
317 .add(&BLOB_CONNECT_TIMEOUT)
318 .add(&BLOB_READ_TIMEOUT)
319 .add(&SHARD_METRICS)
320 .add(&PER_SHARD_METRICS_ENABLE_REGEX)
321 .add(&SHARD_INFO_METRIC)
322 .add(&crate::cfg::CONSENSUS_CONNECTION_POOL_MAX_SIZE)
323 .add(&crate::cfg::CONSENSUS_CONNECTION_POOL_MAX_WAIT)
324 .add(&crate::cfg::CONSENSUS_CONNECTION_POOL_TTL_STAGGER)
325 .add(&crate::cfg::CONSENSUS_CONNECTION_POOL_TTL)
326 .add(&crate::cfg::CRDB_CONNECT_TIMEOUT)
327 .add(&crate::cfg::CRDB_TCP_USER_TIMEOUT)
328 .add(&crate::cfg::CRDB_KEEPALIVES_IDLE)
329 .add(&crate::cfg::CRDB_KEEPALIVES_INTERVAL)
330 .add(&crate::cfg::CRDB_KEEPALIVES_RETRIES)
331 .add(&crate::cfg::USE_CRITICAL_SINCE_TXN)
332 .add(&crate::cfg::USE_CRITICAL_SINCE_CATALOG)
333 .add(&crate::cfg::USE_CRITICAL_SINCE_SOURCE)
334 .add(&crate::cfg::USE_CRITICAL_SINCE_SNAPSHOT)
335 .add(&crate::cfg::SOURCE_HYDRATION_FRONTIER_COALESCE_BYTES)
336 .add(&crate::cfg::SOURCE_FETCH_CONCURRENCY)
337 .add(&BATCH_BUILDER_MAX_OUTSTANDING_PARTS)
338 .add(&COMPACTION_HEURISTIC_MIN_INPUTS)
339 .add(&COMPACTION_HEURISTIC_MIN_PARTS)
340 .add(&COMPACTION_HEURISTIC_MIN_UPDATES)
341 .add(&COMPACTION_MEMORY_BOUND_BYTES)
342 .add(&GC_BLOB_DELETE_CONCURRENCY_LIMIT)
343 .add(&GC_REMOVE_FINALIZED_ROLLUPS)
344 .add(&STATE_VERSIONS_RECENT_LIVE_DIFFS_LIMIT)
345 .add(&USAGE_STATE_FETCH_CONCURRENCY_LIMIT)
346 .add(&crate::cache::STATE_UPDATE_LEASE_TIMEOUT)
347 .add(&crate::cli::admin::CATALOG_FORCE_COMPACTION_FUEL)
348 .add(&crate::cli::admin::CATALOG_FORCE_COMPACTION_WAIT)
349 .add(&crate::cli::admin::EXPRESSION_CACHE_FORCE_COMPACTION_FUEL)
350 .add(&crate::cli::admin::EXPRESSION_CACHE_FORCE_COMPACTION_WAIT)
351 .add(&crate::fetch::FETCH_SEMAPHORE_COST_ADJUSTMENT)
352 .add(&crate::fetch::FETCH_SEMAPHORE_PERMIT_ADJUSTMENT)
353 .add(&crate::fetch::VALIDATE_PART_BOUNDS_ON_READ)
354 .add(&crate::fetch::OPTIMIZE_IGNORED_DATA_FETCH)
355 .add(&crate::internal::cache::BLOB_CACHE_MEM_LIMIT_BYTES)
356 .add(&crate::internal::cache::BLOB_CACHE_SCALE_WITH_THREADS)
357 .add(&crate::internal::cache::BLOB_CACHE_SCALE_FACTOR_BYTES)
358 .add(&crate::internal::compact::COMPACTION_MINIMUM_TIMEOUT)
359 .add(&crate::internal::compact::COMPACTION_CHECK_PROCESS_FLAG)
360 .add(&crate::internal::machine::CLAIM_UNCLAIMED_COMPACTIONS)
361 .add(&crate::internal::machine::CLAIM_COMPACTION_PERCENT)
362 .add(&crate::internal::machine::CLAIM_COMPACTION_MIN_VERSION)
363 .add(&crate::internal::machine::NEXT_LISTEN_BATCH_RETRYER_CLAMP)
364 .add(&crate::internal::machine::NEXT_LISTEN_BATCH_RETRYER_FIXED_SLEEP)
365 .add(&crate::internal::machine::NEXT_LISTEN_BATCH_RETRYER_INITIAL_BACKOFF)
366 .add(&crate::internal::machine::NEXT_LISTEN_BATCH_RETRYER_MULTIPLIER)
367 .add(&crate::internal::state::ROLLUP_THRESHOLD)
368 .add(&crate::internal::state::ROLLUP_USE_ACTIVE_ROLLUP)
369 .add(&crate::internal::state::GC_FALLBACK_THRESHOLD_MS)
370 .add(&crate::internal::state::GC_USE_ACTIVE_GC)
371 .add(&crate::internal::state::GC_MIN_VERSIONS)
372 .add(&crate::internal::state::GC_MAX_VERSIONS)
373 .add(&crate::internal::state::ROLLUP_FALLBACK_THRESHOLD_MS)
374 .add(&crate::internal::state::ENABLE_INCREMENTAL_COMPACTION)
375 .add(&crate::operators::STORAGE_SOURCE_DECODE_FUEL)
376 .add(&crate::read::READER_LEASE_DURATION)
377 .add(&crate::rpc::PUBSUB_CLIENT_ENABLED)
378 .add(&crate::rpc::PUBSUB_PUSH_DIFF_ENABLED)
379 .add(&crate::rpc::PUBSUB_SAME_PROCESS_DELEGATE_ENABLED)
380 .add(&crate::rpc::PUBSUB_CONNECT_ATTEMPT_TIMEOUT)
381 .add(&crate::rpc::PUBSUB_REQUEST_TIMEOUT)
382 .add(&crate::rpc::PUBSUB_CONNECT_MAX_BACKOFF)
383 .add(&crate::rpc::PUBSUB_CLIENT_SENDER_CHANNEL_SIZE)
384 .add(&crate::rpc::PUBSUB_CLIENT_RECEIVER_CHANNEL_SIZE)
385 .add(&crate::rpc::PUBSUB_SERVER_CONNECTION_CHANNEL_SIZE)
386 .add(&crate::rpc::PUBSUB_STATE_CACHE_SHARD_REF_CHANNEL_SIZE)
387 .add(&crate::rpc::PUBSUB_RECONNECT_BACKOFF)
388 .add(&crate::stats::STATS_AUDIT_PERCENT)
389 .add(&crate::stats::STATS_AUDIT_PANIC)
390 .add(&crate::stats::STATS_BUDGET_BYTES)
391 .add(&crate::stats::STATS_COLLECTION_ENABLED)
392 .add(&crate::stats::STATS_FILTER_ENABLED)
393 .add(&crate::stats::STATS_UNTRIMMABLE_COLUMNS_EQUALS)
394 .add(&crate::stats::STATS_UNTRIMMABLE_COLUMNS_PREFIX)
395 .add(&crate::stats::STATS_UNTRIMMABLE_COLUMNS_SUFFIX)
396 .add(&crate::fetch::PART_DECODE_FORMAT)
397 .add(&crate::fetch::PART_DECODE_BATCH_ROWS)
398 .add(&crate::write::COMBINE_INLINE_WRITES)
399 .add(&crate::write::VALIDATE_PART_BOUNDS_ON_WRITE)
400}
401
402impl PersistConfig {
403 pub(crate) const DEFAULT_FALLBACK_ROLLUP_THRESHOLD_MULTIPLIER: usize = 3;
404
405 pub fn set_state_versions_recent_live_diffs_limit(&self, val: usize) {
406 self.set_config(&STATE_VERSIONS_RECENT_LIVE_DIFFS_LIMIT, val);
407 }
408}
409
410pub const CONSENSUS_CONNECTION_POOL_MAX_SIZE: Config<usize> = Config::new(
415 "persist_consensus_connection_pool_max_size",
416 50,
417 "The maximum size the connection pool to Postgres/CRDB will grow to.",
418 ParameterScope::Environment,
419);
420
421const CONSENSUS_CONNECTION_POOL_MAX_WAIT: Config<Duration> = Config::new(
426 "persist_consensus_connection_pool_max_wait",
427 Duration::from_secs(60),
428 "The amount of time we'll wait for a connection to become available.",
429 ParameterScope::Environment,
430);
431
432const CONSENSUS_CONNECTION_POOL_TTL: Config<Duration> = Config::new(
436 "persist_consensus_connection_pool_ttl",
437 Duration::from_secs(300),
438 "\
439 The minimum TTL of a Consensus connection to Postgres/CRDB before it is \
440 proactively terminated",
441 ParameterScope::Environment,
442);
443
444const CONSENSUS_CONNECTION_POOL_TTL_STAGGER: Config<Duration> = Config::new(
452 "persist_consensus_connection_pool_ttl_stagger",
453 Duration::from_secs(6),
454 "The minimum time between TTLing Consensus connections to Postgres/CRDB.",
455 ParameterScope::Environment,
456);
457
458pub const CRDB_CONNECT_TIMEOUT: Config<Duration> = Config::new(
461 "crdb_connect_timeout",
462 Duration::from_secs(5),
463 "The time to connect to CockroachDB before timing out and retrying.",
464 ParameterScope::Environment,
465);
466
467pub const CRDB_TCP_USER_TIMEOUT: Config<Duration> = Config::new(
471 "crdb_tcp_user_timeout",
472 Duration::from_secs(30),
473 "\
474 The TCP timeout for connections to CockroachDB. Specifies the amount of \
475 time that transmitted data may remain unacknowledged before the TCP \
476 connection is forcibly closed.",
477 ParameterScope::Environment,
478);
479
480pub const CRDB_KEEPALIVES_IDLE: Config<Duration> = Config::new(
481 "crdb_keepalives_idle",
482 Duration::from_secs(10),
483 "\
484 The amount of idle time before a TCP keepalive packet is sent on CRDB \
485 connections.",
486 ParameterScope::Environment,
487);
488
489pub const CRDB_KEEPALIVES_INTERVAL: Config<Duration> = Config::new(
490 "crdb_keepalives_interval",
491 Duration::from_secs(5),
492 "The time interval between TCP keepalive probes on CRDB connections.",
493 ParameterScope::Environment,
494);
495
496pub const CRDB_KEEPALIVES_RETRIES: Config<u32> = Config::new(
497 "crdb_keepalives_retries",
498 5,
499 "\
500 The maximum number of TCP keepalive probes that will be sent before \
501 dropping a CRDB connection.",
502 ParameterScope::Environment,
503);
504
505pub const USE_CRITICAL_SINCE_TXN: Config<bool> = Config::new(
507 "persist_use_critical_since_txn",
508 true,
509 "Use the critical since (instead of the overall since) when initializing a subscribe.",
510 ParameterScope::Environment,
511);
512
513pub const USE_CRITICAL_SINCE_CATALOG: Config<bool> = Config::new(
515 "persist_use_critical_since_catalog",
516 false,
517 "Use the critical since (instead of the overall since) for the Persist-backed catalog.",
518 ParameterScope::Environment,
519);
520
521pub const USE_CRITICAL_SINCE_SOURCE: Config<bool> = Config::new(
523 "persist_use_critical_since_source",
524 false,
525 "Use the critical since (instead of the overall since) in the Persist source.",
526 ParameterScope::Environment,
527);
528
529pub const SOURCE_HYDRATION_FRONTIER_COALESCE_BYTES: Config<usize> = Config::new(
534 "persist_source_hydration_frontier_coalesce_bytes",
535 0,
536 "While catching up to the hydration-time upper, the persist source coalesces \
537 frontier downgrades until this many encoded bytes have been emitted (0 disables).",
538 ParameterScope::Environment,
539);
540
541pub const SOURCE_FETCH_CONCURRENCY: Config<usize> = Config::new(
546 "persist_source_fetch_concurrency",
547 1,
548 "Maximum number of part fetches the persist source issues concurrently per worker \
549 (1 = serial).",
550 ParameterScope::Environment,
551);
552
553pub const USE_CRITICAL_SINCE_SNAPSHOT: Config<bool> = Config::new(
555 "persist_use_critical_since_snapshot",
556 false,
557 "Use the critical since (instead of the overall since) when taking snapshots in the controller or in fast-path peeks.",
558 ParameterScope::Environment,
559);
560
561pub const BATCH_BUILDER_MAX_OUTSTANDING_PARTS: Config<usize> = Config::new(
565 "persist_batch_builder_max_outstanding_parts",
566 2,
567 "The number of writes a batch builder can have outstanding before we slow down the writer.",
568 ParameterScope::Environment,
569);
570
571pub const COMPACTION_HEURISTIC_MIN_INPUTS: Config<usize> = Config::new(
575 "persist_compaction_heuristic_min_inputs",
576 8,
577 "Don't skip compaction if we have more than this many hollow batches as input.",
578 ParameterScope::Environment,
579);
580
581pub const COMPACTION_HEURISTIC_MIN_PARTS: Config<usize> = Config::new(
585 "persist_compaction_heuristic_min_parts",
586 8,
587 "Don't skip compaction if we have more than this many parts as input.",
588 ParameterScope::Environment,
589);
590
591pub const COMPACTION_HEURISTIC_MIN_UPDATES: Config<usize> = Config::new(
595 "persist_compaction_heuristic_min_updates",
596 1024,
597 "Don't skip compaction if we have more than this many updates as input.",
598 ParameterScope::Environment,
599);
600
601pub const COMPACTION_MEMORY_BOUND_BYTES: Config<usize> = Config::new(
606 "persist_compaction_memory_bound_bytes",
607 1024 * MiB,
608 "Attempt to limit compaction to this amount of memory.",
609 ParameterScope::Environment,
610);
611
612pub const GC_BLOB_DELETE_CONCURRENCY_LIMIT: Config<usize> = Config::new(
614 "persist_gc_blob_delete_concurrency_limit",
615 32,
616 "Limit the number of concurrent deletes GC can perform to this threshold.",
617 ParameterScope::Environment,
618);
619
620pub const GC_REMOVE_FINALIZED_ROLLUPS: Config<bool> = Config::new(
626 "persist_gc_remove_finalized_rollups",
627 false,
628 "Whether GC removes a finalized shard's old rollups from state and blob.",
629 ParameterScope::Environment,
630);
631
632pub const STATE_VERSIONS_RECENT_LIVE_DIFFS_LIMIT: Config<usize> = Config::new(
642 "persist_state_versions_recent_live_diffs_limit",
643 30 * 128,
644 "Fetch this many diffs when fetching recent diffs.",
645 ParameterScope::Environment,
646);
647
648pub const USAGE_STATE_FETCH_CONCURRENCY_LIMIT: Config<usize> = Config::new(
650 "persist_usage_state_fetch_concurrency_limit",
651 8,
652 "Limit the concurrency in of fetching in the perioding Persist-storage-usage calculation.",
653 ParameterScope::Environment,
654);
655
656impl PostgresClientKnobs for PersistConfig {
657 fn connection_pool_max_size(&self) -> usize {
658 CONSENSUS_CONNECTION_POOL_MAX_SIZE.get(self)
659 }
660
661 fn connection_pool_max_wait(&self) -> Option<Duration> {
662 Some(CONSENSUS_CONNECTION_POOL_MAX_WAIT.get(self))
663 }
664
665 fn connection_pool_ttl(&self) -> Duration {
666 CONSENSUS_CONNECTION_POOL_TTL.get(self)
667 }
668
669 fn connection_pool_ttl_stagger(&self) -> Duration {
670 CONSENSUS_CONNECTION_POOL_TTL_STAGGER.get(self)
671 }
672
673 fn connect_timeout(&self) -> Duration {
674 CRDB_CONNECT_TIMEOUT.get(self)
675 }
676
677 fn tcp_user_timeout(&self) -> Duration {
678 CRDB_TCP_USER_TIMEOUT.get(self)
679 }
680
681 fn keepalives_idle(&self) -> Duration {
682 CRDB_KEEPALIVES_IDLE.get(self)
683 }
684
685 fn keepalives_interval(&self) -> Duration {
686 CRDB_KEEPALIVES_INTERVAL.get(self)
687 }
688
689 fn keepalives_retries(&self) -> u32 {
690 CRDB_KEEPALIVES_RETRIES.get(self)
691 }
692
693 fn statement_timeout(&self) -> Duration {
694 Duration::ZERO
696 }
697}
698
699#[derive(Copy, Clone, Debug, Eq, PartialEq, Arbitrary, Serialize, Deserialize)]
700pub struct RetryParameters {
701 pub fixed_sleep: Duration,
702 pub initial_backoff: Duration,
703 pub multiplier: u32,
704 pub clamp: Duration,
705}
706
707impl RetryParameters {
708 pub fn persist_defaults() -> Self {
709 Self {
710 fixed_sleep: Duration::ZERO,
711 initial_backoff: Duration::from_millis(4),
714 multiplier: 2,
715 clamp: Duration::from_secs(16),
718 }
719 }
720
721 pub(crate) fn into_retry(self, now: SystemTime) -> Retry {
722 let seed = now
723 .duration_since(UNIX_EPOCH)
724 .map_or(0, |x| u64::from(x.subsec_nanos()));
725 Retry {
726 fixed_sleep: self.fixed_sleep,
727 initial_backoff: self.initial_backoff,
728 multiplier: self.multiplier,
729 clamp_backoff: self.clamp,
730 seed,
731 }
732 }
733}
734
735pub(crate) const BLOB_OPERATION_TIMEOUT: Config<Duration> = Config::new(
736 "persist_blob_operation_timeout",
737 Duration::from_secs(180),
738 "Maximum time allowed for a network call, including retry attempts.",
739 ParameterScope::Environment,
740);
741
742pub(crate) const BLOB_OPERATION_ATTEMPT_TIMEOUT: Config<Duration> = Config::new(
743 "persist_blob_operation_attempt_timeout",
744 Duration::from_secs(90),
745 "Maximum time allowed for a single network call.",
746 ParameterScope::Environment,
747);
748
749pub(crate) const BLOB_CONNECT_TIMEOUT: Config<Duration> = Config::new(
750 "persist_blob_connect_timeout",
751 Duration::from_secs(7),
752 "Maximum time to wait for a socket connection to be made.",
753 ParameterScope::Environment,
754);
755
756pub(crate) const BLOB_READ_TIMEOUT: Config<Duration> = Config::new(
757 "persist_blob_read_timeout",
758 Duration::from_secs(10),
759 "Maximum time to wait to read the first byte of a response, including connection time.",
760 ParameterScope::Environment,
761);
762
763pub const SHARD_METRICS: Config<&'static str> = Config::new(
770 "persist_shard_metrics",
771 ShardMetricsExport::default().as_str(),
772 "Which persist shard metrics to export: 'per_shard' (the `mz_persist_shard_*` families \
773 with one series per shard), 'summary' (their bounded `_percentile`, `_sum`, `_topk` and \
774 per-version aggregates), 'both', or 'none'. Shard counts and process-level counters are \
775 always exported.",
776 ParameterScope::Environment,
777);
778
779#[derive(Debug, Clone, Copy, PartialEq, Eq)]
781pub enum ShardMetricsExport {
782 None,
783 Summary,
784 PerShard,
785 Both,
786}
787
788impl ShardMetricsExport {
789 pub const ALL: [Self; 4] = [Self::None, Self::Summary, Self::PerShard, Self::Both];
790
791 pub const fn default() -> Self {
794 Self::Summary
795 }
796
797 pub const fn as_str(self) -> &'static str {
798 match self {
799 Self::None => "none",
800 Self::Summary => "summary",
801 Self::PerShard => "per_shard",
802 Self::Both => "both",
803 }
804 }
805
806 pub fn parse(s: &str) -> Option<Self> {
808 Self::ALL.into_iter().find(|v| v.as_str() == s)
809 }
810
811 pub fn from_str_lossy(s: &str) -> Self {
817 Self::parse(s).unwrap_or_else(Self::default)
818 }
819
820 pub fn get(configs: &ConfigSet) -> Self {
821 Self::from_str_lossy(&SHARD_METRICS.get(configs))
822 }
823
824 pub fn per_shard(self) -> bool {
826 matches!(self, Self::PerShard | Self::Both)
827 }
828
829 pub fn summary(self) -> bool {
831 matches!(self, Self::Summary | Self::Both)
832 }
833}
834
835pub const PER_SHARD_METRICS_ENABLE_REGEX: Config<&'static str> = Config::new(
842 "persist_per_shard_metrics_enable_regex",
843 "",
844 "While `persist_shard_metrics` is 'summary' or 'none', keep exporting the per-shard series \
845 whose `shard` or `name` label matches this regex (unanchored). Empty or invalid keeps \
846 none.",
847 ParameterScope::Environment,
848);
849
850pub const SHARD_INFO_METRIC: Config<bool> = Config::new(
854 "persist_shard_info_metric",
855 false,
856 "Whether to export `mz_persist_shard_info`, which lists this process's shards by `shard` \
857 and `name` with one series each.",
858 ParameterScope::Environment,
859);
860
861impl BlobKnobs for PersistConfig {
862 fn operation_timeout(&self) -> Duration {
863 BLOB_OPERATION_TIMEOUT.get(self)
864 }
865
866 fn operation_attempt_timeout(&self) -> Duration {
867 BLOB_OPERATION_ATTEMPT_TIMEOUT.get(self)
868 }
869
870 fn connect_timeout(&self) -> Duration {
871 BLOB_CONNECT_TIMEOUT.get(self)
872 }
873
874 fn read_timeout(&self) -> Duration {
875 BLOB_READ_TIMEOUT.get(self)
876 }
877
878 fn is_cc_active(&self) -> bool {
879 self.is_cc_active
880 }
881
882 fn http_pool(&self) -> BlobHttpPool {
883 BlobHttpPool::get(self)
884 }
885
886 fn http_pool_max_in_flight_per_client(&self) -> usize {
887 BLOB_HTTP_POOL_MAX_IN_FLIGHT_PER_CLIENT.get(self)
888 }
889}
890
891pub fn code_can_read_data(code_version: &Version, data_version: &Version) -> bool {
912 code_version.cmp_precedence(data_version).is_ge()
915}
916
917pub fn code_can_write_data(code_version: &Version, data_version: &Version) -> bool {
922 if !code_can_read_data(code_version, data_version) {
923 return false;
924 }
925
926 if code_version.major == 0 && code_version.minor <= SELF_MANAGED_VERSIONS[1].minor {
927 true
930 } else if code_version.major == 0 {
931 SELF_MANAGED_VERSIONS[0]
933 .cmp_precedence(data_version)
934 .is_le()
935 } else if code_version.major <= 26 {
936 SELF_MANAGED_VERSIONS[1]
938 .cmp_precedence(data_version)
939 .is_le()
940 } else {
941 code_version.major - 1 <= data_version.major
943 }
944}