1use std::fmt::Debug;
13use std::num::NonZeroI64;
14use std::sync::Arc;
15use std::time::Duration;
16
17use async_trait::async_trait;
18use mz_audit_log::VersionedEvent;
19use mz_controller_types::{ClusterId, ReplicaId};
20use mz_ore::collections::CollectionExt;
21use mz_ore::metrics::MetricsRegistry;
22use mz_persist_client::PersistClient;
23use mz_persist_types::ShardId;
24use mz_repr::{CatalogItemId, GlobalId, RelationDesc, SqlScalarType};
25use mz_sql::catalog::CatalogError as SqlCatalogError;
26use uuid::Uuid;
27
28use crate::config::ClusterReplicaSizeMap;
29use crate::durable::debug::{DebugCatalogState, Trace};
30pub use crate::durable::error::{CatalogError, DurableCatalogError, FenceError};
31pub use crate::durable::metrics::Metrics;
32pub use crate::durable::objects::Snapshot;
33pub use crate::durable::objects::state_update::StateUpdate;
34pub use crate::durable::objects::{
35 BurstState, Cluster, ClusterConfig, ClusterReplica, ClusterReplicaSize,
36 ClusterSystemConfiguration, ClusterVariant, ClusterVariantManaged, Comment, Database,
37 DefaultPrivilege, IndexColumn, IndexColumns, IntrospectionSourceIndex, Item, ItemColumn,
38 ItemColumns, ItemMetadata, NetworkPolicy, ReconfigurationState, ReconfigurationStatus,
39 ReconfigurationTarget, ReplicaConfig, ReplicaLocation, ReplicaSystemConfiguration, Role,
40 RoleAuth, Schema, SourceReference, SourceReferences, StorageCollectionMetadata,
41 SystemConfiguration, SystemObjectDescription, SystemObjectMapping, UnfinalizedShard,
42 managed_cluster_replica_name,
43};
44pub use crate::durable::persist::shard_id;
45use crate::durable::persist::{Timestamp, UnopenedPersistCatalogState};
46use crate::durable::transaction::TransactionBatch;
47pub use crate::durable::transaction::{DryRunTransaction, Transaction};
48pub use crate::durable::upgrade::CATALOG_VERSION;
49use crate::memory;
50
51pub mod debug;
52mod error;
53pub mod initialize;
54mod metrics;
55pub mod objects;
56mod persist;
57mod traits;
58mod transaction;
59mod upgrade;
60
61pub const DATABASE_ID_ALLOC_KEY: &str = "database";
62pub const SCHEMA_ID_ALLOC_KEY: &str = "schema";
63pub const USER_ITEM_ALLOC_KEY: &str = "user";
64pub const SYSTEM_ITEM_ALLOC_KEY: &str = "system";
65pub const USER_ROLE_ID_ALLOC_KEY: &str = "user_role";
66pub const USER_CLUSTER_ID_ALLOC_KEY: &str = "user_compute";
67pub const SYSTEM_CLUSTER_ID_ALLOC_KEY: &str = "system_compute";
68pub const USER_REPLICA_ID_ALLOC_KEY: &str = "replica";
69pub const SYSTEM_REPLICA_ID_ALLOC_KEY: &str = "system_replica";
70pub const AUDIT_LOG_ID_ALLOC_KEY: &str = "auditlog";
71pub const STORAGE_USAGE_ID_ALLOC_KEY: &str = "storage_usage";
72pub const USER_NETWORK_POLICY_ID_ALLOC_KEY: &str = "user_network_policy";
73pub const USER_CLUSTER_REPLICA_SIZE_ID_ALLOC_KEY: &str = "user_cluster_replica_size";
74pub const SYSTEM_CLUSTER_REPLICA_SIZE_ID_ALLOC_KEY: &str = "system_cluster_replica_size";
75pub const OID_ALLOC_KEY: &str = "oid";
76pub(crate) const CATALOG_CONTENT_VERSION_KEY: &str = "catalog_content_version";
77pub const BUILTIN_MIGRATION_SHARD_KEY: &str = "builtin_migration_shard";
78pub const EXPRESSION_CACHE_SHARD_KEY: &str = "expression_cache_shard";
79pub const MOCK_AUTHENTICATION_NONCE_KEY: &str = "mock_authentication_nonce";
80
81#[derive(Clone, Debug)]
82pub struct BootstrapArgs {
83 pub cluster_replica_size_map: ClusterReplicaSizeMap,
84 pub default_cluster_replica_size: String,
85 pub default_cluster_replication_factor: u32,
86 pub bootstrap_role: Option<String>,
87 pub force_catalog_migration: bool,
89}
90
91pub type Epoch = NonZeroI64;
92
93#[async_trait]
97pub trait OpenableDurableCatalogState: Debug + Send {
98 async fn open_savepoint(
115 mut self: Box<Self>,
116 initial_ts: Timestamp,
117 bootstrap_args: &BootstrapArgs,
118 ) -> Result<Box<dyn DurableCatalogState>, CatalogError>;
119
120 async fn open_read_only(
126 mut self: Box<Self>,
127 bootstrap_args: &BootstrapArgs,
128 ) -> Result<Box<dyn DurableCatalogState>, CatalogError>;
129
130 async fn open(
136 mut self: Box<Self>,
137 initial_ts: Timestamp,
138 bootstrap_args: &BootstrapArgs,
139 ) -> Result<Box<dyn DurableCatalogState>, CatalogError>;
140
141 async fn open_debug(mut self: Box<Self>) -> Result<DebugCatalogState, CatalogError>;
144
145 async fn is_initialized(&mut self) -> Result<bool, CatalogError>;
147
148 async fn epoch(&mut self) -> Result<Epoch, CatalogError>;
158
159 async fn get_deployment_generation(&mut self) -> Result<u64, CatalogError>;
162
163 async fn get_0dt_deployment_max_wait(&mut self) -> Result<Option<Duration>, CatalogError>;
169
170 async fn get_0dt_deployment_ddl_check_interval(
176 &mut self,
177 ) -> Result<Option<Duration>, CatalogError>;
178
179 async fn get_enable_0dt_deployment_panic_after_timeout(
186 &mut self,
187 ) -> Result<Option<bool>, CatalogError>;
188
189 async fn has_system_config_synced_once(&mut self) -> Result<bool, DurableCatalogError>;
191
192 async fn trace_unconsolidated(&mut self) -> Result<Trace, CatalogError>;
194
195 async fn trace_consolidated(&mut self) -> Result<Trace, CatalogError>;
197
198 async fn expire(self: Box<Self>);
200}
201
202#[async_trait]
204pub trait ReadOnlyDurableCatalogState: Debug + Send + Sync {
205 fn epoch(&self) -> Epoch;
215
216 fn metrics(&self) -> &Metrics;
218
219 async fn expire(self: Box<Self>);
221
222 fn is_bootstrap_complete(&self) -> bool;
224
225 async fn get_audit_logs(&mut self) -> Result<Vec<VersionedEvent>, CatalogError>;
231
232 async fn get_next_id(&mut self, id_type: &str) -> Result<u64, CatalogError>;
234
235 async fn get_next_user_item_id(&mut self) -> Result<u64, CatalogError> {
237 self.get_next_id(USER_ITEM_ALLOC_KEY).await
238 }
239
240 async fn get_next_system_item_id(&mut self) -> Result<u64, CatalogError> {
242 self.get_next_id(SYSTEM_ITEM_ALLOC_KEY).await
243 }
244
245 async fn get_next_system_replica_id(&mut self) -> Result<u64, CatalogError> {
247 self.get_next_id(SYSTEM_REPLICA_ID_ALLOC_KEY).await
248 }
249
250 async fn get_next_user_replica_id(&mut self) -> Result<u64, CatalogError> {
252 self.get_next_id(USER_REPLICA_ID_ALLOC_KEY).await
253 }
254
255 async fn get_deployment_generation(&mut self) -> Result<u64, CatalogError>;
257
258 async fn snapshot(&mut self) -> Result<Snapshot, CatalogError>;
260
261 async fn sync_to_current_updates(
267 &mut self,
268 ) -> Result<Vec<memory::objects::StateUpdate>, CatalogError>;
269
270 async fn sync_updates(
279 &mut self,
280 target_upper: Timestamp,
281 ) -> Result<Vec<memory::objects::StateUpdate>, CatalogError>;
282
283 async fn ensure_not_out_of_sync(&mut self, target_upper: Timestamp)
287 -> Result<(), CatalogError>;
288
289 async fn current_upper(&mut self) -> Timestamp;
291}
292
293#[async_trait]
295#[allow(mismatched_lifetime_syntaxes)]
296pub trait DurableCatalogState: ReadOnlyDurableCatalogState {
297 fn is_read_only(&self) -> bool;
299
300 fn is_savepoint(&self) -> bool;
302
303 async fn mark_bootstrap_complete(&mut self);
305
306 async fn transaction(&mut self) -> Result<Transaction, CatalogError>;
310
311 fn transaction_from_snapshot(
315 &mut self,
316 snapshot: Snapshot,
317 ) -> Result<DryRunTransaction, CatalogError>;
318
319 async fn commit_transaction(
327 &mut self,
328 txn_batch: TransactionBatch,
329 commit_ts: Timestamp,
330 ) -> Result<Timestamp, CatalogError>;
331
332 async fn advance_upper(&mut self, new_upper: Timestamp) -> Result<(), CatalogError>;
338
339 async fn allocate_id(
344 &mut self,
345 id_type: &str,
346 amount: u64,
347 commit_ts: Timestamp,
348 ) -> Result<Vec<u64>, CatalogError>;
349
350 async fn allocate_user_ids(
354 &mut self,
355 amount: u64,
356 commit_ts: Timestamp,
357 ) -> Result<Vec<(CatalogItemId, GlobalId)>, CatalogError> {
358 let ids = self
359 .allocate_id(USER_ITEM_ALLOC_KEY, amount, commit_ts)
360 .await?;
361 let ids = ids
362 .iter()
363 .map(|id| (CatalogItemId::User(*id), GlobalId::User(*id)))
364 .collect();
365 Ok(ids)
366 }
367
368 async fn allocate_user_id(
372 &mut self,
373 commit_ts: Timestamp,
374 ) -> Result<(CatalogItemId, GlobalId), CatalogError> {
375 let id = self.allocate_id(USER_ITEM_ALLOC_KEY, 1, commit_ts).await?;
376 let id = id.into_element();
377 Ok((CatalogItemId::User(id), GlobalId::User(id)))
378 }
379
380 async fn allocate_user_cluster_id(
384 &mut self,
385 commit_ts: Timestamp,
386 ) -> Result<ClusterId, CatalogError> {
387 let id = self
388 .allocate_id(USER_CLUSTER_ID_ALLOC_KEY, 1, commit_ts)
389 .await?
390 .into_element();
391 Ok(ClusterId::user(id).ok_or(SqlCatalogError::IdExhaustion)?)
392 }
393
394 async fn allocate_user_replica_ids(
398 &mut self,
399 amount: u64,
400 commit_ts: Timestamp,
401 ) -> Result<Vec<ReplicaId>, CatalogError> {
402 let ids = self
403 .allocate_id(USER_REPLICA_ID_ALLOC_KEY, amount, commit_ts)
404 .await?;
405 let ids = ids.into_iter().map(ReplicaId::User).collect();
406 Ok(ids)
407 }
408
409 async fn allocate_system_replica_ids(
413 &mut self,
414 amount: u64,
415 commit_ts: Timestamp,
416 ) -> Result<Vec<ReplicaId>, CatalogError> {
417 let ids = self
418 .allocate_id(SYSTEM_REPLICA_ID_ALLOC_KEY, amount, commit_ts)
419 .await?;
420 let ids = ids.into_iter().map(ReplicaId::System).collect();
421 Ok(ids)
422 }
423
424 fn shard_id(&self) -> ShardId;
425}
426
427pub fn persist_desc() -> RelationDesc {
430 RelationDesc::builder()
431 .with_column("data", SqlScalarType::Jsonb.nullable(false))
432 .finish()
433}
434
435#[derive(Debug, Clone)]
437pub struct TestCatalogStateBuilder {
438 persist_client: PersistClient,
439 organization_id: Uuid,
440 version: semver::Version,
441 deploy_generation: Option<u64>,
442 metrics: Arc<Metrics>,
443}
444
445impl TestCatalogStateBuilder {
446 pub fn new(persist_client: PersistClient) -> Self {
447 Self {
448 persist_client,
449 organization_id: Uuid::new_v4(),
450 version: semver::Version::new(0, 0, 0),
451 deploy_generation: None,
452 metrics: Arc::new(Metrics::new(&MetricsRegistry::new())),
453 }
454 }
455
456 pub fn with_organization_id(mut self, organization_id: Uuid) -> Self {
457 self.organization_id = organization_id;
458 self
459 }
460
461 pub fn with_version(mut self, version: semver::Version) -> Self {
462 self.version = version;
463 self
464 }
465
466 pub fn with_deploy_generation(mut self, deploy_generation: u64) -> Self {
467 self.deploy_generation = Some(deploy_generation);
468 self
469 }
470
471 pub fn with_default_deploy_generation(self) -> Self {
472 self.with_deploy_generation(0)
473 }
474
475 pub fn with_metrics(mut self, metrics: Arc<Metrics>) -> Self {
476 self.metrics = metrics;
477 self
478 }
479
480 pub async fn build(self) -> Result<Box<dyn OpenableDurableCatalogState>, DurableCatalogError> {
481 persist_backed_catalog_state(
482 self.persist_client,
483 self.organization_id,
484 self.version,
485 self.deploy_generation,
486 self.metrics,
487 )
488 .await
489 }
490
491 pub async fn unwrap_build(self) -> Box<dyn OpenableDurableCatalogState> {
492 self.expect_build("failed to build").await
493 }
494
495 pub async fn expect_build(self, msg: &str) -> Box<dyn OpenableDurableCatalogState> {
496 self.build().await.expect(msg)
497 }
498}
499
500pub async fn persist_backed_catalog_state(
504 persist_client: PersistClient,
505 organization_id: Uuid,
506 version: semver::Version,
507 deploy_generation: Option<u64>,
508 metrics: Arc<Metrics>,
509) -> Result<Box<dyn OpenableDurableCatalogState>, DurableCatalogError> {
510 let state = UnopenedPersistCatalogState::new(
511 persist_client,
512 organization_id,
513 version,
514 deploy_generation,
515 metrics,
516 )
517 .await?;
518 Ok(Box::new(state))
519}
520
521pub fn test_bootstrap_args() -> BootstrapArgs {
522 BootstrapArgs {
523 default_cluster_replica_size: "scale=1,workers=1".into(),
524 default_cluster_replication_factor: 1,
525 bootstrap_role: None,
526 cluster_replica_size_map: ClusterReplicaSizeMap::for_tests(),
527 force_catalog_migration: false,
528 }
529}