1use std::borrow::Cow;
70use std::collections::{BTreeMap, BTreeSet, VecDeque};
71use std::net::IpAddr;
72use std::num::NonZeroI64;
73use std::ops::Neg;
74use std::str::FromStr;
75use std::sync::LazyLock;
76use std::sync::{Arc, Mutex};
77use std::thread;
78use std::time::{Duration, Instant};
79use std::{fmt, mem};
80
81use anyhow::Context;
82use chrono::{DateTime, Utc};
83use derivative::Derivative;
84use differential_dataflow::lattice::Lattice;
85use fail::fail_point;
86use futures::StreamExt;
87use futures::future::{BoxFuture, FutureExt, LocalBoxFuture};
88use http::Uri;
89use ipnet::IpNet;
90use itertools::Itertools;
91use mz_adapter_types::bootstrap_builtin_cluster_config::BootstrapBuiltinClusterConfig;
92use mz_adapter_types::compaction::CompactionWindow;
93use mz_adapter_types::connection::ConnectionId;
94use mz_adapter_types::dyncfgs::{
95 ENABLE_SCOPED_SYSTEM_PARAMETERS, USER_ID_POOL_BATCH_SIZE,
96 WITH_0DT_DEPLOYMENT_CAUGHT_UP_CHECK_INTERVAL,
97};
98use mz_auth::password::Password;
99use mz_build_info::BuildInfo;
100use mz_catalog::builtin::{
101 BUILTINS, BUILTINS_STATIC, MZ_OBJECT_ARRANGEMENT_SIZE_HISTORY, MZ_STORAGE_USAGE_BY_SHARD,
102};
103use mz_catalog::config::{AwsPrincipalContext, BuiltinItemMigrationConfig, ClusterReplicaSizeMap};
104use mz_catalog::durable::OpenableDurableCatalogState;
105use mz_catalog::expr_cache::{GlobalExpressions, LocalExpressions};
106use mz_catalog::memory::objects::{
107 CatalogEntry, CatalogItem, ClusterReplicaProcessStatus, ClusterVariantManaged, Connection,
108 DataSourceDesc, ReconfigurationTarget, Table, TableDataSource,
109};
110use mz_cloud_resources::{CloudResourceController, VpcEndpointConfig, VpcEndpointEvent};
111use mz_compute_client::as_of_selection;
112use mz_compute_client::controller::error::{
113 CollectionLookupError, CollectionMissing, DataflowCreationError, InstanceMissing,
114};
115use mz_compute_types::ComputeInstanceId;
116use mz_compute_types::dataflows::DataflowDescription;
117use mz_compute_types::plan::LirRelationExpr;
118use mz_controller::clusters::{
119 ClusterConfig, ClusterEvent, ClusterStatus, ProcessId, ReplicaLocation,
120};
121use mz_controller::{ControllerConfig, Readiness};
122use mz_controller_types::{ClusterId, ReplicaId, WatchSetId};
123use mz_dyncfg::{ConfigUpdates, ParameterScope};
124use mz_expr::{MapFilterProject, MirRelationExpr, OptimizedMirRelationExpr, RowSetFinishing};
125use mz_license_keys::{ExpirationBehavior, ValidatedLicenseKey};
126use mz_orchestrator::OfflineReason;
127use mz_ore::cast::{CastFrom, CastInto, CastLossy};
128use mz_ore::channel::trigger::Trigger;
129use mz_ore::future::TimeoutError;
130use mz_ore::metrics::MetricsRegistry;
131use mz_ore::now::{EpochMillis, NowFn};
132use mz_ore::task::{JoinHandle, spawn};
133use mz_ore::thread::JoinHandleExt;
134use mz_ore::tracing::{OpenTelemetryContext, TracingHandle};
135use mz_ore::url::SensitiveUrl;
136use mz_ore::{
137 assert_none, instrument, soft_assert_eq_or_log, soft_assert_or_log, soft_panic_or_log, stack,
138};
139use mz_persist_client::PersistClient;
140use mz_persist_client::batch::ProtoBatch;
141use mz_persist_client::usage::{ShardsUsageReferenced, StorageUsageClient};
142use mz_repr::adt::numeric::Numeric;
143use mz_repr::explain::{ExplainConfig, ExplainFormat};
144use mz_repr::global_id::TransientIdGen;
145use mz_repr::optimize::{OptimizerFeatureOverrides, OptimizerFeatures, OverrideFrom};
146use mz_repr::role_id::RoleId;
147use mz_repr::{CatalogItemId, Diff, GlobalId, RelationDesc, SqlRelationType, Timestamp};
148use mz_secrets::cache::CachingSecretsReader;
149use mz_secrets::{SecretsController, SecretsReader};
150use mz_sql::ast::{Raw, Statement};
151use mz_sql::catalog::{CatalogCluster, EnvironmentId};
152use mz_sql::names::{QualifiedItemName, ResolvedIds, SchemaSpecifier};
153use mz_sql::optimizer_metrics::OptimizerMetrics;
154use mz_sql::plan::{
155 self, AlterSinkPlan, ConnectionDetails, CreateConnectionPlan, HirRelationExpr,
156 NetworkPolicyRule, OnTimeoutAction, Params, QueryWhen,
157};
158use mz_sql::session::user::User;
159use mz_sql::session::vars::{MAX_CREDIT_CONSUMPTION_RATE, SystemVars, Var};
160use mz_sql_parser::ast::ExplainStage;
161use mz_sql_parser::ast::display::AstDisplay;
162use mz_storage_client::client::TableData;
163use mz_storage_client::controller::{CollectionDescription, DataSource, ExportDescription};
164use mz_storage_types::connections::Connection as StorageConnection;
165use mz_storage_types::connections::ConnectionContext;
166use mz_storage_types::connections::inline::{IntoInlineConnection, ReferencedConnection};
167use mz_storage_types::read_holds::ReadHold;
168use mz_storage_types::sinks::{S3SinkFormat, StorageSinkDesc};
169use mz_storage_types::sources::kafka::KAFKA_PROGRESS_DESC;
170use mz_storage_types::sources::{IngestionDescription, SourceExport, Timeline};
171use mz_timestamp_oracle::{TimestampOracleConfig, WriteTimestamp};
172use mz_transform::dataflow::DataflowMetainfo;
173use opentelemetry::trace::TraceContextExt;
174use serde::Serialize;
175use thiserror::Error;
176use timely::progress::{Antichain, Timestamp as _};
177use tokio::runtime::Handle as TokioHandle;
178use tokio::select;
179use tokio::sync::{Notify, OwnedMutexGuard, mpsc, oneshot, watch};
180use tokio::time::{Interval, MissedTickBehavior};
181use tracing::{Instrument, Level, Span, debug, info, info_span, span, warn};
182use tracing_opentelemetry::OpenTelemetrySpanExt;
183use uuid::Uuid;
184
185use crate::active_compute_sink::{ActiveComputeSink, ActiveCopyFrom};
186use crate::catalog::{BuiltinTableUpdate, Catalog, OpenCatalogResult};
187use crate::client::{Client, Handle};
188use crate::command::{Command, ExecuteResponse};
189use crate::config::{
190 ClusterEvalContext, ReplicaEvalContext, ScopedParameters, ScopedParametersScope,
191 SynchronizedParameters, SystemParameterFrontend, SystemParameterSyncConfig,
192};
193use crate::coord::appends::{
194 BuiltinTableAppendCompletion, BuiltinTableAppendNotify, DeferredOp, GroupCommitPermit,
195 PendingWriteTxn,
196};
197use crate::coord::caught_up::CaughtUpCheckContext;
198use crate::coord::cluster_scheduling::SchedulingDecision;
199use crate::coord::id_bundle::CollectionIdBundle;
200use crate::coord::introspection::IntrospectionSubscribe;
201use crate::coord::peek::PendingPeek;
202use crate::coord::statement_logging::StatementLogging;
203use crate::coord::timeline::{TimelineContext, TimelineState};
204use crate::coord::timestamp_selection::{TimestampContext, TimestampDetermination};
205use crate::coord::validity::PlanValidity;
206use crate::error::AdapterError;
207use crate::explain::insights::PlanInsightsContext;
208use crate::explain::optimizer_trace::{DispatchGuard, OptimizerTrace};
209use crate::metrics::Metrics;
210use crate::optimize::dataflows::{ComputeInstanceSnapshot, DataflowBuilder};
211use crate::optimize::{self, Optimize, OptimizerConfig};
212use crate::session::{EndTransactionAction, Session};
213use crate::statement_logging::{
214 StatementEndedExecutionReason, StatementLifecycleEvent, StatementLoggingId,
215};
216use crate::util::{ClientTransmitter, ResultExt, sort_topological};
217use crate::webhook::{WebhookAppenderInvalidator, WebhookConcurrencyLimiter};
218use crate::{AdapterNotice, ReadHolds, flags};
219
220pub(crate) mod appends;
221pub(crate) mod catalog_serving;
222pub(crate) mod cluster_controller;
223pub(crate) mod cluster_scheduling;
224pub(crate) mod consistency;
225pub(crate) mod id_bundle;
226pub(crate) mod in_memory_oracle;
227pub(crate) mod peek;
228pub(crate) mod read_policy;
229pub(crate) mod read_then_write;
230pub(crate) mod sequencer;
231pub(crate) mod statement_logging;
232pub(crate) mod timeline;
233pub(crate) mod timestamp_selection;
234
235pub mod catalog_implications;
236mod caught_up;
237mod command_handler;
238mod ddl;
239pub(crate) mod group_sync;
240mod indexes;
241mod info_metrics;
242mod introspection;
243mod message_handler;
244mod privatelink_status;
245mod sql;
246mod validity;
247
248#[derive(Debug)]
274pub(crate) struct IdPool {
275 next: u64,
276 upper: u64,
277}
278
279impl IdPool {
280 pub fn empty() -> Self {
282 IdPool { next: 0, upper: 0 }
283 }
284
285 pub fn allocate(&mut self) -> Option<u64> {
287 if self.next < self.upper {
288 let id = self.next;
289 self.next += 1;
290 Some(id)
291 } else {
292 None
293 }
294 }
295
296 pub fn allocate_many(&mut self, n: u64) -> Option<Vec<u64>> {
299 if self.remaining() >= n {
300 let ids = (self.next..self.next + n).collect();
301 self.next += n;
302 Some(ids)
303 } else {
304 None
305 }
306 }
307
308 pub fn remaining(&self) -> u64 {
310 self.upper - self.next
311 }
312
313 pub fn refill(&mut self, next: u64, upper: u64) {
315 assert!(next <= upper, "invalid pool range: {next}..{upper}");
316 self.next = next;
317 self.upper = upper;
318 }
319}
320
321#[derive(Debug)]
325pub struct ArrangementSizeRecord {
326 pub replica_id: String,
327 pub object_id: String,
328 pub size: i64,
329 pub hydration_complete: bool,
330}
331
332#[derive(Debug)]
333pub enum Message {
334 Command(OpenTelemetryContext, Command),
335 ControllerReady {
336 controller: ControllerReadiness,
337 },
338 PurifiedStatementReady(PurifiedStatementReady),
339 CreateConnectionValidationReady(CreateConnectionValidationReady),
340 AlterConnectionValidationReady(AlterConnectionValidationReady),
341 TryDeferred {
342 conn_id: ConnectionId,
344 acquired_lock: Option<(CatalogItemId, tokio::sync::OwnedMutexGuard<()>)>,
354 },
355 GroupCommitInitiate(Span, Option<GroupCommitPermit>),
357 GroupCommitApplied {
361 responses: Vec<crate::util::CompletedClientTransmitter>,
363 statement_logging_ids: Vec<StatementLoggingId>,
365 write_ts: Timestamp,
367 },
368 DeferredStatementReady,
369 AdvanceTimelines,
370 ClusterEvent(ClusterEvent),
371 CancelPendingPeeks {
372 conn_id: ConnectionId,
373 },
374 LinearizeReads,
375 StagedBatches {
376 conn_id: ConnectionId,
377 table_id: CatalogItemId,
378 batches: Vec<Result<ProtoBatch, String>>,
379 },
380 StorageUsageSchedule,
381 StorageUsageFetch,
382 StorageUsageUpdate(ShardsUsageReferenced),
383 StorageUsagePrune(Vec<BuiltinTableUpdate>),
384 ArrangementSizesSchedule,
385 ArrangementSizesSnapshot,
386 ArrangementSizesWrite(Vec<ArrangementSizeRecord>),
387 ArrangementSizesPrune(Vec<BuiltinTableUpdate>),
388 RetireExecute {
391 data: ExecuteContextExtra,
392 otel_ctx: OpenTelemetryContext,
393 reason: StatementEndedExecutionReason,
394 },
395 ExecuteSingleStatementTransaction {
396 ctx: ExecuteContext,
397 otel_ctx: OpenTelemetryContext,
398 stmt: Arc<Statement<Raw>>,
399 params: mz_sql::plan::Params,
400 },
401 PeekStageReady {
402 ctx: ExecuteContext,
403 span: Span,
404 stage: PeekStage,
405 },
406 CreateIndexStageReady {
407 ctx: ExecuteContext,
408 span: Span,
409 stage: CreateIndexStage,
410 },
411 CreateViewStageReady {
412 ctx: ExecuteContext,
413 span: Span,
414 stage: CreateViewStage,
415 },
416 CreateMaterializedViewStageReady {
417 ctx: ExecuteContext,
418 span: Span,
419 stage: CreateMaterializedViewStage,
420 },
421 SubscribeStageReady {
422 ctx: ExecuteContext,
423 span: Span,
424 stage: SubscribeStage,
425 },
426 IntrospectionSubscribeStageReady {
427 span: Span,
428 stage: IntrospectionSubscribeStage,
429 },
430 SecretStageReady {
431 ctx: ExecuteContext,
432 span: Span,
433 stage: SecretStage,
434 },
435 ClusterStageReady {
436 ctx: ExecuteContext,
437 span: Span,
438 stage: ClusterStage,
439 },
440 ExplainTimestampStageReady {
441 ctx: ExecuteContext,
442 span: Span,
443 stage: ExplainTimestampStage,
444 },
445 DrainStatementLog,
446 PrivateLinkVpcEndpointEvents(Vec<VpcEndpointEvent>),
447 CheckSchedulingPolicies,
448
449 SchedulingDecisions(Vec<(&'static str, Vec<(ClusterId, SchedulingDecision)>)>),
454
455 ClusterControllerRequest(cluster_controller::ClusterControllerRequest),
459}
460
461impl Message {
462 pub const fn kind(&self) -> &'static str {
464 match self {
465 Message::Command(_, msg) => match msg {
466 Command::CatalogSnapshot { .. } => "command-catalog_snapshot",
467 Command::Startup { .. } => "command-startup",
468 Command::Execute { .. } => "command-execute",
469 Command::Commit { .. } => "command-commit",
470 Command::CancelRequest { .. } => "command-cancel_request",
471 Command::PrivilegedCancelRequest { .. } => "command-privileged_cancel_request",
472 Command::GetWebhook { .. } => "command-get_webhook",
473 Command::GetSystemVars { .. } => "command-get_system_vars",
474 Command::SetSystemVars { .. } => "command-set_system_vars",
475 Command::UpdateScopedSystemParameters { .. } => {
476 "command-update_scoped_system_parameters"
477 }
478 Command::InstallScopedSystemParameterFrontend { .. } => {
479 "command-install_scoped_system_parameter_frontend"
480 }
481 Command::Terminate { .. } => "command-terminate",
482 Command::RetireExecute { .. } => "command-retire_execute",
483 Command::CheckConsistency { .. } => "command-check_consistency",
484 Command::Dump { .. } => "command-dump",
485 Command::AuthenticatePassword { .. } => "command-auth_check",
486 Command::AuthenticateGetSASLChallenge { .. } => "command-auth_get_sasl_challenge",
487 Command::AuthenticateVerifySASLProof { .. } => "command-auth_verify_sasl_proof",
488 Command::CheckRoleCanLogin { .. } => "command-check_role_can_login",
489 Command::GetComputeInstanceClient { .. } => "get-compute-instance-client",
490 Command::GetOracle { .. } => "get-oracle",
491 Command::DetermineRealTimeRecentTimestamp { .. } => {
492 "determine-real-time-recent-timestamp"
493 }
494 Command::GetTransactionReadHoldsBundle { .. } => {
495 "get-transaction-read-holds-bundle"
496 }
497 Command::StoreTransactionReadHolds { .. } => "store-transaction-read-holds",
498 Command::ExecuteSlowPathPeek { .. } => "execute-slow-path-peek",
499 Command::ExecuteSubscribe { .. } => "execute-subscribe",
500 Command::CopyToPreflight { .. } => "copy-to-preflight",
501 Command::ExecuteCopyTo { .. } => "execute-copy-to",
502 Command::ExecuteSideEffectingFunc { .. } => "execute-side-effecting-func",
503 Command::LookupConnection { .. } => "lookup-connection",
504 Command::RegisterFrontendPeek { .. } => "register-frontend-peek",
505 Command::UnregisterFrontendPeek { .. } => "unregister-frontend-peek",
506 Command::ExplainTimestamp { .. } => "explain-timestamp",
507 Command::FrontendStatementLogging(..) => "frontend-statement-logging",
508 Command::StartCopyFromStdin { .. } => "start-copy-from-stdin",
509 Command::InjectAuditEvents { .. } => "inject-audit-events",
510 },
511 Message::ControllerReady {
512 controller: ControllerReadiness::Compute,
513 } => "controller_ready(compute)",
514 Message::ControllerReady {
515 controller: ControllerReadiness::Storage,
516 } => "controller_ready(storage)",
517 Message::ControllerReady {
518 controller: ControllerReadiness::Metrics,
519 } => "controller_ready(metrics)",
520 Message::ControllerReady {
521 controller: ControllerReadiness::Internal,
522 } => "controller_ready(internal)",
523 Message::PurifiedStatementReady(_) => "purified_statement_ready",
524 Message::CreateConnectionValidationReady(_) => "create_connection_validation_ready",
525 Message::TryDeferred { .. } => "try_deferred",
526 Message::GroupCommitInitiate(..) => "group_commit_initiate",
527 Message::GroupCommitApplied { .. } => "group_commit_applied",
528 Message::AdvanceTimelines => "advance_timelines",
529 Message::ClusterEvent(_) => "cluster_event",
530 Message::CancelPendingPeeks { .. } => "cancel_pending_peeks",
531 Message::LinearizeReads => "linearize_reads",
532 Message::StagedBatches { .. } => "staged_batches",
533 Message::StorageUsageSchedule => "storage_usage_schedule",
534 Message::StorageUsageFetch => "storage_usage_fetch",
535 Message::StorageUsageUpdate(_) => "storage_usage_update",
536 Message::StorageUsagePrune(_) => "storage_usage_prune",
537 Message::ArrangementSizesSchedule => "arrangement_sizes_schedule",
538 Message::ArrangementSizesSnapshot => "arrangement_sizes_snapshot",
539 Message::ArrangementSizesWrite(_) => "arrangement_sizes_write",
540 Message::ArrangementSizesPrune(_) => "arrangement_sizes_prune",
541 Message::RetireExecute { .. } => "retire_execute",
542 Message::ExecuteSingleStatementTransaction { .. } => {
543 "execute_single_statement_transaction"
544 }
545 Message::PeekStageReady { .. } => "peek_stage_ready",
546 Message::ExplainTimestampStageReady { .. } => "explain_timestamp_stage_ready",
547 Message::CreateIndexStageReady { .. } => "create_index_stage_ready",
548 Message::CreateViewStageReady { .. } => "create_view_stage_ready",
549 Message::CreateMaterializedViewStageReady { .. } => {
550 "create_materialized_view_stage_ready"
551 }
552 Message::SubscribeStageReady { .. } => "subscribe_stage_ready",
553 Message::IntrospectionSubscribeStageReady { .. } => {
554 "introspection_subscribe_stage_ready"
555 }
556 Message::SecretStageReady { .. } => "secret_stage_ready",
557 Message::ClusterStageReady { .. } => "cluster_stage_ready",
558 Message::DrainStatementLog => "drain_statement_log",
559 Message::AlterConnectionValidationReady(..) => "alter_connection_validation_ready",
560 Message::PrivateLinkVpcEndpointEvents(_) => "private_link_vpc_endpoint_events",
561 Message::CheckSchedulingPolicies => "check_scheduling_policies",
562 Message::SchedulingDecisions { .. } => "scheduling_decision",
563 Message::ClusterControllerRequest(_) => "cluster_controller_request",
564 Message::DeferredStatementReady => "deferred_statement_ready",
565 }
566 }
567}
568
569#[derive(Debug)]
571pub enum ControllerReadiness {
572 Storage,
574 Compute,
576 Metrics,
578 Internal,
580}
581
582#[derive(Derivative)]
583#[derivative(Debug)]
584pub struct BackgroundWorkResult<T> {
585 #[derivative(Debug = "ignore")]
586 pub ctx: ExecuteContext,
587 pub result: Result<T, AdapterError>,
588 pub params: Params,
589 pub plan_validity: PlanValidity,
590 pub original_stmt: Arc<Statement<Raw>>,
591 pub otel_ctx: OpenTelemetryContext,
592}
593
594pub type PurifiedStatementReady = BackgroundWorkResult<mz_sql::pure::PurifiedStatement>;
595
596#[derive(Derivative)]
597#[derivative(Debug)]
598pub struct ValidationReady<T> {
599 #[derivative(Debug = "ignore")]
600 pub ctx: ExecuteContext,
601 pub result: Result<T, AdapterError>,
602 pub resolved_ids: ResolvedIds,
603 pub connection_id: CatalogItemId,
604 pub connection_gid: GlobalId,
605 pub plan_validity: PlanValidity,
606 pub otel_ctx: OpenTelemetryContext,
607}
608
609pub type CreateConnectionValidationReady = ValidationReady<CreateConnectionPlan>;
610pub type AlterConnectionValidationReady = ValidationReady<Connection>;
611
612#[derive(Debug)]
613pub enum PeekStage {
614 LinearizeTimestamp(PeekStageLinearizeTimestamp),
616 RealTimeRecency(PeekStageRealTimeRecency),
617 TimestampReadHold(PeekStageTimestampReadHold),
618 Optimize(PeekStageOptimize),
619 Finish(PeekStageFinish),
621 ExplainPlan(PeekStageExplainPlan),
623 ExplainPushdown(PeekStageExplainPushdown),
624 CopyToPreflight(PeekStageCopyTo),
626 CopyToDataflow(PeekStageCopyTo),
628}
629
630#[derive(Debug)]
631pub struct CopyToContext {
632 pub desc: RelationDesc,
634 pub uri: Uri,
636 pub connection: StorageConnection<ReferencedConnection>,
638 pub connection_id: CatalogItemId,
640 pub format: S3SinkFormat,
642 pub max_file_size: u64,
644 pub output_batch_count: Option<u64>,
649}
650
651#[derive(Debug)]
652pub struct PeekStageLinearizeTimestamp {
653 validity: PlanValidity,
654 plan: mz_sql::plan::SelectPlan,
655 max_query_result_size: Option<u64>,
656 source_ids: BTreeSet<GlobalId>,
657 target_replica: Option<ReplicaId>,
658 timeline_context: TimelineContext,
659 optimizer: optimize::PeekOptimizer,
660 explain_ctx: ExplainContext,
663}
664
665#[derive(Debug)]
666pub struct PeekStageRealTimeRecency {
667 validity: PlanValidity,
668 plan: mz_sql::plan::SelectPlan,
669 max_query_result_size: Option<u64>,
670 source_ids: BTreeSet<GlobalId>,
671 target_replica: Option<ReplicaId>,
672 timeline_context: TimelineContext,
673 oracle_read_ts: Option<Timestamp>,
674 optimizer: optimize::PeekOptimizer,
675 explain_ctx: ExplainContext,
678}
679
680#[derive(Debug)]
681pub struct PeekStageTimestampReadHold {
682 validity: PlanValidity,
683 plan: mz_sql::plan::SelectPlan,
684 max_query_result_size: Option<u64>,
685 source_ids: BTreeSet<GlobalId>,
686 target_replica: Option<ReplicaId>,
687 timeline_context: TimelineContext,
688 oracle_read_ts: Option<Timestamp>,
689 real_time_recency_ts: Option<mz_repr::Timestamp>,
690 optimizer: optimize::PeekOptimizer,
691 explain_ctx: ExplainContext,
694}
695
696#[derive(Debug)]
697pub struct PeekStageOptimize {
698 validity: PlanValidity,
699 plan: mz_sql::plan::SelectPlan,
700 max_query_result_size: Option<u64>,
701 source_ids: BTreeSet<GlobalId>,
702 id_bundle: CollectionIdBundle,
703 target_replica: Option<ReplicaId>,
704 determination: TimestampDetermination,
705 optimizer: optimize::PeekOptimizer,
706 explain_ctx: ExplainContext,
709}
710
711#[derive(Debug)]
712pub struct PeekStageFinish {
713 validity: PlanValidity,
714 plan: mz_sql::plan::SelectPlan,
715 max_query_result_size: Option<u64>,
716 id_bundle: CollectionIdBundle,
717 target_replica: Option<ReplicaId>,
718 source_ids: BTreeSet<GlobalId>,
719 determination: TimestampDetermination,
720 cluster_id: ComputeInstanceId,
721 finishing: RowSetFinishing,
722 plan_insights_optimizer_trace: Option<OptimizerTrace>,
725 insights_ctx: Option<Box<PlanInsightsContext>>,
726 global_lir_plan: optimize::peek::GlobalLirPlan,
727 optimization_finished_at: EpochMillis,
728}
729
730#[derive(Debug)]
731pub struct PeekStageCopyTo {
732 validity: PlanValidity,
733 optimizer: optimize::copy_to::Optimizer,
734 global_lir_plan: optimize::copy_to::GlobalLirPlan,
735 optimization_finished_at: EpochMillis,
736 target_replica: Option<ReplicaId>,
737 source_ids: BTreeSet<GlobalId>,
738}
739
740#[derive(Debug)]
741pub struct PeekStageExplainPlan {
742 validity: PlanValidity,
743 optimizer: optimize::peek::Optimizer,
744 df_meta: DataflowMetainfo,
745 explain_ctx: ExplainPlanContext,
746 insights_ctx: Option<Box<PlanInsightsContext>>,
747}
748
749#[derive(Debug)]
750pub struct PeekStageExplainPushdown {
751 validity: PlanValidity,
752 determination: TimestampDetermination,
753 imports: BTreeMap<GlobalId, MapFilterProject>,
754}
755
756#[derive(Debug)]
757pub enum CreateIndexStage {
758 Optimize(CreateIndexOptimize),
759 Finish(CreateIndexFinish),
760 Explain(CreateIndexExplain),
761}
762
763#[derive(Debug)]
764pub struct CreateIndexOptimize {
765 validity: PlanValidity,
766 plan: plan::CreateIndexPlan,
767 resolved_ids: ResolvedIds,
768 explain_ctx: ExplainContext,
771}
772
773#[derive(Debug)]
774pub struct CreateIndexFinish {
775 validity: PlanValidity,
776 item_id: CatalogItemId,
777 global_id: GlobalId,
778 plan: plan::CreateIndexPlan,
779 resolved_ids: ResolvedIds,
780 global_mir_plan: optimize::index::GlobalMirPlan,
781 global_lir_plan: optimize::index::GlobalLirPlan,
782 optimizer_features: OptimizerFeatures,
783}
784
785#[derive(Debug)]
786pub struct CreateIndexExplain {
787 validity: PlanValidity,
788 exported_index_id: GlobalId,
789 plan: plan::CreateIndexPlan,
790 df_meta: DataflowMetainfo,
791 explain_ctx: ExplainPlanContext,
792}
793
794#[derive(Debug)]
795pub enum CreateViewStage {
796 Optimize(CreateViewOptimize),
797 Finish(CreateViewFinish),
798 Explain(CreateViewExplain),
799}
800
801#[derive(Debug)]
802pub struct CreateViewOptimize {
803 validity: PlanValidity,
804 plan: plan::CreateViewPlan,
805 resolved_ids: ResolvedIds,
806 explain_ctx: ExplainContext,
809}
810
811#[derive(Debug)]
812pub struct CreateViewFinish {
813 validity: PlanValidity,
814 item_id: CatalogItemId,
816 global_id: GlobalId,
818 plan: plan::CreateViewPlan,
819 resolved_ids: ResolvedIds,
821 optimized_expr: OptimizedMirRelationExpr,
822}
823
824#[derive(Debug)]
825pub struct CreateViewExplain {
826 validity: PlanValidity,
827 id: GlobalId,
828 plan: plan::CreateViewPlan,
829 explain_ctx: ExplainPlanContext,
830}
831
832#[derive(Debug)]
833pub enum ExplainTimestampStage {
834 Optimize(ExplainTimestampOptimize),
835 RealTimeRecency(ExplainTimestampRealTimeRecency),
836 LinearizeTimestamp(ExplainTimestampLinearizeTimestamp),
837 Finish(ExplainTimestampFinish),
838}
839
840#[derive(Debug)]
841pub struct ExplainTimestampOptimize {
842 validity: PlanValidity,
843 plan: plan::ExplainTimestampPlan,
844 cluster_id: ClusterId,
845}
846
847#[derive(Debug)]
848pub struct ExplainTimestampRealTimeRecency {
849 validity: PlanValidity,
850 format: ExplainFormat,
851 optimized_plan: OptimizedMirRelationExpr,
852 cluster_id: ClusterId,
853 when: QueryWhen,
854}
855
856#[derive(Debug)]
857pub struct ExplainTimestampLinearizeTimestamp {
858 validity: PlanValidity,
859 format: ExplainFormat,
860 optimized_plan: OptimizedMirRelationExpr,
861 cluster_id: ClusterId,
862 source_ids: BTreeSet<GlobalId>,
863 when: QueryWhen,
864 real_time_recency_ts: Option<Timestamp>,
865}
866
867#[derive(Debug)]
868pub struct ExplainTimestampFinish {
869 validity: PlanValidity,
870 format: ExplainFormat,
871 cluster_id: ClusterId,
872 source_ids: BTreeSet<GlobalId>,
873 when: QueryWhen,
874 real_time_recency_ts: Option<Timestamp>,
875 timeline_context: TimelineContext,
878 oracle_read_ts: Option<Timestamp>,
882}
883
884#[derive(Debug)]
885pub enum ClusterStage {
886 Alter(AlterCluster),
887 WaitForHydrated(AlterClusterWaitForHydrated),
888 Finalize(AlterClusterFinalize),
889 AwaitReconfiguration(AlterClusterAwaitReconfiguration),
894}
895
896#[derive(Debug)]
897pub struct AlterCluster {
898 validity: PlanValidity,
899 plan: plan::AlterClusterPlan,
900}
901
902#[derive(Debug)]
903pub struct AlterClusterWaitForHydrated {
904 validity: PlanValidity,
905 plan: plan::AlterClusterPlan,
906 new_config: ClusterVariantManaged,
907 workload_class: Option<String>,
908 timeout_time: Instant,
909 on_timeout: OnTimeoutAction,
910}
911
912#[derive(Debug)]
913pub struct AlterClusterFinalize {
914 validity: PlanValidity,
915 plan: plan::AlterClusterPlan,
916 new_config: ClusterVariantManaged,
917 workload_class: Option<String>,
918}
919
920#[derive(Debug)]
921pub struct AlterClusterAwaitReconfiguration {
922 validity: PlanValidity,
923 cluster_id: ClusterId,
924 target: ReconfigurationTarget,
928}
929
930#[derive(Debug)]
931pub enum ExplainContext {
932 None,
934 Plan(ExplainPlanContext),
936 PlanInsightsNotice(OptimizerTrace),
939 Pushdown,
941}
942
943impl ExplainContext {
944 pub(crate) fn dispatch_guard(&self) -> Option<DispatchGuard<'_>> {
948 let optimizer_trace = match self {
949 ExplainContext::Plan(explain_ctx) => Some(&explain_ctx.optimizer_trace),
950 ExplainContext::PlanInsightsNotice(optimizer_trace) => Some(optimizer_trace),
951 _ => None,
952 };
953 optimizer_trace.map(|optimizer_trace| optimizer_trace.as_guard())
954 }
955
956 pub(crate) fn needs_cluster(&self) -> bool {
957 match self {
958 ExplainContext::None => true,
959 ExplainContext::Plan(..) => false,
960 ExplainContext::PlanInsightsNotice(..) => true,
961 ExplainContext::Pushdown => false,
962 }
963 }
964
965 pub(crate) fn needs_plan_insights(&self) -> bool {
966 matches!(
967 self,
968 ExplainContext::Plan(ExplainPlanContext {
969 stage: ExplainStage::PlanInsights,
970 ..
971 }) | ExplainContext::PlanInsightsNotice(_)
972 )
973 }
974}
975
976#[derive(Debug)]
977pub struct ExplainPlanContext {
978 pub broken: bool,
983 pub config: ExplainConfig,
984 pub format: ExplainFormat,
985 pub stage: ExplainStage,
986 pub replan: Option<GlobalId>,
987 pub desc: Option<RelationDesc>,
988 pub optimizer_trace: OptimizerTrace,
989}
990
991#[derive(Debug)]
992pub enum CreateMaterializedViewStage {
993 Optimize(CreateMaterializedViewOptimize),
994 Finish(CreateMaterializedViewFinish),
995 Explain(CreateMaterializedViewExplain),
996}
997
998#[derive(Debug)]
999pub struct CreateMaterializedViewOptimize {
1000 validity: PlanValidity,
1001 plan: plan::CreateMaterializedViewPlan,
1002 resolved_ids: ResolvedIds,
1003 explain_ctx: ExplainContext,
1006}
1007
1008#[derive(Debug)]
1009pub struct CreateMaterializedViewFinish {
1010 item_id: CatalogItemId,
1012 global_id: GlobalId,
1014 validity: PlanValidity,
1015 plan: plan::CreateMaterializedViewPlan,
1016 resolved_ids: ResolvedIds,
1017 local_mir_plan: optimize::materialized_view::LocalMirPlan,
1018 global_mir_plan: optimize::materialized_view::GlobalMirPlan,
1019 global_lir_plan: optimize::materialized_view::GlobalLirPlan,
1020 optimizer_features: OptimizerFeatures,
1021}
1022
1023#[derive(Debug)]
1024pub struct CreateMaterializedViewExplain {
1025 global_id: GlobalId,
1026 validity: PlanValidity,
1027 plan: plan::CreateMaterializedViewPlan,
1028 df_meta: DataflowMetainfo,
1029 explain_ctx: ExplainPlanContext,
1030}
1031
1032#[derive(Debug)]
1033pub enum SubscribeStage {
1034 OptimizeMir(SubscribeOptimizeMir),
1035 LinearizeTimestamp(SubscribeLinearizeTimestamp),
1036 TimestampOptimizeLir(SubscribeTimestampOptimizeLir),
1037 Finish(SubscribeFinish),
1038 Explain(SubscribeExplain),
1039}
1040
1041#[derive(Debug)]
1042pub struct SubscribeOptimizeMir {
1043 validity: PlanValidity,
1044 plan: plan::SubscribePlan,
1045 timeline: TimelineContext,
1046 dependency_ids: BTreeSet<GlobalId>,
1047 cluster_id: ComputeInstanceId,
1048 replica_id: Option<ReplicaId>,
1049 explain_ctx: ExplainContext,
1052}
1053
1054#[derive(Debug)]
1055pub struct SubscribeLinearizeTimestamp {
1056 validity: PlanValidity,
1057 plan: plan::SubscribePlan,
1058 timeline: TimelineContext,
1059 optimizer: optimize::subscribe::Optimizer,
1060 global_mir_plan: optimize::subscribe::GlobalMirPlan<optimize::subscribe::Unresolved>,
1061 dependency_ids: BTreeSet<GlobalId>,
1062 replica_id: Option<ReplicaId>,
1063 explain_ctx: ExplainContext,
1066}
1067
1068#[derive(Debug)]
1069pub struct SubscribeTimestampOptimizeLir {
1070 validity: PlanValidity,
1071 plan: plan::SubscribePlan,
1072 timeline: TimelineContext,
1073 optimizer: optimize::subscribe::Optimizer,
1074 global_mir_plan: optimize::subscribe::GlobalMirPlan<optimize::subscribe::Unresolved>,
1075 dependency_ids: BTreeSet<GlobalId>,
1076 replica_id: Option<ReplicaId>,
1077 oracle_read_ts: Option<Timestamp>,
1081 explain_ctx: ExplainContext,
1084}
1085
1086#[derive(Debug)]
1087pub struct SubscribeFinish {
1088 validity: PlanValidity,
1089 cluster_id: ComputeInstanceId,
1090 replica_id: Option<ReplicaId>,
1091 plan: plan::SubscribePlan,
1092 global_lir_plan: optimize::subscribe::GlobalLirPlan,
1093 dependency_ids: BTreeSet<GlobalId>,
1094}
1095
1096#[derive(Debug)]
1097pub struct SubscribeExplain {
1098 validity: PlanValidity,
1099 optimizer: optimize::subscribe::Optimizer,
1100 df_meta: DataflowMetainfo,
1101 cluster_id: ComputeInstanceId,
1102 explain_ctx: ExplainPlanContext,
1103}
1104
1105#[derive(Debug)]
1106pub enum IntrospectionSubscribeStage {
1107 OptimizeMir(IntrospectionSubscribeOptimizeMir),
1108 TimestampOptimizeLir(IntrospectionSubscribeTimestampOptimizeLir),
1109 Finish(IntrospectionSubscribeFinish),
1110}
1111
1112#[derive(Debug)]
1113pub struct IntrospectionSubscribeOptimizeMir {
1114 validity: PlanValidity,
1115 plan: plan::SubscribePlan,
1116 subscribe_id: GlobalId,
1117 cluster_id: ComputeInstanceId,
1118 replica_id: ReplicaId,
1119}
1120
1121#[derive(Debug)]
1122pub struct IntrospectionSubscribeTimestampOptimizeLir {
1123 validity: PlanValidity,
1124 optimizer: optimize::subscribe::Optimizer,
1125 global_mir_plan: optimize::subscribe::GlobalMirPlan<optimize::subscribe::Unresolved>,
1126 cluster_id: ComputeInstanceId,
1127 replica_id: ReplicaId,
1128}
1129
1130#[derive(Debug)]
1131pub struct IntrospectionSubscribeFinish {
1132 validity: PlanValidity,
1133 global_lir_plan: optimize::subscribe::GlobalLirPlan,
1134 read_holds: ReadHolds,
1135 cluster_id: ComputeInstanceId,
1136 replica_id: ReplicaId,
1137}
1138
1139#[derive(Debug)]
1140pub enum SecretStage {
1141 CreateEnsure(CreateSecretEnsure),
1142 CreateFinish(CreateSecretFinish),
1143 RotateKeysEnsure(RotateKeysSecretEnsure),
1144 RotateKeysFinish(RotateKeysSecretFinish),
1145 Alter(AlterSecret),
1146}
1147
1148#[derive(Debug)]
1149pub struct CreateSecretEnsure {
1150 validity: PlanValidity,
1151 plan: plan::CreateSecretPlan,
1152}
1153
1154#[derive(Debug)]
1155pub struct CreateSecretFinish {
1156 validity: PlanValidity,
1157 item_id: CatalogItemId,
1158 global_id: GlobalId,
1159 plan: plan::CreateSecretPlan,
1160}
1161
1162#[derive(Debug)]
1163pub struct RotateKeysSecretEnsure {
1164 validity: PlanValidity,
1165 id: CatalogItemId,
1166}
1167
1168#[derive(Debug)]
1169pub struct RotateKeysSecretFinish {
1170 validity: PlanValidity,
1171 ops: Vec<crate::catalog::Op>,
1172}
1173
1174#[derive(Debug)]
1175pub struct AlterSecret {
1176 validity: PlanValidity,
1177 plan: plan::AlterSecretPlan,
1178}
1179
1180#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1185pub enum TargetCluster {
1186 CatalogServer,
1188 Active,
1190 Transaction(ClusterId),
1192}
1193
1194pub(crate) enum StageResult<T> {
1196 Handle(JoinHandle<Result<T, AdapterError>>),
1198 HandleRetire(JoinHandle<Result<ExecuteResponse, AdapterError>>),
1200 Immediate(T),
1202 Response(ExecuteResponse),
1204}
1205
1206pub(crate) trait Staged: Send {
1208 type Ctx: StagedContext;
1209
1210 fn validity(&mut self) -> &mut PlanValidity;
1211
1212 async fn stage(
1214 self,
1215 coord: &mut Coordinator,
1216 ctx: &mut Self::Ctx,
1217 ) -> Result<StageResult<Box<Self>>, AdapterError>;
1218
1219 fn message(self, ctx: Self::Ctx, span: Span) -> Message;
1221
1222 fn cancel_enabled(&self) -> bool;
1224}
1225
1226pub trait StagedContext {
1227 fn retire(self, result: Result<ExecuteResponse, AdapterError>);
1228 fn session(&self) -> Option<&Session>;
1229}
1230
1231impl StagedContext for ExecuteContext {
1232 fn retire(self, result: Result<ExecuteResponse, AdapterError>) {
1233 self.retire(result);
1234 }
1235
1236 fn session(&self) -> Option<&Session> {
1237 Some(self.session())
1238 }
1239}
1240
1241impl StagedContext for () {
1242 fn retire(self, _result: Result<ExecuteResponse, AdapterError>) {}
1243
1244 fn session(&self) -> Option<&Session> {
1245 None
1246 }
1247}
1248
1249pub struct Config {
1251 pub controller_config: ControllerConfig,
1252 pub controller_envd_epoch: NonZeroI64,
1253 pub storage: Box<dyn mz_catalog::durable::DurableCatalogState>,
1254 pub timestamp_oracle_url: Option<SensitiveUrl>,
1255 pub unsafe_mode: bool,
1256 pub all_features: bool,
1257 pub build_info: &'static BuildInfo,
1258 pub environment_id: EnvironmentId,
1259 pub metrics_registry: MetricsRegistry,
1260 pub now: NowFn,
1261 pub secrets_controller: Arc<dyn SecretsController>,
1262 pub cloud_resource_controller: Option<Arc<dyn CloudResourceController>>,
1263 pub availability_zones: Vec<String>,
1264 pub cluster_replica_sizes: ClusterReplicaSizeMap,
1265 pub builtin_system_cluster_config: BootstrapBuiltinClusterConfig,
1266 pub builtin_catalog_server_cluster_config: BootstrapBuiltinClusterConfig,
1267 pub builtin_probe_cluster_config: BootstrapBuiltinClusterConfig,
1268 pub builtin_support_cluster_config: BootstrapBuiltinClusterConfig,
1269 pub builtin_analytics_cluster_config: BootstrapBuiltinClusterConfig,
1270 pub system_parameter_defaults: BTreeMap<String, String>,
1271 pub storage_usage_client: StorageUsageClient,
1272 pub storage_usage_collection_interval: Duration,
1273 pub storage_usage_retention_period: Option<Duration>,
1274 pub segment_client: Option<mz_segment::Client>,
1275 pub egress_addresses: Vec<IpNet>,
1276 pub remote_system_parameters: Option<BTreeMap<String, String>>,
1277 pub aws_account_id: Option<String>,
1278 pub aws_privatelink_availability_zones: Option<Vec<String>>,
1279 pub connection_context: ConnectionContext,
1280 pub connection_limit_callback: Box<dyn Fn(u64, u64) -> () + Send + Sync + 'static>,
1281 pub webhook_concurrency_limit: WebhookConcurrencyLimiter,
1282 pub http_host_name: Option<String>,
1283 pub tracing_handle: TracingHandle,
1284 pub read_only_controllers: bool,
1288
1289 pub caught_up_trigger: Option<Trigger>,
1293
1294 pub helm_chart_version: Option<String>,
1295 pub license_key: ValidatedLicenseKey,
1296 pub external_login_password_mz_system: Option<Password>,
1297 pub force_builtin_schema_migration: Option<String>,
1298}
1299
1300#[derive(Debug, Serialize)]
1302pub struct ConnMeta {
1303 secret_key: u32,
1308 connected_at: EpochMillis,
1310 user: User,
1311 application_name: String,
1312 uuid: Uuid,
1313 conn_id: ConnectionId,
1314 client_ip: Option<IpAddr>,
1315
1316 drop_sinks: BTreeSet<GlobalId>,
1319
1320 #[serde(skip)]
1322 deferred_lock: Option<OwnedMutexGuard<()>>,
1323
1324 pending_cluster_alters: BTreeSet<ClusterId>,
1327
1328 #[serde(skip)]
1330 notice_tx: mpsc::UnboundedSender<AdapterNotice>,
1331
1332 authenticated_role: RoleId,
1336}
1337
1338impl ConnMeta {
1339 pub fn conn_id(&self) -> &ConnectionId {
1340 &self.conn_id
1341 }
1342
1343 pub fn user(&self) -> &User {
1344 &self.user
1345 }
1346
1347 pub fn application_name(&self) -> &str {
1348 &self.application_name
1349 }
1350
1351 pub fn authenticated_role_id(&self) -> &RoleId {
1352 &self.authenticated_role
1353 }
1354
1355 pub fn uuid(&self) -> Uuid {
1356 self.uuid
1357 }
1358
1359 pub fn client_ip(&self) -> Option<IpAddr> {
1360 self.client_ip
1361 }
1362
1363 pub fn connected_at(&self) -> EpochMillis {
1364 self.connected_at
1365 }
1366}
1367
1368#[derive(Debug)]
1369pub struct PendingTxn {
1371 ctx: ExecuteContext,
1373 response: Result<PendingTxnResponse, AdapterError>,
1375 action: EndTransactionAction,
1377}
1378
1379#[derive(Debug)]
1380pub enum PendingTxnResponse {
1382 Committed {
1384 params: BTreeMap<&'static str, String>,
1386 },
1387 Rolledback {
1389 params: BTreeMap<&'static str, String>,
1391 },
1392}
1393
1394impl PendingTxnResponse {
1395 pub fn extend_params(&mut self, p: impl IntoIterator<Item = (&'static str, String)>) {
1396 match self {
1397 PendingTxnResponse::Committed { params }
1398 | PendingTxnResponse::Rolledback { params } => params.extend(p),
1399 }
1400 }
1401}
1402
1403impl From<PendingTxnResponse> for ExecuteResponse {
1404 fn from(value: PendingTxnResponse) -> Self {
1405 match value {
1406 PendingTxnResponse::Committed { params } => {
1407 ExecuteResponse::TransactionCommitted { params }
1408 }
1409 PendingTxnResponse::Rolledback { params } => {
1410 ExecuteResponse::TransactionRolledBack { params }
1411 }
1412 }
1413 }
1414}
1415
1416#[derive(Debug)]
1417pub struct PendingReadTxn {
1419 txn: PendingRead,
1421 timestamp_context: TimestampContext,
1423 created: Instant,
1425 num_requeues: u64,
1429 otel_ctx: OpenTelemetryContext,
1431}
1432
1433impl PendingReadTxn {
1434 pub fn timestamp_context(&self) -> &TimestampContext {
1436 &self.timestamp_context
1437 }
1438
1439 pub(crate) fn take_context(self) -> ExecuteContext {
1440 self.txn.take_context()
1441 }
1442}
1443
1444#[derive(Debug)]
1445enum PendingRead {
1447 Read {
1448 txn: PendingTxn,
1450 },
1451 ReadThenWrite {
1452 ctx: ExecuteContext,
1454 tx: oneshot::Sender<Option<ExecuteContext>>,
1457 },
1458}
1459
1460impl PendingRead {
1461 #[instrument(level = "debug")]
1466 pub fn finish(self) -> Option<(ExecuteContext, Result<ExecuteResponse, AdapterError>)> {
1467 match self {
1468 PendingRead::Read {
1469 txn:
1470 PendingTxn {
1471 mut ctx,
1472 response,
1473 action,
1474 },
1475 ..
1476 } => {
1477 let changed = ctx.session_mut().vars_mut().end_transaction(action);
1478 let response = response.map(|mut r| {
1480 r.extend_params(changed);
1481 ExecuteResponse::from(r)
1482 });
1483
1484 Some((ctx, response))
1485 }
1486 PendingRead::ReadThenWrite { ctx, tx, .. } => {
1487 let _ = tx.send(Some(ctx));
1489 None
1490 }
1491 }
1492 }
1493
1494 fn label(&self) -> &'static str {
1495 match self {
1496 PendingRead::Read { .. } => "read",
1497 PendingRead::ReadThenWrite { .. } => "read_then_write",
1498 }
1499 }
1500
1501 pub(crate) fn take_context(self) -> ExecuteContext {
1502 match self {
1503 PendingRead::Read { txn, .. } => txn.ctx,
1504 PendingRead::ReadThenWrite { ctx, tx, .. } => {
1505 let _ = tx.send(None);
1508 ctx
1509 }
1510 }
1511 }
1512}
1513
1514#[derive(Debug, Default)]
1524#[must_use]
1525pub struct ExecuteContextExtra {
1526 statement_uuid: Option<StatementLoggingId>,
1527}
1528
1529impl ExecuteContextExtra {
1530 pub(crate) fn new(statement_uuid: Option<StatementLoggingId>) -> Self {
1531 Self { statement_uuid }
1532 }
1533 pub fn is_trivial(&self) -> bool {
1534 self.statement_uuid.is_none()
1535 }
1536 pub fn contents(&self) -> Option<StatementLoggingId> {
1537 self.statement_uuid
1538 }
1539 #[must_use]
1543 pub(crate) fn retire(self) -> Option<StatementLoggingId> {
1544 self.statement_uuid
1545 }
1546}
1547
1548#[derive(Debug)]
1558#[must_use]
1559pub struct ExecuteContextGuard {
1560 extra: ExecuteContextExtra,
1561 coordinator_tx: mpsc::UnboundedSender<Message>,
1566}
1567
1568impl Default for ExecuteContextGuard {
1569 fn default() -> Self {
1570 let (tx, _rx) = mpsc::unbounded_channel();
1574 Self {
1575 extra: ExecuteContextExtra::default(),
1576 coordinator_tx: tx,
1577 }
1578 }
1579}
1580
1581impl ExecuteContextGuard {
1582 pub(crate) fn new(
1583 statement_uuid: Option<StatementLoggingId>,
1584 coordinator_tx: mpsc::UnboundedSender<Message>,
1585 ) -> Self {
1586 Self {
1587 extra: ExecuteContextExtra::new(statement_uuid),
1588 coordinator_tx,
1589 }
1590 }
1591 pub fn is_trivial(&self) -> bool {
1592 self.extra.is_trivial()
1593 }
1594 pub fn contents(&self) -> Option<StatementLoggingId> {
1595 self.extra.contents()
1596 }
1597 pub(crate) fn defuse(mut self) -> ExecuteContextExtra {
1604 std::mem::take(&mut self.extra)
1606 }
1607}
1608
1609impl Drop for ExecuteContextGuard {
1610 fn drop(&mut self) {
1611 if let Some(statement_uuid) = self.extra.statement_uuid.take() {
1612 let msg = Message::RetireExecute {
1615 data: ExecuteContextExtra {
1616 statement_uuid: Some(statement_uuid),
1617 },
1618 otel_ctx: OpenTelemetryContext::obtain(),
1619 reason: StatementEndedExecutionReason::Aborted,
1620 };
1621 let _ = self.coordinator_tx.send(msg);
1624 }
1625 }
1626}
1627
1628#[derive(Debug)]
1633pub struct ExecuteContext {
1634 inner: Option<Box<ExecuteContextInner>>,
1636}
1637
1638impl std::ops::Deref for ExecuteContext {
1639 type Target = ExecuteContextInner;
1640 fn deref(&self) -> &Self::Target {
1641 self.inner.as_ref().expect("only consumed by value")
1642 }
1643}
1644
1645impl std::ops::DerefMut for ExecuteContext {
1646 fn deref_mut(&mut self) -> &mut Self::Target {
1647 self.inner.as_mut().expect("only consumed by value")
1648 }
1649}
1650
1651impl Drop for ExecuteContext {
1652 fn drop(&mut self) {
1653 let Some(inner) = self.inner.take() else {
1654 return;
1655 };
1656 tracing::warn!("execute context dropped without retirement, failing the client");
1659 let ExecuteContextInner { tx, session, .. } = *inner;
1660 tx.send(
1661 Err(AdapterError::Internal(
1662 "statement execution abandoned, outcome unknown (server shutting down)".into(),
1663 )),
1664 session,
1665 );
1666 }
1667}
1668
1669#[derive(Derivative)]
1670#[derivative(Debug)]
1671pub struct ExecuteContextInner {
1672 tx: ClientTransmitter<ExecuteResponse>,
1673 internal_cmd_tx: mpsc::UnboundedSender<Message>,
1674 session: Session,
1675 extra: ExecuteContextGuard,
1676 #[derivative(Debug = "ignore")]
1677 response_barriers: Vec<BuiltinTableAppendNotify>,
1678}
1679
1680impl ExecuteContext {
1681 pub fn session(&self) -> &Session {
1682 &self.session
1683 }
1684
1685 pub fn session_mut(&mut self) -> &mut Session {
1686 &mut self.session
1687 }
1688
1689 pub fn tx(&self) -> &ClientTransmitter<ExecuteResponse> {
1690 &self.tx
1691 }
1692
1693 pub fn tx_mut(&mut self) -> &mut ClientTransmitter<ExecuteResponse> {
1694 &mut self.tx
1695 }
1696
1697 pub fn from_parts(
1698 tx: ClientTransmitter<ExecuteResponse>,
1699 internal_cmd_tx: mpsc::UnboundedSender<Message>,
1700 session: Session,
1701 extra: ExecuteContextGuard,
1702 ) -> Self {
1703 Self::from_parts_with_response_barriers(tx, internal_cmd_tx, session, extra, Vec::new())
1704 }
1705
1706 pub fn from_parts_with_response_barriers(
1707 tx: ClientTransmitter<ExecuteResponse>,
1708 internal_cmd_tx: mpsc::UnboundedSender<Message>,
1709 session: Session,
1710 extra: ExecuteContextGuard,
1711 response_barriers: Vec<BuiltinTableAppendNotify>,
1712 ) -> Self {
1713 Self {
1714 inner: Some(
1715 ExecuteContextInner {
1716 tx,
1717 session,
1718 extra,
1719 response_barriers,
1720 internal_cmd_tx,
1721 }
1722 .into(),
1723 ),
1724 }
1725 }
1726
1727 pub fn into_parts(
1741 mut self,
1742 ) -> (
1743 ClientTransmitter<ExecuteResponse>,
1744 mpsc::UnboundedSender<Message>,
1745 Session,
1746 ExecuteContextGuard,
1747 Vec<BuiltinTableAppendNotify>,
1748 ) {
1749 let ExecuteContextInner {
1750 tx,
1751 internal_cmd_tx,
1752 session,
1753 extra,
1754 response_barriers,
1755 } = *self.inner.take().expect("only consumed by value");
1756 (tx, internal_cmd_tx, session, extra, response_barriers)
1757 }
1758
1759 #[instrument(level = "debug")]
1761 pub fn retire(mut self, result: Result<ExecuteResponse, AdapterError>) {
1762 let response_barriers = std::mem::take(&mut self.response_barriers);
1763 if response_barriers.is_empty() {
1764 let (tx, internal_cmd_tx, session, extra, _) = self.into_parts();
1765 retire_execution_context(tx, internal_cmd_tx, session, extra, result);
1766 return;
1767 }
1768 spawn(
1771 || "execute_context::retire_after_response_barriers",
1772 async move {
1773 for barrier in response_barriers {
1774 barrier.await;
1775 }
1776 self.retire(result);
1777 },
1778 );
1779 }
1780
1781 pub(crate) fn delay_response_until(&mut self, barrier: BuiltinTableAppendCompletion) {
1783 self.response_barriers.push(barrier.into_notify());
1784 }
1785
1786 pub fn extra(&self) -> &ExecuteContextGuard {
1787 &self.extra
1788 }
1789
1790 pub fn extra_mut(&mut self) -> &mut ExecuteContextGuard {
1791 &mut self.extra
1792 }
1793}
1794
1795fn retire_execution_context(
1796 tx: ClientTransmitter<ExecuteResponse>,
1797 internal_cmd_tx: mpsc::UnboundedSender<Message>,
1798 session: Session,
1799 extra: ExecuteContextGuard,
1800 result: Result<ExecuteResponse, AdapterError>,
1801) {
1802 let reason = if extra.is_trivial() {
1803 None
1804 } else {
1805 Some((&result).into())
1806 };
1807 tx.send(result, session);
1808 if let Some(reason) = reason {
1809 let extra = extra.defuse();
1810 if let Err(e) = internal_cmd_tx.send(Message::RetireExecute {
1811 otel_ctx: OpenTelemetryContext::obtain(),
1812 data: extra,
1813 reason,
1814 }) {
1815 warn!("internal_cmd_rx dropped before we could send: {:?}", e);
1816 }
1817 }
1818}
1819
1820#[derive(Debug)]
1821struct ClusterReplicaStatuses(
1822 BTreeMap<ClusterId, BTreeMap<ReplicaId, BTreeMap<ProcessId, ClusterReplicaProcessStatus>>>,
1823);
1824
1825impl ClusterReplicaStatuses {
1826 pub(crate) fn new() -> ClusterReplicaStatuses {
1827 ClusterReplicaStatuses(BTreeMap::new())
1828 }
1829
1830 pub(crate) fn initialize_cluster_statuses(&mut self, cluster_id: ClusterId) {
1834 let prev = self.0.insert(cluster_id, BTreeMap::new());
1835 assert_eq!(
1836 prev, None,
1837 "cluster {cluster_id} statuses already initialized"
1838 );
1839 }
1840
1841 pub(crate) fn initialize_cluster_replica_statuses(
1845 &mut self,
1846 cluster_id: ClusterId,
1847 replica_id: ReplicaId,
1848 num_processes: usize,
1849 time: DateTime<Utc>,
1850 ) {
1851 tracing::info!(
1852 ?cluster_id,
1853 ?replica_id,
1854 ?time,
1855 "initializing cluster replica status"
1856 );
1857 let replica_statuses = self.0.entry(cluster_id).or_default();
1858 let process_statuses = (0..num_processes)
1859 .map(|process_id| {
1860 let status = ClusterReplicaProcessStatus {
1861 status: ClusterStatus::Offline(Some(OfflineReason::Initializing)),
1862 restart_count: 0,
1863 time: time.clone(),
1864 };
1865 (u64::cast_from(process_id), status)
1866 })
1867 .collect();
1868 let prev = replica_statuses.insert(replica_id, process_statuses);
1869 assert_none!(
1870 prev,
1871 "cluster replica {cluster_id}.{replica_id} statuses already initialized"
1872 );
1873 }
1874
1875 pub(crate) fn remove_cluster_statuses(
1879 &mut self,
1880 cluster_id: &ClusterId,
1881 ) -> BTreeMap<ReplicaId, BTreeMap<ProcessId, ClusterReplicaProcessStatus>> {
1882 let prev = self.0.remove(cluster_id);
1883 prev.unwrap_or_else(|| panic!("unknown cluster: {cluster_id}"))
1884 }
1885
1886 pub(crate) fn remove_cluster_replica_statuses(
1890 &mut self,
1891 cluster_id: &ClusterId,
1892 replica_id: &ReplicaId,
1893 ) -> BTreeMap<ProcessId, ClusterReplicaProcessStatus> {
1894 let replica_statuses = self
1895 .0
1896 .get_mut(cluster_id)
1897 .unwrap_or_else(|| panic!("unknown cluster: {cluster_id}"));
1898 let prev = replica_statuses.remove(replica_id);
1899 prev.unwrap_or_else(|| panic!("unknown cluster replica: {cluster_id}.{replica_id}"))
1900 }
1901
1902 pub(crate) fn ensure_cluster_status(
1906 &mut self,
1907 cluster_id: ClusterId,
1908 replica_id: ReplicaId,
1909 process_id: ProcessId,
1910 status: ClusterReplicaProcessStatus,
1911 ) {
1912 let replica_statuses = self
1913 .0
1914 .get_mut(&cluster_id)
1915 .unwrap_or_else(|| panic!("unknown cluster: {cluster_id}"))
1916 .get_mut(&replica_id)
1917 .unwrap_or_else(|| panic!("unknown cluster replica: {cluster_id}.{replica_id}"));
1918 replica_statuses.insert(process_id, status);
1919 }
1920
1921 pub fn get_cluster_replica_status(
1925 &self,
1926 cluster_id: ClusterId,
1927 replica_id: ReplicaId,
1928 ) -> ClusterStatus {
1929 let process_status = self.get_cluster_replica_statuses(cluster_id, replica_id);
1930 Self::cluster_replica_status(process_status)
1931 }
1932
1933 pub fn cluster_replica_status(
1935 process_status: &BTreeMap<ProcessId, ClusterReplicaProcessStatus>,
1936 ) -> ClusterStatus {
1937 process_status
1938 .values()
1939 .fold(ClusterStatus::Online, |s, p| match (s, p.status) {
1940 (ClusterStatus::Online, ClusterStatus::Online) => ClusterStatus::Online,
1941 (x, y) => {
1942 let reason_x = match x {
1943 ClusterStatus::Offline(reason) => reason,
1944 ClusterStatus::Online => None,
1945 };
1946 let reason_y = match y {
1947 ClusterStatus::Offline(reason) => reason,
1948 ClusterStatus::Online => None,
1949 };
1950 ClusterStatus::Offline(reason_x.or(reason_y))
1952 }
1953 })
1954 }
1955
1956 pub(crate) fn get_cluster_replica_statuses(
1960 &self,
1961 cluster_id: ClusterId,
1962 replica_id: ReplicaId,
1963 ) -> &BTreeMap<ProcessId, ClusterReplicaProcessStatus> {
1964 self.try_get_cluster_replica_statuses(cluster_id, replica_id)
1965 .unwrap_or_else(|| panic!("unknown cluster replica: {cluster_id}.{replica_id}"))
1966 }
1967
1968 pub(crate) fn try_get_cluster_replica_statuses(
1970 &self,
1971 cluster_id: ClusterId,
1972 replica_id: ReplicaId,
1973 ) -> Option<&BTreeMap<ProcessId, ClusterReplicaProcessStatus>> {
1974 self.try_get_cluster_statuses(cluster_id)
1975 .and_then(|statuses| statuses.get(&replica_id))
1976 }
1977
1978 pub(crate) fn try_get_cluster_statuses(
1980 &self,
1981 cluster_id: ClusterId,
1982 ) -> Option<&BTreeMap<ReplicaId, BTreeMap<ProcessId, ClusterReplicaProcessStatus>>> {
1983 self.0.get(&cluster_id)
1984 }
1985}
1986
1987#[derive(Derivative)]
1989#[derivative(Debug)]
1990pub struct Coordinator {
1991 #[derivative(Debug = "ignore")]
1993 controller: mz_controller::Controller,
1994 catalog: Arc<Catalog>,
2002
2003 persist_client: PersistClient,
2006
2007 internal_cmd_tx: mpsc::UnboundedSender<Message>,
2009 group_commit_tx: appends::GroupCommitNotifier,
2011 reconcile_now: Arc<Notify>,
2015 group_committer_tx: mpsc::UnboundedSender<appends::TableWriteCmd>,
2016
2017 strict_serializable_reads_tx: mpsc::UnboundedSender<(ConnectionId, PendingReadTxn)>,
2019
2020 linearize_reads_notify: Arc<Notify>,
2024
2025 global_timelines: BTreeMap<Timeline, TimelineState>,
2028
2029 transient_id_gen: Arc<TransientIdGen>,
2031 active_conns: BTreeMap<ConnectionId, ConnMeta>,
2034
2035 txn_read_holds: BTreeMap<ConnectionId, read_policy::ReadHolds>,
2039
2040 pending_peeks: BTreeMap<Uuid, PendingPeek>,
2044 client_pending_peeks: BTreeMap<ConnectionId, BTreeMap<Uuid, ClusterId>>,
2046
2047 pending_linearize_read_txns: BTreeMap<ConnectionId, PendingReadTxn>,
2049
2050 active_compute_sinks: BTreeMap<GlobalId, ActiveComputeSink>,
2052 active_webhooks: BTreeMap<CatalogItemId, WebhookAppenderInvalidator>,
2054 active_copies: BTreeMap<ConnectionId, ActiveCopyFrom>,
2057
2058 connection_cancel_watches: BTreeMap<ConnectionId, (watch::Sender<bool>, watch::Receiver<bool>)>,
2066 introspection_subscribes: BTreeMap<GlobalId, IntrospectionSubscribe>,
2068
2069 write_locks: BTreeMap<CatalogItemId, Arc<tokio::sync::Mutex<()>>>,
2071 deferred_write_ops: BTreeMap<ConnectionId, DeferredOp>,
2073
2074 pending_writes: Vec<PendingWriteTxn>,
2076
2077 advance_timelines_interval: Interval,
2087
2088 serialized_ddl: LockedVecDeque<DeferredPlanStatement>,
2097
2098 secrets_controller: Arc<dyn SecretsController>,
2101 caching_secrets_reader: CachingSecretsReader,
2103
2104 cloud_resource_controller: Option<Arc<dyn CloudResourceController>>,
2107
2108 storage_usage_client: StorageUsageClient,
2110 storage_usage_collection_interval: Duration,
2112
2113 #[derivative(Debug = "ignore")]
2115 segment_client: Option<mz_segment::Client>,
2116
2117 metrics: Metrics,
2119 optimizer_metrics: OptimizerMetrics,
2121
2122 tracing_handle: TracingHandle,
2124
2125 statement_logging: StatementLogging,
2127
2128 webhook_concurrency_limit: WebhookConcurrencyLimiter,
2130
2131 timestamp_oracle_config: Option<TimestampOracleConfig>,
2134
2135 check_cluster_scheduling_policies_interval: Interval,
2137
2138 cluster_scheduling_decisions: BTreeMap<ClusterId, BTreeMap<&'static str, SchedulingDecision>>,
2142
2143 caught_up_check_interval: Interval,
2146
2147 caught_up_check: Option<CaughtUpCheckContext>,
2150
2151 catalog_info_metrics_registry: MetricsRegistry,
2154
2155 scoped_frontend: Option<Arc<SystemParameterFrontend>>,
2164
2165 installed_watch_sets: BTreeMap<WatchSetId, (ConnectionId, WatchSetResponse)>,
2167
2168 connection_watch_sets: BTreeMap<ConnectionId, BTreeSet<WatchSetId>>,
2170
2171 cluster_replica_statuses: ClusterReplicaStatuses,
2173
2174 read_only_controllers: bool,
2178
2179 buffered_builtin_table_updates: Option<Vec<BuiltinTableUpdate>>,
2187
2188 license_key: ValidatedLicenseKey,
2189
2190 user_id_pool: IdPool,
2192}
2193
2194impl Coordinator {
2195 pub(crate) async fn reconcile_scoped_system_parameters(
2214 &mut self,
2215 scoped: ScopedParameters,
2216 prune_scope: Option<ScopedParametersScope>,
2217 ) {
2218 if self.catalog().state().scoped_system_parameters() == &scoped {
2221 return;
2222 }
2223
2224 if let Err(e) = self
2232 .catalog_transact(
2233 None,
2234 vec![crate::catalog::Op::UpdateScopedSystemParameters {
2235 scoped,
2236 prune_scope,
2237 }],
2238 )
2239 .await
2240 {
2241 tracing::warn!("failed to persist scoped system parameters: {e}");
2242 }
2243 }
2244
2245 fn scoped_overrides_create_op(
2265 &self,
2266 clusters: &[ClusterEvalContext],
2267 replicas: &[ReplicaEvalContext],
2268 ) -> Option<crate::catalog::Op> {
2269 let frontend = self.scoped_frontend.clone()?;
2270 let catalog = self.catalog();
2271 let system_config = catalog.system_config();
2272 if !ENABLE_SCOPED_SYSTEM_PARAMETERS.get(system_config.dyncfgs()) {
2273 return None;
2274 }
2275
2276 let replica_param_names: Vec<&'static str> = system_config
2279 .iter_synced()
2280 .filter(|var| var.scope() == ParameterScope::Replica)
2281 .map(|var| var.name())
2282 .collect();
2283 let cluster_param_names: Vec<&'static str> = system_config
2284 .iter_synced()
2285 .filter(|var| var.scope() == ParameterScope::Cluster)
2286 .map(|var| var.name())
2287 .collect();
2288
2289 let params = SynchronizedParameters::new(system_config.clone());
2290 let mut evaluated = ScopedParameters::default();
2291 if !cluster_param_names.is_empty() && !clusters.is_empty() {
2292 evaluated.cluster =
2293 frontend.pull_cluster_overrides(¶ms, &cluster_param_names, clusters);
2294 }
2295 if !replica_param_names.is_empty() && !replicas.is_empty() {
2296 evaluated.replica =
2297 frontend.pull_replica_overrides(¶ms, &replica_param_names, replicas);
2298 }
2299 if evaluated.is_empty() {
2300 return None;
2301 }
2302
2303 let prune_scope = ScopedParametersScope {
2307 clusters: clusters.iter().map(|cluster| cluster.cluster_id).collect(),
2308 replicas: replicas.iter().map(|replica| replica.replica_id).collect(),
2309 };
2310 Some(crate::catalog::Op::UpdateScopedSystemParameters {
2311 scoped: evaluated,
2312 prune_scope: Some(prune_scope),
2313 })
2314 }
2315
2316 pub(crate) fn push_replica_dyncfg_overrides(&mut self) {
2322 let replica_overrides = self
2325 .catalog()
2326 .state()
2327 .scoped_system_parameters()
2328 .replica
2329 .clone();
2330
2331 let dyncfgs = self.catalog().system_config().dyncfgs();
2332 let mut instance_overrides: BTreeMap<
2333 ComputeInstanceId,
2334 BTreeMap<ReplicaId, ConfigUpdates>,
2335 > = BTreeMap::new();
2336 for cluster in self.catalog().clusters() {
2337 for replica in cluster.replicas() {
2338 let Some(values) = replica_overrides.get(&replica.replica_id) else {
2339 continue;
2340 };
2341 let mut updates = ConfigUpdates::default();
2342 for (name, value) in values {
2343 let Some(entry) = dyncfgs.entry(name) else {
2344 continue;
2347 };
2348 match entry.parse_val(value) {
2349 Ok(val) => updates.add_dynamic(name, val),
2350 Err(e) => {
2351 tracing::warn!(%name, %value, "cannot parse scoped override: {e}")
2352 }
2353 }
2354 }
2355 if !updates.updates.is_empty() {
2356 instance_overrides
2357 .entry(cluster.id)
2358 .or_default()
2359 .insert(replica.replica_id, updates);
2360 }
2361 }
2362 }
2363
2364 self.controller
2375 .compute
2376 .update_replica_dyncfg_overrides(instance_overrides);
2377 let compute_config = crate::flags::compute_config(self.catalog().system_config());
2383 self.controller.compute.update_configuration(compute_config);
2384 }
2385
2386 pub(crate) fn cluster_scoped_optimizer_overrides(
2390 &self,
2391 cluster_id: ClusterId,
2392 ) -> OptimizerFeatureOverrides {
2393 self.catalog()
2394 .state()
2395 .cluster_scoped_optimizer_overrides(cluster_id)
2396 }
2397
2398 #[instrument(name = "coord::bootstrap")]
2402 pub(crate) async fn bootstrap(
2403 &mut self,
2404 boot_ts: Timestamp,
2405 migrated_storage_collections_0dt: BTreeSet<CatalogItemId>,
2406 mut builtin_table_updates: Vec<BuiltinTableUpdate>,
2407 cached_global_exprs: BTreeMap<GlobalId, GlobalExpressions>,
2408 uncached_local_exprs: BTreeMap<GlobalId, LocalExpressions>,
2409 ) -> Result<(), AdapterError> {
2410 let bootstrap_start = Instant::now();
2411 info!("startup: coordinator init: bootstrap beginning");
2412 info!("startup: coordinator init: bootstrap: preamble beginning");
2413
2414 let cluster_statuses: Vec<(_, Vec<_>)> = self
2417 .catalog()
2418 .clusters()
2419 .map(|cluster| {
2420 (
2421 cluster.id(),
2422 cluster
2423 .replicas()
2424 .map(|replica| {
2425 (replica.replica_id, replica.config.location.num_processes())
2426 })
2427 .collect(),
2428 )
2429 })
2430 .collect();
2431 let now = self.now_datetime();
2432 for (cluster_id, replica_statuses) in cluster_statuses {
2433 self.cluster_replica_statuses
2434 .initialize_cluster_statuses(cluster_id);
2435 for (replica_id, num_processes) in replica_statuses {
2436 self.cluster_replica_statuses
2437 .initialize_cluster_replica_statuses(
2438 cluster_id,
2439 replica_id,
2440 num_processes,
2441 now,
2442 );
2443 }
2444 }
2445
2446 let system_config = self.catalog().system_config();
2447
2448 mz_metrics::update_dyncfg(&system_config.dyncfg_updates());
2450
2451 let compute_config = flags::compute_config(system_config);
2453 let storage_config = flags::storage_config(system_config);
2454 let scheduling_config = flags::orchestrator_scheduling_config(system_config);
2455 let dyncfg_updates = system_config.dyncfg_updates();
2456 self.controller.compute.update_configuration(compute_config);
2457 self.controller.storage.update_parameters(storage_config);
2458 self.controller
2459 .update_orchestrator_scheduling_config(scheduling_config);
2460 self.controller.update_configuration(dyncfg_updates);
2461
2462 let enforce_credit_limit_at_bootstrap = !matches!(
2467 self.license_key.expiration_behavior,
2468 ExpirationBehavior::DisableClusterCreation,
2469 );
2470 if enforce_credit_limit_at_bootstrap {
2471 self.validate_resource_limit_numeric(
2472 Numeric::zero(),
2473 self.current_credit_consumption_rate(None),
2474 |system_vars| {
2475 self.license_key
2476 .max_credit_consumption_rate()
2477 .map_or_else(|| system_vars.max_credit_consumption_rate(), Numeric::from)
2478 },
2479 "cluster replica",
2480 MAX_CREDIT_CONSUMPTION_RATE.name(),
2481 )?;
2482 }
2483
2484 let mut policies_to_set: BTreeMap<CompactionWindow, CollectionIdBundle> =
2485 Default::default();
2486
2487 let enable_worker_core_affinity =
2488 self.catalog().system_config().enable_worker_core_affinity();
2489 let enable_storage_introspection_logs = self
2490 .catalog()
2491 .system_config()
2492 .enable_storage_introspection_logs();
2493 for instance in self.catalog.clusters() {
2494 self.controller.create_cluster(
2495 instance.id,
2496 ClusterConfig {
2497 arranged_logs: instance.log_indexes.clone(),
2498 workload_class: instance.config.workload_class.clone(),
2499 },
2500 )?;
2501 for replica in instance.replicas() {
2502 let role = instance.role();
2503 self.controller.create_replica(
2504 instance.id,
2505 replica.replica_id,
2506 instance.name.clone(),
2507 replica.name.clone(),
2508 role,
2509 replica.config.clone(),
2510 enable_worker_core_affinity,
2511 enable_storage_introspection_logs,
2512 )?;
2513 }
2514 }
2515
2516 self.push_replica_dyncfg_overrides();
2528
2529 info!(
2530 "startup: coordinator init: bootstrap: preamble complete in {:?}",
2531 bootstrap_start.elapsed()
2532 );
2533
2534 let init_storage_collections_start = Instant::now();
2535 info!("startup: coordinator init: bootstrap: storage collections init beginning");
2536 self.bootstrap_storage_collections(&migrated_storage_collections_0dt)
2537 .await;
2538 info!(
2539 "startup: coordinator init: bootstrap: storage collections init complete in {:?}",
2540 init_storage_collections_start.elapsed()
2541 );
2542
2543 self.controller.start_compute_introspection_sink();
2548
2549 let sorting_start = Instant::now();
2550 info!("startup: coordinator init: bootstrap: sorting catalog entries");
2551 let entries = self.bootstrap_sort_catalog_entries();
2552 info!(
2553 "startup: coordinator init: bootstrap: sorting catalog entries complete in {:?}",
2554 sorting_start.elapsed()
2555 );
2556
2557 let optimize_dataflows_start = Instant::now();
2558 info!("startup: coordinator init: bootstrap: optimize dataflow plans beginning");
2559 let uncached_global_exps = self.bootstrap_dataflow_plans(&entries, cached_global_exprs)?;
2560 info!(
2561 "startup: coordinator init: bootstrap: optimize dataflow plans complete in {:?}",
2562 optimize_dataflows_start.elapsed()
2563 );
2564
2565 let _fut = self.catalog().update_expression_cache(
2567 uncached_local_exprs.into_iter().collect(),
2568 uncached_global_exps.into_iter().collect(),
2569 Default::default(),
2570 );
2571
2572 let bootstrap_as_ofs_start = Instant::now();
2576 info!("startup: coordinator init: bootstrap: dataflow as-of bootstrapping beginning");
2577 let dataflow_read_holds = self.bootstrap_dataflow_as_ofs().await;
2578 info!(
2579 "startup: coordinator init: bootstrap: dataflow as-of bootstrapping complete in {:?}",
2580 bootstrap_as_ofs_start.elapsed()
2581 );
2582
2583 let postamble_start = Instant::now();
2584 info!("startup: coordinator init: bootstrap: postamble beginning");
2585
2586 let logs: BTreeSet<_> = BUILTINS::logs()
2587 .map(|log| self.catalog().resolve_builtin_log(log))
2588 .flat_map(|item_id| self.catalog().get_global_ids(&item_id))
2589 .collect();
2590
2591 let mut privatelink_connections = BTreeMap::new();
2592
2593 for entry in &entries {
2594 debug!(
2595 "coordinator init: installing {} {}",
2596 entry.item().typ(),
2597 entry.id()
2598 );
2599 let mut policy = entry.item().initial_logical_compaction_window();
2600 match entry.item() {
2601 CatalogItem::Source(source) => {
2607 if source.custom_logical_compaction_window.is_none() {
2609 if let DataSourceDesc::IngestionExport { ingestion_id, .. } =
2610 source.data_source
2611 {
2612 policy = Some(
2613 self.catalog()
2614 .get_entry(&ingestion_id)
2615 .source()
2616 .expect("must be source")
2617 .custom_logical_compaction_window
2618 .unwrap_or_default(),
2619 );
2620 }
2621 }
2622 policies_to_set
2623 .entry(policy.expect("sources have a compaction window"))
2624 .or_insert_with(Default::default)
2625 .storage_ids
2626 .insert(source.global_id());
2627 }
2628 CatalogItem::Table(table) => {
2629 policies_to_set
2630 .entry(policy.expect("tables have a compaction window"))
2631 .or_insert_with(Default::default)
2632 .storage_ids
2633 .extend(table.global_ids());
2634 }
2635 CatalogItem::Index(idx) => {
2636 let policy_entry = policies_to_set
2637 .entry(policy.expect("indexes have a compaction window"))
2638 .or_insert_with(Default::default);
2639
2640 if logs.contains(&idx.on) {
2641 policy_entry
2642 .compute_ids
2643 .entry(idx.cluster_id)
2644 .or_insert_with(BTreeSet::new)
2645 .insert(idx.global_id());
2646 } else {
2647 let df_desc = self
2648 .catalog()
2649 .try_get_physical_plan(&idx.global_id())
2650 .expect("added in `bootstrap_dataflow_plans`")
2651 .clone();
2652
2653 let df_meta = self
2654 .catalog()
2655 .try_get_dataflow_metainfo(&idx.global_id())
2656 .expect("added in `bootstrap_dataflow_plans`");
2657
2658 if self.catalog().state().system_config().enable_mz_notices() {
2659 self.catalog().state().pack_optimizer_notices(
2661 &mut builtin_table_updates,
2662 df_meta.optimizer_notices.iter(),
2663 Diff::ONE,
2664 );
2665 }
2666
2667 policy_entry
2670 .compute_ids
2671 .entry(idx.cluster_id)
2672 .or_insert_with(Default::default)
2673 .extend(df_desc.export_ids());
2674
2675 self.controller
2676 .compute
2677 .create_dataflow(idx.cluster_id, df_desc, None)
2678 .unwrap_or_terminate("cannot fail to create dataflows");
2679 }
2680 }
2681 CatalogItem::View(_) => (),
2682 CatalogItem::MaterializedView(mview) => {
2683 policies_to_set
2689 .entry(policy.expect("materialized views have a compaction window"))
2690 .or_insert_with(Default::default)
2691 .storage_ids
2692 .extend(mview.global_ids());
2693
2694 let mut df_desc = self
2695 .catalog()
2696 .try_get_physical_plan(&mview.global_id_writes())
2697 .expect("added in `bootstrap_dataflow_plans`")
2698 .clone();
2699
2700 if let Some(initial_as_of) = mview.initial_as_of.clone() {
2701 df_desc.set_initial_as_of(initial_as_of);
2702 }
2703
2704 let until = mview
2706 .refresh_schedule
2707 .as_ref()
2708 .and_then(|s| s.last_refresh())
2709 .and_then(|r| r.try_step_forward());
2710 if let Some(until) = until {
2711 df_desc.until.meet_assign(&Antichain::from_elem(until));
2712 }
2713
2714 let df_meta = self
2715 .catalog()
2716 .try_get_dataflow_metainfo(&mview.global_id_writes())
2717 .expect("added in `bootstrap_dataflow_plans`");
2718
2719 if self.catalog().state().system_config().enable_mz_notices() {
2720 self.catalog().state().pack_optimizer_notices(
2722 &mut builtin_table_updates,
2723 df_meta.optimizer_notices.iter(),
2724 Diff::ONE,
2725 );
2726 }
2727
2728 self.ship_dataflow(df_desc, mview.cluster_id, mview.target_replica)
2729 .await;
2730
2731 if mview.replacement_target.is_none() {
2734 self.allow_writes(mview.cluster_id, mview.global_id_writes());
2735 }
2736 }
2737 CatalogItem::Sink(sink) => {
2738 policies_to_set
2739 .entry(CompactionWindow::Default)
2740 .or_insert_with(Default::default)
2741 .storage_ids
2742 .insert(sink.global_id());
2743 }
2744 CatalogItem::Connection(catalog_connection) => {
2745 if let ConnectionDetails::AwsPrivatelink(conn) = &catalog_connection.details {
2746 privatelink_connections.insert(
2747 entry.id(),
2748 VpcEndpointConfig {
2749 aws_service_name: conn.service_name.clone(),
2750 availability_zone_ids: conn.availability_zones.clone(),
2751 },
2752 );
2753 }
2754 }
2755 CatalogItem::Log(_)
2757 | CatalogItem::Type(_)
2758 | CatalogItem::Func(_)
2759 | CatalogItem::Secret(_) => {}
2760 }
2761 }
2762
2763 if let Some(cloud_resource_controller) = &self.cloud_resource_controller {
2764 let existing_vpc_endpoints = cloud_resource_controller
2766 .list_vpc_endpoints()
2767 .await
2768 .context("list vpc endpoints")?;
2769 let existing_vpc_endpoints = BTreeSet::from_iter(existing_vpc_endpoints.into_keys());
2770 let desired_vpc_endpoints = privatelink_connections.keys().cloned().collect();
2771 let vpc_endpoints_to_remove = existing_vpc_endpoints.difference(&desired_vpc_endpoints);
2772 for id in vpc_endpoints_to_remove {
2773 cloud_resource_controller
2774 .delete_vpc_endpoint(*id)
2775 .await
2776 .context("deleting extraneous vpc endpoint")?;
2777 }
2778
2779 for (id, spec) in privatelink_connections {
2781 cloud_resource_controller
2782 .ensure_vpc_endpoint(id, spec)
2783 .await
2784 .context("ensuring vpc endpoint")?;
2785 }
2786 }
2787
2788 drop(dataflow_read_holds);
2791 for (cw, policies) in policies_to_set {
2793 self.initialize_read_policies(&policies, cw).await;
2794 }
2795
2796 builtin_table_updates.extend(
2798 self.catalog().state().resolve_builtin_table_updates(
2799 self.catalog().state().pack_all_replica_size_updates(),
2800 ),
2801 );
2802
2803 debug!("startup: coordinator init: bootstrap: initializing migrated builtin tables");
2804 let migrated_updates_fut = if self.controller.read_only() {
2810 let min_timestamp = Timestamp::minimum();
2811 let migrated_builtin_table_updates: Vec<_> = builtin_table_updates
2812 .extract_if(.., |update| {
2813 let gid = self.catalog().get_entry(&update.id).latest_global_id();
2814 migrated_storage_collections_0dt.contains(&update.id)
2815 && self
2816 .controller
2817 .storage_collections
2818 .collection_frontiers(gid)
2819 .expect("all tables are registered")
2820 .write_frontier
2821 .elements()
2822 == &[min_timestamp]
2823 })
2824 .collect();
2825 if migrated_builtin_table_updates.is_empty() {
2826 futures::future::ready(()).boxed()
2827 } else {
2828 let mut grouped_appends: BTreeMap<GlobalId, Vec<TableData>> = BTreeMap::new();
2830 for update in migrated_builtin_table_updates {
2831 let gid = self.catalog().get_entry(&update.id).latest_global_id();
2832 grouped_appends.entry(gid).or_default().push(update.data);
2833 }
2834 info!(
2835 "coordinator init: rehydrating migrated builtin tables in read-only mode: {:?}",
2836 grouped_appends.keys().collect::<Vec<_>>()
2837 );
2838
2839 let mut all_appends = Vec::with_capacity(grouped_appends.len());
2841 for (item_id, table_data) in grouped_appends.into_iter() {
2842 let mut all_rows = Vec::new();
2843 let mut all_data = Vec::new();
2844 for data in table_data {
2845 match data {
2846 TableData::Rows(rows) => all_rows.extend(rows),
2847 TableData::Batches(_) => all_data.push(data),
2848 }
2849 }
2850 differential_dataflow::consolidation::consolidate(&mut all_rows);
2851 all_data.push(TableData::Rows(all_rows));
2852
2853 all_appends.push((item_id, all_data));
2855 }
2856
2857 let fut = self
2858 .controller
2859 .storage
2860 .append_table(min_timestamp, boot_ts.step_forward(), all_appends)
2861 .expect("cannot fail to append");
2862 async {
2863 fut.await
2864 .expect("One-shot shouldn't be dropped during bootstrap")
2865 .unwrap_or_terminate("cannot fail to append")
2866 }
2867 .boxed()
2868 }
2869 } else {
2870 futures::future::ready(()).boxed()
2871 };
2872
2873 info!(
2874 "startup: coordinator init: bootstrap: postamble complete in {:?}",
2875 postamble_start.elapsed()
2876 );
2877
2878 let builtin_update_start = Instant::now();
2879 info!("startup: coordinator init: bootstrap: generate builtin updates beginning");
2880
2881 if self.controller.read_only() {
2882 info!(
2883 "coordinator init: bootstrap: stashing builtin table updates while in read-only mode"
2884 );
2885
2886 self.buffered_builtin_table_updates
2887 .as_mut()
2888 .expect("in read-only mode")
2889 .append(&mut builtin_table_updates);
2890 } else {
2891 self.bootstrap_tables(&entries, builtin_table_updates).await;
2892 };
2893 info!(
2894 "startup: coordinator init: bootstrap: generate builtin updates complete in {:?}",
2895 builtin_update_start.elapsed()
2896 );
2897
2898 let cleanup_secrets_start = Instant::now();
2899 info!("startup: coordinator init: bootstrap: generate secret cleanup beginning");
2900 {
2904 let Self {
2907 secrets_controller,
2908 catalog,
2909 ..
2910 } = self;
2911
2912 let next_user_item_id = catalog.get_next_user_item_id().await?;
2913 let next_system_item_id = catalog.get_next_system_item_id().await?;
2914 let read_only = self.controller.read_only();
2915 let catalog_ids: BTreeSet<CatalogItemId> =
2920 catalog.entries().map(|entry| entry.id()).collect();
2921 let secrets_controller = Arc::clone(secrets_controller);
2922
2923 spawn(|| "cleanup-orphaned-secrets", async move {
2924 if read_only {
2925 info!(
2926 "coordinator init: not cleaning up orphaned secrets while in read-only mode"
2927 );
2928 return;
2929 }
2930 info!("coordinator init: cleaning up orphaned secrets");
2931
2932 match secrets_controller.list().await {
2933 Ok(controller_secrets) => {
2934 let controller_secrets: BTreeSet<CatalogItemId> =
2935 controller_secrets.into_iter().collect();
2936 let orphaned = controller_secrets.difference(&catalog_ids);
2937 for id in orphaned {
2938 let id_too_large = match id {
2939 CatalogItemId::System(id) => *id >= next_system_item_id,
2940 CatalogItemId::User(id) => *id >= next_user_item_id,
2941 CatalogItemId::IntrospectionSourceIndex(_)
2942 | CatalogItemId::Transient(_) => false,
2943 };
2944 if id_too_large {
2945 info!(
2946 %next_user_item_id, %next_system_item_id,
2947 "coordinator init: not deleting orphaned secret {id} that was likely created by a newer deploy generation"
2948 );
2949 } else {
2950 info!("coordinator init: deleting orphaned secret {id}");
2951 fail_point!("orphan_secrets");
2952 if let Err(e) = secrets_controller.delete(*id).await {
2953 warn!(
2954 "Dropping orphaned secret has encountered an error: {}",
2955 e
2956 );
2957 }
2958 }
2959 }
2960 }
2961 Err(e) => warn!("Failed to list secrets during orphan cleanup: {:?}", e),
2962 }
2963 });
2964 }
2965 info!(
2966 "startup: coordinator init: bootstrap: generate secret cleanup complete in {:?}",
2967 cleanup_secrets_start.elapsed()
2968 );
2969
2970 let final_steps_start = Instant::now();
2972 info!(
2973 "startup: coordinator init: bootstrap: migrate builtin tables in read-only mode beginning"
2974 );
2975 migrated_updates_fut
2976 .instrument(info_span!("coord::bootstrap::final"))
2977 .await;
2978
2979 debug!(
2980 "startup: coordinator init: bootstrap: announcing completion of initialization to controller"
2981 );
2982 self.controller.initialization_complete();
2984
2985 self.bootstrap_introspection_subscribes().await;
2987
2988 info!(
2989 "startup: coordinator init: bootstrap: migrate builtin tables in read-only mode complete in {:?}",
2990 final_steps_start.elapsed()
2991 );
2992
2993 info!(
2994 "startup: coordinator init: bootstrap complete in {:?}",
2995 bootstrap_start.elapsed()
2996 );
2997 Ok(())
2998 }
2999
3000 #[allow(clippy::async_yields_async)]
3005 #[instrument]
3006 async fn bootstrap_tables(
3007 &mut self,
3008 entries: &[CatalogEntry],
3009 mut builtin_table_updates: Vec<BuiltinTableUpdate>,
3010 ) {
3011 struct TableMetadata<'a> {
3013 id: CatalogItemId,
3014 name: &'a QualifiedItemName,
3015 table: &'a Table,
3016 }
3017
3018 let table_metas: Vec<_> = entries
3020 .into_iter()
3021 .filter_map(|entry| {
3022 entry.table().map(|table| TableMetadata {
3023 id: entry.id(),
3024 name: entry.name(),
3025 table,
3026 })
3027 })
3028 .collect();
3029
3030 debug!("coordinator init: advancing all tables to current timestamp");
3032 let WriteTimestamp {
3033 timestamp: write_ts,
3034 advance_to,
3035 } = self.get_local_write_ts().await;
3036 let appends = table_metas
3037 .iter()
3038 .map(|meta| (meta.table.global_id_writes(), Vec::new()))
3039 .collect();
3040 let table_fence_rx = self
3044 .controller
3045 .storage
3046 .append_table(write_ts.clone(), advance_to, appends)
3047 .expect("invalid updates");
3048
3049 self.apply_local_write(write_ts).await;
3050
3051 debug!("coordinator init: resetting system tables");
3053 let read_ts = self.get_local_read_ts().await;
3054
3055 let mz_storage_usage_by_shard_schema: SchemaSpecifier = self
3060 .catalog()
3061 .resolve_system_schema(MZ_STORAGE_USAGE_BY_SHARD.schema)
3062 .into();
3063 let arrangement_size_history_schema: SchemaSpecifier = self
3064 .catalog()
3065 .resolve_system_schema(MZ_OBJECT_ARRANGEMENT_SIZE_HISTORY.schema)
3066 .into();
3067 let is_retained_across_restarts = |meta: &TableMetadata| -> bool {
3068 (meta.name.item == MZ_STORAGE_USAGE_BY_SHARD.name
3069 && meta.name.qualifiers.schema_spec == mz_storage_usage_by_shard_schema)
3070 || (meta.name.item == MZ_OBJECT_ARRANGEMENT_SIZE_HISTORY.name
3071 && meta.name.qualifiers.schema_spec == arrangement_size_history_schema)
3072 };
3073
3074 let mut retraction_tasks = Vec::new();
3075 let system_tables: Vec<_> = table_metas
3076 .iter()
3077 .filter(|meta| meta.id.is_system() && !is_retained_across_restarts(meta))
3078 .collect();
3079
3080 for system_table in system_tables {
3081 let table_id = system_table.id;
3082 let full_name = self.catalog().resolve_full_name(system_table.name, None);
3083 debug!("coordinator init: resetting system table {full_name} ({table_id})");
3084
3085 let snapshot_fut = self
3087 .controller
3088 .storage_collections
3089 .snapshot_cursor(system_table.table.global_id_writes(), read_ts);
3090 let batch_fut = self
3091 .controller
3092 .storage_collections
3093 .create_update_builder(system_table.table.global_id_writes());
3094
3095 let task = spawn(|| format!("snapshot-{table_id}"), async move {
3096 let mut batch = batch_fut
3098 .await
3099 .unwrap_or_terminate("cannot fail to create a batch for a BuiltinTable");
3100 tracing::info!(?table_id, "starting snapshot");
3101 let mut snapshot_cursor = snapshot_fut
3103 .await
3104 .unwrap_or_terminate("cannot fail to snapshot");
3105
3106 while let Some(values) = snapshot_cursor.next().await {
3108 for (key, _t, d) in values {
3109 let d_invert = d.neg();
3110 batch.add(&key, &(), &d_invert).await;
3111 }
3112 }
3113 tracing::info!(?table_id, "finished snapshot");
3114
3115 let batch = batch.finish().await;
3116 BuiltinTableUpdate::batch(table_id, batch)
3117 });
3118 retraction_tasks.push(task);
3119 }
3120
3121 let retractions_res = futures::future::join_all(retraction_tasks).await;
3122 for retractions in retractions_res {
3123 builtin_table_updates.push(retractions);
3124 }
3125
3126 table_fence_rx
3128 .await
3129 .expect("One-shot shouldn't be dropped during bootstrap")
3130 .unwrap_or_terminate("cannot fail to append");
3131
3132 info!("coordinator init: sending builtin table updates");
3133 let builtin_updates_fut = self.builtin_table_update().execute(builtin_table_updates);
3134 builtin_updates_fut.await;
3137 }
3138
3139 #[instrument]
3152 async fn bootstrap_storage_collections(
3153 &mut self,
3154 migrated_storage_collections: &BTreeSet<CatalogItemId>,
3155 ) {
3156 let catalog = self.catalog();
3157
3158 let source_desc = |object_id: GlobalId,
3159 data_source: &DataSourceDesc,
3160 desc: &RelationDesc,
3161 timeline: &Timeline| {
3162 let data_source = match data_source.clone() {
3163 DataSourceDesc::Ingestion { desc, cluster_id } => {
3165 let desc = desc.into_inline_connection(catalog.state());
3166 let ingestion = IngestionDescription::new(desc, cluster_id, object_id);
3167 DataSource::Ingestion(ingestion)
3168 }
3169 DataSourceDesc::OldSyntaxIngestion {
3170 desc,
3171 progress_subsource,
3172 data_config,
3173 details,
3174 cluster_id,
3175 } => {
3176 let desc = desc.into_inline_connection(catalog.state());
3177 let data_config = data_config.into_inline_connection(catalog.state());
3178 let progress_subsource =
3181 catalog.get_entry(&progress_subsource).latest_global_id();
3182 let mut ingestion =
3183 IngestionDescription::new(desc, cluster_id, progress_subsource);
3184 let legacy_export = SourceExport {
3185 storage_metadata: (),
3186 data_config,
3187 details,
3188 };
3189 ingestion.source_exports.insert(object_id, legacy_export);
3190
3191 DataSource::Ingestion(ingestion)
3192 }
3193 DataSourceDesc::IngestionExport {
3194 ingestion_id,
3195 external_reference: _,
3196 details,
3197 data_config,
3198 } => {
3199 let ingestion_id = catalog.get_entry(&ingestion_id).latest_global_id();
3202
3203 DataSource::IngestionExport {
3204 ingestion_id,
3205 details,
3206 data_config: data_config.into_inline_connection(catalog.state()),
3207 }
3208 }
3209 DataSourceDesc::Webhook { .. } => DataSource::Webhook,
3210 DataSourceDesc::Progress => DataSource::Progress,
3211 DataSourceDesc::Introspection(introspection) => {
3212 DataSource::Introspection(introspection)
3213 }
3214 DataSourceDesc::Catalog => DataSource::Other,
3215 };
3216 CollectionDescription {
3217 desc: desc.clone(),
3218 data_source,
3219 since: None,
3220 timeline: Some(timeline.clone()),
3221 primary: None,
3222 }
3223 };
3224
3225 let mut compute_collections = vec![];
3226 let mut collections = vec![];
3227 for entry in catalog.entries() {
3228 match entry.item() {
3229 CatalogItem::Source(source) => {
3230 collections.push((
3231 source.global_id(),
3232 source_desc(
3233 source.global_id(),
3234 &source.data_source,
3235 &source.desc,
3236 &source.timeline,
3237 ),
3238 ));
3239 }
3240 CatalogItem::Table(table) => {
3241 match &table.data_source {
3242 TableDataSource::TableWrites { defaults: _ } => {
3243 let versions: BTreeMap<_, _> = table
3244 .collection_descs()
3245 .map(|(gid, version, desc)| (version, (gid, desc)))
3246 .collect();
3247 let collection_descs = versions.iter().map(|(version, (gid, desc))| {
3248 let next_version = version.bump();
3249 let primary_collection =
3250 versions.get(&next_version).map(|(gid, _desc)| gid).copied();
3251 let mut collection_desc =
3252 CollectionDescription::for_table(desc.clone());
3253 collection_desc.primary = primary_collection;
3254
3255 (*gid, collection_desc)
3256 });
3257 collections.extend(collection_descs);
3258 }
3259 TableDataSource::DataSource {
3260 desc: data_source_desc,
3261 timeline,
3262 } => {
3263 soft_assert_eq_or_log!(table.collections.len(), 1);
3265 let collection_descs =
3266 table.collection_descs().map(|(gid, _version, desc)| {
3267 (
3268 gid,
3269 source_desc(
3270 entry.latest_global_id(),
3271 data_source_desc,
3272 &desc,
3273 timeline,
3274 ),
3275 )
3276 });
3277 collections.extend(collection_descs);
3278 }
3279 };
3280 }
3281 CatalogItem::MaterializedView(mv) => {
3282 let mut primary = mv
3290 .replacement_target
3291 .map(|target_id| catalog.get_entry(&target_id).latest_global_id());
3292 let collection_descs = mv.collection_descs().map(|(gid, _version, desc)| {
3293 let mut collection_desc =
3294 CollectionDescription::for_other(desc, mv.initial_as_of.clone());
3295 collection_desc.primary = primary;
3296 primary = Some(gid);
3297 (gid, collection_desc)
3298 });
3299
3300 collections.extend(collection_descs);
3301 compute_collections.push((mv.global_id_writes(), mv.desc.latest()));
3302 }
3303 CatalogItem::Sink(sink) => {
3304 let storage_sink_from_entry = self.catalog().get_entry_by_global_id(&sink.from);
3305 let from_desc = storage_sink_from_entry
3306 .relation_desc()
3307 .expect("sinks can only be built on items with descs")
3308 .into_owned();
3309 let collection_desc = CollectionDescription {
3310 desc: KAFKA_PROGRESS_DESC.clone(),
3312 data_source: DataSource::Sink {
3313 desc: ExportDescription {
3314 sink: StorageSinkDesc {
3315 from: sink.from,
3316 from_desc,
3317 connection: sink
3318 .connection
3319 .clone()
3320 .into_inline_connection(self.catalog().state()),
3321 envelope: sink.envelope,
3322 as_of: Antichain::from_elem(Timestamp::minimum()),
3323 with_snapshot: sink.with_snapshot,
3324 version: sink.version,
3325 from_storage_metadata: (),
3326 to_storage_metadata: (),
3327 commit_interval: sink.commit_interval,
3328 },
3329 instance_id: sink.cluster_id,
3330 },
3331 },
3332 since: None,
3333 timeline: None,
3334 primary: None,
3335 };
3336 collections.push((sink.global_id, collection_desc));
3337 }
3338 CatalogItem::Log(_)
3339 | CatalogItem::View(_)
3340 | CatalogItem::Index(_)
3341 | CatalogItem::Type(_)
3342 | CatalogItem::Func(_)
3343 | CatalogItem::Secret(_)
3344 | CatalogItem::Connection(_) => (),
3345 }
3346 }
3347
3348 let register_ts = if self.controller.read_only() {
3349 self.get_local_read_ts().await
3350 } else {
3351 self.get_local_write_ts().await.timestamp
3354 };
3355
3356 let storage_metadata = self.catalog.state().storage_metadata();
3357 let migrated_storage_collections = migrated_storage_collections
3358 .into_iter()
3359 .flat_map(|item_id| self.catalog.get_entry(item_id).global_ids())
3360 .collect();
3361
3362 self.controller
3367 .storage
3368 .evolve_nullability_for_bootstrap(storage_metadata, compute_collections)
3369 .await
3370 .unwrap_or_terminate("cannot fail to evolve collections");
3371
3372 let mut pending: BTreeMap<_, _> = collections.into_iter().collect();
3385
3386 let transitive_dep_gids: BTreeMap<_, _> = pending
3388 .keys()
3389 .map(|gid| {
3390 let entry = self.catalog.get_entry_by_global_id(gid);
3391 let item_id = entry.id();
3392 let deps = self.catalog.state().transitive_uses(item_id);
3393 let dep_gids: BTreeSet<_> = deps
3394 .filter(|dep_id| *dep_id != item_id)
3397 .map(|dep_id| self.catalog.get_entry(&dep_id).latest_global_id())
3398 .filter(|dep_gid| pending.contains_key(dep_gid))
3400 .collect();
3401 (*gid, dep_gids)
3402 })
3403 .collect();
3404
3405 let mut created_gids = Vec::new();
3406
3407 while !pending.is_empty() {
3408 let ready_gids: BTreeSet<_> = pending
3411 .keys()
3412 .filter(|gid| {
3413 let mut deps = transitive_dep_gids[gid].iter();
3414 !deps.any(|dep_gid| pending.contains_key(dep_gid))
3415 })
3416 .copied()
3417 .collect();
3418 let mut ready: Vec<_> = pending
3419 .extract_if(.., |gid, _| ready_gids.contains(gid))
3420 .collect();
3421
3422 for (gid, collection) in &mut ready {
3424 if !gid.is_system() || collection.since.is_some() {
3426 continue;
3427 }
3428
3429 let mut derived_since = Antichain::from_elem(Timestamp::MIN);
3430 for dep_gid in &transitive_dep_gids[gid] {
3431 let (since, _) = self
3432 .controller
3433 .storage
3434 .collection_frontiers(*dep_gid)
3435 .expect("previously registered");
3436 derived_since.join_assign(&since);
3437 }
3438 collection.since = Some(derived_since);
3439 }
3440
3441 if ready.is_empty() {
3442 soft_panic_or_log!(
3443 "cycle in storage collections: {:?}",
3444 pending.keys().collect::<Vec<_>>(),
3445 );
3446 ready = mem::take(&mut pending).into_iter().collect();
3450 }
3451
3452 created_gids.extend(ready.iter().map(|(gid, _collection)| *gid));
3453
3454 self.controller
3455 .storage
3456 .create_collections_for_bootstrap(
3457 storage_metadata,
3458 Some(register_ts),
3459 ready,
3460 &migrated_storage_collections,
3461 )
3462 .await
3463 .unwrap_or_terminate("cannot fail to create collections");
3464 }
3465
3466 self.controller
3468 .storage
3469 .register_table_collections(register_ts, created_gids)
3470 .await
3471 .unwrap_or_terminate("cannot fail to register tables");
3472
3473 if !self.controller.read_only() {
3474 self.apply_local_write(register_ts).await;
3475 }
3476 }
3477
3478 fn bootstrap_sort_catalog_entries(&self) -> Vec<CatalogEntry> {
3485 let mut indexes_on = BTreeMap::<_, Vec<_>>::new();
3486 let mut non_indexes = Vec::new();
3487 for entry in self.catalog().entries().cloned() {
3488 if let Some(index) = entry.index() {
3489 let on = self.catalog().get_entry_by_global_id(&index.on);
3490 indexes_on.entry(on.id()).or_default().push(entry);
3491 } else {
3492 non_indexes.push(entry);
3493 }
3494 }
3495
3496 let key_fn = |entry: &CatalogEntry| entry.id;
3497 let dependencies_fn = |entry: &CatalogEntry| entry.uses();
3498 sort_topological(&mut non_indexes, key_fn, dependencies_fn);
3499
3500 let mut result = Vec::new();
3501 for entry in non_indexes {
3502 let id = entry.id();
3503 result.push(entry);
3504 if let Some(mut indexes) = indexes_on.remove(&id) {
3505 result.append(&mut indexes);
3506 }
3507 }
3508
3509 soft_assert_or_log!(
3510 indexes_on.is_empty(),
3511 "indexes with missing dependencies: {indexes_on:?}",
3512 );
3513
3514 result
3515 }
3516
3517 #[instrument]
3528 fn bootstrap_dataflow_plans(
3529 &mut self,
3530 ordered_catalog_entries: &[CatalogEntry],
3531 mut cached_global_exprs: BTreeMap<GlobalId, GlobalExpressions>,
3532 ) -> Result<BTreeMap<GlobalId, GlobalExpressions>, AdapterError> {
3533 let mut instance_snapshots = BTreeMap::new();
3539 let mut uncached_expressions = BTreeMap::new();
3540
3541 let optimizer_config = |catalog: &Catalog, cluster_id| {
3542 let system_config = catalog.system_config();
3543 let overrides = catalog.get_cluster(cluster_id).config.features();
3544 OptimizerConfig::from(system_config)
3545 .override_from(&overrides)
3546 .override_from(
3549 &catalog
3550 .state()
3551 .cluster_scoped_optimizer_overrides(cluster_id),
3552 )
3553 };
3554
3555 for entry in ordered_catalog_entries {
3556 match entry.item() {
3557 CatalogItem::Index(idx) => {
3558 let compute_instance =
3560 instance_snapshots.entry(idx.cluster_id).or_insert_with(|| {
3561 self.instance_snapshot(idx.cluster_id)
3562 .expect("compute instance exists")
3563 });
3564 let global_id = idx.global_id();
3565
3566 if compute_instance.contains_collection(&global_id) {
3569 continue;
3570 }
3571
3572 let optimizer_config = optimizer_config(&self.catalog, idx.cluster_id);
3573
3574 let (optimized_plan, physical_plan, metainfo) =
3575 match cached_global_exprs.remove(&global_id) {
3576 Some(global_expressions)
3577 if global_expressions.optimizer_features
3578 == optimizer_config.features =>
3579 {
3580 debug!("global expression cache hit for {global_id:?}");
3581 (
3582 global_expressions.global_mir,
3583 global_expressions.physical_plan,
3584 global_expressions.dataflow_metainfos,
3585 )
3586 }
3587 Some(_) | None => {
3588 let (optimized_plan, global_lir_plan) = {
3589 let mut optimizer = optimize::index::Optimizer::new(
3591 self.owned_catalog(),
3592 compute_instance.clone(),
3593 global_id,
3594 optimizer_config.clone(),
3595 self.optimizer_metrics(),
3596 );
3597
3598 let index_plan = optimize::index::Index::new(
3600 entry.name().clone(),
3601 idx.on,
3602 idx.keys.to_vec(),
3603 );
3604 let global_mir_plan = optimizer.optimize(index_plan)?;
3605 let optimized_plan = global_mir_plan.df_desc().clone();
3606
3607 let global_lir_plan = optimizer.optimize(global_mir_plan)?;
3609
3610 (optimized_plan, global_lir_plan)
3611 };
3612
3613 let (physical_plan, metainfo) = global_lir_plan.unapply();
3614 let metainfo = {
3615 let notice_ids =
3617 std::iter::repeat_with(|| self.allocate_transient_id())
3618 .map(|(_item_id, gid)| gid)
3619 .take(metainfo.optimizer_notices.len())
3620 .collect::<Vec<_>>();
3621 self.catalog().render_notices(
3623 metainfo,
3624 notice_ids,
3625 Some(idx.global_id()),
3626 )
3627 };
3628 uncached_expressions.insert(
3629 global_id,
3630 GlobalExpressions {
3631 global_mir: optimized_plan.clone(),
3632 physical_plan: physical_plan.clone(),
3633 dataflow_metainfos: metainfo.clone(),
3634 optimizer_features: optimizer_config.features.clone(),
3635 },
3636 );
3637 (optimized_plan, physical_plan, metainfo)
3638 }
3639 };
3640
3641 let catalog = self.catalog_mut();
3642 catalog.set_optimized_plan(idx.global_id(), optimized_plan);
3643 catalog.set_physical_plan(idx.global_id(), physical_plan);
3644 catalog.set_dataflow_metainfo(idx.global_id(), metainfo);
3645
3646 compute_instance.insert_collection(idx.global_id());
3647 }
3648 CatalogItem::MaterializedView(mv) => {
3649 let compute_instance =
3651 instance_snapshots.entry(mv.cluster_id).or_insert_with(|| {
3652 self.instance_snapshot(mv.cluster_id)
3653 .expect("compute instance exists")
3654 });
3655 let global_id = mv.global_id_writes();
3656
3657 let optimizer_config = optimizer_config(&self.catalog, mv.cluster_id);
3658
3659 let (optimized_plan, physical_plan, metainfo) = match cached_global_exprs
3660 .remove(&global_id)
3661 {
3662 Some(global_expressions)
3663 if global_expressions.optimizer_features
3664 == optimizer_config.features =>
3665 {
3666 debug!("global expression cache hit for {global_id:?}");
3667 (
3668 global_expressions.global_mir,
3669 global_expressions.physical_plan,
3670 global_expressions.dataflow_metainfos,
3671 )
3672 }
3673 Some(_) | None => {
3674 let (_, internal_view_id) = self.allocate_transient_id();
3675 let debug_name = self
3676 .catalog()
3677 .resolve_full_name(entry.name(), None)
3678 .to_string();
3679
3680 let (optimized_plan, global_lir_plan) = {
3681 let mut optimizer = optimize::materialized_view::Optimizer::new(
3683 self.owned_catalog().as_optimizer_catalog(),
3684 compute_instance.clone(),
3685 global_id,
3686 internal_view_id,
3687 mv.desc.latest().iter_names().cloned().collect(),
3688 mv.non_null_assertions.clone(),
3689 mv.refresh_schedule.clone(),
3690 debug_name,
3691 optimizer_config.clone(),
3692 self.optimizer_metrics(),
3693 );
3694
3695 let typ = infer_sql_type_for_catalog(
3698 &mv.raw_expr,
3699 &mv.locally_optimized_expr.as_ref().clone(),
3700 );
3701 let global_mir_plan = optimizer
3702 .optimize((mv.locally_optimized_expr.as_ref().clone(), typ))?;
3703 let optimized_plan = global_mir_plan.df_desc().clone();
3704
3705 let global_lir_plan = optimizer.optimize(global_mir_plan)?;
3707
3708 (optimized_plan, global_lir_plan)
3709 };
3710
3711 let (physical_plan, metainfo) = global_lir_plan.unapply();
3712 let metainfo = {
3713 let notice_ids =
3715 std::iter::repeat_with(|| self.allocate_transient_id())
3716 .map(|(_item_id, global_id)| global_id)
3717 .take(metainfo.optimizer_notices.len())
3718 .collect::<Vec<_>>();
3719 self.catalog().render_notices(
3721 metainfo,
3722 notice_ids,
3723 Some(mv.global_id_writes()),
3724 )
3725 };
3726 uncached_expressions.insert(
3727 global_id,
3728 GlobalExpressions {
3729 global_mir: optimized_plan.clone(),
3730 physical_plan: physical_plan.clone(),
3731 dataflow_metainfos: metainfo.clone(),
3732 optimizer_features: optimizer_config.features.clone(),
3733 },
3734 );
3735 (optimized_plan, physical_plan, metainfo)
3736 }
3737 };
3738
3739 let catalog = self.catalog_mut();
3740 catalog.set_optimized_plan(mv.global_id_writes(), optimized_plan);
3741 catalog.set_physical_plan(mv.global_id_writes(), physical_plan);
3742 catalog.set_dataflow_metainfo(mv.global_id_writes(), metainfo);
3743
3744 compute_instance.insert_collection(mv.global_id_writes());
3745 }
3746 CatalogItem::Table(_)
3747 | CatalogItem::Source(_)
3748 | CatalogItem::Log(_)
3749 | CatalogItem::View(_)
3750 | CatalogItem::Sink(_)
3751 | CatalogItem::Type(_)
3752 | CatalogItem::Func(_)
3753 | CatalogItem::Secret(_)
3754 | CatalogItem::Connection(_) => (),
3755 }
3756 }
3757
3758 Ok(uncached_expressions)
3759 }
3760
3761 async fn bootstrap_dataflow_as_ofs(&mut self) -> BTreeMap<GlobalId, ReadHold> {
3771 let mut catalog_ids = Vec::new();
3772 let mut dataflows = Vec::new();
3773 let mut read_policies = BTreeMap::new();
3774 for entry in self.catalog.entries() {
3775 let gid = match entry.item() {
3776 CatalogItem::Index(idx) => idx.global_id(),
3777 CatalogItem::MaterializedView(mv) => mv.global_id_writes(),
3778 CatalogItem::Table(_)
3779 | CatalogItem::Source(_)
3780 | CatalogItem::Log(_)
3781 | CatalogItem::View(_)
3782 | CatalogItem::Sink(_)
3783 | CatalogItem::Type(_)
3784 | CatalogItem::Func(_)
3785 | CatalogItem::Secret(_)
3786 | CatalogItem::Connection(_) => continue,
3787 };
3788 if let Some(plan) = self.catalog.try_get_physical_plan(&gid) {
3789 catalog_ids.push(gid);
3790 dataflows.push(plan.clone());
3791
3792 if let Some(compaction_window) = entry.item().initial_logical_compaction_window() {
3793 read_policies.insert(gid, compaction_window.into());
3794 }
3795 }
3796 }
3797
3798 let read_ts = self.get_local_read_ts().await;
3799 let read_holds = as_of_selection::run(
3800 &mut dataflows,
3801 &read_policies,
3802 &*self.controller.storage_collections,
3803 read_ts,
3804 self.controller.read_only(),
3805 );
3806
3807 let catalog = self.catalog_mut();
3808 for (id, plan) in catalog_ids.into_iter().zip_eq(dataflows) {
3809 catalog.set_physical_plan(id, plan);
3810 }
3811
3812 read_holds
3813 }
3814
3815 fn serve(
3824 mut self,
3825 mut internal_cmd_rx: mpsc::UnboundedReceiver<Message>,
3826 mut strict_serializable_reads_rx: mpsc::UnboundedReceiver<(ConnectionId, PendingReadTxn)>,
3827 mut cmd_rx: mpsc::UnboundedReceiver<(OpenTelemetryContext, Command)>,
3828 group_commit_rx: appends::GroupCommitWaiter,
3829 ) -> LocalBoxFuture<'static, ()> {
3830 async move {
3831 let mut cluster_events = self.controller.events_stream();
3833 let last_message = Arc::new(Mutex::new(LastMessage {
3834 kind: "none",
3835 stmt: None,
3836 }));
3837
3838 let (idle_tx, mut idle_rx) = tokio::sync::mpsc::channel(1);
3839 let idle_metric = self.metrics.queue_busy_seconds.clone();
3840 let last_message_watchdog = Arc::clone(&last_message);
3841
3842 spawn(|| "coord watchdog", async move {
3843 let mut interval = tokio::time::interval(Duration::from_secs(5));
3848 interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Delay);
3852
3853 let mut coord_stuck = false;
3855
3856 loop {
3857 interval.tick().await;
3858
3859 let duration = tokio::time::Duration::from_secs(30);
3861 let timeout = tokio::time::timeout(duration, idle_tx.reserve()).await;
3862 let Ok(maybe_permit) = timeout else {
3863 if !coord_stuck {
3865 let last_message = last_message_watchdog.lock().expect("poisoned");
3866 tracing::warn!(
3867 last_message_kind = %last_message.kind,
3868 last_message_sql = %last_message.stmt_to_string(),
3869 "coordinator stuck for {duration:?}",
3870 );
3871 }
3872 coord_stuck = true;
3873
3874 continue;
3875 };
3876
3877 if coord_stuck {
3879 tracing::info!("Coordinator became unstuck");
3880 }
3881 coord_stuck = false;
3882
3883 let Ok(permit) = maybe_permit else {
3885 break;
3886 };
3887
3888 permit.send(idle_metric.start_timer());
3889 }
3890 });
3891
3892 self.schedule_storage_usage_collection().await;
3893 self.schedule_arrangement_sizes_collection().await;
3894 self.spawn_privatelink_vpc_endpoints_watch_task();
3895 self.spawn_statement_logging_task();
3896 self.spawn_catalog_info_metrics_task();
3897 self.spawn_cluster_controller_task();
3898 flags::tracing_config(self.catalog.system_config()).apply(&self.tracing_handle);
3899
3900 let warn_threshold = self
3902 .catalog()
3903 .system_config()
3904 .coord_slow_message_warn_threshold();
3905
3906 const MESSAGE_BATCH: usize = 64;
3908 let mut messages = Vec::with_capacity(MESSAGE_BATCH);
3909 let mut cmd_messages = Vec::with_capacity(MESSAGE_BATCH);
3910
3911 let message_batch = self.metrics.message_batch.clone();
3912
3913 let linearize_reads_notify = Arc::clone(&self.linearize_reads_notify);
3920 let linearize_reads_notified = linearize_reads_notify.notified();
3921 tokio::pin!(linearize_reads_notified);
3922
3923 loop {
3924 select! {
3928 biased;
3933
3934 _ = internal_cmd_rx.recv_many(&mut messages, MESSAGE_BATCH) => {},
3938 Some(event) = cluster_events.next() => {
3942 messages.push(Message::ClusterEvent(event))
3943 },
3944 () = self.controller.ready() => {
3948 let controller = match self.controller.get_readiness() {
3952 Readiness::Storage => ControllerReadiness::Storage,
3953 Readiness::Compute => ControllerReadiness::Compute,
3954 Readiness::Metrics(_) => ControllerReadiness::Metrics,
3955 Readiness::Internal(_) => ControllerReadiness::Internal,
3956 Readiness::NotReady => unreachable!("just signaled as ready"),
3957 };
3958 messages.push(Message::ControllerReady { controller });
3959 }
3960 permit = group_commit_rx.ready() => {
3963 let user_write_spans = self.pending_writes.iter().flat_map(|x| match x {
3969 PendingWriteTxn::User{span, ..} => Some(span),
3970 PendingWriteTxn::System{..} => None,
3971 });
3972 let span = match user_write_spans.exactly_one() {
3973 Ok(span) => span.clone(),
3974 Err(user_write_spans) => {
3975 let span = info_span!(parent: None, "group_commit_notify");
3976 for s in user_write_spans {
3977 span.follows_from(s);
3978 }
3979 span
3980 }
3981 };
3982 messages.push(Message::GroupCommitInitiate(span, Some(permit)));
3983 },
3984 count = cmd_rx.recv_many(&mut cmd_messages, MESSAGE_BATCH) => {
3988 if count == 0 {
3989 break;
3990 } else {
3991 messages.extend(cmd_messages.drain(..).map(
3992 |(otel_ctx, cmd)| Message::Command(otel_ctx, cmd),
3993 ));
3994 }
3995 },
3996 Some(pending_read_txn) = strict_serializable_reads_rx.recv() => {
4000 let mut pending_read_txns = vec![pending_read_txn];
4001 while let Ok(pending_read_txn) = strict_serializable_reads_rx.try_recv() {
4002 pending_read_txns.push(pending_read_txn);
4003 }
4004 for (conn_id, pending_read_txn) in pending_read_txns {
4005 let prev = self
4006 .pending_linearize_read_txns
4007 .insert(conn_id, pending_read_txn);
4008 soft_assert_or_log!(
4009 prev.is_none(),
4010 "connections can not have multiple concurrent reads, prev: {prev:?}"
4011 )
4012 }
4013 messages.push(Message::LinearizeReads);
4014 }
4015 _ = self.advance_timelines_interval.tick() => {
4019 if self.controller.read_only() {
4023 messages.push(Message::AdvanceTimelines);
4024 } else {
4025 self.group_commit_tx.notify();
4026 }
4027 },
4028 () = linearize_reads_notified.as_mut() => {
4039 linearize_reads_notified.set(linearize_reads_notify.notified());
4040 messages.push(Message::LinearizeReads);
4041 }
4042 _ = self.check_cluster_scheduling_policies_interval.tick() => {
4046 messages.push(Message::CheckSchedulingPolicies);
4047 },
4048
4049 _ = self.caught_up_check_interval.tick() => {
4053 self.maybe_check_caught_up().await;
4058
4059 continue;
4060 },
4061
4062 timer = idle_rx.recv() => {
4067 timer.expect("does not drop").observe_duration();
4068 self.metrics
4069 .message_handling
4070 .with_label_values(&["watchdog"])
4071 .observe(0.0);
4072 continue;
4073 }
4074 };
4075
4076 message_batch.observe(f64::cast_lossy(messages.len()));
4078
4079 for msg in messages.drain(..) {
4080 let msg_kind = msg.kind();
4083 let span = span!(
4084 target: "mz_adapter::coord::handle_message_loop",
4085 Level::INFO,
4086 "coord::handle_message",
4087 kind = msg_kind
4088 );
4089 let otel_context = span.context().span().span_context().clone();
4090
4091 *last_message.lock().expect("poisoned") = LastMessage {
4095 kind: msg_kind,
4096 stmt: match &msg {
4097 Message::Command(
4098 _,
4099 Command::Execute {
4100 portal_name,
4101 session,
4102 ..
4103 },
4104 ) => session
4105 .get_portal_unverified(portal_name)
4106 .and_then(|p| p.stmt.as_ref().map(Arc::clone)),
4107 _ => None,
4108 },
4109 };
4110
4111 let start = Instant::now();
4112 self.handle_message(msg).instrument(span).await;
4113 let duration = start.elapsed();
4114
4115 self.metrics
4116 .message_handling
4117 .with_label_values(&[msg_kind])
4118 .observe(duration.as_secs_f64());
4119
4120 if duration > warn_threshold {
4122 let trace_id = otel_context.is_valid().then(|| otel_context.trace_id());
4123 tracing::error!(
4124 ?msg_kind,
4125 ?trace_id,
4126 ?duration,
4127 "very slow coordinator message"
4128 );
4129 }
4130 }
4131 }
4132 if let Some(catalog) = Arc::into_inner(self.catalog) {
4135 catalog.expire().await;
4136 }
4137 }
4138 .boxed_local()
4139 }
4140
4141 fn catalog(&self) -> &Catalog {
4143 &self.catalog
4144 }
4145
4146 fn owned_catalog(&self) -> Arc<Catalog> {
4149 Arc::clone(&self.catalog)
4150 }
4151
4152 fn optimizer_metrics(&self) -> OptimizerMetrics {
4155 self.optimizer_metrics.clone()
4156 }
4157
4158 fn catalog_mut(&mut self) -> &mut Catalog {
4160 Arc::make_mut(&mut self.catalog)
4168 }
4169
4170 async fn refill_user_id_pool(&mut self, min_count: u64) -> Result<(), AdapterError> {
4175 let batch_size = USER_ID_POOL_BATCH_SIZE.get(self.catalog().system_config().dyncfgs());
4176 let to_allocate = min_count.max(u64::from(batch_size));
4177 let id_ts = self.get_catalog_write_ts().await;
4178 let ids = self.catalog().allocate_user_ids(to_allocate, id_ts).await?;
4179 if let (Some((first_id, _)), Some((last_id, _))) = (ids.first(), ids.last()) {
4180 let start = match first_id {
4181 CatalogItemId::User(id) => *id,
4182 other => {
4183 return Err(AdapterError::Internal(format!(
4184 "expected User CatalogItemId, got {other:?}"
4185 )));
4186 }
4187 };
4188 let end = match last_id {
4189 CatalogItemId::User(id) => *id + 1, other => {
4191 return Err(AdapterError::Internal(format!(
4192 "expected User CatalogItemId, got {other:?}"
4193 )));
4194 }
4195 };
4196 self.user_id_pool.refill(start, end);
4197 } else {
4198 return Err(AdapterError::Internal(
4199 "catalog returned no user IDs".into(),
4200 ));
4201 }
4202 Ok(())
4203 }
4204
4205 async fn allocate_user_id(&mut self) -> Result<(CatalogItemId, GlobalId), AdapterError> {
4207 if let Some(id) = self.user_id_pool.allocate() {
4208 return Ok((CatalogItemId::User(id), GlobalId::User(id)));
4209 }
4210 self.refill_user_id_pool(1).await?;
4211 let id = self.user_id_pool.allocate().expect("ID pool just refilled");
4212 Ok((CatalogItemId::User(id), GlobalId::User(id)))
4213 }
4214
4215 async fn allocate_user_ids(
4217 &mut self,
4218 count: u64,
4219 ) -> Result<Vec<(CatalogItemId, GlobalId)>, AdapterError> {
4220 if self.user_id_pool.remaining() < count {
4221 self.refill_user_id_pool(count).await?;
4222 }
4223 let raw_ids = self
4224 .user_id_pool
4225 .allocate_many(count)
4226 .expect("pool has enough IDs after refill");
4227 Ok(raw_ids
4228 .into_iter()
4229 .map(|id| (CatalogItemId::User(id), GlobalId::User(id)))
4230 .collect())
4231 }
4232
4233 fn connection_context(&self) -> &ConnectionContext {
4235 self.controller.connection_context()
4236 }
4237
4238 fn secrets_reader(&self) -> &Arc<dyn SecretsReader> {
4240 &self.connection_context().secrets_reader
4241 }
4242
4243 #[allow(dead_code)]
4248 pub(crate) fn broadcast_notice(&self, notice: AdapterNotice) {
4249 for meta in self.active_conns.values() {
4250 let _ = meta.notice_tx.send(notice.clone());
4251 }
4252 }
4253
4254 pub(crate) fn broadcast_notice_tx(
4257 &self,
4258 ) -> Box<dyn FnOnce(AdapterNotice) -> () + Send + 'static> {
4259 let senders: Vec<_> = self
4260 .active_conns
4261 .values()
4262 .map(|meta| meta.notice_tx.clone())
4263 .collect();
4264 Box::new(move |notice| {
4265 for tx in senders {
4266 let _ = tx.send(notice.clone());
4267 }
4268 })
4269 }
4270
4271 pub(crate) fn active_conns(&self) -> &BTreeMap<ConnectionId, ConnMeta> {
4272 &self.active_conns
4273 }
4274
4275 #[instrument(level = "debug")]
4276 pub(crate) fn retire_execution(
4277 &mut self,
4278 reason: StatementEndedExecutionReason,
4279 ctx_extra: ExecuteContextExtra,
4280 ) {
4281 if let Some(uuid) = ctx_extra.retire() {
4282 self.end_statement_execution(uuid, reason);
4283 }
4284 }
4285
4286 #[instrument(level = "debug")]
4288 pub fn dataflow_builder(&self, instance: ComputeInstanceId) -> DataflowBuilder<'_> {
4289 let compute = self
4290 .instance_snapshot(instance)
4291 .expect("compute instance does not exist");
4292 DataflowBuilder::new(self.catalog().state(), compute)
4293 }
4294
4295 pub fn instance_snapshot(
4297 &self,
4298 id: ComputeInstanceId,
4299 ) -> Result<ComputeInstanceSnapshot, InstanceMissing> {
4300 ComputeInstanceSnapshot::new(&self.controller, id)
4301 }
4302
4303 pub(crate) async fn ship_dataflow(
4310 &mut self,
4311 dataflow: DataflowDescription<LirRelationExpr>,
4312 instance: ComputeInstanceId,
4313 target_replica: Option<ReplicaId>,
4314 ) {
4315 self.try_ship_dataflow(dataflow, instance, target_replica)
4316 .await
4317 .unwrap_or_terminate("dataflow creation cannot fail");
4318 }
4319
4320 pub(crate) async fn try_ship_dataflow(
4323 &mut self,
4324 dataflow: DataflowDescription<LirRelationExpr>,
4325 instance: ComputeInstanceId,
4326 target_replica: Option<ReplicaId>,
4327 ) -> Result<(), DataflowCreationError> {
4328 let export_ids = dataflow.exported_index_ids().collect();
4331
4332 self.controller
4333 .compute
4334 .create_dataflow(instance, dataflow, target_replica)?;
4335
4336 self.initialize_compute_read_policies(export_ids, instance, CompactionWindow::Default)
4337 .await;
4338
4339 Ok(())
4340 }
4341
4342 pub(crate) fn allow_writes(&mut self, instance: ComputeInstanceId, id: GlobalId) {
4346 self.controller
4347 .compute
4348 .allow_writes(instance, id)
4349 .unwrap_or_terminate("allow_writes cannot fail");
4350 }
4351
4352 pub(crate) async fn ship_dataflow_and_notice_builtin_table_updates(
4354 &mut self,
4355 dataflow: DataflowDescription<LirRelationExpr>,
4356 instance: ComputeInstanceId,
4357 notice_builtin_updates_fut: Option<BuiltinTableAppendNotify>,
4358 target_replica: Option<ReplicaId>,
4359 ) {
4360 if let Some(notice_builtin_updates_fut) = notice_builtin_updates_fut {
4361 let ship_dataflow_fut = self.ship_dataflow(dataflow, instance, target_replica);
4362 let ((), ()) =
4363 futures::future::join(notice_builtin_updates_fut, ship_dataflow_fut).await;
4364 } else {
4365 self.ship_dataflow(dataflow, instance, target_replica).await;
4366 }
4367 }
4368
4369 pub fn install_compute_watch_set(
4373 &mut self,
4374 conn_id: ConnectionId,
4375 objects: BTreeSet<GlobalId>,
4376 t: Timestamp,
4377 state: WatchSetResponse,
4378 ) -> Result<(), CollectionLookupError> {
4379 let ws_id = self.controller.install_compute_watch_set(objects, t)?;
4380 self.connection_watch_sets
4381 .entry(conn_id.clone())
4382 .or_default()
4383 .insert(ws_id);
4384 self.installed_watch_sets.insert(ws_id, (conn_id, state));
4385 Ok(())
4386 }
4387
4388 pub fn install_storage_watch_set(
4392 &mut self,
4393 conn_id: ConnectionId,
4394 objects: BTreeSet<GlobalId>,
4395 t: Timestamp,
4396 state: WatchSetResponse,
4397 ) -> Result<(), CollectionMissing> {
4398 let ws_id = self.controller.install_storage_watch_set(objects, t)?;
4399 self.connection_watch_sets
4400 .entry(conn_id.clone())
4401 .or_default()
4402 .insert(ws_id);
4403 self.installed_watch_sets.insert(ws_id, (conn_id, state));
4404 Ok(())
4405 }
4406
4407 pub fn cancel_pending_watchsets(&mut self, conn_id: &ConnectionId) {
4409 if let Some(ws_ids) = self.connection_watch_sets.remove(conn_id) {
4410 for ws_id in ws_ids {
4411 self.installed_watch_sets.remove(&ws_id);
4412 }
4413 }
4414 }
4415
4416 pub async fn dump(&self) -> Result<serde_json::Value, anyhow::Error> {
4420 let global_timelines: BTreeMap<_, _> = self
4426 .global_timelines
4427 .iter()
4428 .map(|(timeline, state)| (timeline.to_string(), format!("{state:?}")))
4429 .collect();
4430 let active_conns: BTreeMap<_, _> = self
4431 .active_conns
4432 .iter()
4433 .map(|(id, meta)| (id.unhandled().to_string(), format!("{meta:?}")))
4434 .collect();
4435 let txn_read_holds: BTreeMap<_, _> = self
4436 .txn_read_holds
4437 .iter()
4438 .map(|(id, capability)| (id.unhandled().to_string(), format!("{capability:?}")))
4439 .collect();
4440 let pending_peeks: BTreeMap<_, _> = self
4441 .pending_peeks
4442 .iter()
4443 .map(|(id, peek)| (id.to_string(), format!("{peek:?}")))
4444 .collect();
4445 let client_pending_peeks: BTreeMap<_, _> = self
4446 .client_pending_peeks
4447 .iter()
4448 .map(|(id, peek)| {
4449 let peek: BTreeMap<_, _> = peek
4450 .iter()
4451 .map(|(uuid, storage_id)| (uuid.to_string(), storage_id))
4452 .collect();
4453 (id.to_string(), peek)
4454 })
4455 .collect();
4456 let pending_linearize_read_txns: BTreeMap<_, _> = self
4457 .pending_linearize_read_txns
4458 .iter()
4459 .map(|(id, read_txn)| (id.unhandled().to_string(), format!("{read_txn:?}")))
4460 .collect();
4461
4462 Ok(serde_json::json!({
4463 "global_timelines": global_timelines,
4464 "active_conns": active_conns,
4465 "txn_read_holds": txn_read_holds,
4466 "pending_peeks": pending_peeks,
4467 "client_pending_peeks": client_pending_peeks,
4468 "pending_linearize_read_txns": pending_linearize_read_txns,
4469 "controller": self.controller.dump().await?,
4470 }))
4471 }
4472
4473 async fn prune_storage_usage_events_on_startup(&self, retention_period: Duration) {
4487 let item_id = self
4488 .catalog()
4489 .resolve_builtin_table(&MZ_STORAGE_USAGE_BY_SHARD);
4490 let global_id = self.catalog.get_entry(&item_id).latest_global_id();
4491 let read_ts = self.get_local_read_ts().await;
4492 let current_contents_fut = self
4493 .controller
4494 .storage_collections
4495 .snapshot(global_id, read_ts);
4496 let internal_cmd_tx = self.internal_cmd_tx.clone();
4497 spawn(|| "storage_usage_prune", async move {
4498 let mut current_contents = current_contents_fut
4499 .await
4500 .unwrap_or_terminate("cannot fail to fetch snapshot");
4501 differential_dataflow::consolidation::consolidate(&mut current_contents);
4502
4503 let cutoff_ts = u128::from(read_ts).saturating_sub(retention_period.as_millis());
4504 let mut expired = Vec::new();
4505 for (row, diff) in current_contents {
4506 assert_eq!(
4507 diff, 1,
4508 "consolidated contents should not contain retractions: ({row:#?}, {diff:#?})"
4509 );
4510 let collection_timestamp = row
4512 .unpack()
4513 .get(3)
4514 .expect("definition of mz_storage_by_shard changed")
4515 .unwrap_timestamptz();
4516 let collection_timestamp = collection_timestamp.timestamp_millis();
4517 let collection_timestamp: u128 = collection_timestamp
4518 .try_into()
4519 .expect("all collections happen after Jan 1 1970");
4520 if collection_timestamp < cutoff_ts {
4521 debug!("pruning storage event {row:?}");
4522 let builtin_update = BuiltinTableUpdate::row(item_id, row, Diff::MINUS_ONE);
4523 expired.push(builtin_update);
4524 }
4525 }
4526
4527 let _ = internal_cmd_tx.send(Message::StorageUsagePrune(expired));
4529 });
4530 }
4531
4532 async fn prune_arrangement_sizes_history_on_startup(&self) {
4541 if self.controller.read_only() {
4543 return;
4544 }
4545
4546 let retention_period = mz_adapter_types::dyncfgs::ARRANGEMENT_SIZE_HISTORY_RETENTION_PERIOD
4547 .get(self.catalog().system_config().dyncfgs());
4548 let item_id = self
4549 .catalog()
4550 .resolve_builtin_table(&mz_catalog::builtin::MZ_OBJECT_ARRANGEMENT_SIZE_HISTORY);
4551 let global_id = self.catalog.get_entry(&item_id).latest_global_id();
4552 let read_ts = self.get_local_read_ts().await;
4553 let current_contents_fut = self
4554 .controller
4555 .storage_collections
4556 .snapshot(global_id, read_ts);
4557 let internal_cmd_tx = self.internal_cmd_tx.clone();
4558 spawn(|| "arrangement_sizes_history_prune", async move {
4559 let mut current_contents = current_contents_fut
4560 .await
4561 .unwrap_or_terminate("cannot fail to fetch snapshot");
4562 differential_dataflow::consolidation::consolidate(&mut current_contents);
4563
4564 let cutoff_ts = u128::from(read_ts).saturating_sub(retention_period.as_millis());
4565 let expired =
4566 arrangement_sizes_expired_retractions(current_contents, cutoff_ts, item_id);
4567
4568 let _ = internal_cmd_tx.send(Message::ArrangementSizesPrune(expired));
4572 });
4573 }
4574
4575 fn current_credit_consumption_rate(&self, exclude_cluster: Option<ClusterId>) -> Numeric {
4578 self.catalog()
4579 .user_cluster_replicas()
4580 .filter(|replica| Some(replica.cluster_id) != exclude_cluster)
4581 .filter_map(|replica| match &replica.config.location {
4582 ReplicaLocation::Managed(location) => Some(location.size_for_billing()),
4583 ReplicaLocation::Unmanaged(_) => None,
4584 })
4585 .map(|size| {
4586 self.catalog()
4587 .cluster_replica_sizes()
4588 .0
4589 .get(size)
4590 .expect("location size is validated against the cluster replica sizes")
4591 .credits_per_hour
4592 })
4593 .sum()
4594 }
4595}
4596
4597fn arrangement_sizes_expired_retractions(
4605 rows: impl IntoIterator<Item = (mz_repr::Row, i64)>,
4606 cutoff_ts: u128,
4607 item_id: CatalogItemId,
4608) -> Vec<BuiltinTableUpdate> {
4609 let mut expired = Vec::new();
4610 for (row, diff) in rows {
4611 assert_eq!(
4612 diff, 1,
4613 "consolidated contents should not contain retractions: ({row:#?}, {diff:#?})"
4614 );
4615 let collection_timestamp = row
4616 .unpack()
4617 .get(3)
4618 .expect("definition of mz_object_arrangement_size_history changed")
4619 .unwrap_timestamptz()
4620 .timestamp_millis();
4621 let collection_timestamp: u128 = collection_timestamp
4622 .try_into()
4623 .expect("all collections happen after Jan 1 1970");
4624 if collection_timestamp < cutoff_ts {
4625 expired.push(BuiltinTableUpdate::row(item_id, row, Diff::MINUS_ONE));
4626 }
4627 }
4628 expired
4629}
4630
4631#[cfg(test)]
4632impl Coordinator {
4633 #[allow(dead_code)]
4634 async fn verify_ship_dataflow_no_error(
4635 &mut self,
4636 dataflow: DataflowDescription<LirRelationExpr>,
4637 ) {
4638 let compute_instance = ComputeInstanceId::user(1).expect("1 is a valid ID");
4646
4647 let _: () = self.ship_dataflow(dataflow, compute_instance, None).await;
4648 }
4649}
4650
4651struct LastMessage {
4653 kind: &'static str,
4654 stmt: Option<Arc<Statement<Raw>>>,
4655}
4656
4657impl LastMessage {
4658 fn stmt_to_string(&self) -> Cow<'static, str> {
4660 self.stmt
4661 .as_ref()
4662 .map(|stmt| stmt.to_ast_string_redacted().into())
4663 .unwrap_or(Cow::Borrowed("<none>"))
4664 }
4665}
4666
4667impl fmt::Debug for LastMessage {
4668 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
4669 f.debug_struct("LastMessage")
4670 .field("kind", &self.kind)
4671 .field("stmt", &self.stmt_to_string())
4672 .finish()
4673 }
4674}
4675
4676impl Drop for LastMessage {
4677 fn drop(&mut self) {
4678 if std::thread::panicking() {
4680 eprintln!("Coordinator panicking, dumping last message\n{self:?}",);
4682 }
4683 }
4684}
4685
4686pub fn serve(
4698 Config {
4699 controller_config,
4700 controller_envd_epoch,
4701 mut storage,
4702 timestamp_oracle_url,
4703 unsafe_mode,
4704 all_features,
4705 build_info,
4706 environment_id,
4707 metrics_registry,
4708 now,
4709 secrets_controller,
4710 cloud_resource_controller,
4711 cluster_replica_sizes,
4712 builtin_system_cluster_config,
4713 builtin_catalog_server_cluster_config,
4714 builtin_probe_cluster_config,
4715 builtin_support_cluster_config,
4716 builtin_analytics_cluster_config,
4717 system_parameter_defaults,
4718 availability_zones,
4719 storage_usage_client,
4720 storage_usage_collection_interval,
4721 storage_usage_retention_period,
4722 segment_client,
4723 egress_addresses,
4724 aws_account_id,
4725 aws_privatelink_availability_zones,
4726 connection_context,
4727 connection_limit_callback,
4728 remote_system_parameters,
4729 webhook_concurrency_limit,
4730 http_host_name,
4731 tracing_handle,
4732 read_only_controllers,
4733 caught_up_trigger: clusters_caught_up_trigger,
4734 helm_chart_version,
4735 license_key,
4736 external_login_password_mz_system,
4737 force_builtin_schema_migration,
4738 }: Config,
4739) -> BoxFuture<'static, Result<(Handle, Client), AdapterError>> {
4740 async move {
4741 let coord_start = Instant::now();
4742 info!("startup: coordinator init: beginning");
4743 info!("startup: coordinator init: preamble beginning");
4744
4745 let _builtins = LazyLock::force(&BUILTINS_STATIC);
4749
4750 let (cmd_tx, cmd_rx) = mpsc::unbounded_channel();
4751 let (internal_cmd_tx, internal_cmd_rx) = mpsc::unbounded_channel();
4752 let (strict_serializable_reads_tx, strict_serializable_reads_rx) =
4753 mpsc::unbounded_channel();
4754
4755 if !availability_zones.iter().all_unique() {
4757 coord_bail!("availability zones must be unique");
4758 }
4759
4760 let aws_principal_context = match (
4761 aws_account_id,
4762 connection_context.aws_external_id_prefix.clone(),
4763 ) {
4764 (Some(aws_account_id), Some(aws_external_id_prefix)) => Some(AwsPrincipalContext {
4765 aws_account_id,
4766 aws_external_id_prefix,
4767 }),
4768 _ => None,
4769 };
4770
4771 let aws_privatelink_availability_zones = aws_privatelink_availability_zones
4772 .map(|azs_vec| BTreeSet::from_iter(azs_vec.iter().cloned()));
4773
4774 info!(
4775 "startup: coordinator init: preamble complete in {:?}",
4776 coord_start.elapsed()
4777 );
4778 let oracle_init_start = Instant::now();
4779 info!("startup: coordinator init: timestamp oracle init beginning");
4780
4781 let timestamp_oracle_config = timestamp_oracle_url
4782 .map(|url| TimestampOracleConfig::from_url(&url, &metrics_registry))
4783 .transpose()?;
4784 let mut initial_timestamps =
4785 get_initial_oracle_timestamps(×tamp_oracle_config).await?;
4786
4787 initial_timestamps
4791 .entry(Timeline::EpochMilliseconds)
4792 .or_insert_with(mz_repr::Timestamp::minimum);
4793 let mut timestamp_oracles = BTreeMap::new();
4794 for (timeline, initial_timestamp) in initial_timestamps {
4795 Coordinator::ensure_timeline_state_with_initial_time(
4796 &timeline,
4797 initial_timestamp,
4798 now.clone(),
4799 timestamp_oracle_config.clone(),
4800 &mut timestamp_oracles,
4801 read_only_controllers,
4802 )
4803 .await;
4804 }
4805
4806 let catalog_upper = storage.current_upper().await;
4810 let epoch_millis_oracle = ×tamp_oracles
4816 .get(&Timeline::EpochMilliseconds)
4817 .expect("inserted above")
4818 .oracle;
4819
4820 let mut boot_ts = if read_only_controllers {
4821 let read_ts = epoch_millis_oracle.read_ts().await;
4822 std::cmp::max(read_ts, catalog_upper)
4823 } else {
4824 epoch_millis_oracle.apply_write(catalog_upper).await;
4827 epoch_millis_oracle.write_ts().await.timestamp
4828 };
4829
4830 info!(
4831 "startup: coordinator init: timestamp oracle init complete in {:?}",
4832 oracle_init_start.elapsed()
4833 );
4834
4835 let catalog_open_start = Instant::now();
4836 info!("startup: coordinator init: catalog open beginning");
4837 let persist_client = controller_config
4838 .persist_clients
4839 .open(controller_config.persist_location.clone())
4840 .await
4841 .context("opening persist client")?;
4842 let builtin_item_migration_config =
4843 BuiltinItemMigrationConfig {
4844 persist_client: persist_client.clone(),
4845 read_only: read_only_controllers,
4846 force_migration: force_builtin_schema_migration,
4847 }
4848 ;
4849 let OpenCatalogResult {
4850 mut catalog,
4851 migrated_storage_collections_0dt,
4852 new_builtin_collections,
4853 builtin_table_updates,
4854 cached_global_exprs,
4855 uncached_local_exprs,
4856 } = Catalog::open(mz_catalog::config::Config {
4857 storage,
4858 metrics_registry: &metrics_registry,
4859 state: mz_catalog::config::StateConfig {
4860 unsafe_mode,
4861 all_features,
4862 build_info,
4863 environment_id: environment_id.clone(),
4864 read_only: read_only_controllers,
4865 now: now.clone(),
4866 boot_ts: boot_ts.clone(),
4867 skip_migrations: false,
4868 cluster_replica_sizes,
4869 builtin_system_cluster_config,
4870 builtin_catalog_server_cluster_config,
4871 builtin_probe_cluster_config,
4872 builtin_support_cluster_config,
4873 builtin_analytics_cluster_config,
4874 system_parameter_defaults,
4875 remote_system_parameters,
4876 availability_zones,
4877 egress_addresses,
4878 aws_principal_context,
4879 aws_privatelink_availability_zones,
4880 connection_context,
4881 http_host_name,
4882 builtin_item_migration_config,
4883 persist_client: persist_client.clone(),
4884 enable_expression_cache_override: None,
4885 helm_chart_version,
4886 external_login_password_mz_system,
4887 license_key: license_key.clone(),
4888 },
4889 })
4890 .await?;
4891
4892 let catalog_upper = catalog.current_upper().await;
4895 boot_ts = std::cmp::max(boot_ts, catalog_upper);
4896
4897 if !read_only_controllers {
4898 epoch_millis_oracle.apply_write(boot_ts).await;
4899 }
4900
4901 info!(
4902 "startup: coordinator init: catalog open complete in {:?}",
4903 catalog_open_start.elapsed()
4904 );
4905
4906 let coord_thread_start = Instant::now();
4907 info!("startup: coordinator init: coordinator thread start beginning");
4908
4909 let session_id = catalog.config().session_id;
4910 let start_instant = catalog.config().start_instant;
4911
4912 let (bootstrap_tx, bootstrap_rx) = oneshot::channel();
4916 let handle = TokioHandle::current();
4917
4918 let metrics = Metrics::register_into(&metrics_registry);
4919 let metrics_clone = metrics.clone();
4920 let optimizer_metrics = OptimizerMetrics::register_into(
4921 &metrics_registry,
4922 catalog.system_config().optimizer_e2e_latency_warning_threshold(),
4923 );
4924 let segment_client_clone = segment_client.clone();
4925 let coord_now = now.clone();
4926 let advance_timelines_interval =
4927 tokio::time::interval(catalog.system_config().default_timestamp_interval());
4928 let mut check_scheduling_policies_interval = tokio::time::interval(
4929 catalog
4930 .system_config()
4931 .cluster_check_scheduling_policies_interval(),
4932 );
4933 check_scheduling_policies_interval.set_missed_tick_behavior(MissedTickBehavior::Delay);
4934
4935 let clusters_caught_up_check_interval = if read_only_controllers {
4936 let dyncfgs = catalog.system_config().dyncfgs();
4937 let interval = WITH_0DT_DEPLOYMENT_CAUGHT_UP_CHECK_INTERVAL.get(dyncfgs);
4938
4939 let mut interval = tokio::time::interval(interval);
4940 interval.set_missed_tick_behavior(MissedTickBehavior::Skip);
4941 interval
4942 } else {
4943 let mut interval = tokio::time::interval(Duration::from_secs(60 * 60));
4951 interval.set_missed_tick_behavior(MissedTickBehavior::Skip);
4952 interval
4953 };
4954
4955 let clusters_caught_up_check =
4956 clusters_caught_up_trigger.map(|trigger| {
4957 let mut exclude_collections: BTreeSet<GlobalId> =
4958 new_builtin_collections.iter().copied().collect();
4959
4960 let new_builtin_mvs = new_builtin_collections
4975 .iter()
4976 .map(|global_id| {
4977 catalog
4978 .state()
4979 .try_get_entry_by_global_id(global_id)
4980 .expect("new builtin collections have catalog entries")
4981 })
4982 .filter(|entry| entry.is_materialized_view())
4983 .map(|entry| entry.id());
4984 let mut todo: Vec<_> = migrated_storage_collections_0dt
4985 .iter()
4986 .copied()
4987 .filter(|id| catalog.state().get_entry(id).is_materialized_view())
4988 .chain(new_builtin_mvs)
4989 .collect();
4990 while let Some(item_id) = todo.pop() {
4991 let entry = catalog.state().get_entry(&item_id);
4992 exclude_collections.extend(entry.global_ids());
4993 todo.extend_from_slice(entry.used_by());
4994 }
4995
4996 CaughtUpCheckContext {
4997 trigger,
4998 exclude_collections,
4999 cluster_stability: BTreeMap::new(),
5000 }
5001 });
5002
5003 if let Some(TimestampOracleConfig::Postgres(pg_config)) =
5004 timestamp_oracle_config.as_ref()
5005 {
5006 let pg_timestamp_oracle_params =
5009 flags::timestamp_oracle_config(catalog.system_config());
5010 pg_timestamp_oracle_params.apply(pg_config);
5011 }
5012
5013 let connection_limit_callback: Arc<dyn Fn(&SystemVars) + Send + Sync> =
5016 Arc::new(move |system_vars: &SystemVars| {
5017 let limit: u64 = system_vars.max_connections().cast_into();
5018 let superuser_reserved: u64 =
5019 system_vars.superuser_reserved_connections().cast_into();
5020
5021 let superuser_reserved = if superuser_reserved >= limit {
5026 tracing::warn!(
5027 "superuser_reserved ({superuser_reserved}) is greater than max connections ({limit})!"
5028 );
5029 limit
5030 } else {
5031 superuser_reserved
5032 };
5033
5034 (connection_limit_callback)(limit, superuser_reserved);
5035 });
5036 catalog.system_config_mut().register_callback(
5037 &mz_sql::session::vars::MAX_CONNECTIONS,
5038 Arc::clone(&connection_limit_callback),
5039 );
5040 catalog.system_config_mut().register_callback(
5041 &mz_sql::session::vars::SUPERUSER_RESERVED_CONNECTIONS,
5042 connection_limit_callback,
5043 );
5044
5045 let (group_commit_tx, group_commit_rx) = appends::notifier();
5046
5047 let parent_span = tracing::Span::current();
5048 let thread = thread::Builder::new()
5049 .stack_size(3 * stack::STACK_SIZE)
5053 .name("coordinator".to_string())
5054 .spawn(move || {
5055 let span = info_span!(parent: parent_span, "coord::coordinator").entered();
5056
5057 let controller = handle
5058 .block_on({
5059 catalog.initialize_controller(
5060 controller_config,
5061 controller_envd_epoch,
5062 read_only_controllers,
5063 )
5064 })
5065 .unwrap_or_terminate("failed to initialize storage_controller");
5066 let catalog_upper = handle.block_on(catalog.current_upper());
5069 boot_ts = std::cmp::max(boot_ts, catalog_upper);
5070 if !read_only_controllers {
5071 let epoch_millis_oracle = ×tamp_oracles
5072 .get(&Timeline::EpochMilliseconds)
5073 .expect("inserted above")
5074 .oracle;
5075 handle.block_on(epoch_millis_oracle.apply_write(boot_ts));
5076 }
5077
5078 let catalog = Arc::new(catalog);
5079
5080 let caching_secrets_reader = CachingSecretsReader::new(secrets_controller.reader());
5081 let (group_committer_tx, group_committer_rx) = mpsc::unbounded_channel();
5082 let mut coord = Coordinator {
5083 controller,
5084 catalog,
5085 internal_cmd_tx,
5086 group_commit_tx,
5087 reconcile_now: Arc::new(Notify::new()),
5088 group_committer_tx,
5089 strict_serializable_reads_tx,
5090 linearize_reads_notify: Arc::new(Notify::new()),
5091 global_timelines: timestamp_oracles,
5092 transient_id_gen: Arc::new(TransientIdGen::new()),
5093 active_conns: BTreeMap::new(),
5094 txn_read_holds: Default::default(),
5095 pending_peeks: BTreeMap::new(),
5096 client_pending_peeks: BTreeMap::new(),
5097 pending_linearize_read_txns: BTreeMap::new(),
5098 serialized_ddl: LockedVecDeque::new(),
5099 active_compute_sinks: BTreeMap::new(),
5100 active_webhooks: BTreeMap::new(),
5101 active_copies: BTreeMap::new(),
5102 connection_cancel_watches: BTreeMap::new(),
5103 introspection_subscribes: BTreeMap::new(),
5104 write_locks: BTreeMap::new(),
5105 deferred_write_ops: BTreeMap::new(),
5106 pending_writes: Vec::new(),
5107 advance_timelines_interval,
5108 secrets_controller,
5109 caching_secrets_reader,
5110 cloud_resource_controller,
5111 storage_usage_client,
5112 storage_usage_collection_interval,
5113 segment_client,
5114 metrics,
5115 catalog_info_metrics_registry: metrics_registry.clone(),
5116 scoped_frontend: None,
5117 optimizer_metrics,
5118 tracing_handle,
5119 statement_logging: StatementLogging::new(coord_now.clone()),
5120 webhook_concurrency_limit,
5121 timestamp_oracle_config,
5122 check_cluster_scheduling_policies_interval: check_scheduling_policies_interval,
5123 cluster_scheduling_decisions: BTreeMap::new(),
5124 caught_up_check_interval: clusters_caught_up_check_interval,
5125 caught_up_check: clusters_caught_up_check,
5126 installed_watch_sets: BTreeMap::new(),
5127 connection_watch_sets: BTreeMap::new(),
5128 cluster_replica_statuses: ClusterReplicaStatuses::new(),
5129 read_only_controllers,
5130 buffered_builtin_table_updates: Some(Vec::new()),
5131 license_key,
5132 user_id_pool: IdPool::empty(),
5133 persist_client,
5134 };
5135
5136 handle.block_on(async {
5138 appends::spawn_group_committer(
5139 group_committer_rx,
5140 coord.get_local_timestamp_oracle(),
5141 coord.controller.storage.table_write_handle(),
5142 coord.catalog().upper_handle(),
5143 coord.internal_cmd_tx.clone(),
5144 coord.catalog().config().now.clone(),
5145 coord.metrics.clone(),
5146 coord.catalog().system_config().dyncfgs(),
5147 );
5148 });
5149
5150 let bootstrap = handle.block_on(async {
5151 coord
5152 .bootstrap(
5153 boot_ts,
5154 migrated_storage_collections_0dt,
5155 builtin_table_updates,
5156 cached_global_exprs,
5157 uncached_local_exprs,
5158 )
5159 .await?;
5160 coord
5161 .controller
5162 .remove_orphaned_replicas(
5163 coord.catalog().get_next_user_replica_id().await?,
5164 coord.catalog().get_next_system_replica_id().await?,
5165 )
5166 .await
5167 .map_err(AdapterError::Orchestrator)?;
5168
5169 if let Some(retention_period) = storage_usage_retention_period {
5170 coord
5171 .prune_storage_usage_events_on_startup(retention_period)
5172 .await;
5173 }
5174
5175 coord.prune_arrangement_sizes_history_on_startup().await;
5176
5177 Ok(())
5178 });
5179 let ok = bootstrap.is_ok();
5180 drop(span);
5181 bootstrap_tx
5182 .send(bootstrap)
5183 .expect("bootstrap_rx is not dropped until it receives this message");
5184 if ok {
5185 handle.block_on(coord.serve(
5186 internal_cmd_rx,
5187 strict_serializable_reads_rx,
5188 cmd_rx,
5189 group_commit_rx,
5190 ));
5191 }
5192 })
5193 .expect("failed to create coordinator thread");
5194 match bootstrap_rx
5195 .await
5196 .expect("bootstrap_tx always sends a message or panics/halts")
5197 {
5198 Ok(()) => {
5199 info!(
5200 "startup: coordinator init: coordinator thread start complete in {:?}",
5201 coord_thread_start.elapsed()
5202 );
5203 info!(
5204 "startup: coordinator init: complete in {:?}",
5205 coord_start.elapsed()
5206 );
5207 let handle = Handle {
5208 session_id,
5209 start_instant,
5210 _thread: thread.join_on_drop(),
5211 };
5212 let client = Client::new(
5213 build_info,
5214 cmd_tx,
5215 metrics_clone,
5216 now,
5217 environment_id,
5218 segment_client_clone,
5219 );
5220 Ok((handle, client))
5221 }
5222 Err(e) => Err(e),
5223 }
5224 }
5225 .boxed()
5226}
5227
5228async fn get_initial_oracle_timestamps(
5242 timestamp_oracle_config: &Option<TimestampOracleConfig>,
5243) -> Result<BTreeMap<Timeline, Timestamp>, AdapterError> {
5244 let mut initial_timestamps = BTreeMap::new();
5245
5246 if let Some(config) = timestamp_oracle_config {
5247 let oracle_timestamps = config.get_all_timelines().await?;
5248
5249 let debug_msg = || {
5250 oracle_timestamps
5251 .iter()
5252 .map(|(timeline, ts)| format!("{:?} -> {}", timeline, ts))
5253 .join(", ")
5254 };
5255 info!(
5256 "current timestamps from the timestamp oracle: {}",
5257 debug_msg()
5258 );
5259
5260 for (timeline, ts) in oracle_timestamps {
5261 let entry = initial_timestamps
5262 .entry(Timeline::from_str(&timeline).expect("could not parse timeline"));
5263
5264 entry
5265 .and_modify(|current_ts| *current_ts = std::cmp::max(*current_ts, ts))
5266 .or_insert(ts);
5267 }
5268 } else {
5269 info!("no timestamp oracle configured!");
5270 };
5271
5272 let debug_msg = || {
5273 initial_timestamps
5274 .iter()
5275 .map(|(timeline, ts)| format!("{:?}: {}", timeline, ts))
5276 .join(", ")
5277 };
5278 info!("initial oracle timestamps: {}", debug_msg());
5279
5280 Ok(initial_timestamps)
5281}
5282
5283#[instrument]
5284pub async fn load_remote_system_parameters(
5285 storage: &mut Box<dyn OpenableDurableCatalogState>,
5286 system_parameter_sync_config: Option<SystemParameterSyncConfig>,
5287 system_parameter_sync_timeout: Duration,
5288) -> Result<Option<BTreeMap<String, String>>, AdapterError> {
5289 if let Some(system_parameter_sync_config) = system_parameter_sync_config {
5290 tracing::info!("parameter sync on boot: start sync");
5291
5292 let mut params = SynchronizedParameters::new(SystemVars::default());
5332 let frontend_sync = async {
5333 let frontend = SystemParameterFrontend::from(&system_parameter_sync_config).await?;
5334 frontend.pull(&mut params);
5335 let ops = params
5336 .modified()
5337 .into_iter()
5338 .map(|param| {
5339 let name = param.name;
5340 let value = param.value;
5341 tracing::info!(name, value, initial = true, "sync parameter");
5342 (name, value)
5343 })
5344 .collect();
5345 tracing::info!("parameter sync on boot: end sync");
5346 Ok(Some(ops))
5347 };
5348 if !storage.has_system_config_synced_once().await? {
5349 frontend_sync.await
5350 } else {
5351 match mz_ore::future::timeout(system_parameter_sync_timeout, frontend_sync).await {
5352 Ok(ops) => Ok(ops),
5353 Err(TimeoutError::Inner(e)) => Err(e),
5354 Err(TimeoutError::DeadlineElapsed) => {
5355 tracing::info!("parameter sync on boot: sync has timed out");
5356 Ok(None)
5357 }
5358 }
5359 }
5360 } else {
5361 Ok(None)
5362 }
5363}
5364
5365#[derive(Debug)]
5366pub enum WatchSetResponse {
5367 StatementDependenciesReady(StatementLoggingId, StatementLifecycleEvent),
5368 AlterSinkReady(AlterSinkReadyContext),
5369 AlterMaterializedViewReady(AlterMaterializedViewReadyContext),
5370}
5371
5372#[derive(Debug)]
5373pub struct AlterSinkReadyContext {
5374 ctx: Option<ExecuteContext>,
5375 otel_ctx: OpenTelemetryContext,
5376 plan: AlterSinkPlan,
5377 plan_validity: PlanValidity,
5378 read_hold: ReadHolds,
5379}
5380
5381impl AlterSinkReadyContext {
5382 fn ctx(&mut self) -> &mut ExecuteContext {
5383 self.ctx.as_mut().expect("only cleared on drop")
5384 }
5385
5386 fn retire(mut self, result: Result<ExecuteResponse, AdapterError>) {
5387 self.ctx
5388 .take()
5389 .expect("only cleared on drop")
5390 .retire(result);
5391 }
5392}
5393
5394impl Drop for AlterSinkReadyContext {
5395 fn drop(&mut self) {
5396 if let Some(ctx) = self.ctx.take() {
5397 ctx.retire(Err(AdapterError::Canceled));
5398 }
5399 }
5400}
5401
5402#[derive(Debug)]
5403pub struct AlterMaterializedViewReadyContext {
5404 ctx: Option<ExecuteContext>,
5405 otel_ctx: OpenTelemetryContext,
5406 plan: plan::AlterMaterializedViewApplyReplacementPlan,
5407 plan_validity: PlanValidity,
5408}
5409
5410impl AlterMaterializedViewReadyContext {
5411 fn ctx(&mut self) -> &mut ExecuteContext {
5412 self.ctx.as_mut().expect("only cleared on drop")
5413 }
5414
5415 fn retire(mut self, result: Result<ExecuteResponse, AdapterError>) {
5416 self.ctx
5417 .take()
5418 .expect("only cleared on drop")
5419 .retire(result);
5420 }
5421}
5422
5423impl Drop for AlterMaterializedViewReadyContext {
5424 fn drop(&mut self) {
5425 if let Some(ctx) = self.ctx.take() {
5426 ctx.retire(Err(AdapterError::Canceled));
5427 }
5428 }
5429}
5430
5431#[derive(Debug)]
5434struct LockedVecDeque<T> {
5435 items: VecDeque<T>,
5436 lock: Arc<tokio::sync::Mutex<()>>,
5437}
5438
5439impl<T> LockedVecDeque<T> {
5440 pub fn new() -> Self {
5441 Self {
5442 items: VecDeque::new(),
5443 lock: Arc::new(tokio::sync::Mutex::new(())),
5444 }
5445 }
5446
5447 pub fn try_lock_owned(&self) -> Result<OwnedMutexGuard<()>, tokio::sync::TryLockError> {
5448 Arc::clone(&self.lock).try_lock_owned()
5449 }
5450
5451 pub fn is_empty(&self) -> bool {
5452 self.items.is_empty()
5453 }
5454
5455 pub fn push_back(&mut self, value: T) {
5456 self.items.push_back(value)
5457 }
5458
5459 pub fn pop_front(&mut self) -> Option<T> {
5460 self.items.pop_front()
5461 }
5462
5463 pub fn remove(&mut self, index: usize) -> Option<T> {
5464 self.items.remove(index)
5465 }
5466
5467 pub fn iter(&self) -> std::collections::vec_deque::Iter<'_, T> {
5468 self.items.iter()
5469 }
5470}
5471
5472#[derive(Debug)]
5473struct DeferredPlanStatement {
5474 ctx: ExecuteContext,
5475 ps: PlanStatement,
5476}
5477
5478#[derive(Debug)]
5479enum PlanStatement {
5480 Statement {
5481 stmt: Arc<Statement<Raw>>,
5482 params: Params,
5483 },
5484 Plan {
5485 plan: mz_sql::plan::Plan,
5486 resolved_ids: ResolvedIds,
5487 sql_impl_resolved_ids: ResolvedIds,
5488 },
5489}
5490
5491#[derive(Debug, Error)]
5492pub enum NetworkPolicyError {
5493 #[error("Access denied for address {0}")]
5494 AddressDenied(IpAddr),
5495 #[error("Access denied missing IP address")]
5496 MissingIp,
5497}
5498
5499pub(crate) fn validate_ip_with_policy_rules(
5500 ip: &IpAddr,
5501 rules: &Vec<NetworkPolicyRule>,
5502) -> Result<(), NetworkPolicyError> {
5503 if rules.iter().any(|r| r.address.0.contains(ip)) {
5506 Ok(())
5507 } else {
5508 Err(NetworkPolicyError::AddressDenied(ip.clone()))
5509 }
5510}
5511
5512pub(crate) fn infer_sql_type_for_catalog(
5513 hir_expr: &HirRelationExpr,
5514 mir_expr: &MirRelationExpr,
5515) -> SqlRelationType {
5516 let mut typ = hir_expr.top_level_typ();
5517 typ.backport_nullability_and_keys(&mir_expr.typ());
5518 typ
5519}
5520
5521#[cfg(test)]
5522mod execute_context_tests {
5523 use tokio::sync::{mpsc, oneshot};
5524
5525 use super::*;
5526 use crate::session::Session;
5527 use crate::util::ClientTransmitter;
5528
5529 #[mz_ore::test]
5532 fn test_retire_answers_client_when_runtime_shuts_down() {
5533 let runtime = tokio::runtime::Runtime::new().expect("can build runtime");
5534
5535 let (client_tx, mut client_rx) = oneshot::channel();
5536 let (internal_cmd_tx, _internal_cmd_rx) = mpsc::unbounded_channel();
5537
5538 runtime.block_on(async {
5539 let ctx = ExecuteContext::from_parts_with_response_barriers(
5540 ClientTransmitter::new(client_tx, internal_cmd_tx.clone()),
5541 internal_cmd_tx,
5542 Session::dummy(),
5543 ExecuteContextGuard::default(),
5544 vec![Box::pin(std::future::pending())],
5546 );
5547 ctx.retire(Ok(ExecuteResponse::StartedTransaction));
5548 });
5549
5550 drop(runtime);
5551
5552 let response = client_rx.try_recv().expect("client must be answered");
5553 assert!(
5554 matches!(response.result, Err(AdapterError::Internal(_))),
5555 "expected an internal error, got {:?}",
5556 response.result
5557 );
5558 }
5559}
5560
5561#[cfg(test)]
5562mod id_pool_tests {
5563 use super::IdPool;
5564
5565 #[mz_ore::test]
5566 fn test_empty_pool() {
5567 let mut pool = IdPool::empty();
5568 assert_eq!(pool.remaining(), 0);
5569 assert_eq!(pool.allocate(), None);
5570 assert_eq!(pool.allocate_many(1), None);
5571 }
5572
5573 #[mz_ore::test]
5574 fn test_allocate_single() {
5575 let mut pool = IdPool::empty();
5576 pool.refill(10, 13);
5577 assert_eq!(pool.remaining(), 3);
5578 assert_eq!(pool.allocate(), Some(10));
5579 assert_eq!(pool.allocate(), Some(11));
5580 assert_eq!(pool.allocate(), Some(12));
5581 assert_eq!(pool.remaining(), 0);
5582 assert_eq!(pool.allocate(), None);
5583 }
5584
5585 #[mz_ore::test]
5586 fn test_allocate_many() {
5587 let mut pool = IdPool::empty();
5588 pool.refill(100, 105);
5589 assert_eq!(pool.allocate_many(3), Some(vec![100, 101, 102]));
5590 assert_eq!(pool.remaining(), 2);
5591 assert_eq!(pool.allocate_many(3), None);
5593 assert_eq!(pool.allocate_many(2), Some(vec![103, 104]));
5595 assert_eq!(pool.remaining(), 0);
5596 }
5597
5598 #[mz_ore::test]
5599 fn test_allocate_many_zero() {
5600 let mut pool = IdPool::empty();
5601 pool.refill(1, 5);
5602 assert_eq!(pool.allocate_many(0), Some(vec![]));
5603 assert_eq!(pool.remaining(), 4);
5604 }
5605
5606 #[mz_ore::test]
5607 fn test_refill_resets_pool() {
5608 let mut pool = IdPool::empty();
5609 pool.refill(0, 2);
5610 assert_eq!(pool.allocate(), Some(0));
5611 pool.refill(50, 52);
5613 assert_eq!(pool.allocate(), Some(50));
5614 assert_eq!(pool.allocate(), Some(51));
5615 assert_eq!(pool.allocate(), None);
5616 }
5617
5618 #[mz_ore::test]
5619 fn test_mixed_allocate_and_allocate_many() {
5620 let mut pool = IdPool::empty();
5621 pool.refill(0, 10);
5622 assert_eq!(pool.allocate(), Some(0));
5623 assert_eq!(pool.allocate_many(3), Some(vec![1, 2, 3]));
5624 assert_eq!(pool.allocate(), Some(4));
5625 assert_eq!(pool.remaining(), 5);
5626 }
5627
5628 #[mz_ore::test]
5629 #[should_panic(expected = "invalid pool range")]
5630 fn test_refill_invalid_range_panics() {
5631 let mut pool = IdPool::empty();
5632 pool.refill(10, 5);
5633 }
5634}
5635
5636#[cfg(test)]
5637mod arrangement_sizes_pruner_tests {
5638 use mz_repr::catalog_item_id::CatalogItemId;
5639 use mz_repr::{Datum, Row};
5640
5641 use super::arrangement_sizes_expired_retractions;
5642
5643 fn history_row(ts_ms: i64) -> Row {
5647 let dt = mz_ore::now::to_datetime(ts_ms.try_into().expect("non-negative"));
5648 Row::pack_slice(&[
5649 Datum::String("r1"),
5650 Datum::String("u1"),
5651 Datum::Int64(123),
5652 Datum::TimestampTz(dt.try_into().expect("fits in TimestampTz")),
5653 ])
5654 }
5655
5656 fn item_id() -> CatalogItemId {
5657 CatalogItemId::User(42)
5659 }
5660
5661 #[mz_ore::test]
5662 fn empty_input_produces_no_retractions() {
5663 let out = arrangement_sizes_expired_retractions(Vec::new(), 1_000, item_id());
5664 assert!(out.is_empty());
5665 }
5666
5667 #[mz_ore::test]
5668 fn retracts_only_rows_strictly_before_cutoff() {
5669 let rows = vec![
5672 (history_row(100), 1),
5673 (history_row(500), 1),
5674 (history_row(1_000), 1), (history_row(5_000), 1),
5676 ];
5677 let out = arrangement_sizes_expired_retractions(rows, 1_000, item_id());
5678 assert_eq!(out.len(), 2);
5679 }
5680
5681 #[mz_ore::test]
5682 #[should_panic(expected = "consolidated contents should not contain retractions")]
5683 fn retraction_in_input_panics() {
5684 let rows = vec![(history_row(100), -1)];
5685 let _ = arrangement_sizes_expired_retractions(rows, 1_000, item_id());
5686 }
5687}