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::FRONTEND_READ_THEN_WRITE;
95use mz_adapter_types::dyncfgs::{
96 USER_ID_POOL_BATCH_SIZE, 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, Semaphore, 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::id_bundle::CollectionIdBundle;
199use crate::coord::introspection::IntrospectionSubscribe;
200use crate::coord::peek::PendingPeek;
201use crate::coord::statement_logging::StatementLogging;
202use crate::coord::timeline::{TimelineContext, TimelineState};
203use crate::coord::timestamp_selection::{TimestampContext, TimestampDetermination};
204use crate::coord::validity::PlanValidity;
205use crate::error::AdapterError;
206use crate::explain::insights::PlanInsightsContext;
207use crate::explain::optimizer_trace::{DispatchGuard, OptimizerTrace};
208use crate::metrics::Metrics;
209use crate::optimize::dataflows::{ComputeInstanceSnapshot, DataflowBuilder};
210use crate::optimize::{self, Optimize, OptimizerConfig};
211use crate::session::{EndTransactionAction, Session};
212use crate::statement_logging::{
213 StatementEndedExecutionReason, StatementLifecycleEvent, StatementLoggingId,
214};
215use crate::util::{ClientTransmitter, ResultExt, sort_topological};
216use crate::webhook::{WebhookAppenderInvalidator, WebhookConcurrencyLimiter};
217use crate::{AdapterNotice, ReadHolds, flags};
218
219pub(crate) mod appends;
220pub(crate) mod catalog_serving;
221pub(crate) mod cluster_controller;
222pub(crate) mod consistency;
223pub(crate) mod id_bundle;
224pub(crate) mod in_memory_oracle;
225pub(crate) mod peek;
226pub(crate) mod read_policy;
227pub(crate) mod read_then_write;
228pub(crate) mod sequencer;
229pub(crate) mod statement_logging;
230pub(crate) mod timeline;
231pub(crate) mod timestamp_selection;
232
233pub mod catalog_implications;
234mod caught_up;
235mod command_handler;
236mod ddl;
237pub(crate) mod group_sync;
238mod indexes;
239mod info_metrics;
240mod introspection;
241mod message_handler;
242mod privatelink_status;
243mod sql;
244mod validity;
245
246#[derive(Debug)]
272pub(crate) struct IdPool {
273 next: u64,
274 upper: u64,
275}
276
277impl IdPool {
278 pub fn empty() -> Self {
280 IdPool { next: 0, upper: 0 }
281 }
282
283 pub fn allocate(&mut self) -> Option<u64> {
285 if self.next < self.upper {
286 let id = self.next;
287 self.next += 1;
288 Some(id)
289 } else {
290 None
291 }
292 }
293
294 pub fn allocate_many(&mut self, n: u64) -> Option<Vec<u64>> {
297 if self.remaining() >= n {
298 let ids = (self.next..self.next + n).collect();
299 self.next += n;
300 Some(ids)
301 } else {
302 None
303 }
304 }
305
306 pub fn remaining(&self) -> u64 {
308 self.upper - self.next
309 }
310
311 pub fn refill(&mut self, next: u64, upper: u64) {
313 assert!(next <= upper, "invalid pool range: {next}..{upper}");
314 self.next = next;
315 self.upper = upper;
316 }
317}
318
319#[derive(Debug)]
323pub struct ArrangementSizeRecord {
324 pub replica_id: String,
325 pub object_id: String,
326 pub size: i64,
327 pub hydration_complete: bool,
328}
329
330#[derive(Debug)]
331pub enum Message {
332 Command(OpenTelemetryContext, Command),
333 ControllerReady {
334 controller: ControllerReadiness,
335 },
336 PurifiedStatementReady(PurifiedStatementReady),
337 CreateConnectionValidationReady(CreateConnectionValidationReady),
338 AlterConnectionValidationReady(AlterConnectionValidationReady),
339 TryDeferred {
340 conn_id: ConnectionId,
342 acquired_lock: Option<(CatalogItemId, tokio::sync::OwnedMutexGuard<()>)>,
352 },
353 GroupCommitInitiate(Span, Option<GroupCommitPermit>),
355 GroupCommitApplied {
359 responses: Vec<crate::util::CompletedClientTransmitter>,
361 statement_logging_ids: Vec<StatementLoggingId>,
363 internal_results: Vec<crate::coord::appends::InternalWriteResponder>,
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 CreateMetricSinkStageReady {
412 ctx: ExecuteContext,
413 span: Span,
414 stage: CreateMetricSinkStage,
415 },
416 CreateViewStageReady {
417 ctx: ExecuteContext,
418 span: Span,
419 stage: CreateViewStage,
420 },
421 CreateMaterializedViewStageReady {
422 ctx: ExecuteContext,
423 span: Span,
424 stage: CreateMaterializedViewStage,
425 },
426 SubscribeStageReady {
427 ctx: ExecuteContext,
428 span: Span,
429 stage: SubscribeStage,
430 },
431 IntrospectionSubscribeStageReady {
432 span: Span,
433 stage: IntrospectionSubscribeStage,
434 },
435 SecretStageReady {
436 ctx: ExecuteContext,
437 span: Span,
438 stage: SecretStage,
439 },
440 ClusterStageReady {
441 ctx: ExecuteContext,
442 span: Span,
443 stage: ClusterStage,
444 },
445 ExplainTimestampStageReady {
446 ctx: ExecuteContext,
447 span: Span,
448 stage: ExplainTimestampStage,
449 },
450 DrainStatementLog,
451 PrivateLinkVpcEndpointEvents(Vec<VpcEndpointEvent>),
452
453 ClusterControllerRequest(cluster_controller::ClusterControllerRequest),
457}
458
459impl Message {
460 pub const fn kind(&self) -> &'static str {
462 match self {
463 Message::Command(_, msg) => match msg {
464 Command::CatalogSnapshot { .. } => "command-catalog_snapshot",
465 Command::Startup { .. } => "command-startup",
466 Command::Execute { .. } => "command-execute",
467 Command::Commit { .. } => "command-commit",
468 Command::CancelRequest { .. } => "command-cancel_request",
469 Command::PrivilegedCancelRequest { .. } => "command-privileged_cancel_request",
470 Command::GetWebhook { .. } => "command-get_webhook",
471 Command::GetSystemVars { .. } => "command-get_system_vars",
472 Command::SetSystemVars { .. } => "command-set_system_vars",
473 Command::UpdateScopedSystemParameters { .. } => {
474 "command-update_scoped_system_parameters"
475 }
476 Command::InstallScopedSystemParameterFrontend { .. } => {
477 "command-install_scoped_system_parameter_frontend"
478 }
479 Command::Terminate { .. } => "command-terminate",
480 Command::RetireExecute { .. } => "command-retire_execute",
481 Command::CheckConsistency { .. } => "command-check_consistency",
482 Command::Dump { .. } => "command-dump",
483 Command::AuthenticatePassword { .. } => "command-auth_check",
484 Command::AuthenticateGetSASLChallenge { .. } => "command-auth_get_sasl_challenge",
485 Command::AuthenticateVerifySASLProof { .. } => "command-auth_verify_sasl_proof",
486 Command::CheckRoleCanLogin { .. } => "command-check_role_can_login",
487 Command::GetComputeInstanceClient { .. } => "get-compute-instance-client",
488 Command::GetOracle { .. } => "get-oracle",
489 Command::DetermineRealTimeRecentTimestamp { .. } => {
490 "determine-real-time-recent-timestamp"
491 }
492 Command::GetTransactionReadHoldsBundle { .. } => {
493 "get-transaction-read-holds-bundle"
494 }
495 Command::StoreTransactionReadHolds { .. } => "store-transaction-read-holds",
496 Command::ExecuteSlowPathPeek { .. } => "execute-slow-path-peek",
497 Command::ExecuteSubscribe { .. } => "execute-subscribe",
498 Command::CopyToPreflight { .. } => "copy-to-preflight",
499 Command::ExecuteCopyTo { .. } => "execute-copy-to",
500 Command::ExecuteSideEffectingFunc { .. } => "execute-side-effecting-func",
501 Command::LookupConnection { .. } => "lookup-connection",
502 Command::RegisterFrontendPeek { .. } => "register-frontend-peek",
503 Command::UnregisterFrontendPeek { .. } => "unregister-frontend-peek",
504 Command::ExplainTimestamp { .. } => "explain-timestamp",
505 Command::FrontendStatementLogging(..) => "frontend-statement-logging",
506 Command::StartCopyFromStdin { .. } => "start-copy-from-stdin",
507 Command::InjectAuditEvents { .. } => "inject-audit-events",
508 Command::RegisterConnectionCancelWatch { .. } => "register-connection-cancel-watch",
509 Command::CreateInternalSubscribe { .. } => "create-internal-subscribe",
510 Command::AttemptWrite { .. } => "attempt-write",
511 Command::DropInternalSubscribe { .. } => "drop-internal-subscribe",
512 },
513 Message::ControllerReady {
514 controller: ControllerReadiness::Compute,
515 } => "controller_ready(compute)",
516 Message::ControllerReady {
517 controller: ControllerReadiness::Storage,
518 } => "controller_ready(storage)",
519 Message::ControllerReady {
520 controller: ControllerReadiness::Metrics,
521 } => "controller_ready(metrics)",
522 Message::ControllerReady {
523 controller: ControllerReadiness::Internal,
524 } => "controller_ready(internal)",
525 Message::PurifiedStatementReady(_) => "purified_statement_ready",
526 Message::CreateConnectionValidationReady(_) => "create_connection_validation_ready",
527 Message::TryDeferred { .. } => "try_deferred",
528 Message::GroupCommitInitiate(..) => "group_commit_initiate",
529 Message::GroupCommitApplied { .. } => "group_commit_applied",
530 Message::AdvanceTimelines => "advance_timelines",
531 Message::ClusterEvent(_) => "cluster_event",
532 Message::CancelPendingPeeks { .. } => "cancel_pending_peeks",
533 Message::LinearizeReads => "linearize_reads",
534 Message::StagedBatches { .. } => "staged_batches",
535 Message::StorageUsageSchedule => "storage_usage_schedule",
536 Message::StorageUsageFetch => "storage_usage_fetch",
537 Message::StorageUsageUpdate(_) => "storage_usage_update",
538 Message::StorageUsagePrune(_) => "storage_usage_prune",
539 Message::ArrangementSizesSchedule => "arrangement_sizes_schedule",
540 Message::ArrangementSizesSnapshot => "arrangement_sizes_snapshot",
541 Message::ArrangementSizesWrite(_) => "arrangement_sizes_write",
542 Message::ArrangementSizesPrune(_) => "arrangement_sizes_prune",
543 Message::RetireExecute { .. } => "retire_execute",
544 Message::ExecuteSingleStatementTransaction { .. } => {
545 "execute_single_statement_transaction"
546 }
547 Message::PeekStageReady { .. } => "peek_stage_ready",
548 Message::ExplainTimestampStageReady { .. } => "explain_timestamp_stage_ready",
549 Message::CreateIndexStageReady { .. } => "create_index_stage_ready",
550 Message::CreateMetricSinkStageReady { .. } => "create_metric_sink_stage_ready",
551 Message::CreateViewStageReady { .. } => "create_view_stage_ready",
552 Message::CreateMaterializedViewStageReady { .. } => {
553 "create_materialized_view_stage_ready"
554 }
555 Message::SubscribeStageReady { .. } => "subscribe_stage_ready",
556 Message::IntrospectionSubscribeStageReady { .. } => {
557 "introspection_subscribe_stage_ready"
558 }
559 Message::SecretStageReady { .. } => "secret_stage_ready",
560 Message::ClusterStageReady { .. } => "cluster_stage_ready",
561 Message::DrainStatementLog => "drain_statement_log",
562 Message::AlterConnectionValidationReady(..) => "alter_connection_validation_ready",
563 Message::PrivateLinkVpcEndpointEvents(_) => "private_link_vpc_endpoint_events",
564 Message::ClusterControllerRequest(_) => "cluster_controller_request",
565 Message::DeferredStatementReady => "deferred_statement_ready",
566 }
567 }
568}
569
570#[derive(Debug)]
572pub enum ControllerReadiness {
573 Storage,
575 Compute,
577 Metrics,
579 Internal,
581}
582
583#[derive(Derivative)]
584#[derivative(Debug)]
585pub struct BackgroundWorkResult<T> {
586 #[derivative(Debug = "ignore")]
587 pub ctx: ExecuteContext,
588 pub result: Result<T, AdapterError>,
589 pub params: Params,
590 pub plan_validity: PlanValidity,
591 pub original_stmt: Arc<Statement<Raw>>,
592 pub otel_ctx: OpenTelemetryContext,
593}
594
595pub type PurifiedStatementReady = BackgroundWorkResult<mz_sql::pure::PurifiedStatement>;
596
597#[derive(Derivative)]
598#[derivative(Debug)]
599pub struct ValidationReady<T> {
600 #[derivative(Debug = "ignore")]
601 pub ctx: ExecuteContext,
602 pub result: Result<T, AdapterError>,
603 pub resolved_ids: ResolvedIds,
604 pub connection_id: CatalogItemId,
605 pub connection_gid: GlobalId,
606 pub plan_validity: PlanValidity,
607 pub otel_ctx: OpenTelemetryContext,
608}
609
610pub type CreateConnectionValidationReady = ValidationReady<CreateConnectionPlan>;
611pub type AlterConnectionValidationReady = ValidationReady<Connection>;
612
613#[derive(Debug)]
614pub enum PeekStage {
615 LinearizeTimestamp(PeekStageLinearizeTimestamp),
617 RealTimeRecency(PeekStageRealTimeRecency),
618 TimestampReadHold(PeekStageTimestampReadHold),
619 Optimize(PeekStageOptimize),
620 Finish(PeekStageFinish),
622 ExplainPlan(PeekStageExplainPlan),
624 ExplainPushdown(PeekStageExplainPushdown),
625 CopyToPreflight(PeekStageCopyTo),
627 CopyToDataflow(PeekStageCopyTo),
629}
630
631#[derive(Debug)]
632pub struct CopyToContext {
633 pub desc: RelationDesc,
635 pub uri: Uri,
637 pub connection: StorageConnection<ReferencedConnection>,
639 pub connection_id: CatalogItemId,
641 pub format: S3SinkFormat,
643 pub max_file_size: u64,
645 pub output_batch_count: Option<u64>,
650}
651
652#[derive(Debug)]
653pub struct PeekStageLinearizeTimestamp {
654 validity: PlanValidity,
655 plan: mz_sql::plan::SelectPlan,
656 max_query_result_size: Option<u64>,
657 source_ids: BTreeSet<GlobalId>,
658 target_replica: Option<ReplicaId>,
659 timeline_context: TimelineContext,
660 optimizer: optimize::PeekOptimizer,
661 explain_ctx: ExplainContext,
664}
665
666#[derive(Debug)]
667pub struct PeekStageRealTimeRecency {
668 validity: PlanValidity,
669 plan: mz_sql::plan::SelectPlan,
670 max_query_result_size: Option<u64>,
671 source_ids: BTreeSet<GlobalId>,
672 target_replica: Option<ReplicaId>,
673 timeline_context: TimelineContext,
674 oracle_read_ts: Option<Timestamp>,
675 optimizer: optimize::PeekOptimizer,
676 explain_ctx: ExplainContext,
679}
680
681#[derive(Debug)]
682pub struct PeekStageTimestampReadHold {
683 validity: PlanValidity,
684 plan: mz_sql::plan::SelectPlan,
685 max_query_result_size: Option<u64>,
686 source_ids: BTreeSet<GlobalId>,
687 target_replica: Option<ReplicaId>,
688 timeline_context: TimelineContext,
689 oracle_read_ts: Option<Timestamp>,
690 real_time_recency_ts: Option<mz_repr::Timestamp>,
691 optimizer: optimize::PeekOptimizer,
692 explain_ctx: ExplainContext,
695}
696
697#[derive(Debug)]
698pub struct PeekStageOptimize {
699 validity: PlanValidity,
700 plan: mz_sql::plan::SelectPlan,
701 max_query_result_size: Option<u64>,
702 source_ids: BTreeSet<GlobalId>,
703 id_bundle: CollectionIdBundle,
704 target_replica: Option<ReplicaId>,
705 determination: TimestampDetermination,
706 optimizer: optimize::PeekOptimizer,
707 explain_ctx: ExplainContext,
710}
711
712#[derive(Debug)]
713pub struct PeekStageFinish {
714 validity: PlanValidity,
715 plan: mz_sql::plan::SelectPlan,
716 max_query_result_size: Option<u64>,
717 id_bundle: CollectionIdBundle,
718 target_replica: Option<ReplicaId>,
719 source_ids: BTreeSet<GlobalId>,
720 determination: TimestampDetermination,
721 cluster_id: ComputeInstanceId,
722 finishing: RowSetFinishing,
723 plan_insights_optimizer_trace: Option<OptimizerTrace>,
726 insights_ctx: Option<Box<PlanInsightsContext>>,
727 global_lir_plan: optimize::peek::GlobalLirPlan,
728 optimization_finished_at: EpochMillis,
729}
730
731#[derive(Debug)]
732pub struct PeekStageCopyTo {
733 validity: PlanValidity,
734 optimizer: optimize::copy_to::Optimizer,
735 global_lir_plan: optimize::copy_to::GlobalLirPlan,
736 optimization_finished_at: EpochMillis,
737 target_replica: Option<ReplicaId>,
738 source_ids: BTreeSet<GlobalId>,
739}
740
741#[derive(Debug)]
742pub struct PeekStageExplainPlan {
743 validity: PlanValidity,
744 optimizer: optimize::peek::Optimizer,
745 df_meta: DataflowMetainfo,
746 explain_ctx: ExplainPlanContext,
747 insights_ctx: Option<Box<PlanInsightsContext>>,
748}
749
750#[derive(Debug)]
751pub struct PeekStageExplainPushdown {
752 validity: PlanValidity,
753 determination: TimestampDetermination,
754 imports: BTreeMap<GlobalId, MapFilterProject>,
755}
756
757#[derive(Debug)]
758pub enum CreateIndexStage {
759 Optimize(CreateIndexOptimize),
760 Finish(CreateIndexFinish),
761 Explain(CreateIndexExplain),
762}
763
764#[derive(Debug)]
765pub struct CreateIndexOptimize {
766 validity: PlanValidity,
767 plan: plan::CreateIndexPlan,
768 resolved_ids: ResolvedIds,
769 explain_ctx: ExplainContext,
772}
773
774#[derive(Debug)]
775pub struct CreateIndexFinish {
776 validity: PlanValidity,
777 item_id: CatalogItemId,
778 global_id: GlobalId,
779 plan: plan::CreateIndexPlan,
780 resolved_ids: ResolvedIds,
781 global_mir_plan: optimize::index::GlobalMirPlan,
782 global_lir_plan: optimize::index::GlobalLirPlan,
783 optimizer_features: OptimizerFeatures,
784}
785
786#[derive(Debug)]
787pub struct CreateIndexExplain {
788 validity: PlanValidity,
789 exported_index_id: GlobalId,
790 plan: plan::CreateIndexPlan,
791 df_meta: DataflowMetainfo,
792 explain_ctx: ExplainPlanContext,
793}
794
795#[derive(Debug)]
796pub enum CreateMetricSinkStage {
797 Optimize(CreateMetricSinkOptimize),
798 Finish(CreateMetricSinkFinish),
799}
800
801#[derive(Debug)]
802pub struct CreateMetricSinkOptimize {
803 validity: PlanValidity,
804 plan: plan::CreateMetricSinkPlan,
805 resolved_ids: ResolvedIds,
806}
807
808#[derive(Debug)]
809pub struct CreateMetricSinkFinish {
810 validity: PlanValidity,
811 item_id: CatalogItemId,
812 global_id: GlobalId,
813 plan: plan::CreateMetricSinkPlan,
814 resolved_ids: ResolvedIds,
815 global_mir_plan: optimize::metric_sink::GlobalMirPlan,
816 global_lir_plan: optimize::metric_sink::GlobalLirPlan,
817 optimizer_features: OptimizerFeatures,
818}
819
820#[derive(Debug)]
821pub enum CreateViewStage {
822 Optimize(CreateViewOptimize),
823 Finish(CreateViewFinish),
824 Explain(CreateViewExplain),
825}
826
827#[derive(Debug)]
828pub struct CreateViewOptimize {
829 validity: PlanValidity,
830 plan: plan::CreateViewPlan,
831 resolved_ids: ResolvedIds,
832 explain_ctx: ExplainContext,
835}
836
837#[derive(Debug)]
838pub struct CreateViewFinish {
839 validity: PlanValidity,
840 item_id: CatalogItemId,
842 global_id: GlobalId,
844 plan: plan::CreateViewPlan,
845 resolved_ids: ResolvedIds,
847 optimized_expr: OptimizedMirRelationExpr,
848}
849
850#[derive(Debug)]
851pub struct CreateViewExplain {
852 validity: PlanValidity,
853 id: GlobalId,
854 plan: plan::CreateViewPlan,
855 explain_ctx: ExplainPlanContext,
856}
857
858#[derive(Debug)]
859pub enum ExplainTimestampStage {
860 Optimize(ExplainTimestampOptimize),
861 RealTimeRecency(ExplainTimestampRealTimeRecency),
862 LinearizeTimestamp(ExplainTimestampLinearizeTimestamp),
863 Finish(ExplainTimestampFinish),
864}
865
866#[derive(Debug)]
867pub struct ExplainTimestampOptimize {
868 validity: PlanValidity,
869 plan: plan::ExplainTimestampPlan,
870 cluster_id: ClusterId,
871}
872
873#[derive(Debug)]
874pub struct ExplainTimestampRealTimeRecency {
875 validity: PlanValidity,
876 format: ExplainFormat,
877 optimized_plan: OptimizedMirRelationExpr,
878 cluster_id: ClusterId,
879 when: QueryWhen,
880}
881
882#[derive(Debug)]
883pub struct ExplainTimestampLinearizeTimestamp {
884 validity: PlanValidity,
885 format: ExplainFormat,
886 optimized_plan: OptimizedMirRelationExpr,
887 cluster_id: ClusterId,
888 source_ids: BTreeSet<GlobalId>,
889 when: QueryWhen,
890 real_time_recency_ts: Option<Timestamp>,
891}
892
893#[derive(Debug)]
894pub struct ExplainTimestampFinish {
895 validity: PlanValidity,
896 format: ExplainFormat,
897 cluster_id: ClusterId,
898 source_ids: BTreeSet<GlobalId>,
899 when: QueryWhen,
900 real_time_recency_ts: Option<Timestamp>,
901 timeline_context: TimelineContext,
904 oracle_read_ts: Option<Timestamp>,
908}
909
910#[derive(Debug)]
911pub enum ClusterStage {
912 Alter(AlterCluster),
913 WaitForHydrated(AlterClusterWaitForHydrated),
914 Finalize(AlterClusterFinalize),
915 AwaitReconfiguration(AlterClusterAwaitReconfiguration),
920}
921
922#[derive(Debug)]
923pub struct AlterCluster {
924 validity: PlanValidity,
925 plan: plan::AlterClusterPlan,
926}
927
928#[derive(Debug)]
929pub struct AlterClusterWaitForHydrated {
930 validity: PlanValidity,
931 plan: plan::AlterClusterPlan,
932 new_config: ClusterVariantManaged,
933 workload_class: Option<String>,
934 timeout_time: Instant,
935 on_timeout: OnTimeoutAction,
936}
937
938#[derive(Debug)]
939pub struct AlterClusterFinalize {
940 validity: PlanValidity,
941 plan: plan::AlterClusterPlan,
942 new_config: ClusterVariantManaged,
943 workload_class: Option<String>,
944}
945
946#[derive(Debug)]
947pub struct AlterClusterAwaitReconfiguration {
948 validity: PlanValidity,
949 cluster_id: ClusterId,
950 target: ReconfigurationTarget,
954}
955
956#[derive(Debug)]
957pub enum ExplainContext {
958 None,
960 Plan(ExplainPlanContext),
962 PlanInsightsNotice(OptimizerTrace),
965 Pushdown,
967}
968
969impl ExplainContext {
970 pub(crate) fn dispatch_guard(&self) -> Option<DispatchGuard<'_>> {
974 let optimizer_trace = match self {
975 ExplainContext::Plan(explain_ctx) => Some(&explain_ctx.optimizer_trace),
976 ExplainContext::PlanInsightsNotice(optimizer_trace) => Some(optimizer_trace),
977 _ => None,
978 };
979 optimizer_trace.map(|optimizer_trace| optimizer_trace.as_guard())
980 }
981
982 pub(crate) fn needs_cluster(&self) -> bool {
983 match self {
984 ExplainContext::None => true,
985 ExplainContext::Plan(..) => false,
986 ExplainContext::PlanInsightsNotice(..) => true,
987 ExplainContext::Pushdown => false,
988 }
989 }
990
991 pub(crate) fn needs_plan_insights(&self) -> bool {
992 matches!(
993 self,
994 ExplainContext::Plan(ExplainPlanContext {
995 stage: ExplainStage::PlanInsights,
996 ..
997 }) | ExplainContext::PlanInsightsNotice(_)
998 )
999 }
1000}
1001
1002#[derive(Debug)]
1003pub struct ExplainPlanContext {
1004 pub broken: bool,
1009 pub config: ExplainConfig,
1010 pub format: ExplainFormat,
1011 pub stage: ExplainStage,
1012 pub replan: Option<GlobalId>,
1013 pub desc: Option<RelationDesc>,
1014 pub optimizer_trace: OptimizerTrace,
1015}
1016
1017#[derive(Debug)]
1018pub enum CreateMaterializedViewStage {
1019 Optimize(CreateMaterializedViewOptimize),
1020 Finish(CreateMaterializedViewFinish),
1021 Explain(CreateMaterializedViewExplain),
1022}
1023
1024#[derive(Debug)]
1025pub struct CreateMaterializedViewOptimize {
1026 validity: PlanValidity,
1027 plan: plan::CreateMaterializedViewPlan,
1028 resolved_ids: ResolvedIds,
1029 explain_ctx: ExplainContext,
1032}
1033
1034#[derive(Debug)]
1035pub struct CreateMaterializedViewFinish {
1036 item_id: CatalogItemId,
1038 global_id: GlobalId,
1040 validity: PlanValidity,
1041 plan: plan::CreateMaterializedViewPlan,
1042 resolved_ids: ResolvedIds,
1043 local_mir_plan: optimize::materialized_view::LocalMirPlan,
1044 global_mir_plan: optimize::materialized_view::GlobalMirPlan,
1045 global_lir_plan: optimize::materialized_view::GlobalLirPlan,
1046 optimizer_features: OptimizerFeatures,
1047}
1048
1049#[derive(Debug)]
1050pub struct CreateMaterializedViewExplain {
1051 global_id: GlobalId,
1052 validity: PlanValidity,
1053 plan: plan::CreateMaterializedViewPlan,
1054 df_meta: DataflowMetainfo,
1055 explain_ctx: ExplainPlanContext,
1056}
1057
1058#[derive(Debug)]
1059pub enum SubscribeStage {
1060 OptimizeMir(SubscribeOptimizeMir),
1061 LinearizeTimestamp(SubscribeLinearizeTimestamp),
1062 TimestampOptimizeLir(SubscribeTimestampOptimizeLir),
1063 Finish(SubscribeFinish),
1064 Explain(SubscribeExplain),
1065}
1066
1067#[derive(Debug)]
1068pub struct SubscribeOptimizeMir {
1069 validity: PlanValidity,
1070 plan: plan::SubscribePlan,
1071 timeline: TimelineContext,
1072 dependency_ids: BTreeSet<GlobalId>,
1073 cluster_id: ComputeInstanceId,
1074 replica_id: Option<ReplicaId>,
1075 explain_ctx: ExplainContext,
1078}
1079
1080#[derive(Debug)]
1081pub struct SubscribeLinearizeTimestamp {
1082 validity: PlanValidity,
1083 plan: plan::SubscribePlan,
1084 timeline: TimelineContext,
1085 optimizer: optimize::subscribe::Optimizer,
1086 global_mir_plan: optimize::subscribe::GlobalMirPlan<optimize::subscribe::Unresolved>,
1087 dependency_ids: BTreeSet<GlobalId>,
1088 replica_id: Option<ReplicaId>,
1089 explain_ctx: ExplainContext,
1092}
1093
1094#[derive(Debug)]
1095pub struct SubscribeTimestampOptimizeLir {
1096 validity: PlanValidity,
1097 plan: plan::SubscribePlan,
1098 timeline: TimelineContext,
1099 optimizer: optimize::subscribe::Optimizer,
1100 global_mir_plan: optimize::subscribe::GlobalMirPlan<optimize::subscribe::Unresolved>,
1101 dependency_ids: BTreeSet<GlobalId>,
1102 replica_id: Option<ReplicaId>,
1103 oracle_read_ts: Option<Timestamp>,
1107 explain_ctx: ExplainContext,
1110}
1111
1112#[derive(Debug)]
1113pub struct SubscribeFinish {
1114 validity: PlanValidity,
1115 cluster_id: ComputeInstanceId,
1116 replica_id: Option<ReplicaId>,
1117 plan: plan::SubscribePlan,
1118 global_lir_plan: optimize::subscribe::GlobalLirPlan,
1119 dependency_ids: BTreeSet<GlobalId>,
1120}
1121
1122#[derive(Debug)]
1123pub struct SubscribeExplain {
1124 validity: PlanValidity,
1125 optimizer: optimize::subscribe::Optimizer,
1126 df_meta: DataflowMetainfo,
1127 cluster_id: ComputeInstanceId,
1128 explain_ctx: ExplainPlanContext,
1129}
1130
1131#[derive(Debug)]
1132pub enum IntrospectionSubscribeStage {
1133 OptimizeMir(IntrospectionSubscribeOptimizeMir),
1134 TimestampOptimizeLir(IntrospectionSubscribeTimestampOptimizeLir),
1135 Finish(IntrospectionSubscribeFinish),
1136}
1137
1138#[derive(Debug)]
1139pub struct IntrospectionSubscribeOptimizeMir {
1140 validity: PlanValidity,
1141 plan: plan::SubscribePlan,
1142 subscribe_id: GlobalId,
1143 cluster_id: ComputeInstanceId,
1144 replica_id: ReplicaId,
1145}
1146
1147#[derive(Debug)]
1148pub struct IntrospectionSubscribeTimestampOptimizeLir {
1149 validity: PlanValidity,
1150 optimizer: optimize::subscribe::Optimizer,
1151 global_mir_plan: optimize::subscribe::GlobalMirPlan<optimize::subscribe::Unresolved>,
1152 cluster_id: ComputeInstanceId,
1153 replica_id: ReplicaId,
1154}
1155
1156#[derive(Debug)]
1157pub struct IntrospectionSubscribeFinish {
1158 validity: PlanValidity,
1159 global_lir_plan: optimize::subscribe::GlobalLirPlan,
1160 read_holds: ReadHolds,
1161 cluster_id: ComputeInstanceId,
1162 replica_id: ReplicaId,
1163}
1164
1165#[derive(Debug)]
1166pub enum SecretStage {
1167 CreateEnsure(CreateSecretEnsure),
1168 CreateFinish(CreateSecretFinish),
1169 RotateKeysEnsure(RotateKeysSecretEnsure),
1170 RotateKeysFinish(RotateKeysSecretFinish),
1171 Alter(AlterSecret),
1172}
1173
1174#[derive(Debug)]
1175pub struct CreateSecretEnsure {
1176 validity: PlanValidity,
1177 plan: plan::CreateSecretPlan,
1178}
1179
1180#[derive(Debug)]
1181pub struct CreateSecretFinish {
1182 validity: PlanValidity,
1183 item_id: CatalogItemId,
1184 global_id: GlobalId,
1185 plan: plan::CreateSecretPlan,
1186}
1187
1188#[derive(Debug)]
1189pub struct RotateKeysSecretEnsure {
1190 validity: PlanValidity,
1191 id: CatalogItemId,
1192}
1193
1194#[derive(Debug)]
1195pub struct RotateKeysSecretFinish {
1196 validity: PlanValidity,
1197 ops: Vec<crate::catalog::Op>,
1198}
1199
1200#[derive(Debug)]
1201pub struct AlterSecret {
1202 validity: PlanValidity,
1203 plan: plan::AlterSecretPlan,
1204}
1205
1206#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1211pub enum TargetCluster {
1212 CatalogServer,
1214 Active,
1216 Transaction(ClusterId),
1218}
1219
1220pub(crate) enum StageResult<T> {
1222 Handle(JoinHandle<Result<T, AdapterError>>),
1224 HandleRetire(JoinHandle<Result<ExecuteResponse, AdapterError>>),
1226 Immediate(T),
1228 Response(ExecuteResponse),
1230}
1231
1232pub(crate) trait Staged: Send {
1234 type Ctx: StagedContext;
1235
1236 fn validity(&mut self) -> &mut PlanValidity;
1237
1238 async fn stage(
1240 self,
1241 coord: &mut Coordinator,
1242 ctx: &mut Self::Ctx,
1243 ) -> Result<StageResult<Box<Self>>, AdapterError>;
1244
1245 fn message(self, ctx: Self::Ctx, span: Span) -> Message;
1247
1248 fn cancel_enabled(&self) -> bool;
1250}
1251
1252pub trait StagedContext {
1253 fn retire(self, result: Result<ExecuteResponse, AdapterError>);
1254 fn session(&self) -> Option<&Session>;
1255}
1256
1257impl StagedContext for ExecuteContext {
1258 fn retire(self, result: Result<ExecuteResponse, AdapterError>) {
1259 self.retire(result);
1260 }
1261
1262 fn session(&self) -> Option<&Session> {
1263 Some(self.session())
1264 }
1265}
1266
1267impl StagedContext for () {
1268 fn retire(self, _result: Result<ExecuteResponse, AdapterError>) {}
1269
1270 fn session(&self) -> Option<&Session> {
1271 None
1272 }
1273}
1274
1275pub struct Config {
1277 pub controller_config: ControllerConfig,
1278 pub controller_envd_epoch: NonZeroI64,
1279 pub storage: Box<dyn mz_catalog::durable::DurableCatalogState>,
1280 pub timestamp_oracle_url: Option<SensitiveUrl>,
1281 pub unsafe_mode: bool,
1282 pub all_features: bool,
1283 pub build_info: &'static BuildInfo,
1284 pub environment_id: EnvironmentId,
1285 pub metrics_registry: MetricsRegistry,
1286 pub now: NowFn,
1287 pub secrets_controller: Arc<dyn SecretsController>,
1288 pub cloud_resource_controller: Option<Arc<dyn CloudResourceController>>,
1289 pub availability_zones: Vec<String>,
1290 pub cluster_replica_sizes: ClusterReplicaSizeMap,
1291 pub builtin_system_cluster_config: BootstrapBuiltinClusterConfig,
1292 pub builtin_catalog_server_cluster_config: BootstrapBuiltinClusterConfig,
1293 pub builtin_probe_cluster_config: BootstrapBuiltinClusterConfig,
1294 pub builtin_support_cluster_config: BootstrapBuiltinClusterConfig,
1295 pub builtin_analytics_cluster_config: BootstrapBuiltinClusterConfig,
1296 pub system_parameter_defaults: BTreeMap<String, String>,
1297 pub storage_usage_client: StorageUsageClient,
1298 pub storage_usage_collection_interval: Duration,
1299 pub storage_usage_retention_period: Option<Duration>,
1300 pub segment_client: Option<mz_segment::Client>,
1301 pub egress_addresses: Vec<IpNet>,
1302 pub remote_system_parameters: Option<BTreeMap<String, String>>,
1303 pub aws_account_id: Option<String>,
1304 pub aws_privatelink_availability_zones: Option<Vec<String>>,
1305 pub connection_context: ConnectionContext,
1306 pub connection_limit_callback: Box<dyn Fn(u64, u64) -> () + Send + Sync + 'static>,
1307 pub webhook_concurrency_limit: WebhookConcurrencyLimiter,
1308 pub http_host_name: Option<String>,
1309 pub tracing_handle: TracingHandle,
1310 pub read_only_controllers: bool,
1314
1315 pub caught_up_trigger: Option<Trigger>,
1319
1320 pub helm_chart_version: Option<String>,
1321 pub license_key: ValidatedLicenseKey,
1322 pub external_login_password_mz_system: Option<Password>,
1323 pub force_builtin_schema_migration: Option<String>,
1324}
1325
1326#[derive(Debug, Serialize)]
1328pub struct ConnMeta {
1329 secret_key: u32,
1334 connected_at: EpochMillis,
1336 user: User,
1337 application_name: String,
1338 uuid: Uuid,
1339 conn_id: ConnectionId,
1340 client_ip: Option<IpAddr>,
1341
1342 drop_sinks: BTreeSet<GlobalId>,
1345
1346 #[serde(skip)]
1348 deferred_lock: Option<OwnedMutexGuard<()>>,
1349
1350 pending_cluster_alters: BTreeSet<ClusterId>,
1353
1354 #[serde(skip)]
1356 notice_tx: mpsc::UnboundedSender<AdapterNotice>,
1357
1358 authenticated_role: RoleId,
1362}
1363
1364impl ConnMeta {
1365 pub fn conn_id(&self) -> &ConnectionId {
1366 &self.conn_id
1367 }
1368
1369 pub fn user(&self) -> &User {
1370 &self.user
1371 }
1372
1373 pub fn application_name(&self) -> &str {
1374 &self.application_name
1375 }
1376
1377 pub fn authenticated_role_id(&self) -> &RoleId {
1378 &self.authenticated_role
1379 }
1380
1381 pub fn uuid(&self) -> Uuid {
1382 self.uuid
1383 }
1384
1385 pub fn client_ip(&self) -> Option<IpAddr> {
1386 self.client_ip
1387 }
1388
1389 pub fn connected_at(&self) -> EpochMillis {
1390 self.connected_at
1391 }
1392}
1393
1394#[derive(Debug)]
1395pub struct PendingTxn {
1397 ctx: ExecuteContext,
1399 response: Result<PendingTxnResponse, AdapterError>,
1401 action: EndTransactionAction,
1403}
1404
1405#[derive(Debug)]
1406pub enum PendingTxnResponse {
1408 Committed {
1410 params: BTreeMap<&'static str, String>,
1412 },
1413 Rolledback {
1415 params: BTreeMap<&'static str, String>,
1417 },
1418}
1419
1420impl PendingTxnResponse {
1421 pub fn extend_params(&mut self, p: impl IntoIterator<Item = (&'static str, String)>) {
1422 match self {
1423 PendingTxnResponse::Committed { params }
1424 | PendingTxnResponse::Rolledback { params } => params.extend(p),
1425 }
1426 }
1427}
1428
1429impl From<PendingTxnResponse> for ExecuteResponse {
1430 fn from(value: PendingTxnResponse) -> Self {
1431 match value {
1432 PendingTxnResponse::Committed { params } => {
1433 ExecuteResponse::TransactionCommitted { params }
1434 }
1435 PendingTxnResponse::Rolledback { params } => {
1436 ExecuteResponse::TransactionRolledBack { params }
1437 }
1438 }
1439 }
1440}
1441
1442#[derive(Debug)]
1443pub struct PendingReadTxn {
1445 txn: PendingRead,
1447 timestamp_context: TimestampContext,
1449 created: Instant,
1451 num_requeues: u64,
1455 otel_ctx: OpenTelemetryContext,
1457}
1458
1459impl PendingReadTxn {
1460 pub fn timestamp_context(&self) -> &TimestampContext {
1462 &self.timestamp_context
1463 }
1464
1465 pub(crate) fn take_context(self) -> ExecuteContext {
1466 self.txn.take_context()
1467 }
1468}
1469
1470#[derive(Debug)]
1471enum PendingRead {
1473 Read {
1474 txn: PendingTxn,
1476 },
1477 ReadThenWrite {
1478 ctx: ExecuteContext,
1480 tx: oneshot::Sender<Option<ExecuteContext>>,
1483 },
1484}
1485
1486impl PendingRead {
1487 #[instrument(level = "debug")]
1492 pub fn finish(self) -> Option<(ExecuteContext, Result<ExecuteResponse, AdapterError>)> {
1493 match self {
1494 PendingRead::Read {
1495 txn:
1496 PendingTxn {
1497 mut ctx,
1498 response,
1499 action,
1500 },
1501 ..
1502 } => {
1503 let changed = ctx.session_mut().vars_mut().end_transaction(action);
1504 let response = response.map(|mut r| {
1506 r.extend_params(changed);
1507 ExecuteResponse::from(r)
1508 });
1509
1510 Some((ctx, response))
1511 }
1512 PendingRead::ReadThenWrite { ctx, tx, .. } => {
1513 let _ = tx.send(Some(ctx));
1515 None
1516 }
1517 }
1518 }
1519
1520 fn label(&self) -> &'static str {
1521 match self {
1522 PendingRead::Read { .. } => "read",
1523 PendingRead::ReadThenWrite { .. } => "read_then_write",
1524 }
1525 }
1526
1527 pub(crate) fn take_context(self) -> ExecuteContext {
1528 match self {
1529 PendingRead::Read { txn, .. } => txn.ctx,
1530 PendingRead::ReadThenWrite { ctx, tx, .. } => {
1531 let _ = tx.send(None);
1534 ctx
1535 }
1536 }
1537 }
1538}
1539
1540#[derive(Debug, Default)]
1550#[must_use]
1551pub struct ExecuteContextExtra {
1552 statement_uuid: Option<StatementLoggingId>,
1553}
1554
1555impl ExecuteContextExtra {
1556 pub(crate) fn new(statement_uuid: Option<StatementLoggingId>) -> Self {
1557 Self { statement_uuid }
1558 }
1559 pub fn is_trivial(&self) -> bool {
1560 self.statement_uuid.is_none()
1561 }
1562 pub fn contents(&self) -> Option<StatementLoggingId> {
1563 self.statement_uuid
1564 }
1565 #[must_use]
1569 pub(crate) fn retire(self) -> Option<StatementLoggingId> {
1570 self.statement_uuid
1571 }
1572}
1573
1574#[derive(Debug)]
1584#[must_use]
1585pub struct ExecuteContextGuard {
1586 extra: ExecuteContextExtra,
1587 coordinator_tx: mpsc::UnboundedSender<Message>,
1592}
1593
1594impl Default for ExecuteContextGuard {
1595 fn default() -> Self {
1596 let (tx, _rx) = mpsc::unbounded_channel();
1600 Self {
1601 extra: ExecuteContextExtra::default(),
1602 coordinator_tx: tx,
1603 }
1604 }
1605}
1606
1607impl ExecuteContextGuard {
1608 pub(crate) fn new(
1609 statement_uuid: Option<StatementLoggingId>,
1610 coordinator_tx: mpsc::UnboundedSender<Message>,
1611 ) -> Self {
1612 Self {
1613 extra: ExecuteContextExtra::new(statement_uuid),
1614 coordinator_tx,
1615 }
1616 }
1617 pub fn is_trivial(&self) -> bool {
1618 self.extra.is_trivial()
1619 }
1620 pub fn contents(&self) -> Option<StatementLoggingId> {
1621 self.extra.contents()
1622 }
1623 pub(crate) fn defuse(mut self) -> ExecuteContextExtra {
1630 std::mem::take(&mut self.extra)
1632 }
1633}
1634
1635impl Drop for ExecuteContextGuard {
1636 fn drop(&mut self) {
1637 if let Some(statement_uuid) = self.extra.statement_uuid.take() {
1638 let msg = Message::RetireExecute {
1641 data: ExecuteContextExtra {
1642 statement_uuid: Some(statement_uuid),
1643 },
1644 otel_ctx: OpenTelemetryContext::obtain(),
1645 reason: StatementEndedExecutionReason::Aborted,
1646 };
1647 let _ = self.coordinator_tx.send(msg);
1650 }
1651 }
1652}
1653
1654#[derive(Debug)]
1659pub struct ExecuteContext {
1660 inner: Option<Box<ExecuteContextInner>>,
1662}
1663
1664impl std::ops::Deref for ExecuteContext {
1665 type Target = ExecuteContextInner;
1666 fn deref(&self) -> &Self::Target {
1667 self.inner.as_ref().expect("only consumed by value")
1668 }
1669}
1670
1671impl std::ops::DerefMut for ExecuteContext {
1672 fn deref_mut(&mut self) -> &mut Self::Target {
1673 self.inner.as_mut().expect("only consumed by value")
1674 }
1675}
1676
1677impl Drop for ExecuteContext {
1678 fn drop(&mut self) {
1679 let Some(inner) = self.inner.take() else {
1680 return;
1681 };
1682 tracing::warn!("execute context dropped without retirement, failing the client");
1685 let ExecuteContextInner { tx, session, .. } = *inner;
1686 tx.send(
1687 Err(AdapterError::Internal(
1688 "statement execution abandoned, outcome unknown (server shutting down)".into(),
1689 )),
1690 session,
1691 );
1692 }
1693}
1694
1695#[derive(Derivative)]
1696#[derivative(Debug)]
1697pub struct ExecuteContextInner {
1698 tx: ClientTransmitter<ExecuteResponse>,
1699 internal_cmd_tx: mpsc::UnboundedSender<Message>,
1700 session: Session,
1701 extra: ExecuteContextGuard,
1702 #[derivative(Debug = "ignore")]
1703 response_barriers: Vec<BuiltinTableAppendNotify>,
1704}
1705
1706impl ExecuteContext {
1707 pub fn session(&self) -> &Session {
1708 &self.session
1709 }
1710
1711 pub fn session_mut(&mut self) -> &mut Session {
1712 &mut self.session
1713 }
1714
1715 pub fn tx(&self) -> &ClientTransmitter<ExecuteResponse> {
1716 &self.tx
1717 }
1718
1719 pub fn tx_mut(&mut self) -> &mut ClientTransmitter<ExecuteResponse> {
1720 &mut self.tx
1721 }
1722
1723 pub fn from_parts(
1724 tx: ClientTransmitter<ExecuteResponse>,
1725 internal_cmd_tx: mpsc::UnboundedSender<Message>,
1726 session: Session,
1727 extra: ExecuteContextGuard,
1728 ) -> Self {
1729 Self::from_parts_with_response_barriers(tx, internal_cmd_tx, session, extra, Vec::new())
1730 }
1731
1732 pub fn from_parts_with_response_barriers(
1733 tx: ClientTransmitter<ExecuteResponse>,
1734 internal_cmd_tx: mpsc::UnboundedSender<Message>,
1735 session: Session,
1736 extra: ExecuteContextGuard,
1737 response_barriers: Vec<BuiltinTableAppendNotify>,
1738 ) -> Self {
1739 Self {
1740 inner: Some(
1741 ExecuteContextInner {
1742 tx,
1743 session,
1744 extra,
1745 response_barriers,
1746 internal_cmd_tx,
1747 }
1748 .into(),
1749 ),
1750 }
1751 }
1752
1753 pub fn into_parts(
1767 mut self,
1768 ) -> (
1769 ClientTransmitter<ExecuteResponse>,
1770 mpsc::UnboundedSender<Message>,
1771 Session,
1772 ExecuteContextGuard,
1773 Vec<BuiltinTableAppendNotify>,
1774 ) {
1775 let ExecuteContextInner {
1776 tx,
1777 internal_cmd_tx,
1778 session,
1779 extra,
1780 response_barriers,
1781 } = *self.inner.take().expect("only consumed by value");
1782 (tx, internal_cmd_tx, session, extra, response_barriers)
1783 }
1784
1785 #[instrument(level = "debug")]
1787 pub fn retire(mut self, result: Result<ExecuteResponse, AdapterError>) {
1788 let response_barriers = std::mem::take(&mut self.response_barriers);
1789 if response_barriers.is_empty() {
1790 let (tx, internal_cmd_tx, session, extra, _) = self.into_parts();
1791 retire_execution_context(tx, internal_cmd_tx, session, extra, result);
1792 return;
1793 }
1794 spawn(
1797 || "execute_context::retire_after_response_barriers",
1798 async move {
1799 for barrier in response_barriers {
1800 barrier.await;
1801 }
1802 self.retire(result);
1803 },
1804 );
1805 }
1806
1807 pub(crate) fn delay_response_until(&mut self, barrier: BuiltinTableAppendCompletion) {
1809 self.response_barriers.push(barrier.into_notify());
1810 }
1811
1812 pub fn extra(&self) -> &ExecuteContextGuard {
1813 &self.extra
1814 }
1815
1816 pub fn extra_mut(&mut self) -> &mut ExecuteContextGuard {
1817 &mut self.extra
1818 }
1819}
1820
1821fn retire_execution_context(
1822 tx: ClientTransmitter<ExecuteResponse>,
1823 internal_cmd_tx: mpsc::UnboundedSender<Message>,
1824 session: Session,
1825 extra: ExecuteContextGuard,
1826 result: Result<ExecuteResponse, AdapterError>,
1827) {
1828 let reason = if extra.is_trivial() {
1829 None
1830 } else {
1831 Some((&result).into())
1832 };
1833 tx.send(result, session);
1834 if let Some(reason) = reason {
1835 let extra = extra.defuse();
1836 if let Err(e) = internal_cmd_tx.send(Message::RetireExecute {
1837 otel_ctx: OpenTelemetryContext::obtain(),
1838 data: extra,
1839 reason,
1840 }) {
1841 warn!("internal_cmd_rx dropped before we could send: {:?}", e);
1842 }
1843 }
1844}
1845
1846#[derive(Debug)]
1847struct ClusterReplicaStatuses(
1848 BTreeMap<ClusterId, BTreeMap<ReplicaId, BTreeMap<ProcessId, ClusterReplicaProcessStatus>>>,
1849);
1850
1851impl ClusterReplicaStatuses {
1852 pub(crate) fn new() -> ClusterReplicaStatuses {
1853 ClusterReplicaStatuses(BTreeMap::new())
1854 }
1855
1856 pub(crate) fn initialize_cluster_statuses(&mut self, cluster_id: ClusterId) {
1860 let prev = self.0.insert(cluster_id, BTreeMap::new());
1861 assert_eq!(
1862 prev, None,
1863 "cluster {cluster_id} statuses already initialized"
1864 );
1865 }
1866
1867 pub(crate) fn initialize_cluster_replica_statuses(
1871 &mut self,
1872 cluster_id: ClusterId,
1873 replica_id: ReplicaId,
1874 num_processes: usize,
1875 time: DateTime<Utc>,
1876 ) {
1877 tracing::info!(
1878 ?cluster_id,
1879 ?replica_id,
1880 ?time,
1881 "initializing cluster replica status"
1882 );
1883 let replica_statuses = self.0.entry(cluster_id).or_default();
1884 let process_statuses = (0..num_processes)
1885 .map(|process_id| {
1886 let status = ClusterReplicaProcessStatus {
1887 status: ClusterStatus::Offline(Some(OfflineReason::Initializing)),
1888 restart_count: 0,
1889 time: time.clone(),
1890 };
1891 (u64::cast_from(process_id), status)
1892 })
1893 .collect();
1894 let prev = replica_statuses.insert(replica_id, process_statuses);
1895 assert_none!(
1896 prev,
1897 "cluster replica {cluster_id}.{replica_id} statuses already initialized"
1898 );
1899 }
1900
1901 pub(crate) fn remove_cluster_statuses(
1905 &mut self,
1906 cluster_id: &ClusterId,
1907 ) -> BTreeMap<ReplicaId, BTreeMap<ProcessId, ClusterReplicaProcessStatus>> {
1908 let prev = self.0.remove(cluster_id);
1909 prev.unwrap_or_else(|| panic!("unknown cluster: {cluster_id}"))
1910 }
1911
1912 pub(crate) fn remove_cluster_replica_statuses(
1916 &mut self,
1917 cluster_id: &ClusterId,
1918 replica_id: &ReplicaId,
1919 ) -> BTreeMap<ProcessId, ClusterReplicaProcessStatus> {
1920 let replica_statuses = self
1921 .0
1922 .get_mut(cluster_id)
1923 .unwrap_or_else(|| panic!("unknown cluster: {cluster_id}"));
1924 let prev = replica_statuses.remove(replica_id);
1925 prev.unwrap_or_else(|| panic!("unknown cluster replica: {cluster_id}.{replica_id}"))
1926 }
1927
1928 pub(crate) fn ensure_cluster_status(
1932 &mut self,
1933 cluster_id: ClusterId,
1934 replica_id: ReplicaId,
1935 process_id: ProcessId,
1936 status: ClusterReplicaProcessStatus,
1937 ) {
1938 let replica_statuses = self
1939 .0
1940 .get_mut(&cluster_id)
1941 .unwrap_or_else(|| panic!("unknown cluster: {cluster_id}"))
1942 .get_mut(&replica_id)
1943 .unwrap_or_else(|| panic!("unknown cluster replica: {cluster_id}.{replica_id}"));
1944 replica_statuses.insert(process_id, status);
1945 }
1946
1947 pub fn get_cluster_replica_status(
1951 &self,
1952 cluster_id: ClusterId,
1953 replica_id: ReplicaId,
1954 ) -> ClusterStatus {
1955 let process_status = self.get_cluster_replica_statuses(cluster_id, replica_id);
1956 Self::cluster_replica_status(process_status)
1957 }
1958
1959 pub fn cluster_replica_status(
1961 process_status: &BTreeMap<ProcessId, ClusterReplicaProcessStatus>,
1962 ) -> ClusterStatus {
1963 process_status
1964 .values()
1965 .fold(ClusterStatus::Online, |s, p| match (s, p.status) {
1966 (ClusterStatus::Online, ClusterStatus::Online) => ClusterStatus::Online,
1967 (x, y) => {
1968 let reason_x = match x {
1969 ClusterStatus::Offline(reason) => reason,
1970 ClusterStatus::Online => None,
1971 };
1972 let reason_y = match y {
1973 ClusterStatus::Offline(reason) => reason,
1974 ClusterStatus::Online => None,
1975 };
1976 ClusterStatus::Offline(reason_x.or(reason_y))
1978 }
1979 })
1980 }
1981
1982 pub(crate) fn get_cluster_replica_statuses(
1986 &self,
1987 cluster_id: ClusterId,
1988 replica_id: ReplicaId,
1989 ) -> &BTreeMap<ProcessId, ClusterReplicaProcessStatus> {
1990 self.try_get_cluster_replica_statuses(cluster_id, replica_id)
1991 .unwrap_or_else(|| panic!("unknown cluster replica: {cluster_id}.{replica_id}"))
1992 }
1993
1994 pub(crate) fn try_get_cluster_replica_statuses(
1996 &self,
1997 cluster_id: ClusterId,
1998 replica_id: ReplicaId,
1999 ) -> Option<&BTreeMap<ProcessId, ClusterReplicaProcessStatus>> {
2000 self.try_get_cluster_statuses(cluster_id)
2001 .and_then(|statuses| statuses.get(&replica_id))
2002 }
2003
2004 pub(crate) fn try_get_cluster_statuses(
2006 &self,
2007 cluster_id: ClusterId,
2008 ) -> Option<&BTreeMap<ReplicaId, BTreeMap<ProcessId, ClusterReplicaProcessStatus>>> {
2009 self.0.get(&cluster_id)
2010 }
2011}
2012
2013#[derive(Derivative)]
2015#[derivative(Debug)]
2016pub struct Coordinator {
2017 #[derivative(Debug = "ignore")]
2019 controller: mz_controller::Controller,
2020 catalog: Arc<Catalog>,
2028
2029 persist_client: PersistClient,
2032
2033 internal_cmd_tx: mpsc::UnboundedSender<Message>,
2035 group_commit_tx: appends::GroupCommitNotifier,
2037 reconcile_now: Arc<Notify>,
2041 group_committer_tx: mpsc::UnboundedSender<appends::TableWriteCmd>,
2042
2043 strict_serializable_reads_tx: mpsc::UnboundedSender<(ConnectionId, PendingReadTxn)>,
2045
2046 linearize_reads_notify: Arc<Notify>,
2050
2051 global_timelines: BTreeMap<Timeline, TimelineState>,
2054
2055 transient_id_gen: Arc<TransientIdGen>,
2057 active_conns: BTreeMap<ConnectionId, ConnMeta>,
2060
2061 txn_read_holds: BTreeMap<ConnectionId, read_policy::ReadHolds>,
2065
2066 pending_peeks: BTreeMap<Uuid, PendingPeek>,
2070 client_pending_peeks: BTreeMap<ConnectionId, BTreeMap<Uuid, ClusterId>>,
2072
2073 pending_linearize_read_txns: BTreeMap<ConnectionId, PendingReadTxn>,
2075
2076 active_compute_sinks: BTreeMap<GlobalId, ActiveComputeSink>,
2078 active_webhooks: BTreeMap<CatalogItemId, WebhookAppenderInvalidator>,
2080 active_copies: BTreeMap<ConnectionId, ActiveCopyFrom>,
2083
2084 connection_cancel_watches: BTreeMap<ConnectionId, (watch::Sender<bool>, watch::Receiver<bool>)>,
2095 introspection_subscribes: BTreeMap<GlobalId, IntrospectionSubscribe>,
2097
2098 write_locks: BTreeMap<CatalogItemId, Arc<tokio::sync::Mutex<()>>>,
2100 deferred_write_ops: BTreeMap<ConnectionId, DeferredOp>,
2102
2103 pending_writes: Vec<PendingWriteTxn>,
2105
2106 occ_write_semaphore: Arc<Semaphore>,
2117
2118 frontend_read_then_write_enabled: bool,
2123
2124 advance_timelines_interval: Interval,
2134
2135 serialized_ddl: LockedVecDeque<DeferredPlanStatement>,
2144
2145 secrets_controller: Arc<dyn SecretsController>,
2148 caching_secrets_reader: CachingSecretsReader,
2150
2151 cloud_resource_controller: Option<Arc<dyn CloudResourceController>>,
2154
2155 storage_usage_client: StorageUsageClient,
2157 storage_usage_collection_interval: Duration,
2159
2160 #[derivative(Debug = "ignore")]
2162 segment_client: Option<mz_segment::Client>,
2163
2164 metrics: Metrics,
2166 optimizer_metrics: OptimizerMetrics,
2168
2169 tracing_handle: TracingHandle,
2171
2172 statement_logging: StatementLogging,
2174
2175 webhook_concurrency_limit: WebhookConcurrencyLimiter,
2177
2178 timestamp_oracle_config: Option<TimestampOracleConfig>,
2181
2182 caught_up_check_interval: Interval,
2185
2186 caught_up_check: Option<CaughtUpCheckContext>,
2189
2190 catalog_info_metrics_registry: MetricsRegistry,
2193
2194 scoped_frontend: Option<Arc<SystemParameterFrontend>>,
2203
2204 installed_watch_sets: BTreeMap<WatchSetId, (ConnectionId, WatchSetResponse)>,
2206
2207 connection_watch_sets: BTreeMap<ConnectionId, BTreeSet<WatchSetId>>,
2209
2210 cluster_replica_statuses: ClusterReplicaStatuses,
2212
2213 read_only_controllers: bool,
2217
2218 buffered_builtin_table_updates: Option<Vec<BuiltinTableUpdate>>,
2226
2227 license_key: ValidatedLicenseKey,
2228
2229 user_id_pool: IdPool,
2231}
2232
2233impl Coordinator {
2234 pub(crate) async fn reconcile_scoped_system_parameters(
2253 &mut self,
2254 scoped: ScopedParameters,
2255 prune_scope: ScopedParametersScope,
2256 ) {
2257 if self.catalog().state().scoped_system_parameters() == &scoped {
2260 return;
2261 }
2262
2263 if let Err(e) = self
2271 .catalog_transact(
2272 None,
2273 vec![crate::catalog::Op::UpdateScopedSystemParameters {
2274 scoped,
2275 prune_scope,
2276 }],
2277 )
2278 .await
2279 {
2280 tracing::warn!("failed to persist scoped system parameters: {e}");
2281 }
2282 }
2283
2284 fn scoped_overrides_create_op(
2304 &self,
2305 clusters: &[ClusterEvalContext],
2306 replicas: &[ReplicaEvalContext],
2307 ) -> Option<crate::catalog::Op> {
2308 let frontend = self.scoped_frontend.clone()?;
2309 let catalog = self.catalog();
2310 let system_config = catalog.system_config();
2311
2312 let replica_param_names: Vec<&'static str> = system_config
2315 .iter_synced()
2316 .filter(|var| var.scope() == ParameterScope::Replica)
2317 .map(|var| var.name())
2318 .collect();
2319 let cluster_param_names: Vec<&'static str> = system_config
2320 .iter_synced()
2321 .filter(|var| var.scope() == ParameterScope::Cluster)
2322 .map(|var| var.name())
2323 .collect();
2324
2325 let params = SynchronizedParameters::new(system_config.clone());
2326 let mut evaluated = ScopedParameters::default();
2327 if !cluster_param_names.is_empty() && !clusters.is_empty() {
2328 evaluated.cluster =
2329 frontend.pull_cluster_overrides(¶ms, &cluster_param_names, clusters);
2330 }
2331 if !replica_param_names.is_empty() && !replicas.is_empty() {
2332 evaluated.replica =
2333 frontend.pull_replica_overrides(¶ms, &replica_param_names, replicas);
2334 }
2335 if evaluated.is_empty() {
2336 return None;
2337 }
2338
2339 let prune_scope = ScopedParametersScope {
2343 clusters: clusters.iter().map(|cluster| cluster.cluster_id).collect(),
2344 replicas: replicas.iter().map(|replica| replica.replica_id).collect(),
2345 };
2346 Some(crate::catalog::Op::UpdateScopedSystemParameters {
2347 scoped: evaluated,
2348 prune_scope,
2349 })
2350 }
2351
2352 pub(crate) fn replica_dyncfg_overrides(
2358 &self,
2359 ) -> BTreeMap<ComputeInstanceId, BTreeMap<ReplicaId, ConfigUpdates>> {
2360 let replica_overrides = &self.catalog().state().scoped_system_parameters().replica;
2361
2362 let dyncfgs = self.catalog().system_config().dyncfgs();
2363 let mut instance_overrides: BTreeMap<
2364 ComputeInstanceId,
2365 BTreeMap<ReplicaId, ConfigUpdates>,
2366 > = BTreeMap::new();
2367 for cluster in self.catalog().clusters() {
2368 for replica in cluster.replicas() {
2369 let Some(values) = replica_overrides.get(&replica.replica_id) else {
2370 continue;
2371 };
2372 let mut updates = ConfigUpdates::default();
2373 for (name, value) in values {
2374 let Some(entry) = dyncfgs.entry(name) else {
2375 continue;
2378 };
2379 match entry.parse_val(value) {
2380 Ok(val) => updates.add_dynamic(name, val),
2381 Err(e) => {
2382 tracing::warn!(%name, %value, "cannot parse scoped override: {e}")
2383 }
2384 }
2385 }
2386 if !updates.updates.is_empty() {
2387 instance_overrides
2388 .entry(cluster.id)
2389 .or_default()
2390 .insert(replica.replica_id, updates);
2391 }
2392 }
2393 }
2394
2395 instance_overrides
2396 }
2397
2398 pub(crate) fn push_replica_dyncfg_overrides(&mut self) {
2404 let instance_overrides = self.replica_dyncfg_overrides();
2405
2406 self.controller
2421 .update_replica_dyncfg_overrides(instance_overrides);
2422 let compute_config = crate::flags::compute_config(self.catalog().system_config());
2428 self.controller.compute.update_configuration(compute_config);
2429 let storage_config = crate::flags::storage_config(self.catalog().system_config());
2430 self.controller.storage.update_parameters(storage_config);
2431 }
2432
2433 pub(crate) fn cluster_scoped_optimizer_overrides(
2437 &self,
2438 cluster_id: ClusterId,
2439 ) -> OptimizerFeatureOverrides {
2440 self.catalog()
2441 .state()
2442 .cluster_scoped_optimizer_overrides(cluster_id)
2443 }
2444
2445 #[instrument(name = "coord::bootstrap")]
2449 pub(crate) async fn bootstrap(
2450 &mut self,
2451 boot_ts: Timestamp,
2452 migrated_storage_collections_0dt: BTreeSet<CatalogItemId>,
2453 mut builtin_table_updates: Vec<BuiltinTableUpdate>,
2454 cached_global_exprs: BTreeMap<GlobalId, GlobalExpressions>,
2455 uncached_local_exprs: BTreeMap<GlobalId, LocalExpressions>,
2456 ) -> Result<(), AdapterError> {
2457 let bootstrap_start = Instant::now();
2458 info!("startup: coordinator init: bootstrap beginning");
2459 info!("startup: coordinator init: bootstrap: preamble beginning");
2460
2461 let cluster_statuses: Vec<(_, Vec<_>)> = self
2464 .catalog()
2465 .clusters()
2466 .map(|cluster| {
2467 (
2468 cluster.id(),
2469 cluster
2470 .replicas()
2471 .map(|replica| {
2472 (replica.replica_id, replica.config.location.num_processes())
2473 })
2474 .collect(),
2475 )
2476 })
2477 .collect();
2478 let now = self.now_datetime();
2479 for (cluster_id, replica_statuses) in cluster_statuses {
2480 self.cluster_replica_statuses
2481 .initialize_cluster_statuses(cluster_id);
2482 for (replica_id, num_processes) in replica_statuses {
2483 self.cluster_replica_statuses
2484 .initialize_cluster_replica_statuses(
2485 cluster_id,
2486 replica_id,
2487 num_processes,
2488 now,
2489 );
2490 }
2491 }
2492
2493 let system_config = self.catalog().system_config();
2494
2495 mz_metrics::update_dyncfg(&system_config.dyncfg_updates());
2497
2498 let compute_config = flags::compute_config(system_config);
2500 let storage_config = flags::storage_config(system_config);
2501 let scheduling_config = flags::orchestrator_scheduling_config(system_config);
2502 let dyncfg_updates = system_config.dyncfg_updates();
2503 self.controller.compute.update_configuration(compute_config);
2504 self.controller.storage.update_parameters(storage_config);
2505 self.controller
2506 .update_orchestrator_scheduling_config(scheduling_config);
2507 self.controller.update_configuration(dyncfg_updates);
2508
2509 let replica_dyncfg_overrides = self.replica_dyncfg_overrides();
2516 self.controller
2517 .update_replica_dyncfg_overrides(replica_dyncfg_overrides);
2518
2519 let enforce_credit_limit_at_bootstrap = !matches!(
2524 self.license_key.expiration_behavior,
2525 ExpirationBehavior::DisableClusterCreation,
2526 );
2527 if enforce_credit_limit_at_bootstrap {
2528 self.validate_resource_limit_numeric(
2529 Numeric::zero(),
2530 self.current_credit_consumption_rate(None),
2531 |system_vars| {
2532 self.license_key
2533 .max_credit_consumption_rate()
2534 .map_or_else(|| system_vars.max_credit_consumption_rate(), Numeric::from)
2535 },
2536 "cluster replica",
2537 MAX_CREDIT_CONSUMPTION_RATE.name(),
2538 )?;
2539 }
2540
2541 let mut policies_to_set: BTreeMap<CompactionWindow, CollectionIdBundle> =
2542 Default::default();
2543
2544 let enable_worker_core_affinity =
2545 self.catalog().system_config().enable_worker_core_affinity();
2546 let enable_storage_introspection_logs = self
2547 .catalog()
2548 .system_config()
2549 .enable_storage_introspection_logs();
2550 for instance in self.catalog.clusters() {
2551 self.controller.create_cluster(
2552 instance.id,
2553 ClusterConfig {
2554 arranged_logs: instance.log_indexes.clone(),
2555 workload_class: instance.config.workload_class.clone(),
2556 },
2557 )?;
2558 for replica in instance.replicas() {
2559 let role = instance.role();
2560 self.controller.create_replica(
2561 instance.id,
2562 replica.replica_id,
2563 instance.name.clone(),
2564 replica.name.clone(),
2565 role,
2566 replica.config.clone(),
2567 enable_worker_core_affinity,
2568 enable_storage_introspection_logs,
2569 )?;
2570 }
2571 }
2572
2573 self.push_replica_dyncfg_overrides();
2585
2586 info!(
2587 "startup: coordinator init: bootstrap: preamble complete in {:?}",
2588 bootstrap_start.elapsed()
2589 );
2590
2591 let init_storage_collections_start = Instant::now();
2592 info!("startup: coordinator init: bootstrap: storage collections init beginning");
2593 self.bootstrap_storage_collections(&migrated_storage_collections_0dt)
2594 .await;
2595 info!(
2596 "startup: coordinator init: bootstrap: storage collections init complete in {:?}",
2597 init_storage_collections_start.elapsed()
2598 );
2599
2600 self.controller.start_compute_introspection_sink();
2605
2606 let sorting_start = Instant::now();
2607 info!("startup: coordinator init: bootstrap: sorting catalog entries");
2608 let entries = self.bootstrap_sort_catalog_entries();
2609 info!(
2610 "startup: coordinator init: bootstrap: sorting catalog entries complete in {:?}",
2611 sorting_start.elapsed()
2612 );
2613
2614 let optimize_dataflows_start = Instant::now();
2615 info!("startup: coordinator init: bootstrap: optimize dataflow plans beginning");
2616 let uncached_global_exps = self.bootstrap_dataflow_plans(&entries, cached_global_exprs)?;
2617 info!(
2618 "startup: coordinator init: bootstrap: optimize dataflow plans complete in {:?}",
2619 optimize_dataflows_start.elapsed()
2620 );
2621
2622 let _fut = self.catalog().update_expression_cache(
2624 uncached_local_exprs.into_iter().collect(),
2625 uncached_global_exps.into_iter().collect(),
2626 Default::default(),
2627 );
2628
2629 let bootstrap_as_ofs_start = Instant::now();
2633 info!("startup: coordinator init: bootstrap: dataflow as-of bootstrapping beginning");
2634 let dataflow_read_holds = self.bootstrap_dataflow_as_ofs().await;
2635 info!(
2636 "startup: coordinator init: bootstrap: dataflow as-of bootstrapping complete in {:?}",
2637 bootstrap_as_ofs_start.elapsed()
2638 );
2639
2640 let postamble_start = Instant::now();
2641 info!("startup: coordinator init: bootstrap: postamble beginning");
2642
2643 let logs: BTreeSet<_> = BUILTINS::logs()
2644 .map(|log| self.catalog().resolve_builtin_log(log))
2645 .flat_map(|item_id| self.catalog().get_global_ids(&item_id))
2646 .collect();
2647
2648 let mut privatelink_connections = BTreeMap::new();
2649
2650 for entry in &entries {
2651 debug!(
2652 "coordinator init: installing {} {}",
2653 entry.item().typ(),
2654 entry.id()
2655 );
2656 let mut policy = entry.item().initial_logical_compaction_window();
2657 match entry.item() {
2658 CatalogItem::Source(source) => {
2664 if source.custom_logical_compaction_window.is_none() {
2666 if let DataSourceDesc::IngestionExport { ingestion_id, .. } =
2667 source.data_source
2668 {
2669 policy = Some(
2670 self.catalog()
2671 .get_entry(&ingestion_id)
2672 .source()
2673 .expect("must be source")
2674 .custom_logical_compaction_window
2675 .unwrap_or_default(),
2676 );
2677 }
2678 }
2679 policies_to_set
2680 .entry(policy.expect("sources have a compaction window"))
2681 .or_insert_with(Default::default)
2682 .storage_ids
2683 .insert(source.global_id());
2684 }
2685 CatalogItem::Table(table) => {
2686 policies_to_set
2687 .entry(policy.expect("tables have a compaction window"))
2688 .or_insert_with(Default::default)
2689 .storage_ids
2690 .extend(table.global_ids());
2691 }
2692 CatalogItem::Index(idx) => {
2693 let policy_entry = policies_to_set
2694 .entry(policy.expect("indexes have a compaction window"))
2695 .or_insert_with(Default::default);
2696
2697 if logs.contains(&idx.on) {
2698 policy_entry
2699 .compute_ids
2700 .entry(idx.cluster_id)
2701 .or_insert_with(BTreeSet::new)
2702 .insert(idx.global_id());
2703 } else {
2704 let df_desc = self
2705 .catalog()
2706 .try_get_physical_plan(&idx.global_id())
2707 .expect("added in `bootstrap_dataflow_plans`")
2708 .clone();
2709
2710 let df_meta = self
2711 .catalog()
2712 .try_get_dataflow_metainfo(&idx.global_id())
2713 .expect("added in `bootstrap_dataflow_plans`");
2714
2715 if self.catalog().state().system_config().enable_mz_notices() {
2716 self.catalog().state().pack_optimizer_notices(
2718 &mut builtin_table_updates,
2719 df_meta.optimizer_notices.iter(),
2720 Diff::ONE,
2721 );
2722 }
2723
2724 policy_entry
2727 .compute_ids
2728 .entry(idx.cluster_id)
2729 .or_insert_with(Default::default)
2730 .extend(df_desc.export_ids());
2731
2732 self.controller
2733 .compute
2734 .create_dataflow(idx.cluster_id, df_desc, None)
2735 .unwrap_or_terminate("cannot fail to create dataflows");
2736 }
2737 }
2738 CatalogItem::View(_) => (),
2739 CatalogItem::MaterializedView(mview) => {
2740 policies_to_set
2746 .entry(policy.expect("materialized views have a compaction window"))
2747 .or_insert_with(Default::default)
2748 .storage_ids
2749 .extend(mview.global_ids());
2750
2751 let mut df_desc = self
2752 .catalog()
2753 .try_get_physical_plan(&mview.global_id_writes())
2754 .expect("added in `bootstrap_dataflow_plans`")
2755 .clone();
2756
2757 if let Some(initial_as_of) = mview.initial_as_of.clone() {
2758 df_desc.set_initial_as_of(initial_as_of);
2759 }
2760
2761 let until = mview
2763 .refresh_schedule
2764 .as_ref()
2765 .and_then(|s| s.last_refresh())
2766 .and_then(|r| r.try_step_forward());
2767 if let Some(until) = until {
2768 df_desc.until.meet_assign(&Antichain::from_elem(until));
2769 }
2770
2771 let df_meta = self
2772 .catalog()
2773 .try_get_dataflow_metainfo(&mview.global_id_writes())
2774 .expect("added in `bootstrap_dataflow_plans`");
2775
2776 if self.catalog().state().system_config().enable_mz_notices() {
2777 self.catalog().state().pack_optimizer_notices(
2779 &mut builtin_table_updates,
2780 df_meta.optimizer_notices.iter(),
2781 Diff::ONE,
2782 );
2783 }
2784
2785 self.ship_dataflow(df_desc, mview.cluster_id, mview.target_replica)
2786 .await;
2787
2788 if mview.replacement_target.is_none() {
2791 self.allow_writes(mview.cluster_id, mview.global_id_writes());
2792 }
2793 }
2794 CatalogItem::MetricSink(metric_sink) => {
2795 let df_desc = self
2796 .catalog()
2797 .try_get_physical_plan(&metric_sink.global_id)
2798 .expect("added in `bootstrap_dataflow_plans`")
2799 .clone();
2800
2801 let df_meta = self
2802 .catalog()
2803 .try_get_dataflow_metainfo(&metric_sink.global_id)
2804 .expect("added in `bootstrap_dataflow_plans`");
2805
2806 if self.catalog().state().system_config().enable_mz_notices() {
2807 self.catalog().state().pack_optimizer_notices(
2809 &mut builtin_table_updates,
2810 df_meta.optimizer_notices.iter(),
2811 Diff::ONE,
2812 );
2813 }
2814
2815 self.ship_dataflow(df_desc, metric_sink.cluster_id, None)
2818 .await;
2819 }
2820 CatalogItem::Sink(sink) => {
2821 policies_to_set
2822 .entry(CompactionWindow::Default)
2823 .or_insert_with(Default::default)
2824 .storage_ids
2825 .insert(sink.global_id());
2826 }
2827 CatalogItem::Connection(catalog_connection) => {
2828 if let ConnectionDetails::AwsPrivatelink(conn) = &catalog_connection.details {
2829 privatelink_connections.insert(
2830 entry.id(),
2831 VpcEndpointConfig {
2832 aws_service_name: conn.service_name.clone(),
2833 availability_zone_ids: conn.availability_zones.clone(),
2834 },
2835 );
2836 }
2837 }
2838 CatalogItem::Log(_)
2840 | CatalogItem::Type(_)
2841 | CatalogItem::Func(_)
2842 | CatalogItem::Secret(_) => {}
2843 }
2844 }
2845
2846 if let Some(cloud_resource_controller) = &self.cloud_resource_controller {
2847 let existing_vpc_endpoints = cloud_resource_controller
2849 .list_vpc_endpoints()
2850 .await
2851 .context("list vpc endpoints")?;
2852 let existing_vpc_endpoints = BTreeSet::from_iter(existing_vpc_endpoints.into_keys());
2853 let desired_vpc_endpoints = privatelink_connections.keys().cloned().collect();
2854 let vpc_endpoints_to_remove = existing_vpc_endpoints.difference(&desired_vpc_endpoints);
2855 for id in vpc_endpoints_to_remove {
2856 cloud_resource_controller
2857 .delete_vpc_endpoint(*id)
2858 .await
2859 .context("deleting extraneous vpc endpoint")?;
2860 }
2861
2862 for (id, spec) in privatelink_connections {
2864 cloud_resource_controller
2865 .ensure_vpc_endpoint(id, spec)
2866 .await
2867 .context("ensuring vpc endpoint")?;
2868 }
2869 }
2870
2871 drop(dataflow_read_holds);
2874 for (cw, policies) in policies_to_set {
2876 self.initialize_read_policies(&policies, cw).await;
2877 }
2878
2879 builtin_table_updates.extend(
2881 self.catalog().state().resolve_builtin_table_updates(
2882 self.catalog().state().pack_all_replica_size_updates(),
2883 ),
2884 );
2885
2886 debug!("startup: coordinator init: bootstrap: initializing migrated builtin tables");
2887 let migrated_updates_fut = if self.controller.read_only() {
2893 let min_timestamp = Timestamp::minimum();
2894 let migrated_builtin_table_updates: Vec<_> = builtin_table_updates
2895 .extract_if(.., |update| {
2896 let gid = self.catalog().get_entry(&update.id).latest_global_id();
2897 migrated_storage_collections_0dt.contains(&update.id)
2898 && self
2899 .controller
2900 .storage_collections
2901 .collection_frontiers(gid)
2902 .expect("all tables are registered")
2903 .write_frontier
2904 .elements()
2905 == &[min_timestamp]
2906 })
2907 .collect();
2908 if migrated_builtin_table_updates.is_empty() {
2909 futures::future::ready(()).boxed()
2910 } else {
2911 let mut grouped_appends: BTreeMap<GlobalId, Vec<TableData>> = BTreeMap::new();
2913 for update in migrated_builtin_table_updates {
2914 let gid = self.catalog().get_entry(&update.id).latest_global_id();
2915 grouped_appends.entry(gid).or_default().push(update.data);
2916 }
2917 info!(
2918 "coordinator init: rehydrating migrated builtin tables in read-only mode: {:?}",
2919 grouped_appends.keys().collect::<Vec<_>>()
2920 );
2921
2922 let mut all_appends = Vec::with_capacity(grouped_appends.len());
2924 for (item_id, table_data) in grouped_appends.into_iter() {
2925 let mut all_rows = Vec::new();
2926 let mut all_data = Vec::new();
2927 for data in table_data {
2928 match data {
2929 TableData::Rows(rows) => all_rows.extend(rows),
2930 TableData::Batches(_) => all_data.push(data),
2931 }
2932 }
2933 differential_dataflow::consolidation::consolidate(&mut all_rows);
2934 all_data.push(TableData::Rows(all_rows));
2935
2936 all_appends.push((item_id, all_data));
2938 }
2939
2940 let fut = self
2941 .controller
2942 .storage
2943 .append_table(min_timestamp, boot_ts.step_forward(), all_appends)
2944 .expect("cannot fail to append");
2945 async {
2946 fut.await
2947 .expect("One-shot shouldn't be dropped during bootstrap")
2948 .unwrap_or_terminate("cannot fail to append")
2949 }
2950 .boxed()
2951 }
2952 } else {
2953 futures::future::ready(()).boxed()
2954 };
2955
2956 info!(
2957 "startup: coordinator init: bootstrap: postamble complete in {:?}",
2958 postamble_start.elapsed()
2959 );
2960
2961 let builtin_update_start = Instant::now();
2962 info!("startup: coordinator init: bootstrap: generate builtin updates beginning");
2963
2964 if self.controller.read_only() {
2965 info!(
2966 "coordinator init: bootstrap: stashing builtin table updates while in read-only mode"
2967 );
2968
2969 self.buffered_builtin_table_updates
2970 .as_mut()
2971 .expect("in read-only mode")
2972 .append(&mut builtin_table_updates);
2973 } else {
2974 self.bootstrap_tables(&entries, builtin_table_updates).await;
2975 };
2976 info!(
2977 "startup: coordinator init: bootstrap: generate builtin updates complete in {:?}",
2978 builtin_update_start.elapsed()
2979 );
2980
2981 let cleanup_secrets_start = Instant::now();
2982 info!("startup: coordinator init: bootstrap: generate secret cleanup beginning");
2983 {
2987 let Self {
2990 secrets_controller,
2991 catalog,
2992 ..
2993 } = self;
2994
2995 let next_user_item_id = catalog.get_next_user_item_id().await?;
2996 let next_system_item_id = catalog.get_next_system_item_id().await?;
2997 let read_only = self.controller.read_only();
2998 let catalog_ids: BTreeSet<CatalogItemId> =
3003 catalog.entries().map(|entry| entry.id()).collect();
3004 let secrets_controller = Arc::clone(secrets_controller);
3005
3006 spawn(|| "cleanup-orphaned-secrets", async move {
3007 if read_only {
3008 info!(
3009 "coordinator init: not cleaning up orphaned secrets while in read-only mode"
3010 );
3011 return;
3012 }
3013 info!("coordinator init: cleaning up orphaned secrets");
3014
3015 match secrets_controller.list().await {
3016 Ok(controller_secrets) => {
3017 let controller_secrets: BTreeSet<CatalogItemId> =
3018 controller_secrets.into_iter().collect();
3019 let orphaned = controller_secrets.difference(&catalog_ids);
3020 for id in orphaned {
3021 let id_too_large = match id {
3022 CatalogItemId::System(id) => *id >= next_system_item_id,
3023 CatalogItemId::User(id) => *id >= next_user_item_id,
3024 CatalogItemId::IntrospectionSourceIndex(_)
3025 | CatalogItemId::Transient(_) => false,
3026 };
3027 if id_too_large {
3028 info!(
3029 %next_user_item_id, %next_system_item_id,
3030 "coordinator init: not deleting orphaned secret {id} that was likely created by a newer deploy generation"
3031 );
3032 } else {
3033 info!("coordinator init: deleting orphaned secret {id}");
3034 fail_point!("orphan_secrets");
3035 if let Err(e) = secrets_controller.delete(*id).await {
3036 warn!(
3037 "Dropping orphaned secret has encountered an error: {}",
3038 e
3039 );
3040 }
3041 }
3042 }
3043 }
3044 Err(e) => warn!("Failed to list secrets during orphan cleanup: {:?}", e),
3045 }
3046 });
3047 }
3048 info!(
3049 "startup: coordinator init: bootstrap: generate secret cleanup complete in {:?}",
3050 cleanup_secrets_start.elapsed()
3051 );
3052
3053 let final_steps_start = Instant::now();
3055 info!(
3056 "startup: coordinator init: bootstrap: migrate builtin tables in read-only mode beginning"
3057 );
3058 migrated_updates_fut
3059 .instrument(info_span!("coord::bootstrap::final"))
3060 .await;
3061
3062 debug!(
3063 "startup: coordinator init: bootstrap: announcing completion of initialization to controller"
3064 );
3065 self.controller.initialization_complete();
3067
3068 self.bootstrap_introspection_subscribes().await;
3070
3071 info!(
3072 "startup: coordinator init: bootstrap: migrate builtin tables in read-only mode complete in {:?}",
3073 final_steps_start.elapsed()
3074 );
3075
3076 info!(
3077 "startup: coordinator init: bootstrap complete in {:?}",
3078 bootstrap_start.elapsed()
3079 );
3080 Ok(())
3081 }
3082
3083 #[allow(clippy::async_yields_async)]
3088 #[instrument]
3089 async fn bootstrap_tables(
3090 &mut self,
3091 entries: &[CatalogEntry],
3092 mut builtin_table_updates: Vec<BuiltinTableUpdate>,
3093 ) {
3094 struct TableMetadata<'a> {
3096 id: CatalogItemId,
3097 name: &'a QualifiedItemName,
3098 table: &'a Table,
3099 }
3100
3101 let table_metas: Vec<_> = entries
3103 .into_iter()
3104 .filter_map(|entry| {
3105 entry.table().map(|table| TableMetadata {
3106 id: entry.id(),
3107 name: entry.name(),
3108 table,
3109 })
3110 })
3111 .collect();
3112
3113 debug!("coordinator init: advancing all tables to current timestamp");
3115 let WriteTimestamp {
3116 timestamp: write_ts,
3117 advance_to,
3118 } = self.get_local_write_ts().await;
3119 let appends = table_metas
3120 .iter()
3121 .map(|meta| (meta.table.global_id_writes(), Vec::new()))
3122 .collect();
3123 let table_fence_rx = self
3127 .controller
3128 .storage
3129 .append_table(write_ts.clone(), advance_to, appends)
3130 .expect("invalid updates");
3131
3132 self.apply_local_write(write_ts).await;
3133
3134 debug!("coordinator init: resetting system tables");
3136 let read_ts = self.get_local_read_ts().await;
3137
3138 let mz_storage_usage_by_shard_schema: SchemaSpecifier = self
3143 .catalog()
3144 .resolve_system_schema(MZ_STORAGE_USAGE_BY_SHARD.schema)
3145 .into();
3146 let arrangement_size_history_schema: SchemaSpecifier = self
3147 .catalog()
3148 .resolve_system_schema(MZ_OBJECT_ARRANGEMENT_SIZE_HISTORY.schema)
3149 .into();
3150 let is_retained_across_restarts = |meta: &TableMetadata| -> bool {
3151 (meta.name.item == MZ_STORAGE_USAGE_BY_SHARD.name
3152 && meta.name.qualifiers.schema_spec == mz_storage_usage_by_shard_schema)
3153 || (meta.name.item == MZ_OBJECT_ARRANGEMENT_SIZE_HISTORY.name
3154 && meta.name.qualifiers.schema_spec == arrangement_size_history_schema)
3155 };
3156
3157 let mut retraction_tasks = Vec::new();
3158 let system_tables: Vec<_> = table_metas
3159 .iter()
3160 .filter(|meta| meta.id.is_system() && !is_retained_across_restarts(meta))
3161 .collect();
3162
3163 for system_table in system_tables {
3164 let table_id = system_table.id;
3165 let full_name = self.catalog().resolve_full_name(system_table.name, None);
3166 debug!("coordinator init: resetting system table {full_name} ({table_id})");
3167
3168 let snapshot_fut = self
3170 .controller
3171 .storage_collections
3172 .snapshot_cursor(system_table.table.global_id_writes(), read_ts);
3173 let batch_fut = self
3174 .controller
3175 .storage_collections
3176 .create_update_builder(system_table.table.global_id_writes());
3177
3178 let task = spawn(|| format!("snapshot-{table_id}"), async move {
3179 let mut batch = batch_fut
3181 .await
3182 .unwrap_or_terminate("cannot fail to create a batch for a BuiltinTable");
3183 tracing::info!(?table_id, "starting snapshot");
3184 let mut snapshot_cursor = snapshot_fut
3186 .await
3187 .unwrap_or_terminate("cannot fail to snapshot");
3188
3189 while let Some(values) = snapshot_cursor.next().await {
3191 for (key, _t, d) in values {
3192 let d_invert = d.neg();
3193 batch.add(&key, &(), &d_invert).await;
3194 }
3195 }
3196 tracing::info!(?table_id, "finished snapshot");
3197
3198 let batch = batch.finish().await;
3199 BuiltinTableUpdate::batch(table_id, batch)
3200 });
3201 retraction_tasks.push(task);
3202 }
3203
3204 let retractions_res = futures::future::join_all(retraction_tasks).await;
3205 for retractions in retractions_res {
3206 builtin_table_updates.push(retractions);
3207 }
3208
3209 table_fence_rx
3211 .await
3212 .expect("One-shot shouldn't be dropped during bootstrap")
3213 .unwrap_or_terminate("cannot fail to append");
3214
3215 info!("coordinator init: sending builtin table updates");
3216 let builtin_updates_fut = self.builtin_table_update().execute(builtin_table_updates);
3217 builtin_updates_fut.await;
3220 }
3221
3222 #[instrument]
3235 async fn bootstrap_storage_collections(
3236 &mut self,
3237 migrated_storage_collections: &BTreeSet<CatalogItemId>,
3238 ) {
3239 let catalog = self.catalog();
3240
3241 let source_desc = |object_id: GlobalId,
3242 data_source: &DataSourceDesc,
3243 desc: &RelationDesc,
3244 timeline: &Timeline| {
3245 let data_source = match data_source.clone() {
3246 DataSourceDesc::Ingestion { desc, cluster_id } => {
3248 let desc = desc.into_inline_connection(catalog.state());
3249 let ingestion = IngestionDescription::new(desc, cluster_id, object_id);
3250 DataSource::Ingestion(ingestion)
3251 }
3252 DataSourceDesc::OldSyntaxIngestion {
3253 desc,
3254 progress_subsource,
3255 data_config,
3256 details,
3257 cluster_id,
3258 } => {
3259 let desc = desc.into_inline_connection(catalog.state());
3260 let data_config = data_config.into_inline_connection(catalog.state());
3261 let progress_subsource =
3264 catalog.get_entry(&progress_subsource).latest_global_id();
3265 let mut ingestion =
3266 IngestionDescription::new(desc, cluster_id, progress_subsource);
3267 let legacy_export = SourceExport {
3268 storage_metadata: (),
3269 data_config,
3270 details,
3271 };
3272 ingestion.source_exports.insert(object_id, legacy_export);
3273
3274 DataSource::Ingestion(ingestion)
3275 }
3276 DataSourceDesc::IngestionExport {
3277 ingestion_id,
3278 external_reference: _,
3279 details,
3280 data_config,
3281 } => {
3282 let ingestion_id = catalog.get_entry(&ingestion_id).latest_global_id();
3285
3286 DataSource::IngestionExport {
3287 ingestion_id,
3288 details,
3289 data_config: data_config.into_inline_connection(catalog.state()),
3290 }
3291 }
3292 DataSourceDesc::Webhook { .. } => DataSource::Webhook,
3293 DataSourceDesc::Progress => DataSource::Progress,
3294 DataSourceDesc::Introspection(introspection) => {
3295 DataSource::Introspection(introspection)
3296 }
3297 DataSourceDesc::Catalog => DataSource::Other,
3298 };
3299 CollectionDescription {
3300 desc: desc.clone(),
3301 data_source,
3302 since: None,
3303 timeline: Some(timeline.clone()),
3304 primary: None,
3305 }
3306 };
3307
3308 let mut compute_collections = vec![];
3309 let mut collections = vec![];
3310 for entry in catalog.entries() {
3311 match entry.item() {
3312 CatalogItem::Source(source) => {
3313 collections.push((
3314 source.global_id(),
3315 source_desc(
3316 source.global_id(),
3317 &source.data_source,
3318 &source.desc,
3319 &source.timeline,
3320 ),
3321 ));
3322 }
3323 CatalogItem::Table(table) => {
3324 match &table.data_source {
3325 TableDataSource::TableWrites { defaults: _ } => {
3326 let versions: BTreeMap<_, _> = table
3327 .collection_descs()
3328 .map(|(gid, version, desc)| (version, (gid, desc)))
3329 .collect();
3330 let collection_descs = versions.iter().map(|(version, (gid, desc))| {
3331 let next_version = version.bump();
3332 let primary_collection =
3333 versions.get(&next_version).map(|(gid, _desc)| gid).copied();
3334 let mut collection_desc =
3335 CollectionDescription::for_table(desc.clone());
3336 collection_desc.primary = primary_collection;
3337
3338 (*gid, collection_desc)
3339 });
3340 collections.extend(collection_descs);
3341 }
3342 TableDataSource::DataSource {
3343 desc: data_source_desc,
3344 timeline,
3345 } => {
3346 soft_assert_eq_or_log!(table.collections.len(), 1);
3348 let collection_descs =
3349 table.collection_descs().map(|(gid, _version, desc)| {
3350 (
3351 gid,
3352 source_desc(
3353 entry.latest_global_id(),
3354 data_source_desc,
3355 &desc,
3356 timeline,
3357 ),
3358 )
3359 });
3360 collections.extend(collection_descs);
3361 }
3362 };
3363 }
3364 CatalogItem::MaterializedView(mv) => {
3365 let mut primary = mv
3373 .replacement_target
3374 .map(|target_id| catalog.get_entry(&target_id).latest_global_id());
3375 let collection_descs = mv.collection_descs().map(|(gid, _version, desc)| {
3376 let mut collection_desc =
3377 CollectionDescription::for_other(desc, mv.initial_as_of.clone());
3378 collection_desc.primary = primary;
3379 primary = Some(gid);
3380 (gid, collection_desc)
3381 });
3382
3383 collections.extend(collection_descs);
3384 compute_collections.push((mv.global_id_writes(), mv.desc.latest()));
3385 }
3386 CatalogItem::Sink(sink) => {
3387 let storage_sink_from_entry = self.catalog().get_entry_by_global_id(&sink.from);
3388 let from_desc = storage_sink_from_entry
3389 .relation_desc()
3390 .expect("sinks can only be built on items with descs")
3391 .into_owned();
3392 let collection_desc = CollectionDescription {
3393 desc: KAFKA_PROGRESS_DESC.clone(),
3395 data_source: DataSource::Sink {
3396 desc: ExportDescription {
3397 sink: StorageSinkDesc {
3398 from: sink.from,
3399 from_desc,
3400 connection: sink
3401 .connection
3402 .clone()
3403 .into_inline_connection(self.catalog().state()),
3404 envelope: sink.envelope,
3405 as_of: Antichain::from_elem(Timestamp::minimum()),
3406 with_snapshot: sink.with_snapshot,
3407 version: sink.version,
3408 from_storage_metadata: (),
3409 to_storage_metadata: (),
3410 commit_interval: sink.commit_interval,
3411 },
3412 instance_id: sink.cluster_id,
3413 },
3414 },
3415 since: None,
3416 timeline: None,
3417 primary: None,
3418 };
3419 collections.push((sink.global_id, collection_desc));
3420 }
3421 CatalogItem::Log(_)
3422 | CatalogItem::View(_)
3423 | CatalogItem::Index(_)
3424 | CatalogItem::Type(_)
3425 | CatalogItem::Func(_)
3426 | CatalogItem::Secret(_)
3427 | CatalogItem::Connection(_)
3428 | CatalogItem::MetricSink(_) => (),
3431 }
3432 }
3433
3434 let register_ts = if self.controller.read_only() {
3435 self.get_local_read_ts().await
3436 } else {
3437 self.get_local_write_ts().await.timestamp
3440 };
3441
3442 let storage_metadata = self.catalog.state().storage_metadata();
3443 let migrated_storage_collections = migrated_storage_collections
3444 .into_iter()
3445 .flat_map(|item_id| self.catalog.get_entry(item_id).global_ids())
3446 .collect();
3447
3448 self.controller
3453 .storage
3454 .evolve_nullability_for_bootstrap(storage_metadata, compute_collections)
3455 .await
3456 .unwrap_or_terminate("cannot fail to evolve collections");
3457
3458 let mut pending: BTreeMap<_, _> = collections.into_iter().collect();
3471
3472 let transitive_dep_gids: BTreeMap<_, _> = pending
3474 .keys()
3475 .map(|gid| {
3476 let entry = self.catalog.get_entry_by_global_id(gid);
3477 let item_id = entry.id();
3478 let deps = self.catalog.state().transitive_uses(item_id);
3479 let dep_gids: BTreeSet<_> = deps
3480 .filter(|dep_id| *dep_id != item_id)
3483 .map(|dep_id| self.catalog.get_entry(&dep_id).latest_global_id())
3484 .filter(|dep_gid| pending.contains_key(dep_gid))
3486 .collect();
3487 (*gid, dep_gids)
3488 })
3489 .collect();
3490
3491 let mut created_gids = Vec::new();
3492
3493 while !pending.is_empty() {
3494 let ready_gids: BTreeSet<_> = pending
3497 .keys()
3498 .filter(|gid| {
3499 let mut deps = transitive_dep_gids[gid].iter();
3500 !deps.any(|dep_gid| pending.contains_key(dep_gid))
3501 })
3502 .copied()
3503 .collect();
3504 let mut ready: Vec<_> = pending
3505 .extract_if(.., |gid, _| ready_gids.contains(gid))
3506 .collect();
3507
3508 for (gid, collection) in &mut ready {
3510 if !gid.is_system() || collection.since.is_some() {
3512 continue;
3513 }
3514
3515 let mut derived_since = Antichain::from_elem(Timestamp::MIN);
3516 for dep_gid in &transitive_dep_gids[gid] {
3517 let (since, _) = self
3518 .controller
3519 .storage
3520 .collection_frontiers(*dep_gid)
3521 .expect("previously registered");
3522 derived_since.join_assign(&since);
3523 }
3524 collection.since = Some(derived_since);
3525 }
3526
3527 if ready.is_empty() {
3528 soft_panic_or_log!(
3529 "cycle in storage collections: {:?}",
3530 pending.keys().collect::<Vec<_>>(),
3531 );
3532 ready = mem::take(&mut pending).into_iter().collect();
3536 }
3537
3538 created_gids.extend(ready.iter().map(|(gid, _collection)| *gid));
3539
3540 self.controller
3541 .storage
3542 .create_collections_for_bootstrap(
3543 storage_metadata,
3544 Some(register_ts),
3545 ready,
3546 &migrated_storage_collections,
3547 )
3548 .await
3549 .unwrap_or_terminate("cannot fail to create collections");
3550 }
3551
3552 self.controller
3554 .storage
3555 .register_table_collections(register_ts, created_gids)
3556 .await
3557 .unwrap_or_terminate("cannot fail to register tables");
3558
3559 if !self.controller.read_only() {
3560 self.apply_local_write(register_ts).await;
3561 }
3562 }
3563
3564 fn bootstrap_sort_catalog_entries(&self) -> Vec<CatalogEntry> {
3571 let mut indexes_on = BTreeMap::<_, Vec<_>>::new();
3572 let mut non_indexes = Vec::new();
3573 for entry in self.catalog().entries().cloned() {
3574 if let Some(index) = entry.index() {
3575 let on = self.catalog().get_entry_by_global_id(&index.on);
3576 indexes_on.entry(on.id()).or_default().push(entry);
3577 } else {
3578 non_indexes.push(entry);
3579 }
3580 }
3581
3582 let key_fn = |entry: &CatalogEntry| entry.id;
3583 let dependencies_fn = |entry: &CatalogEntry| entry.uses();
3584 sort_topological(&mut non_indexes, key_fn, dependencies_fn);
3585
3586 let mut result = Vec::new();
3587 for entry in non_indexes {
3588 let id = entry.id();
3589 result.push(entry);
3590 if let Some(mut indexes) = indexes_on.remove(&id) {
3591 result.append(&mut indexes);
3592 }
3593 }
3594
3595 soft_assert_or_log!(
3596 indexes_on.is_empty(),
3597 "indexes with missing dependencies: {indexes_on:?}",
3598 );
3599
3600 result
3601 }
3602
3603 #[instrument]
3614 fn bootstrap_dataflow_plans(
3615 &mut self,
3616 ordered_catalog_entries: &[CatalogEntry],
3617 mut cached_global_exprs: BTreeMap<GlobalId, GlobalExpressions>,
3618 ) -> Result<BTreeMap<GlobalId, GlobalExpressions>, AdapterError> {
3619 let mut instance_snapshots = BTreeMap::new();
3625 let mut uncached_expressions = BTreeMap::new();
3626
3627 let optimizer_config = |catalog: &Catalog, cluster_id| {
3628 let system_config = catalog.system_config();
3629 let overrides = catalog.get_cluster(cluster_id).config.features();
3630 OptimizerConfig::from(system_config)
3631 .override_from(&overrides)
3632 .override_from(
3635 &catalog
3636 .state()
3637 .cluster_scoped_optimizer_overrides(cluster_id),
3638 )
3639 };
3640
3641 for entry in ordered_catalog_entries {
3642 match entry.item() {
3643 CatalogItem::Index(idx) => {
3644 let compute_instance =
3646 instance_snapshots.entry(idx.cluster_id).or_insert_with(|| {
3647 self.instance_snapshot(idx.cluster_id)
3648 .expect("compute instance exists")
3649 });
3650 let global_id = idx.global_id();
3651
3652 if compute_instance.contains_collection(&global_id) {
3655 continue;
3656 }
3657
3658 let optimizer_config = optimizer_config(&self.catalog, idx.cluster_id);
3659
3660 let (optimized_plan, physical_plan, metainfo) =
3661 match cached_global_exprs.remove(&global_id) {
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 (optimized_plan, global_lir_plan) = {
3675 let mut optimizer = optimize::index::Optimizer::new(
3677 self.owned_catalog(),
3678 compute_instance.clone(),
3679 global_id,
3680 optimizer_config.clone(),
3681 self.optimizer_metrics(),
3682 );
3683
3684 let index_plan = optimize::index::Index::new(
3686 entry.name().clone(),
3687 idx.on,
3688 idx.keys.to_vec(),
3689 );
3690 let global_mir_plan = optimizer.optimize(index_plan)?;
3691 let optimized_plan = global_mir_plan.df_desc().clone();
3692
3693 let global_lir_plan = optimizer.optimize(global_mir_plan)?;
3695
3696 (optimized_plan, global_lir_plan)
3697 };
3698
3699 let (physical_plan, metainfo) = global_lir_plan.unapply();
3700 let metainfo = {
3701 let notice_ids =
3703 std::iter::repeat_with(|| self.allocate_transient_id())
3704 .map(|(_item_id, gid)| gid)
3705 .take(metainfo.optimizer_notices.len())
3706 .collect::<Vec<_>>();
3707 self.catalog().render_notices(
3709 metainfo,
3710 notice_ids,
3711 Some(idx.global_id()),
3712 )
3713 };
3714 uncached_expressions.insert(
3715 global_id,
3716 GlobalExpressions {
3717 global_mir: optimized_plan.clone(),
3718 physical_plan: physical_plan.clone(),
3719 dataflow_metainfos: metainfo.clone(),
3720 optimizer_features: optimizer_config.features.clone(),
3721 },
3722 );
3723 (optimized_plan, physical_plan, metainfo)
3724 }
3725 };
3726
3727 let catalog = self.catalog_mut();
3728 catalog.set_optimized_plan(idx.global_id(), optimized_plan);
3729 catalog.set_physical_plan(idx.global_id(), physical_plan);
3730 catalog.set_dataflow_metainfo(idx.global_id(), metainfo);
3731
3732 compute_instance.insert_collection(idx.global_id());
3733 }
3734 CatalogItem::MaterializedView(mv) => {
3735 let compute_instance =
3737 instance_snapshots.entry(mv.cluster_id).or_insert_with(|| {
3738 self.instance_snapshot(mv.cluster_id)
3739 .expect("compute instance exists")
3740 });
3741 let global_id = mv.global_id_writes();
3742
3743 let optimizer_config = optimizer_config(&self.catalog, mv.cluster_id);
3744
3745 let (optimized_plan, physical_plan, metainfo) = match cached_global_exprs
3746 .remove(&global_id)
3747 {
3748 Some(global_expressions)
3749 if global_expressions.optimizer_features
3750 == optimizer_config.features =>
3751 {
3752 debug!("global expression cache hit for {global_id:?}");
3753 (
3754 global_expressions.global_mir,
3755 global_expressions.physical_plan,
3756 global_expressions.dataflow_metainfos,
3757 )
3758 }
3759 Some(_) | None => {
3760 let (_, internal_view_id) = self.allocate_transient_id();
3761 let debug_name = self
3762 .catalog()
3763 .resolve_full_name(entry.name(), None)
3764 .to_string();
3765
3766 let (optimized_plan, global_lir_plan) = {
3767 let mut optimizer = optimize::materialized_view::Optimizer::new(
3769 self.owned_catalog().as_optimizer_catalog(),
3770 compute_instance.clone(),
3771 global_id,
3772 internal_view_id,
3773 mv.desc.latest().iter_names().cloned().collect(),
3774 mv.non_null_assertions.clone(),
3775 mv.refresh_schedule.clone(),
3776 debug_name,
3777 optimizer_config.clone(),
3778 self.optimizer_metrics(),
3779 );
3780
3781 let typ = infer_sql_type_for_catalog(
3784 &mv.raw_expr,
3785 &mv.locally_optimized_expr.as_ref().clone(),
3786 );
3787 let global_mir_plan = optimizer
3788 .optimize((mv.locally_optimized_expr.as_ref().clone(), typ))?;
3789 let optimized_plan = global_mir_plan.df_desc().clone();
3790
3791 let global_lir_plan = optimizer.optimize(global_mir_plan)?;
3793
3794 (optimized_plan, global_lir_plan)
3795 };
3796
3797 let (physical_plan, metainfo) = global_lir_plan.unapply();
3798 let metainfo = {
3799 let notice_ids =
3801 std::iter::repeat_with(|| self.allocate_transient_id())
3802 .map(|(_item_id, global_id)| global_id)
3803 .take(metainfo.optimizer_notices.len())
3804 .collect::<Vec<_>>();
3805 self.catalog().render_notices(
3807 metainfo,
3808 notice_ids,
3809 Some(mv.global_id_writes()),
3810 )
3811 };
3812 uncached_expressions.insert(
3813 global_id,
3814 GlobalExpressions {
3815 global_mir: optimized_plan.clone(),
3816 physical_plan: physical_plan.clone(),
3817 dataflow_metainfos: metainfo.clone(),
3818 optimizer_features: optimizer_config.features.clone(),
3819 },
3820 );
3821 (optimized_plan, physical_plan, metainfo)
3822 }
3823 };
3824
3825 let catalog = self.catalog_mut();
3826 catalog.set_optimized_plan(mv.global_id_writes(), optimized_plan);
3827 catalog.set_physical_plan(mv.global_id_writes(), physical_plan);
3828 catalog.set_dataflow_metainfo(mv.global_id_writes(), metainfo);
3829
3830 compute_instance.insert_collection(mv.global_id_writes());
3831 }
3832 CatalogItem::MetricSink(metric_sink) => {
3833 let compute_instance = instance_snapshots
3835 .entry(metric_sink.cluster_id)
3836 .or_insert_with(|| {
3837 self.instance_snapshot(metric_sink.cluster_id)
3838 .expect("compute instance exists")
3839 });
3840 let global_id = metric_sink.global_id;
3841 let optimizer_config = optimizer_config(&self.catalog, metric_sink.cluster_id);
3842
3843 let (optimized_plan, physical_plan, metainfo) = match cached_global_exprs
3844 .remove(&global_id)
3845 {
3846 Some(global_expressions)
3847 if global_expressions.optimizer_features
3848 == optimizer_config.features =>
3849 {
3850 debug!("global expression cache hit for {global_id:?}");
3851 (
3852 global_expressions.global_mir,
3853 global_expressions.physical_plan,
3854 global_expressions.dataflow_metainfos,
3855 )
3856 }
3857 Some(_) | None => {
3858 let (_, view_id) = self.allocate_transient_id();
3865
3866 let (optimized_plan, global_lir_plan) = {
3867 let mut optimizer = optimize::metric_sink::Optimizer::new(
3868 self.owned_catalog(),
3869 compute_instance.clone(),
3870 view_id,
3871 global_id,
3872 optimizer_config.clone(),
3873 self.optimizer_metrics(),
3874 );
3875
3876 let metric_sink_plan = optimize::metric_sink::MetricSink::new(
3878 entry.name().clone(),
3879 metric_sink.from,
3880 metric_sink.prefix.clone(),
3881 );
3882 let global_mir_plan = optimizer.optimize(metric_sink_plan)?;
3883 let optimized_plan = global_mir_plan.df_desc().clone();
3884
3885 let global_lir_plan = optimizer.optimize(global_mir_plan)?;
3887
3888 (optimized_plan, global_lir_plan)
3889 };
3890
3891 let (physical_plan, metainfo) = global_lir_plan.unapply();
3892 let metainfo = {
3893 let notice_ids =
3895 std::iter::repeat_with(|| self.allocate_transient_id())
3896 .map(|(_item_id, gid)| gid)
3897 .take(metainfo.optimizer_notices.len())
3898 .collect::<Vec<_>>();
3899 self.catalog()
3901 .render_notices(metainfo, notice_ids, Some(global_id))
3902 };
3903 uncached_expressions.insert(
3904 global_id,
3905 GlobalExpressions {
3906 global_mir: optimized_plan.clone(),
3907 physical_plan: physical_plan.clone(),
3908 dataflow_metainfos: metainfo.clone(),
3909 optimizer_features: optimizer_config.features.clone(),
3910 },
3911 );
3912 (optimized_plan, physical_plan, metainfo)
3913 }
3914 };
3915
3916 let catalog = self.catalog_mut();
3917 catalog.set_optimized_plan(global_id, optimized_plan);
3918 catalog.set_physical_plan(global_id, physical_plan);
3919 catalog.set_dataflow_metainfo(global_id, metainfo);
3920
3921 }
3925 CatalogItem::Table(_)
3926 | CatalogItem::Source(_)
3927 | CatalogItem::Log(_)
3928 | CatalogItem::View(_)
3929 | CatalogItem::Sink(_)
3930 | CatalogItem::Type(_)
3931 | CatalogItem::Func(_)
3932 | CatalogItem::Secret(_)
3933 | CatalogItem::Connection(_) => (),
3934 }
3935 }
3936
3937 Ok(uncached_expressions)
3938 }
3939
3940 async fn bootstrap_dataflow_as_ofs(&mut self) -> BTreeMap<GlobalId, ReadHold> {
3950 let mut catalog_ids = Vec::new();
3951 let mut dataflows = Vec::new();
3952 let mut read_policies = BTreeMap::new();
3953 for entry in self.catalog.entries() {
3954 let gid = match entry.item() {
3955 CatalogItem::Index(idx) => idx.global_id(),
3956 CatalogItem::MaterializedView(mv) => mv.global_id_writes(),
3957 CatalogItem::MetricSink(metric_sink) => metric_sink.global_id,
3958 CatalogItem::Table(_)
3959 | CatalogItem::Source(_)
3960 | CatalogItem::Log(_)
3961 | CatalogItem::View(_)
3962 | CatalogItem::Sink(_)
3963 | CatalogItem::Type(_)
3964 | CatalogItem::Func(_)
3965 | CatalogItem::Secret(_)
3966 | CatalogItem::Connection(_) => continue,
3967 };
3968 if let Some(plan) = self.catalog.try_get_physical_plan(&gid) {
3969 catalog_ids.push(gid);
3970 dataflows.push(plan.clone());
3971
3972 if let Some(compaction_window) = entry.item().initial_logical_compaction_window() {
3973 read_policies.insert(gid, compaction_window.into());
3974 }
3975 }
3976 }
3977
3978 let read_ts = self.get_local_read_ts().await;
3979 let read_holds = as_of_selection::run(
3980 &mut dataflows,
3981 &read_policies,
3982 &*self.controller.storage_collections,
3983 read_ts,
3984 self.controller.read_only(),
3985 );
3986
3987 let catalog = self.catalog_mut();
3988 for (id, plan) in catalog_ids.into_iter().zip_eq(dataflows) {
3989 catalog.set_physical_plan(id, plan);
3990 }
3991
3992 read_holds
3993 }
3994
3995 fn serve(
4004 mut self,
4005 mut internal_cmd_rx: mpsc::UnboundedReceiver<Message>,
4006 mut strict_serializable_reads_rx: mpsc::UnboundedReceiver<(ConnectionId, PendingReadTxn)>,
4007 mut cmd_rx: mpsc::UnboundedReceiver<(OpenTelemetryContext, Command)>,
4008 group_commit_rx: appends::GroupCommitWaiter,
4009 ) -> LocalBoxFuture<'static, ()> {
4010 async move {
4011 let mut cluster_events = self.controller.events_stream();
4013 let last_message = Arc::new(Mutex::new(LastMessage {
4014 kind: "none",
4015 stmt: None,
4016 }));
4017
4018 let (idle_tx, mut idle_rx) = tokio::sync::mpsc::channel(1);
4019 let idle_metric = self.metrics.queue_busy_seconds.clone();
4020 let last_message_watchdog = Arc::clone(&last_message);
4021
4022 spawn(|| "coord watchdog", async move {
4023 let mut interval = tokio::time::interval(Duration::from_secs(5));
4028 interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Delay);
4032
4033 let mut coord_stuck = false;
4035
4036 loop {
4037 interval.tick().await;
4038
4039 let duration = tokio::time::Duration::from_secs(30);
4041 let timeout = tokio::time::timeout(duration, idle_tx.reserve()).await;
4042 let Ok(maybe_permit) = timeout else {
4043 if !coord_stuck {
4045 let last_message = last_message_watchdog.lock().expect("poisoned");
4046 tracing::warn!(
4047 last_message_kind = %last_message.kind,
4048 last_message_sql = %last_message.stmt_to_string(),
4049 "coordinator stuck for {duration:?}",
4050 );
4051 }
4052 coord_stuck = true;
4053
4054 continue;
4055 };
4056
4057 if coord_stuck {
4059 tracing::info!("Coordinator became unstuck");
4060 }
4061 coord_stuck = false;
4062
4063 let Ok(permit) = maybe_permit else {
4065 break;
4066 };
4067
4068 permit.send(idle_metric.start_timer());
4069 }
4070 });
4071
4072 self.schedule_storage_usage_collection().await;
4073 self.schedule_arrangement_sizes_collection().await;
4074 self.spawn_privatelink_vpc_endpoints_watch_task();
4075 self.spawn_statement_logging_task();
4076 self.spawn_catalog_info_metrics_task();
4077 self.spawn_cluster_controller_task();
4078 flags::tracing_config(self.catalog.system_config()).apply(&self.tracing_handle);
4079
4080 let warn_threshold = self
4082 .catalog()
4083 .system_config()
4084 .coord_slow_message_warn_threshold();
4085
4086 const MESSAGE_BATCH: usize = 64;
4088 let mut messages = Vec::with_capacity(MESSAGE_BATCH);
4089 let mut cmd_messages = Vec::with_capacity(MESSAGE_BATCH);
4090
4091 let message_batch = self.metrics.message_batch.clone();
4092
4093 let linearize_reads_notify = Arc::clone(&self.linearize_reads_notify);
4100 let linearize_reads_notified = linearize_reads_notify.notified();
4101 tokio::pin!(linearize_reads_notified);
4102
4103 loop {
4104 select! {
4108 biased;
4113
4114 _ = internal_cmd_rx.recv_many(&mut messages, MESSAGE_BATCH) => {},
4118 Some(event) = cluster_events.next() => {
4122 messages.push(Message::ClusterEvent(event))
4123 },
4124 () = self.controller.ready() => {
4128 let controller = match self.controller.get_readiness() {
4132 Readiness::Storage => ControllerReadiness::Storage,
4133 Readiness::Compute => ControllerReadiness::Compute,
4134 Readiness::Metrics(_) => ControllerReadiness::Metrics,
4135 Readiness::Internal(_) => ControllerReadiness::Internal,
4136 Readiness::NotReady => unreachable!("just signaled as ready"),
4137 };
4138 messages.push(Message::ControllerReady { controller });
4139 }
4140 permit = group_commit_rx.ready() => {
4143 let user_write_spans = self.pending_writes.iter().flat_map(|x| match x {
4149 PendingWriteTxn::User { span, .. } => Some(span),
4150 PendingWriteTxn::System { .. } => None,
4151 });
4152 let span = match user_write_spans.exactly_one() {
4153 Ok(span) => span.clone(),
4154 Err(user_write_spans) => {
4155 let span = info_span!(parent: None, "group_commit_notify");
4156 for s in user_write_spans {
4157 span.follows_from(s);
4158 }
4159 span
4160 }
4161 };
4162 messages.push(Message::GroupCommitInitiate(span, Some(permit)));
4163 },
4164 count = cmd_rx.recv_many(&mut cmd_messages, MESSAGE_BATCH) => {
4168 if count == 0 {
4169 break;
4170 } else {
4171 messages.extend(cmd_messages.drain(..).map(
4172 |(otel_ctx, cmd)| Message::Command(otel_ctx, cmd),
4173 ));
4174 }
4175 },
4176 Some(pending_read_txn) = strict_serializable_reads_rx.recv() => {
4180 let mut pending_read_txns = vec![pending_read_txn];
4181 while let Ok(pending_read_txn) = strict_serializable_reads_rx.try_recv() {
4182 pending_read_txns.push(pending_read_txn);
4183 }
4184 for (conn_id, pending_read_txn) in pending_read_txns {
4185 let prev = self
4186 .pending_linearize_read_txns
4187 .insert(conn_id, pending_read_txn);
4188 soft_assert_or_log!(
4189 prev.is_none(),
4190 "connections can not have multiple concurrent reads, prev: {prev:?}"
4191 )
4192 }
4193 messages.push(Message::LinearizeReads);
4194 }
4195 _ = self.advance_timelines_interval.tick() => {
4199 if self.controller.read_only() {
4203 messages.push(Message::AdvanceTimelines);
4204 } else {
4205 self.group_commit_tx.notify();
4206 }
4207 },
4208 () = linearize_reads_notified.as_mut() => {
4219 linearize_reads_notified.set(linearize_reads_notify.notified());
4220 messages.push(Message::LinearizeReads);
4221 }
4222 _ = self.caught_up_check_interval.tick() => {
4226 self.maybe_check_caught_up().await;
4231
4232 continue;
4233 },
4234
4235 timer = idle_rx.recv() => {
4240 timer.expect("does not drop").observe_duration();
4241 self.metrics
4242 .message_handling
4243 .with_label_values(&["watchdog"])
4244 .observe(0.0);
4245 continue;
4246 }
4247 };
4248
4249 message_batch.observe(f64::cast_lossy(messages.len()));
4251
4252 for msg in messages.drain(..) {
4253 let msg_kind = msg.kind();
4256 let span = span!(
4257 target: "mz_adapter::coord::handle_message_loop",
4258 Level::INFO,
4259 "coord::handle_message",
4260 kind = msg_kind
4261 );
4262 let otel_context = span.context().span().span_context().clone();
4263
4264 *last_message.lock().expect("poisoned") = LastMessage {
4268 kind: msg_kind,
4269 stmt: match &msg {
4270 Message::Command(
4271 _,
4272 Command::Execute {
4273 portal_name,
4274 session,
4275 ..
4276 },
4277 ) => session
4278 .get_portal_unverified(portal_name)
4279 .and_then(|p| p.stmt.as_ref().map(Arc::clone)),
4280 _ => None,
4281 },
4282 };
4283
4284 let start = Instant::now();
4285 self.handle_message(msg).instrument(span).await;
4286 let duration = start.elapsed();
4287
4288 self.metrics
4289 .message_handling
4290 .with_label_values(&[msg_kind])
4291 .observe(duration.as_secs_f64());
4292
4293 if duration > warn_threshold {
4295 let trace_id = otel_context.is_valid().then(|| otel_context.trace_id());
4296 tracing::error!(
4297 ?msg_kind,
4298 ?trace_id,
4299 ?duration,
4300 "very slow coordinator message"
4301 );
4302 }
4303 }
4304 }
4305 if let Some(catalog) = Arc::into_inner(self.catalog) {
4308 catalog.expire().await;
4309 }
4310 }
4311 .boxed_local()
4312 }
4313
4314 fn catalog(&self) -> &Catalog {
4316 &self.catalog
4317 }
4318
4319 fn owned_catalog(&self) -> Arc<Catalog> {
4322 Arc::clone(&self.catalog)
4323 }
4324
4325 fn optimizer_metrics(&self) -> OptimizerMetrics {
4328 self.optimizer_metrics.clone()
4329 }
4330
4331 fn catalog_mut(&mut self) -> &mut Catalog {
4333 Arc::make_mut(&mut self.catalog)
4341 }
4342
4343 async fn refill_user_id_pool(&mut self, min_count: u64) -> Result<(), AdapterError> {
4348 let batch_size = USER_ID_POOL_BATCH_SIZE.get(self.catalog().system_config().dyncfgs());
4349 let to_allocate = min_count.max(u64::from(batch_size));
4350 let id_ts = self.get_catalog_write_ts().await;
4351 let ids = self.catalog().allocate_user_ids(to_allocate, id_ts).await?;
4352 if let (Some((first_id, _)), Some((last_id, _))) = (ids.first(), ids.last()) {
4353 let start = match first_id {
4354 CatalogItemId::User(id) => *id,
4355 other => {
4356 return Err(AdapterError::Internal(format!(
4357 "expected User CatalogItemId, got {other:?}"
4358 )));
4359 }
4360 };
4361 let end = match last_id {
4362 CatalogItemId::User(id) => *id + 1, other => {
4364 return Err(AdapterError::Internal(format!(
4365 "expected User CatalogItemId, got {other:?}"
4366 )));
4367 }
4368 };
4369 self.user_id_pool.refill(start, end);
4370 } else {
4371 return Err(AdapterError::Internal(
4372 "catalog returned no user IDs".into(),
4373 ));
4374 }
4375 Ok(())
4376 }
4377
4378 async fn allocate_user_id(&mut self) -> Result<(CatalogItemId, GlobalId), AdapterError> {
4380 if let Some(id) = self.user_id_pool.allocate() {
4381 return Ok((CatalogItemId::User(id), GlobalId::User(id)));
4382 }
4383 self.refill_user_id_pool(1).await?;
4384 let id = self.user_id_pool.allocate().expect("ID pool just refilled");
4385 Ok((CatalogItemId::User(id), GlobalId::User(id)))
4386 }
4387
4388 async fn allocate_user_ids(
4390 &mut self,
4391 count: u64,
4392 ) -> Result<Vec<(CatalogItemId, GlobalId)>, AdapterError> {
4393 if self.user_id_pool.remaining() < count {
4394 self.refill_user_id_pool(count).await?;
4395 }
4396 let raw_ids = self
4397 .user_id_pool
4398 .allocate_many(count)
4399 .expect("pool has enough IDs after refill");
4400 Ok(raw_ids
4401 .into_iter()
4402 .map(|id| (CatalogItemId::User(id), GlobalId::User(id)))
4403 .collect())
4404 }
4405
4406 fn connection_context(&self) -> &ConnectionContext {
4408 self.controller.connection_context()
4409 }
4410
4411 fn secrets_reader(&self) -> &Arc<dyn SecretsReader> {
4413 &self.connection_context().secrets_reader
4414 }
4415
4416 #[allow(dead_code)]
4421 pub(crate) fn broadcast_notice(&self, notice: AdapterNotice) {
4422 for meta in self.active_conns.values() {
4423 let _ = meta.notice_tx.send(notice.clone());
4424 }
4425 }
4426
4427 pub(crate) fn broadcast_notice_tx(
4430 &self,
4431 ) -> Box<dyn FnOnce(AdapterNotice) -> () + Send + 'static> {
4432 let senders: Vec<_> = self
4433 .active_conns
4434 .values()
4435 .map(|meta| meta.notice_tx.clone())
4436 .collect();
4437 Box::new(move |notice| {
4438 for tx in senders {
4439 let _ = tx.send(notice.clone());
4440 }
4441 })
4442 }
4443
4444 pub(crate) fn active_conns(&self) -> &BTreeMap<ConnectionId, ConnMeta> {
4445 &self.active_conns
4446 }
4447
4448 #[instrument(level = "debug")]
4449 pub(crate) fn retire_execution(
4450 &mut self,
4451 reason: StatementEndedExecutionReason,
4452 ctx_extra: ExecuteContextExtra,
4453 ) {
4454 if let Some(uuid) = ctx_extra.retire() {
4455 let ended_at = self.now();
4456 self.end_statement_execution(uuid, reason, ended_at);
4457 }
4458 }
4459
4460 #[instrument(level = "debug")]
4462 pub fn dataflow_builder(&self, instance: ComputeInstanceId) -> DataflowBuilder<'_> {
4463 let compute = self
4464 .instance_snapshot(instance)
4465 .expect("compute instance does not exist");
4466 DataflowBuilder::new(self.catalog().state(), compute)
4467 }
4468
4469 pub fn instance_snapshot(
4471 &self,
4472 id: ComputeInstanceId,
4473 ) -> Result<ComputeInstanceSnapshot, InstanceMissing> {
4474 ComputeInstanceSnapshot::new(&self.controller, id)
4475 }
4476
4477 pub(crate) async fn ship_dataflow(
4484 &mut self,
4485 dataflow: DataflowDescription<LirRelationExpr>,
4486 instance: ComputeInstanceId,
4487 target_replica: Option<ReplicaId>,
4488 ) {
4489 self.try_ship_dataflow(dataflow, instance, target_replica)
4490 .await
4491 .unwrap_or_terminate("dataflow creation cannot fail");
4492 }
4493
4494 pub(crate) async fn try_ship_dataflow(
4497 &mut self,
4498 dataflow: DataflowDescription<LirRelationExpr>,
4499 instance: ComputeInstanceId,
4500 target_replica: Option<ReplicaId>,
4501 ) -> Result<(), DataflowCreationError> {
4502 let export_ids = dataflow.exported_index_ids().collect();
4505
4506 self.controller
4507 .compute
4508 .create_dataflow(instance, dataflow, target_replica)?;
4509
4510 self.initialize_compute_read_policies(export_ids, instance, CompactionWindow::Default)
4511 .await;
4512
4513 Ok(())
4514 }
4515
4516 pub(crate) fn allow_writes(&mut self, instance: ComputeInstanceId, id: GlobalId) {
4520 self.controller
4521 .compute
4522 .allow_writes(instance, id)
4523 .unwrap_or_terminate("allow_writes cannot fail");
4524 }
4525
4526 pub(crate) async fn ship_dataflow_and_notice_builtin_table_updates(
4528 &mut self,
4529 dataflow: DataflowDescription<LirRelationExpr>,
4530 instance: ComputeInstanceId,
4531 notice_builtin_updates_fut: Option<BuiltinTableAppendNotify>,
4532 target_replica: Option<ReplicaId>,
4533 ) {
4534 if let Some(notice_builtin_updates_fut) = notice_builtin_updates_fut {
4535 let ship_dataflow_fut = self.ship_dataflow(dataflow, instance, target_replica);
4536 let ((), ()) =
4537 futures::future::join(notice_builtin_updates_fut, ship_dataflow_fut).await;
4538 } else {
4539 self.ship_dataflow(dataflow, instance, target_replica).await;
4540 }
4541 }
4542
4543 pub fn install_compute_watch_set(
4547 &mut self,
4548 conn_id: ConnectionId,
4549 objects: BTreeSet<GlobalId>,
4550 t: Timestamp,
4551 state: WatchSetResponse,
4552 ) -> Result<(), CollectionLookupError> {
4553 let ws_id = self.controller.install_compute_watch_set(objects, t)?;
4554 self.connection_watch_sets
4555 .entry(conn_id.clone())
4556 .or_default()
4557 .insert(ws_id);
4558 self.installed_watch_sets.insert(ws_id, (conn_id, state));
4559 Ok(())
4560 }
4561
4562 pub fn install_storage_watch_set(
4566 &mut self,
4567 conn_id: ConnectionId,
4568 objects: BTreeSet<GlobalId>,
4569 t: Timestamp,
4570 state: WatchSetResponse,
4571 ) -> Result<(), CollectionMissing> {
4572 let ws_id = self.controller.install_storage_watch_set(objects, t)?;
4573 self.connection_watch_sets
4574 .entry(conn_id.clone())
4575 .or_default()
4576 .insert(ws_id);
4577 self.installed_watch_sets.insert(ws_id, (conn_id, state));
4578 Ok(())
4579 }
4580
4581 pub fn cancel_pending_watchsets(&mut self, conn_id: &ConnectionId) {
4583 if let Some(ws_ids) = self.connection_watch_sets.remove(conn_id) {
4584 for ws_id in ws_ids {
4585 self.installed_watch_sets.remove(&ws_id);
4586 }
4587 }
4588 }
4589
4590 pub async fn dump(&self) -> Result<serde_json::Value, anyhow::Error> {
4594 let global_timelines: BTreeMap<_, _> = self
4600 .global_timelines
4601 .iter()
4602 .map(|(timeline, state)| (timeline.to_string(), format!("{state:?}")))
4603 .collect();
4604 let active_conns: BTreeMap<_, _> = self
4605 .active_conns
4606 .iter()
4607 .map(|(id, meta)| (id.unhandled().to_string(), format!("{meta:?}")))
4608 .collect();
4609 let txn_read_holds: BTreeMap<_, _> = self
4610 .txn_read_holds
4611 .iter()
4612 .map(|(id, capability)| (id.unhandled().to_string(), format!("{capability:?}")))
4613 .collect();
4614 let pending_peeks: BTreeMap<_, _> = self
4615 .pending_peeks
4616 .iter()
4617 .map(|(id, peek)| (id.to_string(), format!("{peek:?}")))
4618 .collect();
4619 let client_pending_peeks: BTreeMap<_, _> = self
4620 .client_pending_peeks
4621 .iter()
4622 .map(|(id, peek)| {
4623 let peek: BTreeMap<_, _> = peek
4624 .iter()
4625 .map(|(uuid, storage_id)| (uuid.to_string(), storage_id))
4626 .collect();
4627 (id.to_string(), peek)
4628 })
4629 .collect();
4630 let pending_linearize_read_txns: BTreeMap<_, _> = self
4631 .pending_linearize_read_txns
4632 .iter()
4633 .map(|(id, read_txn)| (id.unhandled().to_string(), format!("{read_txn:?}")))
4634 .collect();
4635
4636 Ok(serde_json::json!({
4637 "global_timelines": global_timelines,
4638 "active_conns": active_conns,
4639 "txn_read_holds": txn_read_holds,
4640 "pending_peeks": pending_peeks,
4641 "client_pending_peeks": client_pending_peeks,
4642 "pending_linearize_read_txns": pending_linearize_read_txns,
4643 "controller": self.controller.dump().await?,
4644 }))
4645 }
4646
4647 async fn prune_storage_usage_events_on_startup(&self, retention_period: Duration) {
4661 let item_id = self
4662 .catalog()
4663 .resolve_builtin_table(&MZ_STORAGE_USAGE_BY_SHARD);
4664 let global_id = self.catalog.get_entry(&item_id).latest_global_id();
4665 let read_ts = self.get_local_read_ts().await;
4666 let current_contents_fut = self
4667 .controller
4668 .storage_collections
4669 .snapshot(global_id, read_ts);
4670 let internal_cmd_tx = self.internal_cmd_tx.clone();
4671 spawn(|| "storage_usage_prune", async move {
4672 let mut current_contents = current_contents_fut
4673 .await
4674 .unwrap_or_terminate("cannot fail to fetch snapshot");
4675 differential_dataflow::consolidation::consolidate(&mut current_contents);
4676
4677 let cutoff_ts = u128::from(read_ts).saturating_sub(retention_period.as_millis());
4678 let mut expired = Vec::new();
4679 for (row, diff) in current_contents {
4680 assert_eq!(
4681 diff, 1,
4682 "consolidated contents should not contain retractions: ({row:#?}, {diff:#?})"
4683 );
4684 let collection_timestamp = row
4686 .unpack()
4687 .get(3)
4688 .expect("definition of mz_storage_by_shard changed")
4689 .unwrap_timestamptz();
4690 let collection_timestamp = collection_timestamp.timestamp_millis();
4691 let collection_timestamp: u128 = collection_timestamp
4692 .try_into()
4693 .expect("all collections happen after Jan 1 1970");
4694 if collection_timestamp < cutoff_ts {
4695 debug!("pruning storage event {row:?}");
4696 let builtin_update = BuiltinTableUpdate::row(item_id, row, Diff::MINUS_ONE);
4697 expired.push(builtin_update);
4698 }
4699 }
4700
4701 let _ = internal_cmd_tx.send(Message::StorageUsagePrune(expired));
4703 });
4704 }
4705
4706 async fn prune_arrangement_sizes_history_on_startup(&self) {
4715 if self.controller.read_only() {
4717 return;
4718 }
4719
4720 let retention_period = mz_adapter_types::dyncfgs::ARRANGEMENT_SIZE_HISTORY_RETENTION_PERIOD
4721 .get(self.catalog().system_config().dyncfgs());
4722 let item_id = self
4723 .catalog()
4724 .resolve_builtin_table(&mz_catalog::builtin::MZ_OBJECT_ARRANGEMENT_SIZE_HISTORY);
4725 let global_id = self.catalog.get_entry(&item_id).latest_global_id();
4726 let read_ts = self.get_local_read_ts().await;
4727 let current_contents_fut = self
4728 .controller
4729 .storage_collections
4730 .snapshot(global_id, read_ts);
4731 let internal_cmd_tx = self.internal_cmd_tx.clone();
4732 spawn(|| "arrangement_sizes_history_prune", async move {
4733 let mut current_contents = current_contents_fut
4734 .await
4735 .unwrap_or_terminate("cannot fail to fetch snapshot");
4736 differential_dataflow::consolidation::consolidate(&mut current_contents);
4737
4738 let cutoff_ts = u128::from(read_ts).saturating_sub(retention_period.as_millis());
4739 let expired =
4740 arrangement_sizes_expired_retractions(current_contents, cutoff_ts, item_id);
4741
4742 let _ = internal_cmd_tx.send(Message::ArrangementSizesPrune(expired));
4746 });
4747 }
4748
4749 fn current_credit_consumption_rate(&self, exclude_cluster: Option<ClusterId>) -> Numeric {
4752 self.catalog()
4753 .user_cluster_replicas()
4754 .filter(|replica| Some(replica.cluster_id) != exclude_cluster)
4755 .filter_map(|replica| match &replica.config.location {
4756 ReplicaLocation::Managed(location) => Some(location.size_for_billing()),
4757 ReplicaLocation::Unmanaged(_) => None,
4758 })
4759 .map(|size| {
4760 self.catalog()
4761 .cluster_replica_sizes()
4762 .0
4763 .get(size)
4764 .expect("location size is validated against the cluster replica sizes")
4765 .credits_per_hour
4766 })
4767 .sum()
4768 }
4769}
4770
4771fn arrangement_sizes_expired_retractions(
4779 rows: impl IntoIterator<Item = (mz_repr::Row, i64)>,
4780 cutoff_ts: u128,
4781 item_id: CatalogItemId,
4782) -> Vec<BuiltinTableUpdate> {
4783 let mut expired = Vec::new();
4784 for (row, diff) in rows {
4785 assert_eq!(
4786 diff, 1,
4787 "consolidated contents should not contain retractions: ({row:#?}, {diff:#?})"
4788 );
4789 let collection_timestamp = row
4790 .unpack()
4791 .get(3)
4792 .expect("definition of mz_object_arrangement_size_history changed")
4793 .unwrap_timestamptz()
4794 .timestamp_millis();
4795 let collection_timestamp: u128 = collection_timestamp
4796 .try_into()
4797 .expect("all collections happen after Jan 1 1970");
4798 if collection_timestamp < cutoff_ts {
4799 expired.push(BuiltinTableUpdate::row(item_id, row, Diff::MINUS_ONE));
4800 }
4801 }
4802 expired
4803}
4804
4805#[cfg(test)]
4806impl Coordinator {
4807 #[allow(dead_code)]
4808 async fn verify_ship_dataflow_no_error(
4809 &mut self,
4810 dataflow: DataflowDescription<LirRelationExpr>,
4811 ) {
4812 let compute_instance = ComputeInstanceId::user(1).expect("1 is a valid ID");
4820
4821 let _: () = self.ship_dataflow(dataflow, compute_instance, None).await;
4822 }
4823}
4824
4825struct LastMessage {
4827 kind: &'static str,
4828 stmt: Option<Arc<Statement<Raw>>>,
4829}
4830
4831impl LastMessage {
4832 fn stmt_to_string(&self) -> Cow<'static, str> {
4834 self.stmt
4835 .as_ref()
4836 .map(|stmt| stmt.to_ast_string_redacted().into())
4837 .unwrap_or(Cow::Borrowed("<none>"))
4838 }
4839}
4840
4841impl fmt::Debug for LastMessage {
4842 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
4843 f.debug_struct("LastMessage")
4844 .field("kind", &self.kind)
4845 .field("stmt", &self.stmt_to_string())
4846 .finish()
4847 }
4848}
4849
4850impl Drop for LastMessage {
4851 fn drop(&mut self) {
4852 if std::thread::panicking() {
4854 eprintln!("Coordinator panicking, dumping last message\n{self:?}",);
4856 }
4857 }
4858}
4859
4860pub fn serve(
4872 Config {
4873 controller_config,
4874 controller_envd_epoch,
4875 mut storage,
4876 timestamp_oracle_url,
4877 unsafe_mode,
4878 all_features,
4879 build_info,
4880 environment_id,
4881 metrics_registry,
4882 now,
4883 secrets_controller,
4884 cloud_resource_controller,
4885 cluster_replica_sizes,
4886 builtin_system_cluster_config,
4887 builtin_catalog_server_cluster_config,
4888 builtin_probe_cluster_config,
4889 builtin_support_cluster_config,
4890 builtin_analytics_cluster_config,
4891 system_parameter_defaults,
4892 availability_zones,
4893 storage_usage_client,
4894 storage_usage_collection_interval,
4895 storage_usage_retention_period,
4896 segment_client,
4897 egress_addresses,
4898 aws_account_id,
4899 aws_privatelink_availability_zones,
4900 connection_context,
4901 connection_limit_callback,
4902 remote_system_parameters,
4903 webhook_concurrency_limit,
4904 http_host_name,
4905 tracing_handle,
4906 read_only_controllers,
4907 caught_up_trigger: clusters_caught_up_trigger,
4908 helm_chart_version,
4909 license_key,
4910 external_login_password_mz_system,
4911 force_builtin_schema_migration,
4912 }: Config,
4913) -> BoxFuture<'static, Result<(Handle, Client), AdapterError>> {
4914 async move {
4915 let coord_start = Instant::now();
4916 info!("startup: coordinator init: beginning");
4917 info!("startup: coordinator init: preamble beginning");
4918
4919 let _builtins = LazyLock::force(&BUILTINS_STATIC);
4923
4924 let (cmd_tx, cmd_rx) = mpsc::unbounded_channel();
4925 let (internal_cmd_tx, internal_cmd_rx) = mpsc::unbounded_channel();
4926 let (strict_serializable_reads_tx, strict_serializable_reads_rx) =
4927 mpsc::unbounded_channel();
4928
4929 if !availability_zones.iter().all_unique() {
4931 coord_bail!("availability zones must be unique");
4932 }
4933
4934 let aws_principal_context = match (
4935 aws_account_id,
4936 connection_context.aws_external_id_prefix.clone(),
4937 ) {
4938 (Some(aws_account_id), Some(aws_external_id_prefix)) => Some(AwsPrincipalContext {
4939 aws_account_id,
4940 aws_external_id_prefix,
4941 }),
4942 _ => None,
4943 };
4944
4945 let aws_privatelink_availability_zones = aws_privatelink_availability_zones
4946 .map(|azs_vec| BTreeSet::from_iter(azs_vec.iter().cloned()));
4947
4948 info!(
4949 "startup: coordinator init: preamble complete in {:?}",
4950 coord_start.elapsed()
4951 );
4952 let oracle_init_start = Instant::now();
4953 info!("startup: coordinator init: timestamp oracle init beginning");
4954
4955 let timestamp_oracle_config = timestamp_oracle_url
4956 .map(|url| TimestampOracleConfig::from_url(&url, &metrics_registry))
4957 .transpose()?;
4958 let mut initial_timestamps =
4959 get_initial_oracle_timestamps(×tamp_oracle_config).await?;
4960
4961 initial_timestamps
4965 .entry(Timeline::EpochMilliseconds)
4966 .or_insert_with(mz_repr::Timestamp::minimum);
4967 let mut timestamp_oracles = BTreeMap::new();
4968 for (timeline, initial_timestamp) in initial_timestamps {
4969 Coordinator::ensure_timeline_state_with_initial_time(
4970 &timeline,
4971 initial_timestamp,
4972 now.clone(),
4973 timestamp_oracle_config.clone(),
4974 &mut timestamp_oracles,
4975 read_only_controllers,
4976 )
4977 .await;
4978 }
4979
4980 let catalog_upper = storage.current_upper().await;
4984 let epoch_millis_oracle = ×tamp_oracles
4990 .get(&Timeline::EpochMilliseconds)
4991 .expect("inserted above")
4992 .oracle;
4993
4994 let boot_now: mz_repr::Timestamp = (now)().into();
4999 if catalog_upper > timeline::write_ts_upper_bound(&boot_now) {
5000 tracing::error!(
5001 %catalog_upper, %boot_now,
5002 "catalog upper is far ahead of the wall clock, so writes and \
5003 strict-serializable reads on the EpochMilliseconds timeline will block \
5004 until the clock catches up",
5005 );
5006 }
5007
5008 let mut boot_ts = if read_only_controllers {
5009 let read_ts = epoch_millis_oracle.read_ts().await;
5010 std::cmp::max(read_ts, catalog_upper)
5011 } else {
5012 epoch_millis_oracle.apply_write(catalog_upper).await;
5015 epoch_millis_oracle.write_ts().await.timestamp
5016 };
5017
5018 info!(
5019 "startup: coordinator init: timestamp oracle init complete in {:?}",
5020 oracle_init_start.elapsed()
5021 );
5022
5023 let catalog_open_start = Instant::now();
5024 info!("startup: coordinator init: catalog open beginning");
5025 let persist_client = controller_config
5026 .persist_clients
5027 .open(controller_config.persist_location.clone())
5028 .await
5029 .context("opening persist client")?;
5030 let builtin_item_migration_config =
5031 BuiltinItemMigrationConfig {
5032 persist_client: persist_client.clone(),
5033 read_only: read_only_controllers,
5034 force_migration: force_builtin_schema_migration,
5035 }
5036 ;
5037 let OpenCatalogResult {
5038 mut catalog,
5039 migrated_storage_collections_0dt,
5040 new_builtin_collections,
5041 builtin_table_updates,
5042 cached_global_exprs,
5043 uncached_local_exprs,
5044 } = Catalog::open(mz_catalog::config::Config {
5045 storage,
5046 metrics_registry: &metrics_registry,
5047 state: mz_catalog::config::StateConfig {
5048 unsafe_mode,
5049 all_features,
5050 build_info,
5051 environment_id: environment_id.clone(),
5052 read_only: read_only_controllers,
5053 now: now.clone(),
5054 boot_ts: boot_ts.clone(),
5055 skip_migrations: false,
5056 cluster_replica_sizes,
5057 builtin_system_cluster_config,
5058 builtin_catalog_server_cluster_config,
5059 builtin_probe_cluster_config,
5060 builtin_support_cluster_config,
5061 builtin_analytics_cluster_config,
5062 system_parameter_defaults,
5063 remote_system_parameters,
5064 availability_zones,
5065 egress_addresses,
5066 aws_principal_context,
5067 aws_privatelink_availability_zones,
5068 connection_context,
5069 http_host_name,
5070 builtin_item_migration_config,
5071 persist_client: persist_client.clone(),
5072 enable_expression_cache_override: None,
5073 helm_chart_version,
5074 external_login_password_mz_system,
5075 license_key: license_key.clone(),
5076 },
5077 })
5078 .await?;
5079
5080 let catalog_upper = catalog.current_upper().await;
5083 boot_ts = std::cmp::max(boot_ts, catalog_upper);
5084
5085 if !read_only_controllers {
5086 epoch_millis_oracle.apply_write(boot_ts).await;
5087 }
5088
5089 info!(
5090 "startup: coordinator init: catalog open complete in {:?}",
5091 catalog_open_start.elapsed()
5092 );
5093
5094 let coord_thread_start = Instant::now();
5095 info!("startup: coordinator init: coordinator thread start beginning");
5096
5097 let session_id = catalog.config().session_id;
5098 let start_instant = catalog.config().start_instant;
5099
5100 let (bootstrap_tx, bootstrap_rx) = oneshot::channel();
5104 let handle = TokioHandle::current();
5105
5106 let metrics = Metrics::register_into(&metrics_registry);
5107 let metrics_clone = metrics.clone();
5108 let optimizer_metrics = OptimizerMetrics::register_into(
5109 &metrics_registry,
5110 catalog.system_config().optimizer_e2e_latency_warning_threshold(),
5111 );
5112 let segment_client_clone = segment_client.clone();
5113 let coord_now = now.clone();
5114 let advance_timelines_interval =
5115 tokio::time::interval(catalog.system_config().default_timestamp_interval());
5116
5117 let clusters_caught_up_check_interval = if read_only_controllers {
5118 let dyncfgs = catalog.system_config().dyncfgs();
5119 let interval = WITH_0DT_DEPLOYMENT_CAUGHT_UP_CHECK_INTERVAL.get(dyncfgs);
5120
5121 let mut interval = tokio::time::interval(interval);
5122 interval.set_missed_tick_behavior(MissedTickBehavior::Skip);
5123 interval
5124 } else {
5125 let mut interval = tokio::time::interval(Duration::from_secs(60 * 60));
5133 interval.set_missed_tick_behavior(MissedTickBehavior::Skip);
5134 interval
5135 };
5136
5137 let clusters_caught_up_check =
5138 clusters_caught_up_trigger.map(|trigger| {
5139 let mut exclude_collections: BTreeSet<GlobalId> =
5140 new_builtin_collections.iter().copied().collect();
5141
5142 let new_builtin_mvs = new_builtin_collections
5157 .iter()
5158 .map(|global_id| {
5159 catalog
5160 .state()
5161 .try_get_entry_by_global_id(global_id)
5162 .expect("new builtin collections have catalog entries")
5163 })
5164 .filter(|entry| entry.is_materialized_view())
5165 .map(|entry| entry.id());
5166 let mut todo: Vec<_> = migrated_storage_collections_0dt
5167 .iter()
5168 .copied()
5169 .filter(|id| catalog.state().get_entry(id).is_materialized_view())
5170 .chain(new_builtin_mvs)
5171 .collect();
5172 while let Some(item_id) = todo.pop() {
5173 let entry = catalog.state().get_entry(&item_id);
5174 exclude_collections.extend(entry.global_ids());
5175 todo.extend_from_slice(entry.used_by());
5176 }
5177
5178 CaughtUpCheckContext {
5179 trigger,
5180 exclude_collections,
5181 cluster_stability: BTreeMap::new(),
5182 }
5183 });
5184
5185 if let Some(TimestampOracleConfig::Postgres(pg_config)) =
5186 timestamp_oracle_config.as_ref()
5187 {
5188 let pg_timestamp_oracle_params =
5191 flags::timestamp_oracle_config(catalog.system_config());
5192 pg_timestamp_oracle_params.apply(pg_config);
5193 }
5194
5195 let connection_limit_callback: Arc<dyn Fn(&SystemVars) + Send + Sync> =
5198 Arc::new(move |system_vars: &SystemVars| {
5199 let limit: u64 = system_vars.max_connections().cast_into();
5200 let superuser_reserved: u64 =
5201 system_vars.superuser_reserved_connections().cast_into();
5202
5203 let superuser_reserved = if superuser_reserved >= limit {
5208 tracing::warn!(
5209 "superuser_reserved ({superuser_reserved}) is greater than max connections ({limit})!"
5210 );
5211 limit
5212 } else {
5213 superuser_reserved
5214 };
5215
5216 (connection_limit_callback)(limit, superuser_reserved);
5217 });
5218 catalog.system_config_mut().register_callback(
5219 &mz_sql::session::vars::MAX_CONNECTIONS,
5220 Arc::clone(&connection_limit_callback),
5221 );
5222 catalog.system_config_mut().register_callback(
5223 &mz_sql::session::vars::SUPERUSER_RESERVED_CONNECTIONS,
5224 connection_limit_callback,
5225 );
5226
5227 let (group_commit_tx, group_commit_rx) = appends::notifier();
5228
5229 let parent_span = tracing::Span::current();
5230 let thread = thread::Builder::new()
5231 .stack_size(3 * stack::STACK_SIZE)
5235 .name("coordinator".to_string())
5236 .spawn(move || {
5237 let span = info_span!(parent: parent_span, "coord::coordinator").entered();
5238
5239 let controller = handle
5240 .block_on({
5241 catalog.initialize_controller(
5242 controller_config,
5243 controller_envd_epoch,
5244 read_only_controllers,
5245 )
5246 })
5247 .unwrap_or_terminate("failed to initialize storage_controller");
5248 let catalog_upper = handle.block_on(catalog.current_upper());
5251 boot_ts = std::cmp::max(boot_ts, catalog_upper);
5252 if !read_only_controllers {
5253 let epoch_millis_oracle = ×tamp_oracles
5254 .get(&Timeline::EpochMilliseconds)
5255 .expect("inserted above")
5256 .oracle;
5257 handle.block_on(epoch_millis_oracle.apply_write(boot_ts));
5258 }
5259
5260 let catalog = Arc::new(catalog);
5261 let max_concurrent_occ_writes =
5264 usize::cast_from(catalog.system_config().max_concurrent_occ_writes());
5265 let frontend_read_then_write_enabled = {
5266 FRONTEND_READ_THEN_WRITE.get(catalog.system_config().dyncfgs())
5267 };
5268
5269 let caching_secrets_reader = CachingSecretsReader::new(secrets_controller.reader());
5270 let (group_committer_tx, group_committer_rx) = mpsc::unbounded_channel();
5271 let mut coord = Coordinator {
5272 controller,
5273 catalog,
5274 internal_cmd_tx,
5275 group_commit_tx,
5276 reconcile_now: Arc::new(Notify::new()),
5277 group_committer_tx,
5278 strict_serializable_reads_tx,
5279 linearize_reads_notify: Arc::new(Notify::new()),
5280 global_timelines: timestamp_oracles,
5281 transient_id_gen: Arc::new(TransientIdGen::new()),
5282 active_conns: BTreeMap::new(),
5283 txn_read_holds: Default::default(),
5284 pending_peeks: BTreeMap::new(),
5285 client_pending_peeks: BTreeMap::new(),
5286 pending_linearize_read_txns: BTreeMap::new(),
5287 serialized_ddl: LockedVecDeque::new(),
5288 active_compute_sinks: BTreeMap::new(),
5289 active_webhooks: BTreeMap::new(),
5290 active_copies: BTreeMap::new(),
5291 connection_cancel_watches: BTreeMap::new(),
5292 introspection_subscribes: BTreeMap::new(),
5293 write_locks: BTreeMap::new(),
5294 deferred_write_ops: BTreeMap::new(),
5295 pending_writes: Vec::new(),
5296 occ_write_semaphore: Arc::new(Semaphore::new(max_concurrent_occ_writes)),
5297 frontend_read_then_write_enabled,
5298 advance_timelines_interval,
5299 secrets_controller,
5300 caching_secrets_reader,
5301 cloud_resource_controller,
5302 storage_usage_client,
5303 storage_usage_collection_interval,
5304 segment_client,
5305 metrics,
5306 catalog_info_metrics_registry: metrics_registry.clone(),
5307 scoped_frontend: None,
5308 optimizer_metrics,
5309 tracing_handle,
5310 statement_logging: StatementLogging::new(coord_now.clone()),
5311 webhook_concurrency_limit,
5312 timestamp_oracle_config,
5313 caught_up_check_interval: clusters_caught_up_check_interval,
5314 caught_up_check: clusters_caught_up_check,
5315 installed_watch_sets: BTreeMap::new(),
5316 connection_watch_sets: BTreeMap::new(),
5317 cluster_replica_statuses: ClusterReplicaStatuses::new(),
5318 read_only_controllers,
5319 buffered_builtin_table_updates: Some(Vec::new()),
5320 license_key,
5321 user_id_pool: IdPool::empty(),
5322 persist_client,
5323 };
5324
5325 handle.block_on(async {
5327 appends::spawn_group_committer(
5328 group_committer_rx,
5329 coord.get_local_timestamp_oracle(),
5330 coord.controller.storage.table_write_handle(),
5331 coord.catalog().upper_handle(),
5332 coord.internal_cmd_tx.clone(),
5333 coord.catalog().config().now.clone(),
5334 coord.metrics.clone(),
5335 coord.catalog().system_config().dyncfgs(),
5336 );
5337 });
5338
5339 let bootstrap = handle.block_on(async {
5340 coord
5341 .bootstrap(
5342 boot_ts,
5343 migrated_storage_collections_0dt,
5344 builtin_table_updates,
5345 cached_global_exprs,
5346 uncached_local_exprs,
5347 )
5348 .await?;
5349 coord
5350 .controller
5351 .remove_orphaned_replicas(
5352 coord.catalog().get_next_user_replica_id().await?,
5353 coord.catalog().get_next_system_replica_id().await?,
5354 )
5355 .await
5356 .map_err(AdapterError::Orchestrator)?;
5357
5358 if let Some(retention_period) = storage_usage_retention_period {
5359 coord
5360 .prune_storage_usage_events_on_startup(retention_period)
5361 .await;
5362 }
5363
5364 coord.prune_arrangement_sizes_history_on_startup().await;
5365
5366 Ok(())
5367 });
5368 let ok = bootstrap.is_ok();
5369 drop(span);
5370 bootstrap_tx
5371 .send(bootstrap)
5372 .expect("bootstrap_rx is not dropped until it receives this message");
5373 if ok {
5374 handle.block_on(coord.serve(
5375 internal_cmd_rx,
5376 strict_serializable_reads_rx,
5377 cmd_rx,
5378 group_commit_rx,
5379 ));
5380 }
5381 })
5382 .expect("failed to create coordinator thread");
5383 match bootstrap_rx
5384 .await
5385 .expect("bootstrap_tx always sends a message or panics/halts")
5386 {
5387 Ok(()) => {
5388 info!(
5389 "startup: coordinator init: coordinator thread start complete in {:?}",
5390 coord_thread_start.elapsed()
5391 );
5392 info!(
5393 "startup: coordinator init: complete in {:?}",
5394 coord_start.elapsed()
5395 );
5396 let handle = Handle {
5397 session_id,
5398 start_instant,
5399 _thread: thread.join_on_drop(),
5400 };
5401 let client = Client::new(
5402 build_info,
5403 cmd_tx,
5404 metrics_clone,
5405 now,
5406 environment_id,
5407 segment_client_clone,
5408 );
5409 Ok((handle, client))
5410 }
5411 Err(e) => Err(e),
5412 }
5413 }
5414 .boxed()
5415}
5416
5417async fn get_initial_oracle_timestamps(
5431 timestamp_oracle_config: &Option<TimestampOracleConfig>,
5432) -> Result<BTreeMap<Timeline, Timestamp>, AdapterError> {
5433 let mut initial_timestamps = BTreeMap::new();
5434
5435 if let Some(config) = timestamp_oracle_config {
5436 let oracle_timestamps = config.get_all_timelines().await?;
5437
5438 let debug_msg = || {
5439 oracle_timestamps
5440 .iter()
5441 .map(|(timeline, ts)| format!("{:?} -> {}", timeline, ts))
5442 .join(", ")
5443 };
5444 info!(
5445 "current timestamps from the timestamp oracle: {}",
5446 debug_msg()
5447 );
5448
5449 for (timeline, ts) in oracle_timestamps {
5450 let entry = initial_timestamps
5451 .entry(Timeline::from_str(&timeline).expect("could not parse timeline"));
5452
5453 entry
5454 .and_modify(|current_ts| *current_ts = std::cmp::max(*current_ts, ts))
5455 .or_insert(ts);
5456 }
5457 } else {
5458 info!("no timestamp oracle configured!");
5459 };
5460
5461 let debug_msg = || {
5462 initial_timestamps
5463 .iter()
5464 .map(|(timeline, ts)| format!("{:?}: {}", timeline, ts))
5465 .join(", ")
5466 };
5467 info!("initial oracle timestamps: {}", debug_msg());
5468
5469 Ok(initial_timestamps)
5470}
5471
5472#[instrument]
5473pub async fn load_remote_system_parameters(
5474 storage: &mut Box<dyn OpenableDurableCatalogState>,
5475 system_parameter_sync_config: Option<SystemParameterSyncConfig>,
5476 system_parameter_sync_timeout: Duration,
5477) -> Result<Option<BTreeMap<String, String>>, AdapterError> {
5478 if let Some(system_parameter_sync_config) = system_parameter_sync_config {
5479 tracing::info!("parameter sync on boot: start sync");
5480
5481 let mut params = SynchronizedParameters::new(SystemVars::default());
5521 let frontend_sync = async {
5522 let frontend = SystemParameterFrontend::from(&system_parameter_sync_config).await?;
5523 frontend.pull(&mut params);
5524 let ops = params
5525 .modified()
5526 .into_iter()
5527 .map(|param| {
5528 let name = param.name;
5529 let value = param.value;
5530 tracing::info!(name, value, initial = true, "sync parameter");
5531 (name, value)
5532 })
5533 .collect();
5534 tracing::info!("parameter sync on boot: end sync");
5535 Ok(Some(ops))
5536 };
5537 if !storage.has_system_config_synced_once().await? {
5538 frontend_sync.await
5539 } else {
5540 match mz_ore::future::timeout(system_parameter_sync_timeout, frontend_sync).await {
5541 Ok(ops) => Ok(ops),
5542 Err(TimeoutError::Inner(e)) => Err(e),
5543 Err(TimeoutError::DeadlineElapsed) => {
5544 tracing::info!("parameter sync on boot: sync has timed out");
5545 Ok(None)
5546 }
5547 }
5548 }
5549 } else {
5550 Ok(None)
5551 }
5552}
5553
5554#[derive(Debug)]
5555pub enum WatchSetResponse {
5556 StatementDependenciesReady(StatementLoggingId, StatementLifecycleEvent),
5557 AlterSinkReady(AlterSinkReadyContext),
5558 AlterMaterializedViewReady(AlterMaterializedViewReadyContext),
5559}
5560
5561#[derive(Debug)]
5562pub struct AlterSinkReadyContext {
5563 ctx: Option<ExecuteContext>,
5564 otel_ctx: OpenTelemetryContext,
5565 plan: AlterSinkPlan,
5566 plan_validity: PlanValidity,
5567 read_hold: ReadHolds,
5568}
5569
5570impl AlterSinkReadyContext {
5571 fn ctx(&mut self) -> &mut ExecuteContext {
5572 self.ctx.as_mut().expect("only cleared on drop")
5573 }
5574
5575 fn retire(mut self, result: Result<ExecuteResponse, AdapterError>) {
5576 self.ctx
5577 .take()
5578 .expect("only cleared on drop")
5579 .retire(result);
5580 }
5581}
5582
5583impl Drop for AlterSinkReadyContext {
5584 fn drop(&mut self) {
5585 if let Some(ctx) = self.ctx.take() {
5586 ctx.retire(Err(AdapterError::Canceled));
5587 }
5588 }
5589}
5590
5591#[derive(Debug)]
5592pub struct AlterMaterializedViewReadyContext {
5593 ctx: Option<ExecuteContext>,
5594 otel_ctx: OpenTelemetryContext,
5595 plan: plan::AlterMaterializedViewApplyReplacementPlan,
5596 plan_validity: PlanValidity,
5597}
5598
5599impl AlterMaterializedViewReadyContext {
5600 fn ctx(&mut self) -> &mut ExecuteContext {
5601 self.ctx.as_mut().expect("only cleared on drop")
5602 }
5603
5604 fn retire(mut self, result: Result<ExecuteResponse, AdapterError>) {
5605 self.ctx
5606 .take()
5607 .expect("only cleared on drop")
5608 .retire(result);
5609 }
5610}
5611
5612impl Drop for AlterMaterializedViewReadyContext {
5613 fn drop(&mut self) {
5614 if let Some(ctx) = self.ctx.take() {
5615 ctx.retire(Err(AdapterError::Canceled));
5616 }
5617 }
5618}
5619
5620#[derive(Debug)]
5623struct LockedVecDeque<T> {
5624 items: VecDeque<T>,
5625 lock: Arc<tokio::sync::Mutex<()>>,
5626}
5627
5628impl<T> LockedVecDeque<T> {
5629 pub fn new() -> Self {
5630 Self {
5631 items: VecDeque::new(),
5632 lock: Arc::new(tokio::sync::Mutex::new(())),
5633 }
5634 }
5635
5636 pub fn try_lock_owned(&self) -> Result<OwnedMutexGuard<()>, tokio::sync::TryLockError> {
5637 Arc::clone(&self.lock).try_lock_owned()
5638 }
5639
5640 pub fn is_empty(&self) -> bool {
5641 self.items.is_empty()
5642 }
5643
5644 pub fn push_back(&mut self, value: T) {
5645 self.items.push_back(value)
5646 }
5647
5648 pub fn pop_front(&mut self) -> Option<T> {
5649 self.items.pop_front()
5650 }
5651
5652 pub fn remove(&mut self, index: usize) -> Option<T> {
5653 self.items.remove(index)
5654 }
5655
5656 pub fn iter(&self) -> std::collections::vec_deque::Iter<'_, T> {
5657 self.items.iter()
5658 }
5659}
5660
5661#[derive(Debug)]
5662struct DeferredPlanStatement {
5663 ctx: ExecuteContext,
5664 ps: PlanStatement,
5665}
5666
5667#[derive(Debug)]
5668enum PlanStatement {
5669 Statement {
5670 stmt: Arc<Statement<Raw>>,
5671 params: Params,
5672 },
5673 Plan {
5674 plan: mz_sql::plan::Plan,
5675 resolved_ids: ResolvedIds,
5676 sql_impl_resolved_ids: ResolvedIds,
5677 },
5678}
5679
5680#[derive(Debug, Error)]
5681pub enum NetworkPolicyError {
5682 #[error("Access denied for address {0}")]
5683 AddressDenied(IpAddr),
5684 #[error("Access denied missing IP address")]
5685 MissingIp,
5686}
5687
5688pub(crate) fn validate_ip_with_policy_rules(
5689 ip: &IpAddr,
5690 rules: &Vec<NetworkPolicyRule>,
5691) -> Result<(), NetworkPolicyError> {
5692 if rules.iter().any(|r| r.address.0.contains(ip)) {
5695 Ok(())
5696 } else {
5697 Err(NetworkPolicyError::AddressDenied(ip.clone()))
5698 }
5699}
5700
5701pub(crate) fn infer_sql_type_for_catalog(
5702 hir_expr: &HirRelationExpr,
5703 mir_expr: &MirRelationExpr,
5704) -> SqlRelationType {
5705 let mut typ = hir_expr.top_level_typ();
5706 typ.backport_nullability_and_keys(&mir_expr.typ());
5707 typ
5708}
5709
5710#[cfg(test)]
5711mod execute_context_tests {
5712 use tokio::sync::{mpsc, oneshot};
5713
5714 use super::*;
5715 use crate::session::Session;
5716 use crate::util::ClientTransmitter;
5717
5718 #[mz_ore::test]
5721 fn test_retire_answers_client_when_runtime_shuts_down() {
5722 let runtime = tokio::runtime::Runtime::new().expect("can build runtime");
5723
5724 let (client_tx, mut client_rx) = oneshot::channel();
5725 let (internal_cmd_tx, _internal_cmd_rx) = mpsc::unbounded_channel();
5726
5727 runtime.block_on(async {
5728 let ctx = ExecuteContext::from_parts_with_response_barriers(
5729 ClientTransmitter::new(client_tx, internal_cmd_tx.clone()),
5730 internal_cmd_tx,
5731 Session::dummy(),
5732 ExecuteContextGuard::default(),
5733 vec![Box::pin(std::future::pending())],
5735 );
5736 ctx.retire(Ok(ExecuteResponse::StartedTransaction));
5737 });
5738
5739 drop(runtime);
5740
5741 let response = client_rx.try_recv().expect("client must be answered");
5742 assert!(
5743 matches!(response.result, Err(AdapterError::Internal(_))),
5744 "expected an internal error, got {:?}",
5745 response.result
5746 );
5747 }
5748}
5749
5750#[cfg(test)]
5751mod id_pool_tests {
5752 use super::IdPool;
5753
5754 #[mz_ore::test]
5755 fn test_empty_pool() {
5756 let mut pool = IdPool::empty();
5757 assert_eq!(pool.remaining(), 0);
5758 assert_eq!(pool.allocate(), None);
5759 assert_eq!(pool.allocate_many(1), None);
5760 }
5761
5762 #[mz_ore::test]
5763 fn test_allocate_single() {
5764 let mut pool = IdPool::empty();
5765 pool.refill(10, 13);
5766 assert_eq!(pool.remaining(), 3);
5767 assert_eq!(pool.allocate(), Some(10));
5768 assert_eq!(pool.allocate(), Some(11));
5769 assert_eq!(pool.allocate(), Some(12));
5770 assert_eq!(pool.remaining(), 0);
5771 assert_eq!(pool.allocate(), None);
5772 }
5773
5774 #[mz_ore::test]
5775 fn test_allocate_many() {
5776 let mut pool = IdPool::empty();
5777 pool.refill(100, 105);
5778 assert_eq!(pool.allocate_many(3), Some(vec![100, 101, 102]));
5779 assert_eq!(pool.remaining(), 2);
5780 assert_eq!(pool.allocate_many(3), None);
5782 assert_eq!(pool.allocate_many(2), Some(vec![103, 104]));
5784 assert_eq!(pool.remaining(), 0);
5785 }
5786
5787 #[mz_ore::test]
5788 fn test_allocate_many_zero() {
5789 let mut pool = IdPool::empty();
5790 pool.refill(1, 5);
5791 assert_eq!(pool.allocate_many(0), Some(vec![]));
5792 assert_eq!(pool.remaining(), 4);
5793 }
5794
5795 #[mz_ore::test]
5796 fn test_refill_resets_pool() {
5797 let mut pool = IdPool::empty();
5798 pool.refill(0, 2);
5799 assert_eq!(pool.allocate(), Some(0));
5800 pool.refill(50, 52);
5802 assert_eq!(pool.allocate(), Some(50));
5803 assert_eq!(pool.allocate(), Some(51));
5804 assert_eq!(pool.allocate(), None);
5805 }
5806
5807 #[mz_ore::test]
5808 fn test_mixed_allocate_and_allocate_many() {
5809 let mut pool = IdPool::empty();
5810 pool.refill(0, 10);
5811 assert_eq!(pool.allocate(), Some(0));
5812 assert_eq!(pool.allocate_many(3), Some(vec![1, 2, 3]));
5813 assert_eq!(pool.allocate(), Some(4));
5814 assert_eq!(pool.remaining(), 5);
5815 }
5816
5817 #[mz_ore::test]
5818 #[should_panic(expected = "invalid pool range")]
5819 fn test_refill_invalid_range_panics() {
5820 let mut pool = IdPool::empty();
5821 pool.refill(10, 5);
5822 }
5823}
5824
5825#[cfg(test)]
5826mod arrangement_sizes_pruner_tests {
5827 use mz_repr::catalog_item_id::CatalogItemId;
5828 use mz_repr::{Datum, Row};
5829
5830 use super::arrangement_sizes_expired_retractions;
5831
5832 fn history_row(ts_ms: i64) -> Row {
5836 let dt = mz_ore::now::to_datetime(ts_ms.try_into().expect("non-negative"));
5837 Row::pack_slice(&[
5838 Datum::String("r1"),
5839 Datum::String("u1"),
5840 Datum::Int64(123),
5841 Datum::TimestampTz(dt.try_into().expect("fits in TimestampTz")),
5842 ])
5843 }
5844
5845 fn item_id() -> CatalogItemId {
5846 CatalogItemId::User(42)
5848 }
5849
5850 #[mz_ore::test]
5851 fn empty_input_produces_no_retractions() {
5852 let out = arrangement_sizes_expired_retractions(Vec::new(), 1_000, item_id());
5853 assert!(out.is_empty());
5854 }
5855
5856 #[mz_ore::test]
5857 fn retracts_only_rows_strictly_before_cutoff() {
5858 let rows = vec![
5861 (history_row(100), 1),
5862 (history_row(500), 1),
5863 (history_row(1_000), 1), (history_row(5_000), 1),
5865 ];
5866 let out = arrangement_sizes_expired_retractions(rows, 1_000, item_id());
5867 assert_eq!(out.len(), 2);
5868 }
5869
5870 #[mz_ore::test]
5871 #[should_panic(expected = "consolidated contents should not contain retractions")]
5872 fn retraction_in_input_panics() {
5873 let rows = vec![(history_row(100), -1)];
5874 let _ = arrangement_sizes_expired_retractions(rows, 1_000, item_id());
5875 }
5876}