1#![allow(missing_docs)]
11
12use std::sync::Arc;
15use std::sync::atomic::AtomicBool;
16use std::time::{Duration, SystemTime, UNIX_EPOCH};
17
18use mz_build_info::BuildInfo;
19use mz_dyncfg::{Config, ConfigDefault, ConfigSet, ConfigUpdates};
20use mz_ore::instrument;
21use mz_ore::now::NowFn;
22use mz_persist::cfg::BlobKnobs;
23use mz_persist::retry::Retry;
24use mz_postgres_client::PostgresClientKnobs;
25use proptest_derive::Arbitrary;
26use semver::Version;
27use serde::{Deserialize, Serialize};
28use tokio::sync::watch;
29
30use crate::async_runtime;
31use crate::internal::machine::{
32 NEXT_LISTEN_BATCH_RETRYER_CLAMP, NEXT_LISTEN_BATCH_RETRYER_INITIAL_BACKOFF,
33 NEXT_LISTEN_BATCH_RETRYER_MULTIPLIER,
34};
35use crate::internal::state::ROLLUP_THRESHOLD;
36use crate::operators::STORAGE_SOURCE_DECODE_FUEL;
37use crate::read::READER_LEASE_DURATION;
38
39const SELF_MANAGED_VERSIONS: &[Version; 2] = &[
41 Version::new(0, 130, 0),
43 Version::new(0, 147, 0),
45];
46
47#[derive(Debug, Clone)]
99pub struct PersistConfig {
100 pub build_version: Version,
102 pub hostname: String,
105 pub is_cc_active: bool,
107 pub announce_memory_limit: Option<usize>,
109 pub now: NowFn,
111 pub configs: Arc<ConfigSet>,
116 configs_synced_once: Arc<watch::Sender<bool>>,
119 pub compaction_enabled: bool,
121 pub compaction_process_requests: Arc<AtomicBool>,
123 pub compaction_concurrency_limit: usize,
126 pub compaction_queue_size: usize,
129 pub compaction_yield_after_n_updates: usize,
132 pub writer_lease_duration: Duration,
135 pub critical_downgrade_interval: Duration,
137 pub isolated_runtime_worker_threads: usize,
140}
141
142impl std::ops::Deref for PersistConfig {
144 type Target = ConfigSet;
145 fn deref(&self) -> &Self::Target {
146 &self.configs
147 }
148}
149
150impl PersistConfig {
151 pub fn new_default_configs(build_info: &BuildInfo, now: NowFn) -> Self {
154 Self::new(build_info, now, all_dyncfgs(ConfigSet::default()))
155 }
156
157 pub fn new(build_info: &BuildInfo, now: NowFn, configs: ConfigSet) -> Self {
160 let compaction_disabled = mz_ore::env::is_var_truthy("MZ_PERSIST_COMPACTION_DISABLED");
162
163 let (configs_synced_once, _) = watch::channel(false);
165
166 Self {
167 build_version: build_info.semver_version(),
168 is_cc_active: false,
169 announce_memory_limit: None,
170 now,
171 configs: Arc::new(configs),
172 configs_synced_once: Arc::new(configs_synced_once),
173 compaction_enabled: !compaction_disabled,
174 compaction_process_requests: Arc::new(AtomicBool::new(true)),
175 compaction_concurrency_limit: 5,
176 compaction_queue_size: 20,
177 compaction_yield_after_n_updates: 100_000,
178 writer_lease_duration: 60 * Duration::from_secs(60),
179 critical_downgrade_interval: Duration::from_secs(30),
180 isolated_runtime_worker_threads: num_cpus::get(),
181 hostname: {
186 use std::fmt::Write;
187 let mut name = std::env::var("HOSTNAME").unwrap_or_else(|_| "unknown".to_owned());
188 write!(&mut name, " {}", build_info.version)
189 .expect("writing to string should not fail");
190 name
191 },
192 }
193 }
194
195 pub(crate) fn set_config<T: ConfigDefault>(&self, cfg: &Config<T>, val: T) {
196 let mut updates = ConfigUpdates::default();
197 updates.add(cfg, val);
198 updates.apply(self)
199 }
200
201 pub fn apply_from(&self, updates: &ConfigUpdates) {
207 updates.apply(&self.configs);
208 self.configs_synced_once.send_replace(true);
209 }
210
211 #[instrument(level = "info")]
220 pub async fn configs_synced_once(&self) {
221 self.configs_synced_once
222 .subscribe()
223 .wait_for(|synced| *synced)
224 .await
225 .expect("we have a borrow on sender so it cannot drop");
226 }
227
228 pub fn storage_source_decode_fuel(&self) -> usize {
231 STORAGE_SOURCE_DECODE_FUEL.get(self)
232 }
233
234 pub fn set_reader_lease_duration(&self, val: Duration) {
236 self.set_config(&READER_LEASE_DURATION, val);
237 }
238
239 pub fn set_rollup_threshold(&self, val: usize) {
241 self.set_config(&ROLLUP_THRESHOLD, val);
242 }
243
244 pub fn set_next_listen_batch_retryer(&self, val: RetryParameters) {
247 self.set_config(
248 &NEXT_LISTEN_BATCH_RETRYER_INITIAL_BACKOFF,
249 val.initial_backoff,
250 );
251 self.set_config(&NEXT_LISTEN_BATCH_RETRYER_MULTIPLIER, val.multiplier);
252 self.set_config(&NEXT_LISTEN_BATCH_RETRYER_CLAMP, val.clamp);
253 }
254
255 pub fn disable_compaction(&self) {
256 tracing::info!("Disabling Persist Compaction");
257 self.compaction_process_requests
258 .store(false, std::sync::atomic::Ordering::Relaxed);
259 }
260
261 pub fn enable_compaction(&self) {
262 tracing::info!("Enabling Persist Compaction");
263 self.compaction_process_requests
264 .store(true, std::sync::atomic::Ordering::Relaxed);
265 }
266
267 pub fn new_for_tests() -> Self {
269 use mz_build_info::DUMMY_BUILD_INFO;
270 use mz_ore::now::SYSTEM_TIME;
271
272 let mut cfg = Self::new_default_configs(&DUMMY_BUILD_INFO, SYSTEM_TIME.clone());
273 cfg.hostname = "tests".into();
274 cfg.isolated_runtime_worker_threads = async_runtime::TEST_THREADS;
275 cfg
276 }
277}
278
279#[allow(non_upper_case_globals)]
280pub(crate) const MiB: usize = 1024 * 1024;
281
282pub fn all_dyncfgs(configs: ConfigSet) -> ConfigSet {
288 mz_persist::cfg::all_dyn_configs(configs)
289 .add(&crate::batch::BATCH_DELETE_ENABLED)
290 .add(&crate::batch::BLOB_TARGET_SIZE)
291 .add(&crate::batch::INLINE_WRITES_TOTAL_MAX_BYTES)
292 .add(&crate::batch::INLINE_WRITES_SINGLE_MAX_BYTES)
293 .add(&crate::batch::ENCODING_ENABLE_DICTIONARY)
294 .add(&crate::batch::ENCODING_COMPRESSION_FORMAT)
295 .add(&crate::batch::STRUCTURED_KEY_LOWER_LEN)
296 .add(&crate::batch::MAX_RUN_LEN)
297 .add(&crate::batch::MAX_RUNS)
298 .add(&BLOB_OPERATION_TIMEOUT)
299 .add(&BLOB_OPERATION_ATTEMPT_TIMEOUT)
300 .add(&BLOB_CONNECT_TIMEOUT)
301 .add(&BLOB_READ_TIMEOUT)
302 .add(&crate::cfg::CONSENSUS_CONNECTION_POOL_MAX_SIZE)
303 .add(&crate::cfg::CONSENSUS_CONNECTION_POOL_MAX_WAIT)
304 .add(&crate::cfg::CONSENSUS_CONNECTION_POOL_TTL_STAGGER)
305 .add(&crate::cfg::CONSENSUS_CONNECTION_POOL_TTL)
306 .add(&crate::cfg::CRDB_CONNECT_TIMEOUT)
307 .add(&crate::cfg::CRDB_TCP_USER_TIMEOUT)
308 .add(&crate::cfg::CRDB_KEEPALIVES_IDLE)
309 .add(&crate::cfg::CRDB_KEEPALIVES_INTERVAL)
310 .add(&crate::cfg::CRDB_KEEPALIVES_RETRIES)
311 .add(&crate::cfg::USE_CRITICAL_SINCE_TXN)
312 .add(&crate::cfg::USE_CRITICAL_SINCE_CATALOG)
313 .add(&crate::cfg::USE_CRITICAL_SINCE_SOURCE)
314 .add(&crate::cfg::USE_CRITICAL_SINCE_SNAPSHOT)
315 .add(&crate::cfg::SOURCE_HYDRATION_FRONTIER_COALESCE_BYTES)
316 .add(&crate::cfg::SOURCE_FETCH_CONCURRENCY)
317 .add(&BATCH_BUILDER_MAX_OUTSTANDING_PARTS)
318 .add(&COMPACTION_HEURISTIC_MIN_INPUTS)
319 .add(&COMPACTION_HEURISTIC_MIN_PARTS)
320 .add(&COMPACTION_HEURISTIC_MIN_UPDATES)
321 .add(&COMPACTION_MEMORY_BOUND_BYTES)
322 .add(&GC_BLOB_DELETE_CONCURRENCY_LIMIT)
323 .add(&STATE_VERSIONS_RECENT_LIVE_DIFFS_LIMIT)
324 .add(&USAGE_STATE_FETCH_CONCURRENCY_LIMIT)
325 .add(&crate::cache::STATE_UPDATE_LEASE_TIMEOUT)
326 .add(&crate::cli::admin::CATALOG_FORCE_COMPACTION_FUEL)
327 .add(&crate::cli::admin::CATALOG_FORCE_COMPACTION_WAIT)
328 .add(&crate::cli::admin::EXPRESSION_CACHE_FORCE_COMPACTION_FUEL)
329 .add(&crate::cli::admin::EXPRESSION_CACHE_FORCE_COMPACTION_WAIT)
330 .add(&crate::fetch::FETCH_SEMAPHORE_COST_ADJUSTMENT)
331 .add(&crate::fetch::FETCH_SEMAPHORE_PERMIT_ADJUSTMENT)
332 .add(&crate::fetch::VALIDATE_PART_BOUNDS_ON_READ)
333 .add(&crate::fetch::OPTIMIZE_IGNORED_DATA_FETCH)
334 .add(&crate::internal::cache::BLOB_CACHE_MEM_LIMIT_BYTES)
335 .add(&crate::internal::cache::BLOB_CACHE_SCALE_WITH_THREADS)
336 .add(&crate::internal::cache::BLOB_CACHE_SCALE_FACTOR_BYTES)
337 .add(&crate::internal::compact::COMPACTION_MINIMUM_TIMEOUT)
338 .add(&crate::internal::compact::COMPACTION_CHECK_PROCESS_FLAG)
339 .add(&crate::internal::machine::CLAIM_UNCLAIMED_COMPACTIONS)
340 .add(&crate::internal::machine::CLAIM_COMPACTION_PERCENT)
341 .add(&crate::internal::machine::CLAIM_COMPACTION_MIN_VERSION)
342 .add(&crate::internal::machine::NEXT_LISTEN_BATCH_RETRYER_CLAMP)
343 .add(&crate::internal::machine::NEXT_LISTEN_BATCH_RETRYER_FIXED_SLEEP)
344 .add(&crate::internal::machine::NEXT_LISTEN_BATCH_RETRYER_INITIAL_BACKOFF)
345 .add(&crate::internal::machine::NEXT_LISTEN_BATCH_RETRYER_MULTIPLIER)
346 .add(&crate::internal::state::ROLLUP_THRESHOLD)
347 .add(&crate::internal::state::ROLLUP_USE_ACTIVE_ROLLUP)
348 .add(&crate::internal::state::GC_FALLBACK_THRESHOLD_MS)
349 .add(&crate::internal::state::GC_USE_ACTIVE_GC)
350 .add(&crate::internal::state::GC_MIN_VERSIONS)
351 .add(&crate::internal::state::GC_MAX_VERSIONS)
352 .add(&crate::internal::state::ROLLUP_FALLBACK_THRESHOLD_MS)
353 .add(&crate::internal::state::ENABLE_INCREMENTAL_COMPACTION)
354 .add(&crate::operators::STORAGE_SOURCE_DECODE_FUEL)
355 .add(&crate::read::READER_LEASE_DURATION)
356 .add(&crate::rpc::PUBSUB_CLIENT_ENABLED)
357 .add(&crate::rpc::PUBSUB_PUSH_DIFF_ENABLED)
358 .add(&crate::rpc::PUBSUB_SAME_PROCESS_DELEGATE_ENABLED)
359 .add(&crate::rpc::PUBSUB_CONNECT_ATTEMPT_TIMEOUT)
360 .add(&crate::rpc::PUBSUB_REQUEST_TIMEOUT)
361 .add(&crate::rpc::PUBSUB_CONNECT_MAX_BACKOFF)
362 .add(&crate::rpc::PUBSUB_CLIENT_SENDER_CHANNEL_SIZE)
363 .add(&crate::rpc::PUBSUB_CLIENT_RECEIVER_CHANNEL_SIZE)
364 .add(&crate::rpc::PUBSUB_SERVER_CONNECTION_CHANNEL_SIZE)
365 .add(&crate::rpc::PUBSUB_STATE_CACHE_SHARD_REF_CHANNEL_SIZE)
366 .add(&crate::rpc::PUBSUB_RECONNECT_BACKOFF)
367 .add(&crate::stats::STATS_AUDIT_PERCENT)
368 .add(&crate::stats::STATS_AUDIT_PANIC)
369 .add(&crate::stats::STATS_BUDGET_BYTES)
370 .add(&crate::stats::STATS_COLLECTION_ENABLED)
371 .add(&crate::stats::STATS_FILTER_ENABLED)
372 .add(&crate::stats::STATS_UNTRIMMABLE_COLUMNS_EQUALS)
373 .add(&crate::stats::STATS_UNTRIMMABLE_COLUMNS_PREFIX)
374 .add(&crate::stats::STATS_UNTRIMMABLE_COLUMNS_SUFFIX)
375 .add(&crate::fetch::PART_DECODE_FORMAT)
376 .add(&crate::write::COMBINE_INLINE_WRITES)
377 .add(&crate::write::VALIDATE_PART_BOUNDS_ON_WRITE)
378}
379
380impl PersistConfig {
381 pub(crate) const DEFAULT_FALLBACK_ROLLUP_THRESHOLD_MULTIPLIER: usize = 3;
382
383 pub fn set_state_versions_recent_live_diffs_limit(&self, val: usize) {
384 self.set_config(&STATE_VERSIONS_RECENT_LIVE_DIFFS_LIMIT, val);
385 }
386}
387
388pub const CONSENSUS_CONNECTION_POOL_MAX_SIZE: Config<usize> = Config::new(
392 "persist_consensus_connection_pool_max_size",
393 50,
394 "The maximum size the connection pool to Postgres/CRDB will grow to.",
395);
396
397const CONSENSUS_CONNECTION_POOL_MAX_WAIT: Config<Duration> = Config::new(
402 "persist_consensus_connection_pool_max_wait",
403 Duration::from_secs(60),
404 "The amount of time we'll wait for a connection to become available.",
405);
406
407const CONSENSUS_CONNECTION_POOL_TTL: Config<Duration> = Config::new(
411 "persist_consensus_connection_pool_ttl",
412 Duration::from_secs(300),
413 "\
414 The minimum TTL of a Consensus connection to Postgres/CRDB before it is \
415 proactively terminated",
416);
417
418const CONSENSUS_CONNECTION_POOL_TTL_STAGGER: Config<Duration> = Config::new(
426 "persist_consensus_connection_pool_ttl_stagger",
427 Duration::from_secs(6),
428 "The minimum time between TTLing Consensus connections to Postgres/CRDB.",
429);
430
431pub const CRDB_CONNECT_TIMEOUT: Config<Duration> = Config::new(
434 "crdb_connect_timeout",
435 Duration::from_secs(5),
436 "The time to connect to CockroachDB before timing out and retrying.",
437);
438
439pub const CRDB_TCP_USER_TIMEOUT: Config<Duration> = Config::new(
443 "crdb_tcp_user_timeout",
444 Duration::from_secs(30),
445 "\
446 The TCP timeout for connections to CockroachDB. Specifies the amount of \
447 time that transmitted data may remain unacknowledged before the TCP \
448 connection is forcibly closed.",
449);
450
451pub const CRDB_KEEPALIVES_IDLE: Config<Duration> = Config::new(
452 "crdb_keepalives_idle",
453 Duration::from_secs(10),
454 "\
455 The amount of idle time before a TCP keepalive packet is sent on CRDB \
456 connections.",
457);
458
459pub const CRDB_KEEPALIVES_INTERVAL: Config<Duration> = Config::new(
460 "crdb_keepalives_interval",
461 Duration::from_secs(5),
462 "The time interval between TCP keepalive probes on CRDB connections.",
463);
464
465pub const CRDB_KEEPALIVES_RETRIES: Config<u32> = Config::new(
466 "crdb_keepalives_retries",
467 5,
468 "\
469 The maximum number of TCP keepalive probes that will be sent before \
470 dropping a CRDB connection.",
471);
472
473pub const USE_CRITICAL_SINCE_TXN: Config<bool> = Config::new(
475 "persist_use_critical_since_txn",
476 true,
477 "Use the critical since (instead of the overall since) when initializing a subscribe.",
478);
479
480pub const USE_CRITICAL_SINCE_CATALOG: Config<bool> = Config::new(
482 "persist_use_critical_since_catalog",
483 false,
484 "Use the critical since (instead of the overall since) for the Persist-backed catalog.",
485);
486
487pub const USE_CRITICAL_SINCE_SOURCE: Config<bool> = Config::new(
489 "persist_use_critical_since_source",
490 false,
491 "Use the critical since (instead of the overall since) in the Persist source.",
492);
493
494pub const SOURCE_HYDRATION_FRONTIER_COALESCE_BYTES: Config<usize> = Config::new(
499 "persist_source_hydration_frontier_coalesce_bytes",
500 0,
501 "While catching up to the hydration-time upper, the persist source coalesces \
502 frontier downgrades until this many encoded bytes have been emitted (0 disables).",
503);
504
505pub const SOURCE_FETCH_CONCURRENCY: Config<usize> = Config::new(
510 "persist_source_fetch_concurrency",
511 1,
512 "Maximum number of part fetches the persist source issues concurrently per worker \
513 (1 = serial).",
514);
515
516pub const USE_CRITICAL_SINCE_SNAPSHOT: Config<bool> = Config::new(
518 "persist_use_critical_since_snapshot",
519 false,
520 "Use the critical since (instead of the overall since) when taking snapshots in the controller or in fast-path peeks.",
521);
522
523pub const BATCH_BUILDER_MAX_OUTSTANDING_PARTS: Config<usize> = Config::new(
527 "persist_batch_builder_max_outstanding_parts",
528 2,
529 "The number of writes a batch builder can have outstanding before we slow down the writer.",
530);
531
532pub const COMPACTION_HEURISTIC_MIN_INPUTS: Config<usize> = Config::new(
536 "persist_compaction_heuristic_min_inputs",
537 8,
538 "Don't skip compaction if we have more than this many hollow batches as input.",
539);
540
541pub const COMPACTION_HEURISTIC_MIN_PARTS: Config<usize> = Config::new(
545 "persist_compaction_heuristic_min_parts",
546 8,
547 "Don't skip compaction if we have more than this many parts as input.",
548);
549
550pub const COMPACTION_HEURISTIC_MIN_UPDATES: Config<usize> = Config::new(
554 "persist_compaction_heuristic_min_updates",
555 1024,
556 "Don't skip compaction if we have more than this many updates as input.",
557);
558
559pub const COMPACTION_MEMORY_BOUND_BYTES: Config<usize> = Config::new(
564 "persist_compaction_memory_bound_bytes",
565 1024 * MiB,
566 "Attempt to limit compaction to this amount of memory.",
567);
568
569pub const GC_BLOB_DELETE_CONCURRENCY_LIMIT: Config<usize> = Config::new(
571 "persist_gc_blob_delete_concurrency_limit",
572 32,
573 "Limit the number of concurrent deletes GC can perform to this threshold.",
574);
575
576pub const STATE_VERSIONS_RECENT_LIVE_DIFFS_LIMIT: Config<usize> = Config::new(
586 "persist_state_versions_recent_live_diffs_limit",
587 30 * 128,
588 "Fetch this many diffs when fetching recent diffs.",
589);
590
591pub const USAGE_STATE_FETCH_CONCURRENCY_LIMIT: Config<usize> = Config::new(
593 "persist_usage_state_fetch_concurrency_limit",
594 8,
595 "Limit the concurrency in of fetching in the perioding Persist-storage-usage calculation.",
596);
597
598impl PostgresClientKnobs for PersistConfig {
599 fn connection_pool_max_size(&self) -> usize {
600 CONSENSUS_CONNECTION_POOL_MAX_SIZE.get(self)
601 }
602
603 fn connection_pool_max_wait(&self) -> Option<Duration> {
604 Some(CONSENSUS_CONNECTION_POOL_MAX_WAIT.get(self))
605 }
606
607 fn connection_pool_ttl(&self) -> Duration {
608 CONSENSUS_CONNECTION_POOL_TTL.get(self)
609 }
610
611 fn connection_pool_ttl_stagger(&self) -> Duration {
612 CONSENSUS_CONNECTION_POOL_TTL_STAGGER.get(self)
613 }
614
615 fn connect_timeout(&self) -> Duration {
616 CRDB_CONNECT_TIMEOUT.get(self)
617 }
618
619 fn tcp_user_timeout(&self) -> Duration {
620 CRDB_TCP_USER_TIMEOUT.get(self)
621 }
622
623 fn keepalives_idle(&self) -> Duration {
624 CRDB_KEEPALIVES_IDLE.get(self)
625 }
626
627 fn keepalives_interval(&self) -> Duration {
628 CRDB_KEEPALIVES_INTERVAL.get(self)
629 }
630
631 fn keepalives_retries(&self) -> u32 {
632 CRDB_KEEPALIVES_RETRIES.get(self)
633 }
634
635 fn statement_timeout(&self) -> Duration {
636 Duration::ZERO
638 }
639}
640
641#[derive(Copy, Clone, Debug, Eq, PartialEq, Arbitrary, Serialize, Deserialize)]
642pub struct RetryParameters {
643 pub fixed_sleep: Duration,
644 pub initial_backoff: Duration,
645 pub multiplier: u32,
646 pub clamp: Duration,
647}
648
649impl RetryParameters {
650 pub fn persist_defaults() -> Self {
651 Self {
652 fixed_sleep: Duration::ZERO,
653 initial_backoff: Duration::from_millis(4),
656 multiplier: 2,
657 clamp: Duration::from_secs(16),
660 }
661 }
662
663 pub(crate) fn into_retry(self, now: SystemTime) -> Retry {
664 let seed = now
665 .duration_since(UNIX_EPOCH)
666 .map_or(0, |x| u64::from(x.subsec_nanos()));
667 Retry {
668 fixed_sleep: self.fixed_sleep,
669 initial_backoff: self.initial_backoff,
670 multiplier: self.multiplier,
671 clamp_backoff: self.clamp,
672 seed,
673 }
674 }
675}
676
677pub(crate) const BLOB_OPERATION_TIMEOUT: Config<Duration> = Config::new(
678 "persist_blob_operation_timeout",
679 Duration::from_secs(180),
680 "Maximum time allowed for a network call, including retry attempts.",
681);
682
683pub(crate) const BLOB_OPERATION_ATTEMPT_TIMEOUT: Config<Duration> = Config::new(
684 "persist_blob_operation_attempt_timeout",
685 Duration::from_secs(90),
686 "Maximum time allowed for a single network call.",
687);
688
689pub(crate) const BLOB_CONNECT_TIMEOUT: Config<Duration> = Config::new(
690 "persist_blob_connect_timeout",
691 Duration::from_secs(7),
692 "Maximum time to wait for a socket connection to be made.",
693);
694
695pub(crate) const BLOB_READ_TIMEOUT: Config<Duration> = Config::new(
696 "persist_blob_read_timeout",
697 Duration::from_secs(10),
698 "Maximum time to wait to read the first byte of a response, including connection time.",
699);
700
701impl BlobKnobs for PersistConfig {
702 fn operation_timeout(&self) -> Duration {
703 BLOB_OPERATION_TIMEOUT.get(self)
704 }
705
706 fn operation_attempt_timeout(&self) -> Duration {
707 BLOB_OPERATION_ATTEMPT_TIMEOUT.get(self)
708 }
709
710 fn connect_timeout(&self) -> Duration {
711 BLOB_CONNECT_TIMEOUT.get(self)
712 }
713
714 fn read_timeout(&self) -> Duration {
715 BLOB_READ_TIMEOUT.get(self)
716 }
717
718 fn is_cc_active(&self) -> bool {
719 self.is_cc_active
720 }
721}
722
723pub fn code_can_read_data(code_version: &Version, data_version: &Version) -> bool {
744 code_version.cmp_precedence(data_version).is_ge()
747}
748
749pub fn code_can_write_data(code_version: &Version, data_version: &Version) -> bool {
754 if !code_can_read_data(code_version, data_version) {
755 return false;
756 }
757
758 if code_version.major == 0 && code_version.minor <= SELF_MANAGED_VERSIONS[1].minor {
759 true
762 } else if code_version.major == 0 {
763 SELF_MANAGED_VERSIONS[0]
765 .cmp_precedence(data_version)
766 .is_le()
767 } else if code_version.major <= 26 {
768 SELF_MANAGED_VERSIONS[1]
770 .cmp_precedence(data_version)
771 .is_le()
772 } else {
773 code_version.major - 1 <= data_version.major
775 }
776}