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::{ENABLE_0DT_HYDRATE_MIGRATED_BUILTIN_MVS, USER_ID_POOL_BATCH_SIZE};
95use mz_auth::password::Password;
96use mz_build_info::BuildInfo;
97use mz_catalog::builtin::{
98 BUILTINS, BUILTINS_STATIC, MZ_OBJECT_ARRANGEMENT_SIZE_HISTORY, MZ_OBJECT_HYDRATION_HISTORY,
99 MZ_REPLICA_HYDRATION_HISTORY, MZ_STORAGE_USAGE_BY_SHARD,
100};
101use mz_catalog::config::{AwsPrincipalContext, BuiltinItemMigrationConfig, ClusterReplicaSizeMap};
102use mz_catalog::durable::OpenableDurableCatalogState;
103use mz_catalog::expr_cache::{GlobalExpressions, LocalExpressions, latest_item_version};
104use mz_catalog::memory::objects::{
105 CatalogEntry, CatalogItem, ClusterReplicaProcessStatus, Connection, DataSourceDesc,
106 ReconfigurationTarget, Table, TableDataSource,
107};
108use mz_cloud_resources::{CloudResourceController, VpcEndpointConfig, VpcEndpointEvent};
109use mz_compute_client::as_of_selection;
110use mz_compute_client::controller::error::{
111 CollectionLookupError, CollectionMissing, DataflowCreationError, InstanceMissing,
112};
113use mz_compute_types::ComputeInstanceId;
114use mz_compute_types::dataflows::LirDataflowDescription;
115use mz_controller::clusters::{
116 ClusterConfig, ClusterEvent, ClusterStatus, ManagedReplicaLocation, ProcessId, ReplicaLocation,
117};
118use mz_controller::{ControllerConfig, Readiness};
119use mz_controller_types::{ClusterId, ReplicaId, WatchSetId};
120use mz_dyncfg::{ConfigUpdates, ParameterScope};
121use mz_expr::{MirRelationExpr, OptimizedMirRelationExpr};
122use mz_license_keys::{ExpirationBehavior, ValidatedLicenseKey};
123use mz_orchestrator::OfflineReason;
124use mz_ore::cast::{CastFrom, CastInto, CastLossy};
125use mz_ore::channel::trigger::Trigger;
126use mz_ore::future::TimeoutError;
127use mz_ore::metrics::MetricsRegistry;
128use mz_ore::now::{EpochMillis, NowFn};
129use mz_ore::task::{AbortOnDropHandle, JoinHandle, spawn};
130use mz_ore::thread::JoinHandleExt;
131use mz_ore::tracing::{OpenTelemetryContext, TracingHandle};
132use mz_ore::url::SensitiveUrl;
133use mz_ore::{
134 assert_none, instrument, soft_assert_eq_or_log, soft_assert_or_log, soft_panic_or_log, stack,
135};
136use mz_persist_client::PersistClient;
137use mz_persist_client::batch::ProtoBatch;
138use mz_persist_client::usage::{ShardsUsageReferenced, StorageUsageClient};
139use mz_repr::adt::numeric::Numeric;
140use mz_repr::explain::{ExplainConfig, ExplainFormat};
141use mz_repr::global_id::TransientIdGen;
142use mz_repr::optimize::{OptimizerFeatureOverrides, OptimizerFeatures, OverrideFrom};
143use mz_repr::role_id::RoleId;
144use mz_repr::{
145 CatalogItemId, Diff, GlobalId, RelationDesc, RelationVersion, SqlRelationType, Timestamp,
146};
147use mz_secrets::cache::CachingSecretsReader;
148use mz_secrets::{SecretsController, SecretsReader};
149use mz_sql::ast::{Raw, Statement};
150use mz_sql::catalog::{CatalogCluster, EnvironmentId};
151use mz_sql::names::{QualifiedItemName, ResolvedIds};
152use mz_sql::optimizer_metrics::OptimizerMetrics;
153use mz_sql::plan::{
154 self, AlterSinkPlan, ConnectionDetails, CreateConnectionPlan, HirRelationExpr,
155 NetworkPolicyRule, Params, QueryWhen,
156};
157use mz_sql::session::user::User;
158use mz_sql::session::vars::{MAX_CREDIT_CONSUMPTION_RATE, SystemVars, Var};
159use mz_sql_parser::ast::ExplainStage;
160use mz_sql_parser::ast::display::AstDisplay;
161use mz_storage_client::client::TableData;
162use mz_storage_client::controller::{CollectionDescription, DataSource, ExportDescription};
163use mz_storage_types::connections::Connection as StorageConnection;
164use mz_storage_types::connections::ConnectionContext;
165use mz_storage_types::connections::inline::{IntoInlineConnection, ReferencedConnection};
166use mz_storage_types::read_holds::ReadHold;
167use mz_storage_types::sinks::{S3SinkFormat, StorageSinkDesc};
168use mz_storage_types::sources::kafka::KAFKA_PROGRESS_DESC;
169use mz_storage_types::sources::{IngestionDescription, SourceExport, Timeline};
170use mz_timestamp_oracle::{TimestampOracleConfig, WriteTimestamp};
171use mz_transform::dataflow::DataflowMetainfo;
172use opentelemetry::trace::TraceContextExt;
173use semver::Version;
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;
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, truncate_sql_for_logging};
188use crate::command::{Command, ExecuteResponse};
189use crate::config::{
190 ClusterEvalContext, ClusterScopeContext, ReplicaEvalContext, ReplicaScopeContext,
191 ScopedParameters, ScopedParametersScope, SynchronizedParameters, SystemParameterFrontend,
192 SystemParameterSyncConfig,
193};
194use crate::coord::appends::{
195 BuiltinTableAppendCompletion, BuiltinTableAppendNotify, DeferredPlan, GroupCommitPermit,
196 PendingWriteTxn,
197};
198use crate::coord::caught_up::CaughtUpCheckContext;
199use crate::coord::id_bundle::CollectionIdBundle;
200use crate::coord::introspection::IntrospectionSubscribe;
201use crate::coord::metric_sink::{CuratedMetricSink, InstalledMetricSink, PlannedMetricSink};
202use crate::coord::peek::PendingPeek;
203use crate::coord::statement_logging::StatementLogging;
204use crate::coord::timeline::{TimelineContext, TimelineState};
205use crate::coord::timestamp_selection::TimestampContext;
206use crate::coord::validity::PlanValidity;
207use crate::error::AdapterError;
208use crate::explain::optimizer_trace::{DispatchGuard, OptimizerTrace};
209use crate::metrics::Metrics;
210use crate::optimize::dataflows::{ComputeInstanceSnapshot, DataflowBuilder};
211use crate::optimize::{self, Optimize, OptimizerConfig};
212use crate::session::{EndTransactionAction, Session};
213use crate::statement_logging::{
214 StatementEndedExecutionReason, StatementLifecycleEvent, StatementLoggingId,
215};
216use crate::util::{ClientTransmitter, ResultExt, sort_topological};
217use crate::webhook::{WebhookAppenderInvalidator, WebhookConcurrencyLimiter};
218use crate::{AdapterNotice, ReadHolds, flags};
219
220pub(crate) mod appends;
221pub(crate) mod catalog_serving;
222pub(crate) mod cluster_controller;
223pub(crate) mod consistency;
224pub(crate) mod id_bundle;
225pub(crate) mod in_memory_oracle;
226pub(crate) mod peek;
227pub(crate) mod read_policy;
228pub(crate) mod read_then_write;
229pub(crate) mod sequencer;
230pub(crate) mod statement_logging;
231pub(crate) mod timeline;
232pub(crate) mod timestamp_selection;
233
234pub mod catalog_implications;
235mod caught_up;
236mod command_handler;
237mod ddl;
238pub(crate) mod group_sync;
239mod hydration_history;
240mod indexes;
241mod info_metrics;
242mod introspection;
243mod message_handler;
244mod metric_sink;
245mod privatelink_status;
246mod sql;
247mod validity;
248
249const MIN_LEADER_VERSION_FOR_MIGRATED_MV_WRITES: Version = Version::new(26, 17, 0);
259
260#[derive(Debug)]
286pub(crate) struct IdPool {
287 next: u64,
288 upper: u64,
289}
290
291impl IdPool {
292 pub fn empty() -> Self {
294 IdPool { next: 0, upper: 0 }
295 }
296
297 pub fn allocate(&mut self) -> Option<u64> {
299 if self.next < self.upper {
300 let id = self.next;
301 self.next += 1;
302 Some(id)
303 } else {
304 None
305 }
306 }
307
308 pub fn allocate_many(&mut self, n: u64) -> Option<Vec<u64>> {
311 if self.remaining() >= n {
312 let ids = (self.next..self.next + n).collect();
313 self.next += n;
314 Some(ids)
315 } else {
316 None
317 }
318 }
319
320 pub fn remaining(&self) -> u64 {
322 self.upper - self.next
323 }
324
325 pub fn refill(&mut self, next: u64, upper: u64) {
327 assert!(next <= upper, "invalid pool range: {next}..{upper}");
328 self.next = next;
329 self.upper = upper;
330 }
331}
332
333#[derive(Debug)]
337pub struct ArrangementSizeRecord {
338 pub replica_id: String,
339 pub object_id: String,
340 pub size: i64,
341 pub hydration_complete: bool,
342}
343
344#[derive(Debug)]
345pub enum Message {
346 Command(OpenTelemetryContext, Command),
347 ControllerReady {
348 controller: ControllerReadiness,
349 },
350 PurifiedStatementReady(PurifiedStatementReady),
351 CreateConnectionValidationReady(CreateConnectionValidationReady),
352 AlterConnectionValidationReady(AlterConnectionValidationReady),
353 DeferredPlanReady {
354 conn_id: ConnectionId,
356 },
357 GroupCommitInitiate(Span, Option<GroupCommitPermit>),
359 GroupCommitApplied {
363 responses: Vec<crate::util::CompletedClientTransmitter>,
365 statement_logging_ids: Vec<StatementLoggingId>,
367 internal_results: Vec<crate::coord::appends::InternalWriteResponder>,
369 write_ts: Timestamp,
371 },
372 DeferredStatementReady,
373 AdvanceTimelines,
374 ClusterEvent(ClusterEvent),
375 LinearizeReads,
376 StagedBatches {
377 conn_id: ConnectionId,
378 table_id: CatalogItemId,
379 batches: Vec<Result<ProtoBatch, String>>,
380 },
381 StorageUsageSchedule,
382 StorageUsageFetch,
383 StorageUsageUpdate(ShardsUsageReferenced),
384 StorageUsagePrune(Vec<BuiltinTableUpdate>),
385 ArrangementSizesSchedule,
386 ArrangementSizesSnapshot,
387 ArrangementSizesWrite {
388 records: Vec<ArrangementSizeRecord>,
389 collection_ts: EpochMillis,
390 },
391 ArrangementSizesPrune(Vec<BuiltinTableUpdate>),
392 HydrationHistorySchedule,
393 HydrationHistoryRun,
394 CaughtUpCheck(caught_up::CaughtUpCheckRequest),
395 RetireExecute {
398 data: ExecuteContextExtra,
399 otel_ctx: OpenTelemetryContext,
400 reason: StatementEndedExecutionReason,
401 },
402 ExecuteSingleStatementTransaction {
403 ctx: ExecuteContext,
404 otel_ctx: OpenTelemetryContext,
405 stmt: Arc<Statement<Raw>>,
406 params: mz_sql::plan::Params,
407 },
408 CreateIndexStageReady {
409 ctx: ExecuteContext,
410 span: Span,
411 stage: CreateIndexStage,
412 },
413 CreateMetricSinkStageReady {
414 ctx: ExecuteContext,
415 span: Span,
416 stage: CreateMetricSinkStage,
417 },
418 CreateViewStageReady {
419 ctx: ExecuteContext,
420 span: Span,
421 stage: CreateViewStage,
422 },
423 CreateMaterializedViewStageReady {
424 ctx: ExecuteContext,
425 span: Span,
426 stage: CreateMaterializedViewStage,
427 },
428 SubscribeStageReady {
429 ctx: ExecuteContext,
430 span: Span,
431 stage: SubscribeStage,
432 },
433 IntrospectionSubscribeStageReady {
434 span: Span,
435 stage: IntrospectionSubscribeStage,
436 },
437 MetricSinkStageReady {
438 span: Span,
439 stage: MetricSinkStage,
440 },
441 SecretStageReady {
442 ctx: ExecuteContext,
443 span: Span,
444 stage: SecretStage,
445 },
446 ClusterStageReady {
447 ctx: ExecuteContext,
448 span: Span,
449 stage: ClusterStage,
450 },
451 ExplainTimestampStageReady {
452 ctx: ExecuteContext,
453 span: Span,
454 stage: ExplainTimestampStage,
455 },
456 DrainStatementLog,
457 PrivateLinkVpcEndpointEvents(Vec<VpcEndpointEvent>),
458
459 ClusterControllerRequest(cluster_controller::ClusterControllerRequest),
463}
464
465impl Message {
466 pub const fn kind(&self) -> &'static str {
468 match self {
469 Message::Command(_, msg) => match msg {
470 Command::CatalogSnapshot { .. } => "command-catalog_snapshot",
471 Command::Startup { .. } => "command-startup",
472 Command::Execute { .. } => "command-execute",
473 Command::Commit { .. } => "command-commit",
474 Command::CancelRequest { .. } => "command-cancel_request",
475 Command::PrivilegedCancelRequest { .. } => "command-privileged_cancel_request",
476 Command::GetWebhook { .. } => "command-get_webhook",
477 Command::GetSystemVars { .. } => "command-get_system_vars",
478 Command::SetSystemVars { .. } => "command-set_system_vars",
479 Command::UpdateScopedSystemParameters { .. } => {
480 "command-update_scoped_system_parameters"
481 }
482 Command::InstallScopedSystemParameterFrontend { .. } => {
483 "command-install_scoped_system_parameter_frontend"
484 }
485 Command::Terminate { .. } => "command-terminate",
486 Command::RetireExecute { .. } => "command-retire_execute",
487 Command::CheckConsistency { .. } => "command-check_consistency",
488 Command::Dump { .. } => "command-dump",
489 Command::AuthenticatePassword { .. } => "command-auth_check",
490 Command::AuthenticateGetSASLChallenge { .. } => "command-auth_get_sasl_challenge",
491 Command::AuthenticateVerifySASLProof { .. } => "command-auth_verify_sasl_proof",
492 Command::CheckRoleCanLogin { .. } => "command-check_role_can_login",
493 Command::GetComputeInstanceClient { .. } => "get-compute-instance-client",
494 Command::GetOracle { .. } => "get-oracle",
495 Command::DetermineRealTimeRecentTimestamp { .. } => {
496 "determine-real-time-recent-timestamp"
497 }
498 Command::GetTransactionReadHoldsBundle { .. } => {
499 "get-transaction-read-holds-bundle"
500 }
501 Command::StoreTransactionReadHolds { .. } => "store-transaction-read-holds",
502 Command::ExecuteSlowPathPeek { .. } => "execute-slow-path-peek",
503 Command::ExecuteSubscribe { .. } => "execute-subscribe",
504 Command::CopyToPreflight { .. } => "copy-to-preflight",
505 Command::ExecuteCopyTo { .. } => "execute-copy-to",
506 Command::ExecuteSideEffectingFunc { .. } => "execute-side-effecting-func",
507 Command::LookupConnection { .. } => "lookup-connection",
508 Command::RegisterFrontendPeek { .. } => "register-frontend-peek",
509 Command::UnregisterFrontendPeek { .. } => "unregister-frontend-peek",
510 Command::ExplainTimestamp { .. } => "explain-timestamp",
511 Command::FrontendStatementLogging(..) => "frontend-statement-logging",
512 Command::StartCopyFromStdin { .. } => "start-copy-from-stdin",
513 Command::InjectAuditEvents { .. } => "inject-audit-events",
514 Command::RegisterConnectionCancelWatch { .. } => "register-connection-cancel-watch",
515 Command::CreateInternalSubscribe { .. } => "create-internal-subscribe",
516 Command::AttemptWrite { .. } => "attempt-write",
517 Command::DropInternalSubscribe { .. } => "drop-internal-subscribe",
518 },
519 Message::ControllerReady {
520 controller: ControllerReadiness::Compute,
521 } => "controller_ready(compute)",
522 Message::ControllerReady {
523 controller: ControllerReadiness::Storage,
524 } => "controller_ready(storage)",
525 Message::ControllerReady {
526 controller: ControllerReadiness::Metrics,
527 } => "controller_ready(metrics)",
528 Message::ControllerReady {
529 controller: ControllerReadiness::Internal,
530 } => "controller_ready(internal)",
531 Message::PurifiedStatementReady(_) => "purified_statement_ready",
532 Message::CreateConnectionValidationReady(_) => "create_connection_validation_ready",
533 Message::DeferredPlanReady { .. } => "deferred_plan_ready",
534 Message::GroupCommitInitiate(..) => "group_commit_initiate",
535 Message::GroupCommitApplied { .. } => "group_commit_applied",
536 Message::AdvanceTimelines => "advance_timelines",
537 Message::ClusterEvent(_) => "cluster_event",
538 Message::LinearizeReads => "linearize_reads",
539 Message::StagedBatches { .. } => "staged_batches",
540 Message::StorageUsageSchedule => "storage_usage_schedule",
541 Message::StorageUsageFetch => "storage_usage_fetch",
542 Message::StorageUsageUpdate(_) => "storage_usage_update",
543 Message::StorageUsagePrune(_) => "storage_usage_prune",
544 Message::ArrangementSizesSchedule => "arrangement_sizes_schedule",
545 Message::ArrangementSizesSnapshot => "arrangement_sizes_snapshot",
546 Message::ArrangementSizesWrite { .. } => "arrangement_sizes_write",
547 Message::ArrangementSizesPrune(_) => "arrangement_sizes_prune",
548 Message::HydrationHistorySchedule => "hydration_history_schedule",
549 Message::HydrationHistoryRun => "hydration_history_run",
550 Message::CaughtUpCheck(_) => "caught_up_check",
551 Message::RetireExecute { .. } => "retire_execute",
552 Message::ExecuteSingleStatementTransaction { .. } => {
553 "execute_single_statement_transaction"
554 }
555 Message::ExplainTimestampStageReady { .. } => "explain_timestamp_stage_ready",
556 Message::CreateIndexStageReady { .. } => "create_index_stage_ready",
557 Message::CreateMetricSinkStageReady { .. } => "create_metric_sink_stage_ready",
558 Message::CreateViewStageReady { .. } => "create_view_stage_ready",
559 Message::CreateMaterializedViewStageReady { .. } => {
560 "create_materialized_view_stage_ready"
561 }
562 Message::SubscribeStageReady { .. } => "subscribe_stage_ready",
563 Message::IntrospectionSubscribeStageReady { .. } => {
564 "introspection_subscribe_stage_ready"
565 }
566 Message::MetricSinkStageReady { .. } => "metric_sink_stage_ready",
567 Message::SecretStageReady { .. } => "secret_stage_ready",
568 Message::ClusterStageReady { .. } => "cluster_stage_ready",
569 Message::DrainStatementLog => "drain_statement_log",
570 Message::AlterConnectionValidationReady(..) => "alter_connection_validation_ready",
571 Message::PrivateLinkVpcEndpointEvents(_) => "private_link_vpc_endpoint_events",
572 Message::ClusterControllerRequest(_) => "cluster_controller_request",
573 Message::DeferredStatementReady => "deferred_statement_ready",
574 }
575 }
576}
577
578#[derive(Debug)]
580pub enum ControllerReadiness {
581 Storage,
583 Compute,
585 Metrics,
587 Internal,
589}
590
591#[derive(Derivative)]
592#[derivative(Debug)]
593pub struct BackgroundWorkResult<T> {
594 #[derivative(Debug = "ignore")]
595 pub ctx: ExecuteContext,
596 pub result: Result<T, AdapterError>,
597 pub params: Params,
598 pub plan_validity: PlanValidity,
599 pub original_stmt: Arc<Statement<Raw>>,
600 pub otel_ctx: OpenTelemetryContext,
601}
602
603pub type PurifiedStatementReady = BackgroundWorkResult<mz_sql::pure::PurifiedStatement>;
604
605#[derive(Derivative)]
606#[derivative(Debug)]
607pub struct ValidationReady<T> {
608 #[derivative(Debug = "ignore")]
609 pub ctx: ExecuteContext,
610 pub result: Result<T, AdapterError>,
611 pub resolved_ids: ResolvedIds,
612 pub connection_id: CatalogItemId,
613 pub connection_gid: GlobalId,
614 pub plan_validity: PlanValidity,
615 pub otel_ctx: OpenTelemetryContext,
616}
617
618pub type CreateConnectionValidationReady = ValidationReady<CreateConnectionPlan>;
619pub type AlterConnectionValidationReady = ValidationReady<Connection>;
620
621#[derive(Debug)]
622pub struct CopyToContext {
623 pub desc: RelationDesc,
625 pub uri: Uri,
627 pub connection: StorageConnection<ReferencedConnection>,
629 pub connection_id: CatalogItemId,
631 pub format: S3SinkFormat,
633 pub max_file_size: u64,
635 pub output_batch_count: Option<u64>,
640}
641
642#[derive(Debug)]
643pub enum CreateIndexStage {
644 Optimize(CreateIndexOptimize),
645 Finish(CreateIndexFinish),
646 Explain(CreateIndexExplain),
647}
648
649#[derive(Debug)]
650pub struct CreateIndexOptimize {
651 validity: PlanValidity,
652 plan: plan::CreateIndexPlan,
653 resolved_ids: ResolvedIds,
654 explain_ctx: ExplainContext,
657}
658
659#[derive(Debug)]
660pub struct CreateIndexFinish {
661 validity: PlanValidity,
662 item_id: CatalogItemId,
663 global_id: GlobalId,
664 plan: plan::CreateIndexPlan,
665 resolved_ids: ResolvedIds,
666 global_mir_plan: optimize::index::GlobalMirPlan,
667 global_lir_plan: optimize::index::GlobalLirPlan,
668 optimizer_features: OptimizerFeatures,
669}
670
671#[derive(Debug)]
672pub struct CreateIndexExplain {
673 validity: PlanValidity,
674 exported_index_id: GlobalId,
675 plan: plan::CreateIndexPlan,
676 df_meta: DataflowMetainfo,
677 explain_ctx: ExplainPlanContext,
678}
679
680#[derive(Debug)]
681pub enum CreateMetricSinkStage {
682 Optimize(CreateMetricSinkOptimize),
683 Finish(CreateMetricSinkFinish),
684}
685
686#[derive(Debug)]
687pub struct CreateMetricSinkOptimize {
688 validity: PlanValidity,
689 plan: plan::CreateMetricSinkPlan,
690 resolved_ids: ResolvedIds,
691}
692
693#[derive(Debug)]
694pub struct CreateMetricSinkFinish {
695 validity: PlanValidity,
696 item_id: CatalogItemId,
697 global_id: GlobalId,
698 plan: plan::CreateMetricSinkPlan,
699 resolved_ids: ResolvedIds,
700 global_mir_plan: optimize::metric_sink::GlobalMirPlan,
701 global_lir_plan: optimize::metric_sink::GlobalLirPlan,
702 optimizer_features: OptimizerFeatures,
703}
704
705#[derive(Debug)]
706pub enum CreateViewStage {
707 Optimize(CreateViewOptimize),
708 Finish(CreateViewFinish),
709 Explain(CreateViewExplain),
710}
711
712#[derive(Debug)]
713pub struct CreateViewOptimize {
714 validity: PlanValidity,
715 plan: plan::CreateViewPlan,
716 resolved_ids: ResolvedIds,
717 explain_ctx: ExplainContext,
720}
721
722#[derive(Debug)]
723pub struct CreateViewFinish {
724 validity: PlanValidity,
725 item_id: CatalogItemId,
727 global_id: GlobalId,
729 plan: plan::CreateViewPlan,
730 resolved_ids: ResolvedIds,
732 optimized_expr: OptimizedMirRelationExpr,
733}
734
735#[derive(Debug)]
736pub struct CreateViewExplain {
737 validity: PlanValidity,
738 id: GlobalId,
739 plan: plan::CreateViewPlan,
740 explain_ctx: ExplainPlanContext,
741}
742
743#[derive(Debug)]
744pub enum ExplainTimestampStage {
745 Optimize(ExplainTimestampOptimize),
746 RealTimeRecency(ExplainTimestampRealTimeRecency),
747 LinearizeTimestamp(ExplainTimestampLinearizeTimestamp),
748 Finish(ExplainTimestampFinish),
749}
750
751#[derive(Debug)]
752pub struct ExplainTimestampOptimize {
753 validity: PlanValidity,
754 plan: plan::ExplainTimestampPlan,
755 cluster_id: ClusterId,
756}
757
758#[derive(Debug)]
759pub struct ExplainTimestampRealTimeRecency {
760 validity: PlanValidity,
761 format: ExplainFormat,
762 optimized_plan: OptimizedMirRelationExpr,
763 cluster_id: ClusterId,
764 when: QueryWhen,
765}
766
767#[derive(Debug)]
768pub struct ExplainTimestampLinearizeTimestamp {
769 validity: PlanValidity,
770 format: ExplainFormat,
771 optimized_plan: OptimizedMirRelationExpr,
772 cluster_id: ClusterId,
773 source_ids: BTreeSet<GlobalId>,
774 when: QueryWhen,
775 real_time_recency_ts: Option<Timestamp>,
776}
777
778#[derive(Debug)]
779pub struct ExplainTimestampFinish {
780 validity: PlanValidity,
781 format: ExplainFormat,
782 cluster_id: ClusterId,
783 source_ids: BTreeSet<GlobalId>,
784 when: QueryWhen,
785 real_time_recency_ts: Option<Timestamp>,
786 timeline_context: TimelineContext,
789 oracle_read_ts: Option<Timestamp>,
793}
794
795#[derive(Debug)]
796pub enum ClusterStage {
797 Alter(AlterCluster),
798 AwaitReconfiguration(AlterClusterAwaitReconfiguration),
803}
804
805#[derive(Debug)]
806pub struct AlterCluster {
807 validity: PlanValidity,
808 plan: plan::AlterClusterPlan,
809}
810
811#[derive(Debug)]
812pub struct AlterClusterAwaitReconfiguration {
813 validity: PlanValidity,
814 cluster_id: ClusterId,
815 target: ReconfigurationTarget,
819}
820
821#[derive(Debug)]
822pub enum ExplainContext {
823 None,
825 Plan(ExplainPlanContext),
827 PlanInsightsNotice(OptimizerTrace),
830 Pushdown,
832}
833
834impl ExplainContext {
835 pub(crate) fn dispatch_guard(&self) -> Option<DispatchGuard<'_>> {
839 let optimizer_trace = match self {
840 ExplainContext::Plan(explain_ctx) => Some(&explain_ctx.optimizer_trace),
841 ExplainContext::PlanInsightsNotice(optimizer_trace) => Some(optimizer_trace),
842 _ => None,
843 };
844 optimizer_trace.map(|optimizer_trace| optimizer_trace.as_guard())
845 }
846
847 pub(crate) fn needs_cluster(&self) -> bool {
848 match self {
849 ExplainContext::None => true,
850 ExplainContext::Plan(..) => false,
851 ExplainContext::PlanInsightsNotice(..) => true,
852 ExplainContext::Pushdown => false,
853 }
854 }
855
856 pub(crate) fn needs_plan_insights(&self) -> bool {
857 matches!(
858 self,
859 ExplainContext::Plan(ExplainPlanContext {
860 stage: ExplainStage::PlanInsights,
861 ..
862 }) | ExplainContext::PlanInsightsNotice(_)
863 )
864 }
865}
866
867#[derive(Debug)]
868pub struct ExplainPlanContext {
869 pub broken: bool,
874 pub config: ExplainConfig,
875 pub format: ExplainFormat,
876 pub stage: ExplainStage,
877 pub replan: Option<GlobalId>,
878 pub desc: Option<RelationDesc>,
879 pub optimizer_trace: OptimizerTrace,
880}
881
882#[derive(Debug)]
883pub enum CreateMaterializedViewStage {
884 Optimize(CreateMaterializedViewOptimize),
885 Finish(CreateMaterializedViewFinish),
886 Explain(CreateMaterializedViewExplain),
887}
888
889#[derive(Debug)]
890pub struct CreateMaterializedViewOptimize {
891 validity: PlanValidity,
892 plan: plan::CreateMaterializedViewPlan,
893 resolved_ids: ResolvedIds,
894 explain_ctx: ExplainContext,
897}
898
899#[derive(Debug)]
900pub struct CreateMaterializedViewFinish {
901 item_id: CatalogItemId,
903 global_id: GlobalId,
905 validity: PlanValidity,
906 plan: plan::CreateMaterializedViewPlan,
907 resolved_ids: ResolvedIds,
908 local_mir_plan: optimize::materialized_view::LocalMirPlan,
909 global_mir_plan: optimize::materialized_view::GlobalMirPlan,
910 global_lir_plan: optimize::materialized_view::GlobalLirPlan,
911 optimizer_features: OptimizerFeatures,
912}
913
914#[derive(Debug)]
915pub struct CreateMaterializedViewExplain {
916 global_id: GlobalId,
917 validity: PlanValidity,
918 plan: plan::CreateMaterializedViewPlan,
919 df_meta: DataflowMetainfo,
920 explain_ctx: ExplainPlanContext,
921}
922
923#[derive(Debug)]
924pub enum SubscribeStage {
925 OptimizeMir(SubscribeOptimizeMir),
926 LinearizeTimestamp(SubscribeLinearizeTimestamp),
927 TimestampOptimizeLir(SubscribeTimestampOptimizeLir),
928 Finish(SubscribeFinish),
929 Explain(SubscribeExplain),
930}
931
932#[derive(Debug)]
933pub struct SubscribeOptimizeMir {
934 validity: PlanValidity,
935 plan: plan::SubscribePlan,
936 timeline: TimelineContext,
937 dependency_ids: BTreeSet<GlobalId>,
938 cluster_id: ComputeInstanceId,
939 replica_id: Option<ReplicaId>,
940 explain_ctx: ExplainContext,
943}
944
945#[derive(Debug)]
946pub struct SubscribeLinearizeTimestamp {
947 validity: PlanValidity,
948 plan: plan::SubscribePlan,
949 timeline: TimelineContext,
950 optimizer: optimize::subscribe::Optimizer,
951 global_mir_plan: optimize::subscribe::GlobalMirPlan<optimize::subscribe::Unresolved>,
952 dependency_ids: BTreeSet<GlobalId>,
953 replica_id: Option<ReplicaId>,
954 explain_ctx: ExplainContext,
957}
958
959#[derive(Debug)]
960pub struct SubscribeTimestampOptimizeLir {
961 validity: PlanValidity,
962 plan: plan::SubscribePlan,
963 timeline: TimelineContext,
964 optimizer: optimize::subscribe::Optimizer,
965 global_mir_plan: optimize::subscribe::GlobalMirPlan<optimize::subscribe::Unresolved>,
966 dependency_ids: BTreeSet<GlobalId>,
967 replica_id: Option<ReplicaId>,
968 oracle_read_ts: Option<Timestamp>,
972 explain_ctx: ExplainContext,
975}
976
977#[derive(Debug)]
978pub struct SubscribeFinish {
979 validity: PlanValidity,
980 cluster_id: ComputeInstanceId,
981 replica_id: Option<ReplicaId>,
982 plan: plan::SubscribePlan,
983 global_lir_plan: optimize::subscribe::GlobalLirPlan,
984 dependency_ids: BTreeSet<GlobalId>,
985}
986
987#[derive(Debug)]
988pub struct SubscribeExplain {
989 validity: PlanValidity,
990 optimizer: optimize::subscribe::Optimizer,
991 df_meta: DataflowMetainfo,
992 cluster_id: ComputeInstanceId,
993 explain_ctx: ExplainPlanContext,
994}
995
996#[derive(Debug)]
997pub enum IntrospectionSubscribeStage {
998 OptimizeMir(IntrospectionSubscribeOptimizeMir),
999 TimestampOptimizeLir(IntrospectionSubscribeTimestampOptimizeLir),
1000 Finish(IntrospectionSubscribeFinish),
1001}
1002
1003#[derive(Debug)]
1004pub struct IntrospectionSubscribeOptimizeMir {
1005 validity: PlanValidity,
1006 plan: plan::SubscribePlan,
1007 subscribe_id: GlobalId,
1008 cluster_id: ComputeInstanceId,
1009 replica_id: ReplicaId,
1010}
1011
1012#[derive(Debug)]
1013pub struct IntrospectionSubscribeTimestampOptimizeLir {
1014 validity: PlanValidity,
1015 optimizer: optimize::subscribe::Optimizer,
1016 global_mir_plan: optimize::subscribe::GlobalMirPlan<optimize::subscribe::Unresolved>,
1017 cluster_id: ComputeInstanceId,
1018 replica_id: ReplicaId,
1019}
1020
1021#[derive(Debug)]
1022pub struct IntrospectionSubscribeFinish {
1023 validity: PlanValidity,
1024 global_lir_plan: optimize::subscribe::GlobalLirPlan,
1025 read_holds: ReadHolds,
1026 cluster_id: ComputeInstanceId,
1027 replica_id: ReplicaId,
1028}
1029
1030#[derive(Debug)]
1031pub enum MetricSinkStage {
1032 Optimize(MetricSinkOptimize),
1033 Finish(MetricSinkFinish),
1034}
1035
1036#[derive(Debug)]
1037pub struct MetricSinkOptimize {
1038 validity: PlanValidity,
1039 definition: &'static CuratedMetricSink,
1040 sink_id: GlobalId,
1043 expr: HirRelationExpr,
1045 desc: RelationDesc,
1046 cluster_id: ComputeInstanceId,
1047 replica_id: ReplicaId,
1048}
1049
1050#[derive(Debug)]
1051pub struct MetricSinkFinish {
1052 validity: PlanValidity,
1053 definition: &'static CuratedMetricSink,
1054 sink_id: GlobalId,
1055 global_lir_plan: optimize::metric_sink::GlobalLirPlan,
1056 cluster_id: ComputeInstanceId,
1057 replica_id: ReplicaId,
1058}
1059
1060#[derive(Debug)]
1061pub enum SecretStage {
1062 CreateEnsure(CreateSecretEnsure),
1063 CreateFinish(CreateSecretFinish),
1064 RotateKeysEnsure(RotateKeysSecretEnsure),
1065 RotateKeysFinish(RotateKeysSecretFinish),
1066 Alter(AlterSecret),
1067}
1068
1069#[derive(Debug)]
1070pub struct CreateSecretEnsure {
1071 validity: PlanValidity,
1072 plan: plan::CreateSecretPlan,
1073}
1074
1075#[derive(Debug)]
1076pub struct CreateSecretFinish {
1077 validity: PlanValidity,
1078 item_id: CatalogItemId,
1079 global_id: GlobalId,
1080 plan: plan::CreateSecretPlan,
1081}
1082
1083#[derive(Debug)]
1084pub struct RotateKeysSecretEnsure {
1085 validity: PlanValidity,
1086 id: CatalogItemId,
1087}
1088
1089#[derive(Debug)]
1090pub struct RotateKeysSecretFinish {
1091 validity: PlanValidity,
1092 ops: Vec<crate::catalog::Op>,
1093}
1094
1095#[derive(Debug)]
1096pub struct AlterSecret {
1097 validity: PlanValidity,
1098 plan: plan::AlterSecretPlan,
1099}
1100
1101#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1106pub enum TargetCluster {
1107 CatalogServer,
1109 Active,
1111 Transaction(ClusterId),
1113}
1114
1115pub(crate) enum StageResult<T> {
1117 Handle(JoinHandle<Result<T, AdapterError>>),
1119 HandleRetire(JoinHandle<Result<ExecuteResponse, AdapterError>>),
1121 Immediate(T),
1123 Response(ExecuteResponse),
1125}
1126
1127pub(crate) trait Staged: Send {
1129 type Ctx: StagedContext;
1130
1131 fn validity(&mut self) -> &mut PlanValidity;
1132
1133 async fn stage(
1135 self,
1136 coord: &mut Coordinator,
1137 ctx: &mut Self::Ctx,
1138 ) -> Result<StageResult<Box<Self>>, AdapterError>;
1139
1140 fn message(self, ctx: Self::Ctx, span: Span) -> Message;
1142
1143 fn cancel_enabled(&self) -> bool;
1145}
1146
1147pub trait StagedContext {
1148 fn retire(self, result: Result<ExecuteResponse, AdapterError>);
1149 fn session(&self) -> Option<&Session>;
1150}
1151
1152impl StagedContext for ExecuteContext {
1153 fn retire(self, result: Result<ExecuteResponse, AdapterError>) {
1154 self.retire(result);
1155 }
1156
1157 fn session(&self) -> Option<&Session> {
1158 Some(self.session())
1159 }
1160}
1161
1162impl StagedContext for () {
1163 fn retire(self, _result: Result<ExecuteResponse, AdapterError>) {}
1164
1165 fn session(&self) -> Option<&Session> {
1166 None
1167 }
1168}
1169
1170pub struct Config {
1172 pub controller_config: ControllerConfig,
1173 pub controller_envd_epoch: NonZeroI64,
1174 pub storage: Box<dyn mz_catalog::durable::DurableCatalogState>,
1175 pub timestamp_oracle_url: Option<SensitiveUrl>,
1176 pub unsafe_mode: bool,
1177 pub all_features: bool,
1178 pub build_info: &'static BuildInfo,
1179 pub environment_id: EnvironmentId,
1180 pub metrics_registry: MetricsRegistry,
1181 pub now: NowFn,
1182 pub secrets_controller: Arc<dyn SecretsController>,
1183 pub cloud_resource_controller: Option<Arc<dyn CloudResourceController>>,
1184 pub availability_zones: Vec<String>,
1185 pub cluster_replica_sizes: ClusterReplicaSizeMap,
1186 pub builtin_system_cluster_config: BootstrapBuiltinClusterConfig,
1187 pub builtin_catalog_server_cluster_config: BootstrapBuiltinClusterConfig,
1188 pub builtin_probe_cluster_config: BootstrapBuiltinClusterConfig,
1189 pub builtin_support_cluster_config: BootstrapBuiltinClusterConfig,
1190 pub builtin_analytics_cluster_config: BootstrapBuiltinClusterConfig,
1191 pub system_parameter_defaults: BTreeMap<String, String>,
1192 pub storage_usage_client: StorageUsageClient,
1193 pub storage_usage_collection_interval: Duration,
1194 pub storage_usage_retention_period: Option<Duration>,
1195 pub segment_client: Option<mz_segment::Client>,
1196 pub egress_addresses: Vec<IpNet>,
1197 pub remote_system_parameters: Option<BTreeMap<String, String>>,
1198 pub aws_account_id: Option<String>,
1199 pub aws_privatelink_availability_zones: Option<Vec<String>>,
1200 pub connection_context: ConnectionContext,
1201 pub connection_limit_callback: Box<dyn Fn(u64, u64) -> () + Send + Sync + 'static>,
1202 pub webhook_concurrency_limit: WebhookConcurrencyLimiter,
1203 pub http_host_name: Option<String>,
1204 pub tracing_handle: TracingHandle,
1205 pub read_only_controllers: bool,
1209
1210 pub caught_up_trigger: Option<Trigger>,
1214
1215 pub helm_chart_version: Option<String>,
1216 pub license_key: ValidatedLicenseKey,
1217 pub external_login_password_mz_system: Option<Password>,
1218 pub force_builtin_schema_migration: Option<String>,
1219}
1220
1221#[derive(Debug, Serialize)]
1223pub struct ConnMeta {
1224 secret_key: u32,
1229 connected_at: EpochMillis,
1231 user: User,
1232 application_name: String,
1233 uuid: Uuid,
1234 conn_id: ConnectionId,
1235 client_ip: Option<IpAddr>,
1236
1237 drop_sinks: BTreeSet<GlobalId>,
1240
1241 #[serde(skip)]
1243 deferred_lock: Option<OwnedMutexGuard<()>>,
1244
1245 #[serde(skip)]
1247 notice_tx: mpsc::UnboundedSender<AdapterNotice>,
1248
1249 authenticated_role: RoleId,
1253}
1254
1255impl ConnMeta {
1256 pub fn conn_id(&self) -> &ConnectionId {
1257 &self.conn_id
1258 }
1259
1260 pub fn user(&self) -> &User {
1261 &self.user
1262 }
1263
1264 pub fn application_name(&self) -> &str {
1265 &self.application_name
1266 }
1267
1268 pub fn authenticated_role_id(&self) -> &RoleId {
1269 &self.authenticated_role
1270 }
1271
1272 pub fn uuid(&self) -> Uuid {
1273 self.uuid
1274 }
1275
1276 pub fn client_ip(&self) -> Option<IpAddr> {
1277 self.client_ip
1278 }
1279
1280 pub fn connected_at(&self) -> EpochMillis {
1281 self.connected_at
1282 }
1283}
1284
1285#[derive(Debug)]
1286pub struct PendingTxn {
1288 ctx: ExecuteContext,
1290 response: Result<PendingTxnResponse, AdapterError>,
1292 action: EndTransactionAction,
1294}
1295
1296#[derive(Debug)]
1297pub enum PendingTxnResponse {
1299 Committed {
1301 params: BTreeMap<&'static str, String>,
1303 },
1304 Rolledback {
1306 params: BTreeMap<&'static str, String>,
1308 },
1309}
1310
1311impl PendingTxnResponse {
1312 pub fn extend_params(&mut self, p: impl IntoIterator<Item = (&'static str, String)>) {
1313 match self {
1314 PendingTxnResponse::Committed { params }
1315 | PendingTxnResponse::Rolledback { params } => params.extend(p),
1316 }
1317 }
1318}
1319
1320impl From<PendingTxnResponse> for ExecuteResponse {
1321 fn from(value: PendingTxnResponse) -> Self {
1322 match value {
1323 PendingTxnResponse::Committed { params } => {
1324 ExecuteResponse::TransactionCommitted { params }
1325 }
1326 PendingTxnResponse::Rolledback { params } => {
1327 ExecuteResponse::TransactionRolledBack { params }
1328 }
1329 }
1330 }
1331}
1332
1333#[derive(Debug)]
1334pub struct PendingReadTxn {
1336 txn: PendingTxn,
1337 timestamp_context: TimestampContext,
1339 created: Instant,
1341 num_requeues: u64,
1345 otel_ctx: OpenTelemetryContext,
1347}
1348
1349impl PendingReadTxn {
1350 pub fn timestamp_context(&self) -> &TimestampContext {
1352 &self.timestamp_context
1353 }
1354
1355 pub(crate) fn take_context(self) -> ExecuteContext {
1356 self.txn.ctx
1357 }
1358
1359 #[instrument(level = "debug")]
1361 pub fn finish(self) {
1362 let PendingTxn {
1363 mut ctx,
1364 response,
1365 action,
1366 } = self.txn;
1367 let changed = ctx.session_mut().vars_mut().end_transaction(action);
1368 let response = response.map(|mut r| {
1369 r.extend_params(changed);
1370 ExecuteResponse::from(r)
1371 });
1372 ctx.retire(response);
1373 }
1374}
1375
1376#[derive(Debug, Default)]
1386#[must_use]
1387pub struct ExecuteContextExtra {
1388 statement_uuid: Option<StatementLoggingId>,
1389}
1390
1391impl ExecuteContextExtra {
1392 pub(crate) fn new(statement_uuid: Option<StatementLoggingId>) -> Self {
1393 Self { statement_uuid }
1394 }
1395 pub fn is_trivial(&self) -> bool {
1396 self.statement_uuid.is_none()
1397 }
1398 pub fn contents(&self) -> Option<StatementLoggingId> {
1399 self.statement_uuid
1400 }
1401 #[must_use]
1405 pub(crate) fn retire(self) -> Option<StatementLoggingId> {
1406 self.statement_uuid
1407 }
1408}
1409
1410#[derive(Debug)]
1420#[must_use]
1421pub struct ExecuteContextGuard {
1422 extra: ExecuteContextExtra,
1423 coordinator_tx: mpsc::UnboundedSender<Message>,
1428}
1429
1430impl Default for ExecuteContextGuard {
1431 fn default() -> Self {
1432 let (tx, _rx) = mpsc::unbounded_channel();
1436 Self {
1437 extra: ExecuteContextExtra::default(),
1438 coordinator_tx: tx,
1439 }
1440 }
1441}
1442
1443impl ExecuteContextGuard {
1444 pub(crate) fn new(
1445 statement_uuid: Option<StatementLoggingId>,
1446 coordinator_tx: mpsc::UnboundedSender<Message>,
1447 ) -> Self {
1448 Self {
1449 extra: ExecuteContextExtra::new(statement_uuid),
1450 coordinator_tx,
1451 }
1452 }
1453 pub fn is_trivial(&self) -> bool {
1454 self.extra.is_trivial()
1455 }
1456 pub fn contents(&self) -> Option<StatementLoggingId> {
1457 self.extra.contents()
1458 }
1459 pub(crate) fn defuse(mut self) -> ExecuteContextExtra {
1466 std::mem::take(&mut self.extra)
1468 }
1469}
1470
1471impl Drop for ExecuteContextGuard {
1472 fn drop(&mut self) {
1473 if let Some(statement_uuid) = self.extra.statement_uuid.take() {
1474 let msg = Message::RetireExecute {
1477 data: ExecuteContextExtra {
1478 statement_uuid: Some(statement_uuid),
1479 },
1480 otel_ctx: OpenTelemetryContext::obtain(),
1481 reason: StatementEndedExecutionReason::Aborted,
1482 };
1483 let _ = self.coordinator_tx.send(msg);
1486 }
1487 }
1488}
1489
1490#[derive(Debug)]
1495pub struct ExecuteContext {
1496 inner: Option<Box<ExecuteContextInner>>,
1498}
1499
1500impl std::ops::Deref for ExecuteContext {
1501 type Target = ExecuteContextInner;
1502 fn deref(&self) -> &Self::Target {
1503 self.inner.as_ref().expect("only consumed by value")
1504 }
1505}
1506
1507impl std::ops::DerefMut for ExecuteContext {
1508 fn deref_mut(&mut self) -> &mut Self::Target {
1509 self.inner.as_mut().expect("only consumed by value")
1510 }
1511}
1512
1513impl Drop for ExecuteContext {
1514 fn drop(&mut self) {
1515 let Some(inner) = self.inner.take() else {
1516 return;
1517 };
1518 tracing::warn!("execute context dropped without retirement, failing the client");
1521 let ExecuteContextInner { tx, session, .. } = *inner;
1522 tx.send(
1523 Err(AdapterError::Internal(
1524 "statement execution abandoned, outcome unknown (server shutting down)".into(),
1525 )),
1526 session,
1527 );
1528 }
1529}
1530
1531#[derive(Derivative)]
1532#[derivative(Debug)]
1533pub struct ExecuteContextInner {
1534 tx: ClientTransmitter<ExecuteResponse>,
1535 internal_cmd_tx: mpsc::UnboundedSender<Message>,
1536 session: Session,
1537 extra: ExecuteContextGuard,
1538 #[derivative(Debug = "ignore")]
1539 response_barriers: Vec<BuiltinTableAppendNotify>,
1540}
1541
1542impl ExecuteContext {
1543 pub fn session(&self) -> &Session {
1544 &self.session
1545 }
1546
1547 pub fn session_mut(&mut self) -> &mut Session {
1548 &mut self.session
1549 }
1550
1551 pub fn tx(&self) -> &ClientTransmitter<ExecuteResponse> {
1552 &self.tx
1553 }
1554
1555 pub fn tx_mut(&mut self) -> &mut ClientTransmitter<ExecuteResponse> {
1556 &mut self.tx
1557 }
1558
1559 pub fn from_parts(
1560 tx: ClientTransmitter<ExecuteResponse>,
1561 internal_cmd_tx: mpsc::UnboundedSender<Message>,
1562 session: Session,
1563 extra: ExecuteContextGuard,
1564 ) -> Self {
1565 Self::from_parts_with_response_barriers(tx, internal_cmd_tx, session, extra, Vec::new())
1566 }
1567
1568 pub fn from_parts_with_response_barriers(
1569 tx: ClientTransmitter<ExecuteResponse>,
1570 internal_cmd_tx: mpsc::UnboundedSender<Message>,
1571 session: Session,
1572 extra: ExecuteContextGuard,
1573 response_barriers: Vec<BuiltinTableAppendNotify>,
1574 ) -> Self {
1575 Self {
1576 inner: Some(
1577 ExecuteContextInner {
1578 tx,
1579 session,
1580 extra,
1581 response_barriers,
1582 internal_cmd_tx,
1583 }
1584 .into(),
1585 ),
1586 }
1587 }
1588
1589 pub fn into_parts(
1603 mut self,
1604 ) -> (
1605 ClientTransmitter<ExecuteResponse>,
1606 mpsc::UnboundedSender<Message>,
1607 Session,
1608 ExecuteContextGuard,
1609 Vec<BuiltinTableAppendNotify>,
1610 ) {
1611 let ExecuteContextInner {
1612 tx,
1613 internal_cmd_tx,
1614 session,
1615 extra,
1616 response_barriers,
1617 } = *self.inner.take().expect("only consumed by value");
1618 (tx, internal_cmd_tx, session, extra, response_barriers)
1619 }
1620
1621 #[instrument(level = "debug")]
1623 pub fn retire(mut self, result: Result<ExecuteResponse, AdapterError>) {
1624 let response_barriers = std::mem::take(&mut self.response_barriers);
1625 if response_barriers.is_empty() {
1626 let (tx, internal_cmd_tx, session, extra, _) = self.into_parts();
1627 retire_execution_context(tx, internal_cmd_tx, session, extra, result);
1628 return;
1629 }
1630 spawn(
1633 || "execute_context::retire_after_response_barriers",
1634 async move {
1635 for barrier in response_barriers {
1636 barrier.await;
1637 }
1638 self.retire(result);
1639 },
1640 );
1641 }
1642
1643 pub(crate) fn delay_response_until(&mut self, barrier: BuiltinTableAppendCompletion) {
1645 self.response_barriers.push(barrier.into_notify());
1646 }
1647
1648 pub fn extra(&self) -> &ExecuteContextGuard {
1649 &self.extra
1650 }
1651
1652 pub fn extra_mut(&mut self) -> &mut ExecuteContextGuard {
1653 &mut self.extra
1654 }
1655}
1656
1657fn retire_execution_context(
1658 tx: ClientTransmitter<ExecuteResponse>,
1659 internal_cmd_tx: mpsc::UnboundedSender<Message>,
1660 session: Session,
1661 extra: ExecuteContextGuard,
1662 result: Result<ExecuteResponse, AdapterError>,
1663) {
1664 let reason = if extra.is_trivial() {
1665 None
1666 } else {
1667 Some((&result).into())
1668 };
1669 tx.send(result, session);
1670 if let Some(reason) = reason {
1671 let extra = extra.defuse();
1672 if let Err(e) = internal_cmd_tx.send(Message::RetireExecute {
1673 otel_ctx: OpenTelemetryContext::obtain(),
1674 data: extra,
1675 reason,
1676 }) {
1677 warn!("internal_cmd_rx dropped before we could send: {:?}", e);
1678 }
1679 }
1680}
1681
1682#[derive(Debug)]
1683struct ClusterReplicaStatuses(
1684 BTreeMap<ClusterId, BTreeMap<ReplicaId, BTreeMap<ProcessId, ClusterReplicaProcessStatus>>>,
1685);
1686
1687impl ClusterReplicaStatuses {
1688 pub(crate) fn new() -> ClusterReplicaStatuses {
1689 ClusterReplicaStatuses(BTreeMap::new())
1690 }
1691
1692 pub(crate) fn initialize_cluster_statuses(&mut self, cluster_id: ClusterId) {
1696 let prev = self.0.insert(cluster_id, BTreeMap::new());
1697 assert_eq!(
1698 prev, None,
1699 "cluster {cluster_id} statuses already initialized"
1700 );
1701 }
1702
1703 pub(crate) fn initialize_cluster_replica_statuses(
1707 &mut self,
1708 cluster_id: ClusterId,
1709 replica_id: ReplicaId,
1710 num_processes: usize,
1711 time: DateTime<Utc>,
1712 ) {
1713 tracing::info!(
1714 ?cluster_id,
1715 ?replica_id,
1716 ?time,
1717 "initializing cluster replica status"
1718 );
1719 let replica_statuses = self.0.entry(cluster_id).or_default();
1720 let process_statuses = (0..num_processes)
1721 .map(|process_id| {
1722 let status = ClusterReplicaProcessStatus {
1723 status: ClusterStatus::Offline(Some(OfflineReason::Initializing)),
1724 restart_count: 0,
1725 time: time.clone(),
1726 };
1727 (u64::cast_from(process_id), status)
1728 })
1729 .collect();
1730 let prev = replica_statuses.insert(replica_id, process_statuses);
1731 assert_none!(
1732 prev,
1733 "cluster replica {cluster_id}.{replica_id} statuses already initialized"
1734 );
1735 }
1736
1737 pub(crate) fn remove_cluster_statuses(
1741 &mut self,
1742 cluster_id: &ClusterId,
1743 ) -> BTreeMap<ReplicaId, BTreeMap<ProcessId, ClusterReplicaProcessStatus>> {
1744 let prev = self.0.remove(cluster_id);
1745 prev.unwrap_or_else(|| panic!("unknown cluster: {cluster_id}"))
1746 }
1747
1748 pub(crate) fn remove_cluster_replica_statuses(
1752 &mut self,
1753 cluster_id: &ClusterId,
1754 replica_id: &ReplicaId,
1755 ) -> BTreeMap<ProcessId, ClusterReplicaProcessStatus> {
1756 let replica_statuses = self
1757 .0
1758 .get_mut(cluster_id)
1759 .unwrap_or_else(|| panic!("unknown cluster: {cluster_id}"));
1760 let prev = replica_statuses.remove(replica_id);
1761 prev.unwrap_or_else(|| panic!("unknown cluster replica: {cluster_id}.{replica_id}"))
1762 }
1763
1764 pub(crate) fn ensure_cluster_status(
1768 &mut self,
1769 cluster_id: ClusterId,
1770 replica_id: ReplicaId,
1771 process_id: ProcessId,
1772 status: ClusterReplicaProcessStatus,
1773 ) {
1774 let replica_statuses = self
1775 .0
1776 .get_mut(&cluster_id)
1777 .unwrap_or_else(|| panic!("unknown cluster: {cluster_id}"))
1778 .get_mut(&replica_id)
1779 .unwrap_or_else(|| panic!("unknown cluster replica: {cluster_id}.{replica_id}"));
1780 replica_statuses.insert(process_id, status);
1781 }
1782
1783 pub fn get_cluster_replica_status(
1787 &self,
1788 cluster_id: ClusterId,
1789 replica_id: ReplicaId,
1790 ) -> ClusterStatus {
1791 let process_status = self.get_cluster_replica_statuses(cluster_id, replica_id);
1792 Self::cluster_replica_status(process_status)
1793 }
1794
1795 pub fn cluster_replica_status(
1797 process_status: &BTreeMap<ProcessId, ClusterReplicaProcessStatus>,
1798 ) -> ClusterStatus {
1799 process_status
1800 .values()
1801 .fold(ClusterStatus::Online, |s, p| match (s, p.status) {
1802 (ClusterStatus::Online, ClusterStatus::Online) => ClusterStatus::Online,
1803 (x, y) => {
1804 let reason_x = match x {
1805 ClusterStatus::Offline(reason) => reason,
1806 ClusterStatus::Online => None,
1807 };
1808 let reason_y = match y {
1809 ClusterStatus::Offline(reason) => reason,
1810 ClusterStatus::Online => None,
1811 };
1812 ClusterStatus::Offline(reason_x.or(reason_y))
1814 }
1815 })
1816 }
1817
1818 pub(crate) fn get_cluster_replica_statuses(
1822 &self,
1823 cluster_id: ClusterId,
1824 replica_id: ReplicaId,
1825 ) -> &BTreeMap<ProcessId, ClusterReplicaProcessStatus> {
1826 self.try_get_cluster_replica_statuses(cluster_id, replica_id)
1827 .unwrap_or_else(|| panic!("unknown cluster replica: {cluster_id}.{replica_id}"))
1828 }
1829
1830 pub(crate) fn try_get_cluster_replica_statuses(
1832 &self,
1833 cluster_id: ClusterId,
1834 replica_id: ReplicaId,
1835 ) -> Option<&BTreeMap<ProcessId, ClusterReplicaProcessStatus>> {
1836 self.try_get_cluster_statuses(cluster_id)
1837 .and_then(|statuses| statuses.get(&replica_id))
1838 }
1839
1840 pub(crate) fn try_get_cluster_statuses(
1842 &self,
1843 cluster_id: ClusterId,
1844 ) -> Option<&BTreeMap<ReplicaId, BTreeMap<ProcessId, ClusterReplicaProcessStatus>>> {
1845 self.0.get(&cluster_id)
1846 }
1847}
1848
1849#[derive(Derivative)]
1851#[derivative(Debug)]
1852pub struct Coordinator {
1853 #[derivative(Debug = "ignore")]
1855 controller: mz_controller::Controller,
1856 catalog: Arc<Catalog>,
1864
1865 persist_client: PersistClient,
1868
1869 internal_cmd_tx: mpsc::UnboundedSender<Message>,
1871 group_commit_tx: appends::GroupCommitNotifier,
1873 reconcile_now: Arc<Notify>,
1877 group_committer_tx: mpsc::UnboundedSender<appends::TableWriteCmd>,
1878
1879 strict_serializable_reads_tx: mpsc::UnboundedSender<(ConnectionId, PendingReadTxn)>,
1881
1882 linearize_reads_notify: Arc<Notify>,
1886
1887 global_timelines: BTreeMap<Timeline, TimelineState>,
1890
1891 transient_id_gen: Arc<TransientIdGen>,
1893 active_conns: BTreeMap<ConnectionId, ConnMeta>,
1896
1897 txn_read_holds: BTreeMap<ConnectionId, read_policy::ReadHolds>,
1901
1902 pending_peeks: BTreeMap<Uuid, PendingPeek>,
1906 client_pending_peeks: BTreeMap<ConnectionId, BTreeMap<Uuid, ClusterId>>,
1908
1909 pending_linearize_read_txns: BTreeMap<ConnectionId, PendingReadTxn>,
1911
1912 active_compute_sinks: BTreeMap<GlobalId, ActiveComputeSink>,
1914 active_webhooks: BTreeMap<CatalogItemId, WebhookAppenderInvalidator>,
1916 active_copies: BTreeMap<ConnectionId, ActiveCopyFrom>,
1919
1920 connection_cancel_watches: BTreeMap<ConnectionId, (watch::Sender<bool>, watch::Receiver<bool>)>,
1931 introspection_subscribes: BTreeMap<GlobalId, IntrospectionSubscribe>,
1933 hydration_history_replica_cursor: Option<ReplicaId>,
1935 hydration_history_sweep: Option<AbortOnDropHandle<()>>,
1937 metric_sinks: BTreeMap<(ReplicaId, &'static str), InstalledMetricSink>,
1942 metric_sink_plans: BTreeMap<&'static str, PlannedMetricSink>,
1945
1946 deferred_plans: BTreeMap<ConnectionId, DeferredPlan>,
1948
1949 pending_writes: Vec<PendingWriteTxn>,
1951
1952 occ_write_semaphore: Arc<Semaphore>,
1963
1964 advance_timelines_interval: Interval,
1974
1975 serialized_ddl: LockedVecDeque<DeferredPlanStatement>,
1984
1985 secrets_controller: Arc<dyn SecretsController>,
1988 caching_secrets_reader: CachingSecretsReader,
1990
1991 cloud_resource_controller: Option<Arc<dyn CloudResourceController>>,
1994
1995 storage_usage_client: StorageUsageClient,
1997 storage_usage_collection_interval: Duration,
1999
2000 #[derivative(Debug = "ignore")]
2002 segment_client: Option<mz_segment::Client>,
2003
2004 metrics: Metrics,
2006 optimizer_metrics: OptimizerMetrics,
2008
2009 tracing_handle: TracingHandle,
2011
2012 statement_logging: StatementLogging,
2014
2015 webhook_concurrency_limit: WebhookConcurrencyLimiter,
2017
2018 timestamp_oracle_config: Option<TimestampOracleConfig>,
2021
2022 caught_up_check: Option<CaughtUpCheckContext>,
2026
2027 catalog_info_metrics_registry: MetricsRegistry,
2030
2031 scoped_frontend: Option<Arc<SystemParameterFrontend>>,
2040
2041 installed_watch_sets: BTreeMap<WatchSetId, (ConnectionId, WatchSetResponse)>,
2043
2044 connection_watch_sets: BTreeMap<ConnectionId, BTreeSet<WatchSetId>>,
2046
2047 cluster_replica_statuses: ClusterReplicaStatuses,
2049
2050 read_only_controllers: bool,
2054
2055 buffered_builtin_table_updates: Option<Vec<BuiltinTableUpdate>>,
2063
2064 license_key: ValidatedLicenseKey,
2065
2066 user_id_pool: IdPool,
2068}
2069
2070impl Coordinator {
2071 pub(crate) async fn reconcile_scoped_system_parameters(
2090 &mut self,
2091 scoped: ScopedParameters,
2092 prune_scope: ScopedParametersScope,
2093 ) {
2094 if self.catalog().state().scoped_system_parameters() == &scoped {
2097 return;
2098 }
2099
2100 if let Err(e) = self
2108 .catalog_transact(
2109 None,
2110 vec![crate::catalog::Op::UpdateScopedSystemParameters {
2111 scoped,
2112 prune_scope,
2113 }],
2114 )
2115 .await
2116 {
2117 tracing::warn!("failed to persist scoped system parameters: {e}");
2118 }
2119 }
2120
2121 fn scoped_overrides_create_op(&self, ops: &[crate::catalog::Op]) -> Option<crate::catalog::Op> {
2139 let mut created_clusters = BTreeMap::new();
2140 let mut clusters = Vec::new();
2141 for op in ops {
2142 let crate::catalog::Op::CreateCluster { id, name, .. } = op else {
2143 continue;
2144 };
2145 let cluster = ClusterScopeContext {
2146 id: id.to_string(),
2147 name: name.clone(),
2148 is_builtin: id.is_system(),
2149 };
2150 created_clusters.insert(*id, cluster.clone());
2151 clusters.push(ClusterEvalContext {
2152 cluster_id: *id,
2153 cluster,
2154 });
2155 }
2156
2157 let mut replicas = Vec::new();
2158 for op in ops {
2159 let crate::catalog::Op::CreateClusterReplica {
2160 cluster_id,
2161 replica_id,
2162 name,
2163 config,
2164 ..
2165 } = op
2166 else {
2167 continue;
2168 };
2169 let ReplicaLocation::Managed(location) = &config.location else {
2170 continue;
2171 };
2172 let Some(cluster) = created_clusters.get(cluster_id).cloned().or_else(|| {
2173 self.catalog()
2174 .try_get_cluster(*cluster_id)
2175 .map(|cluster| ClusterScopeContext {
2176 id: cluster_id.to_string(),
2177 name: cluster.name.clone(),
2178 is_builtin: cluster_id.is_system(),
2179 })
2180 }) else {
2181 continue;
2182 };
2183 replicas.push(ReplicaEvalContext {
2184 cluster_id: *cluster_id,
2185 replica_id: *replica_id,
2186 replica: ReplicaScopeContext {
2187 id: replica_id.to_string(),
2188 name: name.clone(),
2189 is_builtin: cluster_id.is_system(),
2190 size: location.size.clone(),
2191 size_family: location.allocation.family().to_string(),
2192 cluster_id: cluster_id.to_string(),
2193 cluster_name: cluster.name.clone(),
2194 },
2195 cluster,
2196 });
2197 }
2198
2199 if clusters.is_empty() && replicas.is_empty() {
2200 return None;
2201 }
2202 let frontend = self.scoped_frontend.clone()?;
2203 let catalog = self.catalog();
2204 let system_config = catalog.system_config();
2205
2206 let replica_param_names: Vec<&'static str> = system_config
2209 .iter_synced()
2210 .filter(|var| var.scope() == ParameterScope::Replica)
2211 .map(|var| var.name())
2212 .collect();
2213 let cluster_param_names: Vec<&'static str> = system_config
2214 .iter_synced()
2215 .filter(|var| var.scope() == ParameterScope::Cluster)
2216 .map(|var| var.name())
2217 .collect();
2218
2219 let params = SynchronizedParameters::new(system_config.clone());
2220 let mut evaluated = ScopedParameters::default();
2221 if !cluster_param_names.is_empty() && !clusters.is_empty() {
2222 evaluated.cluster =
2223 frontend.pull_cluster_overrides(¶ms, &cluster_param_names, &clusters);
2224 }
2225 if !replica_param_names.is_empty() && !replicas.is_empty() {
2226 evaluated.replica =
2227 frontend.pull_replica_overrides(¶ms, &replica_param_names, &replicas);
2228 }
2229 let prune_scope = ScopedParametersScope {
2233 clusters: clusters.iter().map(|cluster| cluster.cluster_id).collect(),
2234 replicas: replicas.iter().map(|replica| replica.replica_id).collect(),
2235 };
2236 Some(crate::catalog::Op::UpdateScopedSystemParameters {
2237 scoped: evaluated,
2238 prune_scope,
2239 })
2240 }
2241
2242 pub(crate) fn replica_dyncfg_overrides(
2248 &self,
2249 ) -> BTreeMap<ComputeInstanceId, BTreeMap<ReplicaId, ConfigUpdates>> {
2250 let replica_overrides = &self.catalog().state().scoped_system_parameters().replica;
2251
2252 let dyncfgs = self.catalog().system_config().dyncfgs();
2253 let mut instance_overrides: BTreeMap<
2254 ComputeInstanceId,
2255 BTreeMap<ReplicaId, ConfigUpdates>,
2256 > = BTreeMap::new();
2257 for cluster in self.catalog().clusters() {
2258 for replica in cluster.replicas() {
2259 let Some(values) = replica_overrides.get(&replica.replica_id) else {
2260 continue;
2261 };
2262 let mut updates = ConfigUpdates::default();
2263 for (name, value) in values {
2264 let Some(entry) = dyncfgs.entry(name) else {
2265 continue;
2268 };
2269 match entry.parse_val(value) {
2270 Ok(val) => updates.add_dynamic(name, val),
2271 Err(e) => {
2272 tracing::warn!(%name, %value, "cannot parse scoped override: {e}")
2273 }
2274 }
2275 }
2276 if !updates.updates.is_empty() {
2277 instance_overrides
2278 .entry(cluster.id)
2279 .or_default()
2280 .insert(replica.replica_id, updates);
2281 }
2282 }
2283 }
2284
2285 instance_overrides
2286 }
2287
2288 pub(crate) fn push_replica_dyncfg_overrides(&mut self) {
2294 let instance_overrides = self.replica_dyncfg_overrides();
2295
2296 self.controller
2311 .update_replica_dyncfg_overrides(instance_overrides);
2312 let compute_config = crate::flags::compute_config(self.catalog().system_config());
2318 self.controller.compute.update_configuration(compute_config);
2319 let storage_config = crate::flags::storage_config(self.catalog().system_config());
2320 self.controller.storage.update_parameters(storage_config);
2321 }
2322
2323 pub(crate) fn cluster_scoped_optimizer_overrides(
2327 &self,
2328 cluster_id: ClusterId,
2329 ) -> OptimizerFeatureOverrides {
2330 self.catalog()
2331 .state()
2332 .cluster_scoped_optimizer_overrides(cluster_id)
2333 }
2334
2335 #[instrument(name = "coord::bootstrap")]
2339 pub(crate) async fn bootstrap(
2340 &mut self,
2341 boot_ts: Timestamp,
2342 migrated_storage_collections_0dt: BTreeSet<CatalogItemId>,
2343 hydrate_migrated_mvs: bool,
2344 mut builtin_table_updates: Vec<BuiltinTableUpdate>,
2345 cached_global_exprs: BTreeMap<GlobalId, GlobalExpressions>,
2346 uncached_local_exprs: BTreeMap<GlobalId, LocalExpressions>,
2347 ) -> Result<(), AdapterError> {
2348 let bootstrap_start = Instant::now();
2349 info!("startup: coordinator init: bootstrap beginning");
2350 info!("startup: coordinator init: bootstrap: preamble beginning");
2351
2352 let cluster_statuses: Vec<(_, Vec<_>)> = self
2355 .catalog()
2356 .clusters()
2357 .map(|cluster| {
2358 (
2359 cluster.id(),
2360 cluster
2361 .replicas()
2362 .map(|replica| {
2363 (replica.replica_id, replica.config.location.num_processes())
2364 })
2365 .collect(),
2366 )
2367 })
2368 .collect();
2369 let now = self.now_datetime();
2370 for (cluster_id, replica_statuses) in cluster_statuses {
2371 self.cluster_replica_statuses
2372 .initialize_cluster_statuses(cluster_id);
2373 for (replica_id, num_processes) in replica_statuses {
2374 self.cluster_replica_statuses
2375 .initialize_cluster_replica_statuses(
2376 cluster_id,
2377 replica_id,
2378 num_processes,
2379 now,
2380 );
2381 }
2382 }
2383
2384 let system_config = self.catalog().system_config();
2385
2386 mz_metrics::update_dyncfg(&system_config.dyncfg_updates());
2388
2389 let compute_config = flags::compute_config(system_config);
2391 let storage_config = flags::storage_config(system_config);
2392 let scheduling_config = flags::orchestrator_scheduling_config(system_config);
2393 let dyncfg_updates = system_config.dyncfg_updates();
2394 self.controller.compute.update_configuration(compute_config);
2395 self.controller.storage.update_parameters(storage_config);
2396 self.controller
2397 .update_orchestrator_scheduling_config(scheduling_config);
2398 self.controller.update_configuration(dyncfg_updates);
2399
2400 let replica_dyncfg_overrides = self.replica_dyncfg_overrides();
2407 self.controller
2408 .update_replica_dyncfg_overrides(replica_dyncfg_overrides);
2409
2410 let enforce_credit_limit_at_bootstrap = !matches!(
2415 self.license_key.expiration_behavior,
2416 ExpirationBehavior::DisableClusterCreation,
2417 );
2418 if enforce_credit_limit_at_bootstrap {
2419 self.validate_resource_limit_numeric(
2420 Numeric::zero(),
2421 self.current_credit_consumption_rate(None),
2422 |system_vars| {
2423 self.license_key
2424 .max_credit_consumption_rate()
2425 .map_or_else(|| system_vars.max_credit_consumption_rate(), Numeric::from)
2426 },
2427 "cluster replica",
2428 MAX_CREDIT_CONSUMPTION_RATE.name(),
2429 )?;
2430 }
2431
2432 let mut policies_to_set: BTreeMap<CompactionWindow, CollectionIdBundle> =
2433 Default::default();
2434
2435 let enable_worker_core_affinity =
2436 self.catalog().system_config().enable_worker_core_affinity();
2437 for instance in self.catalog.clusters() {
2438 self.controller.create_cluster(
2439 instance.id,
2440 ClusterConfig {
2441 arranged_logs: instance.log_indexes.clone(),
2442 workload_class: instance.config.workload_class.clone(),
2443 },
2444 )?;
2445 for replica in instance.replicas() {
2446 let role = instance.role();
2447 self.controller.create_replica(
2448 instance.id,
2449 replica.replica_id,
2450 instance.name.clone(),
2451 replica.name.clone(),
2452 role,
2453 replica.config.clone(),
2454 enable_worker_core_affinity,
2455 )?;
2456 }
2457 }
2458
2459 self.push_replica_dyncfg_overrides();
2471
2472 info!(
2473 "startup: coordinator init: bootstrap: preamble complete in {:?}",
2474 bootstrap_start.elapsed()
2475 );
2476
2477 let init_storage_collections_start = Instant::now();
2478 info!("startup: coordinator init: bootstrap: storage collections init beginning");
2479 self.bootstrap_storage_collections(&migrated_storage_collections_0dt)
2480 .await;
2481 info!(
2482 "startup: coordinator init: bootstrap: storage collections init complete in {:?}",
2483 init_storage_collections_start.elapsed()
2484 );
2485
2486 self.controller.start_compute_introspection_sink();
2491
2492 let sorting_start = Instant::now();
2493 info!("startup: coordinator init: bootstrap: sorting catalog entries");
2494 let entries = self.bootstrap_sort_catalog_entries();
2495 info!(
2496 "startup: coordinator init: bootstrap: sorting catalog entries complete in {:?}",
2497 sorting_start.elapsed()
2498 );
2499
2500 let optimize_dataflows_start = Instant::now();
2501 info!("startup: coordinator init: bootstrap: optimize dataflow plans beginning");
2502 let uncached_global_exps = self.bootstrap_dataflow_plans(&entries, cached_global_exprs)?;
2503 info!(
2504 "startup: coordinator init: bootstrap: optimize dataflow plans complete in {:?}",
2505 optimize_dataflows_start.elapsed()
2506 );
2507
2508 let _fut = self.catalog().update_expression_cache(
2510 uncached_local_exprs.into_iter().collect(),
2511 uncached_global_exps.into_iter().collect(),
2512 Default::default(),
2513 );
2514
2515 let bootstrap_as_ofs_start = Instant::now();
2519 info!("startup: coordinator init: bootstrap: dataflow as-of bootstrapping beginning");
2520 let dataflow_read_holds = self.bootstrap_dataflow_as_ofs().await;
2521 info!(
2522 "startup: coordinator init: bootstrap: dataflow as-of bootstrapping complete in {:?}",
2523 bootstrap_as_ofs_start.elapsed()
2524 );
2525
2526 let postamble_start = Instant::now();
2527 info!("startup: coordinator init: bootstrap: postamble beginning");
2528
2529 let logs: BTreeSet<_> = BUILTINS::logs()
2530 .map(|log| self.catalog().resolve_builtin_log(log))
2531 .flat_map(|item_id| self.catalog().get_global_ids(&item_id))
2532 .collect();
2533
2534 let mut privatelink_connections = BTreeMap::new();
2535
2536 for entry in &entries {
2537 debug!(
2538 "coordinator init: installing {} {}",
2539 entry.item().typ(),
2540 entry.id()
2541 );
2542 let mut policy = entry.item().initial_logical_compaction_window();
2543 match entry.item() {
2544 CatalogItem::Source(source) => {
2550 if source.custom_logical_compaction_window.is_none() {
2552 if let DataSourceDesc::IngestionExport { ingestion_id, .. } =
2553 source.data_source
2554 {
2555 policy = Some(
2556 self.catalog()
2557 .get_entry(&ingestion_id)
2558 .source()
2559 .expect("must be source")
2560 .custom_logical_compaction_window
2561 .unwrap_or_default(),
2562 );
2563 }
2564 }
2565 policies_to_set
2566 .entry(policy.expect("sources have a compaction window"))
2567 .or_insert_with(Default::default)
2568 .storage_ids
2569 .insert(source.global_id());
2570 }
2571 CatalogItem::Table(table) => {
2572 policies_to_set
2573 .entry(policy.expect("tables have a compaction window"))
2574 .or_insert_with(Default::default)
2575 .storage_ids
2576 .extend(table.global_ids());
2577 }
2578 CatalogItem::Index(idx) => {
2579 let policy_entry = policies_to_set
2580 .entry(policy.expect("indexes have a compaction window"))
2581 .or_insert_with(Default::default);
2582
2583 if logs.contains(&idx.on) {
2584 policy_entry
2585 .compute_ids
2586 .entry(idx.cluster_id)
2587 .or_insert_with(BTreeSet::new)
2588 .insert(idx.global_id());
2589 } else {
2590 let df_desc = self
2591 .catalog()
2592 .try_get_physical_plan(&idx.global_id())
2593 .expect("added in `bootstrap_dataflow_plans`")
2594 .clone();
2595
2596 let df_meta = self
2597 .catalog()
2598 .try_get_dataflow_metainfo(&idx.global_id())
2599 .expect("added in `bootstrap_dataflow_plans`");
2600
2601 if self.catalog().state().system_config().enable_mz_notices() {
2602 self.catalog().state().pack_optimizer_notices(
2604 &mut builtin_table_updates,
2605 df_meta.optimizer_notices.iter(),
2606 Diff::ONE,
2607 );
2608 }
2609
2610 policy_entry
2613 .compute_ids
2614 .entry(idx.cluster_id)
2615 .or_insert_with(Default::default)
2616 .extend(df_desc.export_ids());
2617
2618 self.controller
2619 .compute
2620 .create_dataflow(idx.cluster_id, df_desc, None)
2621 .unwrap_or_terminate("cannot fail to create dataflows");
2622 }
2623 }
2624 CatalogItem::View(_) => (),
2625 CatalogItem::MaterializedView(mview) => {
2626 policies_to_set
2632 .entry(policy.expect("materialized views have a compaction window"))
2633 .or_insert_with(Default::default)
2634 .storage_ids
2635 .extend(mview.global_ids());
2636
2637 let mut df_desc = self
2638 .catalog()
2639 .try_get_physical_plan(&mview.global_id_writes())
2640 .expect("added in `bootstrap_dataflow_plans`")
2641 .clone();
2642
2643 if let Some(initial_as_of) = mview.initial_as_of.clone() {
2644 df_desc.set_initial_as_of(initial_as_of);
2645 }
2646
2647 let until = mview
2649 .refresh_schedule
2650 .as_ref()
2651 .and_then(|s| s.last_refresh())
2652 .and_then(|r| r.try_step_forward());
2653 if let Some(until) = until {
2654 df_desc.until.meet_assign(&Antichain::from_elem(until));
2655 }
2656
2657 let df_meta = self
2658 .catalog()
2659 .try_get_dataflow_metainfo(&mview.global_id_writes())
2660 .expect("added in `bootstrap_dataflow_plans`");
2661
2662 if self.catalog().state().system_config().enable_mz_notices() {
2663 self.catalog().state().pack_optimizer_notices(
2665 &mut builtin_table_updates,
2666 df_meta.optimizer_notices.iter(),
2667 Diff::ONE,
2668 );
2669 }
2670
2671 self.ship_dataflow(df_desc, mview.cluster_id, mview.target_replica)
2672 .await;
2673
2674 if mview.replacement_target.is_none() {
2678 let gid = mview.global_id_writes();
2679 if hydrate_migrated_mvs
2680 && migrated_storage_collections_0dt.contains(&entry.id())
2681 {
2682 self.controller
2692 .compute
2693 .allow_writes_in_read_only(mview.cluster_id, gid)
2694 .unwrap_or_terminate("allow_writes cannot fail");
2695 } else {
2696 self.allow_writes(mview.cluster_id, gid);
2697 }
2698 }
2699 }
2700 CatalogItem::MetricSink(metric_sink) => {
2701 let df_desc = self
2702 .catalog()
2703 .try_get_physical_plan(&metric_sink.global_id)
2704 .expect("added in `bootstrap_dataflow_plans`")
2705 .clone();
2706
2707 let df_meta = self
2708 .catalog()
2709 .try_get_dataflow_metainfo(&metric_sink.global_id)
2710 .expect("added in `bootstrap_dataflow_plans`");
2711
2712 if self.catalog().state().system_config().enable_mz_notices() {
2713 self.catalog().state().pack_optimizer_notices(
2715 &mut builtin_table_updates,
2716 df_meta.optimizer_notices.iter(),
2717 Diff::ONE,
2718 );
2719 }
2720
2721 self.ship_dataflow(df_desc, metric_sink.cluster_id, None)
2724 .await;
2725 }
2726 CatalogItem::Sink(sink) => {
2727 policies_to_set
2728 .entry(CompactionWindow::Default)
2729 .or_insert_with(Default::default)
2730 .storage_ids
2731 .insert(sink.global_id());
2732 }
2733 CatalogItem::Connection(catalog_connection) => {
2734 if let ConnectionDetails::AwsPrivatelink(conn) = &catalog_connection.details {
2735 privatelink_connections.insert(
2736 entry.id(),
2737 VpcEndpointConfig {
2738 aws_service_name: conn.service_name.clone(),
2739 availability_zone_ids: conn.availability_zones.clone(),
2740 },
2741 );
2742 }
2743 }
2744 CatalogItem::Log(_)
2746 | CatalogItem::Type(_)
2747 | CatalogItem::Func(_)
2748 | CatalogItem::Secret(_) => {}
2749 }
2750 }
2751
2752 if let Some(cloud_resource_controller) = &self.cloud_resource_controller {
2753 let existing_vpc_endpoints = cloud_resource_controller
2755 .list_vpc_endpoints()
2756 .await
2757 .context("list vpc endpoints")?;
2758 let existing_vpc_endpoints = BTreeSet::from_iter(existing_vpc_endpoints.into_keys());
2759 let desired_vpc_endpoints = privatelink_connections.keys().cloned().collect();
2760 let vpc_endpoints_to_remove = existing_vpc_endpoints.difference(&desired_vpc_endpoints);
2761 for id in vpc_endpoints_to_remove {
2762 cloud_resource_controller
2763 .delete_vpc_endpoint(*id)
2764 .await
2765 .context("deleting extraneous vpc endpoint")?;
2766 }
2767
2768 for (id, spec) in privatelink_connections {
2770 cloud_resource_controller
2771 .ensure_vpc_endpoint(id, spec)
2772 .await
2773 .context("ensuring vpc endpoint")?;
2774 }
2775 }
2776
2777 drop(dataflow_read_holds);
2780 for (cw, policies) in policies_to_set {
2782 self.initialize_read_policies(&policies, cw).await;
2783 }
2784
2785 builtin_table_updates.extend(
2787 self.catalog().state().resolve_builtin_table_updates(
2788 self.catalog().state().pack_all_replica_size_updates(),
2789 ),
2790 );
2791
2792 debug!("startup: coordinator init: bootstrap: initializing migrated builtin tables");
2793 let migrated_updates_fut = if self.controller.read_only() {
2799 let min_timestamp = Timestamp::minimum();
2800 let migrated_builtin_table_updates: Vec<_> = builtin_table_updates
2801 .extract_if(.., |update| {
2802 let gid = self.catalog().get_entry(&update.id).latest_global_id();
2803 migrated_storage_collections_0dt.contains(&update.id)
2804 && self
2805 .controller
2806 .storage_collections
2807 .collection_frontiers(gid)
2808 .expect("all tables are registered")
2809 .write_frontier
2810 .elements()
2811 == &[min_timestamp]
2812 })
2813 .collect();
2814 if migrated_builtin_table_updates.is_empty() {
2815 futures::future::ready(()).boxed()
2816 } else {
2817 let mut grouped_appends: BTreeMap<GlobalId, Vec<TableData>> = BTreeMap::new();
2819 for update in migrated_builtin_table_updates {
2820 let gid = self.catalog().get_entry(&update.id).latest_global_id();
2821 grouped_appends.entry(gid).or_default().push(update.data);
2822 }
2823 info!(
2824 "coordinator init: rehydrating migrated builtin tables in read-only mode: {:?}",
2825 grouped_appends.keys().collect::<Vec<_>>()
2826 );
2827
2828 let mut all_appends = Vec::with_capacity(grouped_appends.len());
2830 for (item_id, table_data) in grouped_appends.into_iter() {
2831 let mut all_rows = Vec::new();
2832 let mut all_data = Vec::new();
2833 for data in table_data {
2834 match data {
2835 TableData::Rows(rows) => all_rows.extend(rows),
2836 TableData::Batches(_) => all_data.push(data),
2837 }
2838 }
2839 differential_dataflow::consolidation::consolidate(&mut all_rows);
2840 all_data.push(TableData::Rows(all_rows));
2841
2842 all_appends.push((item_id, all_data));
2844 }
2845
2846 let fut = self
2847 .controller
2848 .storage
2849 .append_table(min_timestamp, boot_ts.step_forward(), all_appends)
2850 .expect("cannot fail to append");
2851 async {
2852 fut.await
2853 .expect("One-shot shouldn't be dropped during bootstrap")
2854 .unwrap_or_terminate("cannot fail to append")
2855 }
2856 .boxed()
2857 }
2858 } else {
2859 futures::future::ready(()).boxed()
2860 };
2861
2862 info!(
2863 "startup: coordinator init: bootstrap: postamble complete in {:?}",
2864 postamble_start.elapsed()
2865 );
2866
2867 let builtin_update_start = Instant::now();
2868 info!("startup: coordinator init: bootstrap: generate builtin updates beginning");
2869
2870 if self.controller.read_only() {
2871 info!(
2872 "coordinator init: bootstrap: stashing builtin table updates while in read-only mode"
2873 );
2874
2875 self.buffered_builtin_table_updates
2876 .as_mut()
2877 .expect("in read-only mode")
2878 .append(&mut builtin_table_updates);
2879 } else {
2880 self.bootstrap_tables(&entries, builtin_table_updates).await;
2881 };
2882 info!(
2883 "startup: coordinator init: bootstrap: generate builtin updates complete in {:?}",
2884 builtin_update_start.elapsed()
2885 );
2886
2887 let cleanup_secrets_start = Instant::now();
2888 info!("startup: coordinator init: bootstrap: generate secret cleanup beginning");
2889 {
2893 let Self {
2896 secrets_controller,
2897 catalog,
2898 ..
2899 } = self;
2900
2901 let next_user_item_id = catalog.get_next_user_item_id().await?;
2902 let next_system_item_id = catalog.get_next_system_item_id().await?;
2903 let read_only = self.controller.read_only();
2904 let catalog_ids: BTreeSet<CatalogItemId> =
2909 catalog.entries().map(|entry| entry.id()).collect();
2910 let secrets_controller = Arc::clone(secrets_controller);
2911
2912 spawn(|| "cleanup-orphaned-secrets", async move {
2913 if read_only {
2914 info!(
2915 "coordinator init: not cleaning up orphaned secrets while in read-only mode"
2916 );
2917 return;
2918 }
2919 info!("coordinator init: cleaning up orphaned secrets");
2920
2921 match secrets_controller.list().await {
2922 Ok(controller_secrets) => {
2923 let controller_secrets: BTreeSet<CatalogItemId> =
2924 controller_secrets.into_iter().collect();
2925 let orphaned = controller_secrets.difference(&catalog_ids);
2926 for id in orphaned {
2927 let id_too_large = match id {
2928 CatalogItemId::System(id) => *id >= next_system_item_id,
2929 CatalogItemId::User(id) => *id >= next_user_item_id,
2930 CatalogItemId::IntrospectionSourceIndex(_)
2931 | CatalogItemId::Transient(_) => false,
2932 };
2933 if id_too_large {
2934 info!(
2935 %next_user_item_id, %next_system_item_id,
2936 "coordinator init: not deleting orphaned secret {id} that was likely created by a newer deploy generation"
2937 );
2938 } else {
2939 info!("coordinator init: deleting orphaned secret {id}");
2940 fail_point!("orphan_secrets");
2941 if let Err(e) = secrets_controller.delete(*id).await {
2942 warn!(
2943 "Dropping orphaned secret has encountered an error: {}",
2944 e
2945 );
2946 }
2947 }
2948 }
2949 }
2950 Err(e) => warn!("Failed to list secrets during orphan cleanup: {:?}", e),
2951 }
2952 });
2953 }
2954 info!(
2955 "startup: coordinator init: bootstrap: generate secret cleanup complete in {:?}",
2956 cleanup_secrets_start.elapsed()
2957 );
2958
2959 let final_steps_start = Instant::now();
2961 info!(
2962 "startup: coordinator init: bootstrap: migrate builtin tables in read-only mode beginning"
2963 );
2964 migrated_updates_fut
2965 .instrument(info_span!("coord::bootstrap::final"))
2966 .await;
2967
2968 debug!(
2969 "startup: coordinator init: bootstrap: announcing completion of initialization to controller"
2970 );
2971 self.controller.initialization_complete();
2973
2974 self.bootstrap_introspection_subscribes().await;
2976
2977 self.bootstrap_metric_sinks().await;
2979
2980 info!(
2981 "startup: coordinator init: bootstrap: migrate builtin tables in read-only mode complete in {:?}",
2982 final_steps_start.elapsed()
2983 );
2984
2985 info!(
2986 "startup: coordinator init: bootstrap complete in {:?}",
2987 bootstrap_start.elapsed()
2988 );
2989 Ok(())
2990 }
2991
2992 #[allow(clippy::async_yields_async)]
2997 #[instrument]
2998 async fn bootstrap_tables(
2999 &mut self,
3000 entries: &[CatalogEntry],
3001 mut builtin_table_updates: Vec<BuiltinTableUpdate>,
3002 ) {
3003 struct TableMetadata<'a> {
3005 id: CatalogItemId,
3006 name: &'a QualifiedItemName,
3007 table: &'a Table,
3008 }
3009
3010 let table_metas: Vec<_> = entries
3012 .into_iter()
3013 .filter_map(|entry| {
3014 entry.table().map(|table| TableMetadata {
3015 id: entry.id(),
3016 name: entry.name(),
3017 table,
3018 })
3019 })
3020 .collect();
3021
3022 debug!("coordinator init: advancing all tables to current timestamp");
3024 let WriteTimestamp {
3025 timestamp: write_ts,
3026 advance_to,
3027 } = self.get_local_write_ts().await;
3028 let appends = table_metas
3029 .iter()
3030 .map(|meta| (meta.table.global_id_writes(), Vec::new()))
3031 .collect();
3032 let table_fence_rx = self
3036 .controller
3037 .storage
3038 .append_table(write_ts.clone(), advance_to, appends)
3039 .expect("invalid updates");
3040
3041 self.apply_local_write(write_ts).await;
3042
3043 debug!("coordinator init: resetting system tables");
3045 let read_ts = self.get_local_read_ts().await;
3046
3047 let retained_across_restarts = BTreeSet::from([
3048 self.catalog()
3049 .resolve_builtin_table(&MZ_STORAGE_USAGE_BY_SHARD),
3050 self.catalog()
3051 .resolve_builtin_table(&MZ_OBJECT_ARRANGEMENT_SIZE_HISTORY),
3052 self.catalog()
3053 .resolve_builtin_table(&MZ_OBJECT_HYDRATION_HISTORY),
3054 self.catalog()
3055 .resolve_builtin_table(&MZ_REPLICA_HYDRATION_HISTORY),
3056 ]);
3057
3058 let mut retraction_tasks = Vec::new();
3059 let system_tables: Vec<_> = table_metas
3060 .iter()
3061 .filter(|meta| meta.id.is_system() && !retained_across_restarts.contains(&meta.id))
3062 .collect();
3063
3064 for system_table in system_tables {
3065 let table_id = system_table.id;
3066 let full_name = self.catalog().resolve_full_name(system_table.name, None);
3067 debug!("coordinator init: resetting system table {full_name} ({table_id})");
3068
3069 let snapshot_fut = self
3071 .controller
3072 .storage_collections
3073 .snapshot_cursor(system_table.table.global_id_writes(), read_ts);
3074 let batch_fut = self
3075 .controller
3076 .storage_collections
3077 .create_update_builder(system_table.table.global_id_writes());
3078
3079 let task = spawn(|| format!("snapshot-{table_id}"), async move {
3080 let mut batch = batch_fut
3082 .await
3083 .unwrap_or_terminate("cannot fail to create a batch for a BuiltinTable");
3084 tracing::info!(?table_id, "starting snapshot");
3085 let mut snapshot_cursor = snapshot_fut
3087 .await
3088 .unwrap_or_terminate("cannot fail to snapshot");
3089
3090 while let Some(values) = snapshot_cursor.next().await {
3092 for (key, _t, d) in values {
3093 let d_invert = d.neg();
3094 batch.add(&key, &(), &d_invert).await;
3095 }
3096 }
3097 tracing::info!(?table_id, "finished snapshot");
3098
3099 let batch = batch.finish().await;
3100 BuiltinTableUpdate::batch(table_id, batch)
3101 });
3102 retraction_tasks.push(task);
3103 }
3104
3105 let retractions_res = futures::future::join_all(retraction_tasks).await;
3106 for retractions in retractions_res {
3107 builtin_table_updates.push(retractions);
3108 }
3109
3110 table_fence_rx
3112 .await
3113 .expect("One-shot shouldn't be dropped during bootstrap")
3114 .unwrap_or_terminate("cannot fail to append");
3115
3116 info!("coordinator init: sending builtin table updates");
3117 let builtin_updates_fut = self.builtin_table_update().execute(builtin_table_updates);
3118 builtin_updates_fut.await;
3121 }
3122
3123 #[instrument]
3136 async fn bootstrap_storage_collections(
3137 &mut self,
3138 migrated_storage_collections: &BTreeSet<CatalogItemId>,
3139 ) {
3140 let catalog = self.catalog();
3141
3142 let source_desc = |object_id: GlobalId,
3143 data_source: &DataSourceDesc,
3144 desc: &RelationDesc,
3145 timeline: &Timeline| {
3146 let data_source = match data_source.clone() {
3147 DataSourceDesc::Ingestion { desc, cluster_id } => {
3149 let desc = desc.into_inline_connection(catalog.state());
3150 let ingestion = IngestionDescription::new(desc, cluster_id, object_id);
3151 DataSource::Ingestion(ingestion)
3152 }
3153 DataSourceDesc::OldSyntaxIngestion {
3154 desc,
3155 progress_subsource,
3156 data_config,
3157 details,
3158 cluster_id,
3159 } => {
3160 let desc = desc.into_inline_connection(catalog.state());
3161 let data_config = data_config.into_inline_connection(catalog.state());
3162 let progress_subsource =
3165 catalog.get_entry(&progress_subsource).latest_global_id();
3166 let mut ingestion =
3167 IngestionDescription::new(desc, cluster_id, progress_subsource);
3168 let legacy_export = SourceExport {
3169 storage_metadata: (),
3170 data_config,
3171 details,
3172 };
3173 ingestion.source_exports.insert(object_id, legacy_export);
3174
3175 DataSource::Ingestion(ingestion)
3176 }
3177 DataSourceDesc::IngestionExport {
3178 ingestion_id,
3179 external_reference: _,
3180 details,
3181 data_config,
3182 } => {
3183 let ingestion_id = catalog.get_entry(&ingestion_id).latest_global_id();
3186
3187 DataSource::IngestionExport {
3188 ingestion_id,
3189 details,
3190 data_config: data_config.into_inline_connection(catalog.state()),
3191 }
3192 }
3193 DataSourceDesc::Webhook { .. } => DataSource::Webhook,
3194 DataSourceDesc::Progress => DataSource::Progress,
3195 DataSourceDesc::Introspection(introspection) => {
3196 DataSource::Introspection(introspection)
3197 }
3198 DataSourceDesc::Catalog => DataSource::Other,
3199 };
3200 CollectionDescription {
3201 desc: desc.clone(),
3202 data_source,
3203 since: None,
3204 timeline: Some(timeline.clone()),
3205 primary: None,
3206 }
3207 };
3208
3209 let mut compute_collections = vec![];
3210 let mut collections = vec![];
3211 for entry in catalog.entries() {
3212 match entry.item() {
3213 CatalogItem::Source(source) => {
3214 collections.push((
3215 source.global_id(),
3216 source_desc(
3217 source.global_id(),
3218 &source.data_source,
3219 &source.desc,
3220 &source.timeline,
3221 ),
3222 ));
3223 }
3224 CatalogItem::Table(table) => {
3225 match &table.data_source {
3226 TableDataSource::TableWrites { defaults: _ } => {
3227 let versions: BTreeMap<_, _> = table
3228 .collection_descs()
3229 .map(|(gid, version, desc)| (version, (gid, desc)))
3230 .collect();
3231 let collection_descs = versions.iter().map(|(version, (gid, desc))| {
3232 let next_version = version.bump();
3233 let primary_collection =
3234 versions.get(&next_version).map(|(gid, _desc)| gid).copied();
3235 let mut collection_desc =
3236 CollectionDescription::for_table(desc.clone());
3237 collection_desc.primary = primary_collection;
3238
3239 (*gid, collection_desc)
3240 });
3241 collections.extend(collection_descs);
3242 }
3243 TableDataSource::DataSource {
3244 desc: data_source_desc,
3245 timeline,
3246 } => {
3247 soft_assert_eq_or_log!(table.collections.len(), 1);
3249 let collection_descs =
3250 table.collection_descs().map(|(gid, _version, desc)| {
3251 (
3252 gid,
3253 source_desc(
3254 entry.latest_global_id(),
3255 data_source_desc,
3256 &desc,
3257 timeline,
3258 ),
3259 )
3260 });
3261 collections.extend(collection_descs);
3262 }
3263 };
3264 }
3265 CatalogItem::MaterializedView(mv) => {
3266 let mut primary = mv
3274 .replacement_target
3275 .map(|target_id| catalog.get_entry(&target_id).latest_global_id());
3276 let collection_descs = mv.collection_descs().map(|(gid, _version, desc)| {
3277 let mut collection_desc =
3278 CollectionDescription::for_other(desc, mv.initial_as_of.clone());
3279 collection_desc.primary = primary;
3280 primary = Some(gid);
3281 (gid, collection_desc)
3282 });
3283
3284 collections.extend(collection_descs);
3285 compute_collections.push((mv.global_id_writes(), mv.desc.latest()));
3286 }
3287 CatalogItem::Sink(sink) => {
3288 let storage_sink_from_entry = self.catalog().get_entry_by_global_id(&sink.from);
3289 let from_desc = storage_sink_from_entry
3290 .relation_desc()
3291 .expect("sinks can only be built on items with descs")
3292 .into_owned();
3293 let collection_desc = CollectionDescription {
3294 desc: KAFKA_PROGRESS_DESC.clone(),
3296 data_source: DataSource::Sink {
3297 desc: ExportDescription {
3298 sink: StorageSinkDesc {
3299 from: sink.from,
3300 from_desc,
3301 connection: sink
3302 .connection
3303 .clone()
3304 .into_inline_connection(self.catalog().state()),
3305 envelope: sink.envelope,
3306 as_of: Antichain::from_elem(Timestamp::minimum()),
3307 with_snapshot: sink.with_snapshot,
3308 version: sink.version,
3309 from_storage_metadata: (),
3310 to_storage_metadata: (),
3311 commit_interval: sink.commit_interval,
3312 },
3313 instance_id: sink.cluster_id,
3314 },
3315 },
3316 since: None,
3317 timeline: None,
3318 primary: None,
3319 };
3320 collections.push((sink.global_id, collection_desc));
3321 }
3322 CatalogItem::Log(_)
3323 | CatalogItem::View(_)
3324 | CatalogItem::Index(_)
3325 | CatalogItem::Type(_)
3326 | CatalogItem::Func(_)
3327 | CatalogItem::Secret(_)
3328 | CatalogItem::Connection(_)
3329 | CatalogItem::MetricSink(_) => (),
3332 }
3333 }
3334
3335 let register_ts = if self.controller.read_only() {
3336 self.get_local_read_ts().await
3337 } else {
3338 self.get_local_write_ts().await.timestamp
3341 };
3342
3343 let storage_metadata = self.catalog.state().storage_metadata();
3344 let migrated_storage_collections = migrated_storage_collections
3345 .into_iter()
3346 .flat_map(|item_id| self.catalog.get_entry(item_id).global_ids())
3347 .collect();
3348
3349 self.controller
3354 .storage
3355 .evolve_nullability_for_bootstrap(storage_metadata, compute_collections)
3356 .await
3357 .unwrap_or_terminate("cannot fail to evolve collections");
3358
3359 let mut pending: BTreeMap<_, _> = collections.into_iter().collect();
3372
3373 let transitive_dep_gids: BTreeMap<_, _> = pending
3375 .keys()
3376 .map(|gid| {
3377 let entry = self.catalog.get_entry_by_global_id(gid);
3378 let item_id = entry.id();
3379 let deps = self.catalog.state().transitive_uses(item_id);
3380 let dep_gids: BTreeSet<_> = deps
3381 .filter(|dep_id| *dep_id != item_id)
3384 .map(|dep_id| self.catalog.get_entry(&dep_id).latest_global_id())
3385 .filter(|dep_gid| pending.contains_key(dep_gid))
3387 .collect();
3388 (*gid, dep_gids)
3389 })
3390 .collect();
3391
3392 let mut created_gids = Vec::new();
3393
3394 while !pending.is_empty() {
3395 let ready_gids: BTreeSet<_> = pending
3398 .keys()
3399 .filter(|gid| {
3400 let mut deps = transitive_dep_gids[gid].iter();
3401 !deps.any(|dep_gid| pending.contains_key(dep_gid))
3402 })
3403 .copied()
3404 .collect();
3405 let mut ready: Vec<_> = pending
3406 .extract_if(.., |gid, _| ready_gids.contains(gid))
3407 .collect();
3408
3409 for (gid, collection) in &mut ready {
3411 if !gid.is_system() || collection.since.is_some() {
3413 continue;
3414 }
3415
3416 let mut derived_since = Antichain::from_elem(Timestamp::MIN);
3417 for dep_gid in &transitive_dep_gids[gid] {
3418 let (since, _) = self
3419 .controller
3420 .storage
3421 .collection_frontiers(*dep_gid)
3422 .expect("previously registered");
3423 derived_since.join_assign(&since);
3424 }
3425 collection.since = Some(derived_since);
3426 }
3427
3428 if ready.is_empty() {
3429 soft_panic_or_log!(
3430 "cycle in storage collections: {:?}",
3431 pending.keys().collect::<Vec<_>>(),
3432 );
3433 ready = mem::take(&mut pending).into_iter().collect();
3437 }
3438
3439 created_gids.extend(ready.iter().map(|(gid, _collection)| *gid));
3440
3441 self.controller
3442 .storage
3443 .create_collections_for_bootstrap(
3444 storage_metadata,
3445 Some(register_ts),
3446 ready,
3447 &migrated_storage_collections,
3448 )
3449 .await
3450 .unwrap_or_terminate("cannot fail to create collections");
3451 }
3452
3453 self.controller
3455 .storage
3456 .register_table_collections(register_ts, created_gids)
3457 .await
3458 .unwrap_or_terminate("cannot fail to register tables");
3459
3460 if !self.controller.read_only() {
3461 self.apply_local_write(register_ts).await;
3462 }
3463 }
3464
3465 fn bootstrap_sort_catalog_entries(&self) -> Vec<CatalogEntry> {
3472 let mut indexes_on = BTreeMap::<_, Vec<_>>::new();
3473 let mut non_indexes = Vec::new();
3474 for entry in self.catalog().entries().cloned() {
3475 if let Some(index) = entry.index() {
3476 let on = self.catalog().get_entry_by_global_id(&index.on);
3477 indexes_on.entry(on.id()).or_default().push(entry);
3478 } else {
3479 non_indexes.push(entry);
3480 }
3481 }
3482
3483 let key_fn = |entry: &CatalogEntry| entry.id;
3484 let dependencies_fn = |entry: &CatalogEntry| entry.uses();
3485 sort_topological(&mut non_indexes, key_fn, dependencies_fn);
3486
3487 let mut result = Vec::new();
3488 for entry in non_indexes {
3489 let id = entry.id();
3490 result.push(entry);
3491 if let Some(mut indexes) = indexes_on.remove(&id) {
3492 result.append(&mut indexes);
3493 }
3494 }
3495
3496 soft_assert_or_log!(
3497 indexes_on.is_empty(),
3498 "indexes with missing dependencies: {indexes_on:?}",
3499 );
3500
3501 result
3502 }
3503
3504 #[instrument]
3515 fn bootstrap_dataflow_plans(
3516 &mut self,
3517 ordered_catalog_entries: &[CatalogEntry],
3518 mut cached_global_exprs: BTreeMap<GlobalId, GlobalExpressions>,
3519 ) -> Result<BTreeMap<GlobalId, GlobalExpressions>, AdapterError> {
3520 let mut instance_snapshots = BTreeMap::new();
3526 let mut uncached_expressions = BTreeMap::new();
3527
3528 let optimizer_config = |catalog: &Catalog, cluster_id| {
3529 let system_config = catalog.system_config();
3530 let overrides = catalog.get_cluster(cluster_id).config.features();
3531 OptimizerConfig::from(system_config)
3532 .override_from(&overrides)
3533 .override_from(
3536 &catalog
3537 .state()
3538 .cluster_scoped_optimizer_overrides(cluster_id),
3539 )
3540 };
3541
3542 for entry in ordered_catalog_entries {
3543 match entry.item() {
3544 CatalogItem::Index(idx) => {
3545 let compute_instance =
3547 instance_snapshots.entry(idx.cluster_id).or_insert_with(|| {
3548 self.instance_snapshot(idx.cluster_id)
3549 .expect("compute instance exists")
3550 });
3551 let global_id = idx.global_id();
3552
3553 if compute_instance.contains_collection(&global_id) {
3556 continue;
3557 }
3558
3559 let optimizer_config = optimizer_config(&self.catalog, idx.cluster_id);
3560
3561 let (optimized_plan, physical_plan, metainfo) =
3562 match cached_global_exprs.remove(&global_id) {
3563 Some(global_expressions)
3564 if global_expressions.optimizer_features
3565 == optimizer_config.features =>
3566 {
3567 debug!("global expression cache hit for {global_id:?}");
3568 (
3569 global_expressions.global_mir,
3570 global_expressions.physical_plan,
3571 global_expressions.dataflow_metainfos,
3572 )
3573 }
3574 Some(_) | None => {
3575 let (optimized_plan, global_lir_plan) = {
3576 let mut optimizer = optimize::index::Optimizer::new(
3578 self.owned_catalog(),
3579 compute_instance.clone(),
3580 global_id,
3581 optimizer_config.clone(),
3582 self.optimizer_metrics(),
3583 );
3584
3585 let index_plan = optimize::index::Index::new(
3587 entry.name().clone(),
3588 idx.on,
3589 idx.keys.to_vec(),
3590 );
3591 let global_mir_plan = optimizer.optimize(index_plan)?;
3592 let optimized_plan = global_mir_plan.df_desc().clone();
3593
3594 let global_lir_plan = optimizer.optimize(global_mir_plan)?;
3596
3597 (optimized_plan, global_lir_plan)
3598 };
3599
3600 let (physical_plan, metainfo) = global_lir_plan.unapply();
3601 let metainfo = {
3602 let notice_ids =
3604 std::iter::repeat_with(|| self.allocate_transient_id())
3605 .map(|(_item_id, gid)| gid)
3606 .take(metainfo.optimizer_notices.len())
3607 .collect::<Vec<_>>();
3608 self.catalog().render_notices(
3610 metainfo,
3611 notice_ids,
3612 Some(idx.global_id()),
3613 )
3614 };
3615 uncached_expressions.insert(
3616 global_id,
3617 GlobalExpressions {
3618 global_mir: optimized_plan.clone(),
3619 physical_plan: physical_plan.clone(),
3620 dataflow_metainfos: metainfo.clone(),
3621 optimizer_features: optimizer_config.features.clone(),
3622 item_version: RelationVersion::root(),
3623 },
3624 );
3625 (optimized_plan, physical_plan, metainfo)
3626 }
3627 };
3628
3629 let catalog = self.catalog_mut();
3630 catalog.set_optimized_plan(idx.global_id(), optimized_plan);
3631 catalog.set_physical_plan(idx.global_id(), physical_plan);
3632 catalog.set_dataflow_metainfo(idx.global_id(), metainfo);
3633
3634 compute_instance.insert_collection(idx.global_id());
3635 }
3636 CatalogItem::MaterializedView(mv) => {
3637 let compute_instance =
3639 instance_snapshots.entry(mv.cluster_id).or_insert_with(|| {
3640 self.instance_snapshot(mv.cluster_id)
3641 .expect("compute instance exists")
3642 });
3643 let global_id = mv.global_id_writes();
3644
3645 let optimizer_config = optimizer_config(&self.catalog, mv.cluster_id);
3646
3647 let (optimized_plan, physical_plan, metainfo) = match cached_global_exprs
3648 .remove(&global_id)
3649 {
3650 Some(global_expressions)
3651 if global_expressions.optimizer_features
3652 == optimizer_config.features =>
3653 {
3654 debug!("global expression cache hit for {global_id:?}");
3655 (
3656 global_expressions.global_mir,
3657 global_expressions.physical_plan,
3658 global_expressions.dataflow_metainfos,
3659 )
3660 }
3661 Some(_) | None => {
3662 let (_, internal_view_id) = self.allocate_transient_id();
3663 let debug_name = self
3664 .catalog()
3665 .resolve_full_name(entry.name(), None)
3666 .to_string();
3667
3668 let (optimized_plan, global_lir_plan) = {
3669 let mut optimizer = optimize::materialized_view::Optimizer::new(
3671 self.owned_catalog().as_optimizer_catalog(),
3672 compute_instance.clone(),
3673 global_id,
3674 internal_view_id,
3675 mv.desc.latest().iter_names().cloned().collect(),
3676 mv.non_null_assertions.clone(),
3677 mv.refresh_schedule.clone(),
3678 debug_name,
3679 optimizer_config.clone(),
3680 self.optimizer_metrics(),
3681 );
3682
3683 let typ = infer_sql_type_for_catalog(
3686 &mv.raw_expr,
3687 &mv.locally_optimized_expr.as_ref().clone(),
3688 );
3689 let global_mir_plan = optimizer
3690 .optimize((mv.locally_optimized_expr.as_ref().clone(), typ))?;
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, global_id)| global_id)
3705 .take(metainfo.optimizer_notices.len())
3706 .collect::<Vec<_>>();
3707 self.catalog().render_notices(
3709 metainfo,
3710 notice_ids,
3711 Some(mv.global_id_writes()),
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 item_version: latest_item_version(&mv.collections),
3722 },
3723 );
3724 (optimized_plan, physical_plan, metainfo)
3725 }
3726 };
3727
3728 let catalog = self.catalog_mut();
3729 catalog.set_optimized_plan(mv.global_id_writes(), optimized_plan);
3730 catalog.set_physical_plan(mv.global_id_writes(), physical_plan);
3731 catalog.set_dataflow_metainfo(mv.global_id_writes(), metainfo);
3732
3733 compute_instance.insert_collection(mv.global_id_writes());
3734 }
3735 CatalogItem::MetricSink(metric_sink) => {
3736 let compute_instance = instance_snapshots
3738 .entry(metric_sink.cluster_id)
3739 .or_insert_with(|| {
3740 self.instance_snapshot(metric_sink.cluster_id)
3741 .expect("compute instance exists")
3742 });
3743 let global_id = metric_sink.global_id;
3744 let optimizer_config = optimizer_config(&self.catalog, metric_sink.cluster_id);
3745
3746 let (optimized_plan, physical_plan, metainfo) = match cached_global_exprs
3747 .remove(&global_id)
3748 {
3749 Some(global_expressions)
3750 if global_expressions.optimizer_features
3751 == optimizer_config.features =>
3752 {
3753 debug!("global expression cache hit for {global_id:?}");
3754 (
3755 global_expressions.global_mir,
3756 global_expressions.physical_plan,
3757 global_expressions.dataflow_metainfos,
3758 )
3759 }
3760 Some(_) | None => {
3761 let (_, view_id) = self.allocate_transient_id();
3768
3769 let (optimized_plan, global_lir_plan) = {
3770 let mut optimizer = optimize::metric_sink::Optimizer::new(
3771 self.owned_catalog(),
3772 compute_instance.clone(),
3773 view_id,
3774 global_id,
3775 optimizer_config.clone(),
3776 self.optimizer_metrics(),
3777 );
3778
3779 let metric_sink_plan = optimize::metric_sink::MetricSink::new(
3781 self.catalog()
3782 .resolve_full_name(entry.name(), None)
3783 .to_string(),
3784 optimize::metric_sink::MetricSinkFrom::Id(metric_sink.from),
3785 metric_sink.prefix.clone(),
3786 None,
3787 );
3788 let global_mir_plan = optimizer.optimize(metric_sink_plan)?;
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, gid)| gid)
3803 .take(metainfo.optimizer_notices.len())
3804 .collect::<Vec<_>>();
3805 self.catalog()
3807 .render_notices(metainfo, notice_ids, Some(global_id))
3808 };
3809 uncached_expressions.insert(
3810 global_id,
3811 GlobalExpressions {
3812 global_mir: optimized_plan.clone(),
3813 physical_plan: physical_plan.clone(),
3814 dataflow_metainfos: metainfo.clone(),
3815 optimizer_features: optimizer_config.features.clone(),
3816 item_version: RelationVersion::root(),
3817 },
3818 );
3819 (optimized_plan, physical_plan, metainfo)
3820 }
3821 };
3822
3823 let catalog = self.catalog_mut();
3824 catalog.set_optimized_plan(global_id, optimized_plan);
3825 catalog.set_physical_plan(global_id, physical_plan);
3826 catalog.set_dataflow_metainfo(global_id, metainfo);
3827
3828 }
3832 CatalogItem::Table(_)
3833 | CatalogItem::Source(_)
3834 | CatalogItem::Log(_)
3835 | CatalogItem::View(_)
3836 | CatalogItem::Sink(_)
3837 | CatalogItem::Type(_)
3838 | CatalogItem::Func(_)
3839 | CatalogItem::Secret(_)
3840 | CatalogItem::Connection(_) => (),
3841 }
3842 }
3843
3844 Ok(uncached_expressions)
3845 }
3846
3847 async fn bootstrap_dataflow_as_ofs(&mut self) -> BTreeMap<GlobalId, ReadHold> {
3857 let mut catalog_ids = Vec::new();
3858 let mut dataflows = Vec::new();
3859 let mut read_policies = BTreeMap::new();
3860 for entry in self.catalog.entries() {
3861 let gid = match entry.item() {
3862 CatalogItem::Index(idx) => idx.global_id(),
3863 CatalogItem::MaterializedView(mv) => mv.global_id_writes(),
3864 CatalogItem::MetricSink(metric_sink) => metric_sink.global_id,
3865 CatalogItem::Table(_)
3866 | CatalogItem::Source(_)
3867 | CatalogItem::Log(_)
3868 | CatalogItem::View(_)
3869 | CatalogItem::Sink(_)
3870 | CatalogItem::Type(_)
3871 | CatalogItem::Func(_)
3872 | CatalogItem::Secret(_)
3873 | CatalogItem::Connection(_) => continue,
3874 };
3875 if let Some(plan) = self.catalog.try_get_physical_plan(&gid) {
3876 catalog_ids.push(gid);
3877 dataflows.push(plan.clone());
3878
3879 if let Some(compaction_window) = entry.item().initial_logical_compaction_window() {
3880 read_policies.insert(gid, compaction_window.into());
3881 }
3882 }
3883 }
3884
3885 let read_ts = self.get_local_read_ts().await;
3886 let read_holds = as_of_selection::run(
3887 &mut dataflows,
3888 &read_policies,
3889 &*self.controller.storage_collections,
3890 read_ts,
3891 self.controller.read_only(),
3892 );
3893
3894 let catalog = self.catalog_mut();
3895 for (id, plan) in catalog_ids.into_iter().zip_eq(dataflows) {
3896 catalog.set_physical_plan(id, plan);
3897 }
3898
3899 read_holds
3900 }
3901
3902 fn serve(
3911 mut self,
3912 mut internal_cmd_rx: mpsc::UnboundedReceiver<Message>,
3913 mut strict_serializable_reads_rx: mpsc::UnboundedReceiver<(ConnectionId, PendingReadTxn)>,
3914 mut cmd_rx: mpsc::UnboundedReceiver<(OpenTelemetryContext, Command)>,
3915 group_commit_rx: appends::GroupCommitWaiter,
3916 ) -> LocalBoxFuture<'static, ()> {
3917 async move {
3918 let mut cluster_events = self.controller.events_stream();
3920 let last_message = Arc::new(Mutex::new(LastMessage {
3921 kind: "none",
3922 stmt: None,
3923 }));
3924
3925 let (idle_tx, mut idle_rx) = tokio::sync::mpsc::channel(1);
3926 let idle_metric = self.metrics.queue_busy_seconds.clone();
3927 let last_message_watchdog = Arc::clone(&last_message);
3928
3929 spawn(|| "coord watchdog", async move {
3930 let mut interval = tokio::time::interval(Duration::from_secs(5));
3935 interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Delay);
3939
3940 let mut coord_stuck = false;
3942
3943 loop {
3944 interval.tick().await;
3945
3946 let duration = tokio::time::Duration::from_secs(30);
3948 let timeout = tokio::time::timeout(duration, idle_tx.reserve()).await;
3949 let Ok(maybe_permit) = timeout else {
3950 if !coord_stuck {
3952 let last_message = last_message_watchdog.lock().expect("poisoned");
3953 tracing::warn!(
3954 last_message_kind = %last_message.kind,
3955 last_message_sql = %last_message.stmt_to_string(),
3956 "coordinator stuck for {duration:?}",
3957 );
3958 }
3959 coord_stuck = true;
3960
3961 continue;
3962 };
3963
3964 if coord_stuck {
3966 tracing::info!("Coordinator became unstuck");
3967 }
3968 coord_stuck = false;
3969
3970 let Ok(permit) = maybe_permit else {
3972 break;
3973 };
3974
3975 permit.send(idle_metric.start_timer());
3976 }
3977 });
3978
3979 self.schedule_storage_usage_collection().await;
3980 self.schedule_arrangement_sizes_collection();
3981 self.schedule_hydration_history_collection();
3982 self.spawn_privatelink_vpc_endpoints_watch_task();
3983 self.spawn_statement_logging_task();
3984 self.spawn_catalog_info_metrics_task();
3985 self.spawn_cluster_controller_task();
3986 self.spawn_caught_up_check_task();
3987 flags::tracing_config(self.catalog.system_config()).apply(&self.tracing_handle);
3988
3989 let warn_threshold = self
3991 .catalog()
3992 .system_config()
3993 .coord_slow_message_warn_threshold();
3994
3995 const MESSAGE_BATCH: usize = 64;
3997 let mut messages = Vec::with_capacity(MESSAGE_BATCH);
3998 let mut cmd_messages = Vec::with_capacity(MESSAGE_BATCH);
3999
4000 let message_batch = self.metrics.message_batch.clone();
4001
4002 let linearize_reads_notify = Arc::clone(&self.linearize_reads_notify);
4009 let linearize_reads_notified = linearize_reads_notify.notified();
4010 tokio::pin!(linearize_reads_notified);
4011
4012 loop {
4013 select! {
4017 biased;
4022
4023 _ = internal_cmd_rx.recv_many(&mut messages, MESSAGE_BATCH) => {},
4027 Some(event) = cluster_events.next() => {
4031 messages.push(Message::ClusterEvent(event))
4032 },
4033 () = self.controller.ready() => {
4037 let controller = match self.controller.get_readiness() {
4041 Readiness::Storage => ControllerReadiness::Storage,
4042 Readiness::Compute => ControllerReadiness::Compute,
4043 Readiness::Metrics(_) => ControllerReadiness::Metrics,
4044 Readiness::Internal(_) => ControllerReadiness::Internal,
4045 Readiness::NotReady => unreachable!("just signaled as ready"),
4046 };
4047 messages.push(Message::ControllerReady { controller });
4048 }
4049 permit = group_commit_rx.ready() => {
4052 let user_write_spans = self.pending_writes.iter().flat_map(|x| match x {
4058 PendingWriteTxn::User { span, .. } => Some(span),
4059 PendingWriteTxn::System { .. } => None,
4060 });
4061 let span = match user_write_spans.exactly_one() {
4062 Ok(span) => span.clone(),
4063 Err(user_write_spans) => {
4064 let span = info_span!(parent: None, "group_commit_notify");
4065 for s in user_write_spans {
4066 span.follows_from(s);
4067 }
4068 span
4069 }
4070 };
4071 messages.push(Message::GroupCommitInitiate(span, Some(permit)));
4072 },
4073 count = cmd_rx.recv_many(&mut cmd_messages, MESSAGE_BATCH) => {
4077 if count == 0 {
4078 break;
4079 } else {
4080 messages.extend(cmd_messages.drain(..).map(
4081 |(otel_ctx, cmd)| Message::Command(otel_ctx, cmd),
4082 ));
4083 }
4084 },
4085 Some(pending_read_txn) = strict_serializable_reads_rx.recv() => {
4089 let mut pending_read_txns = vec![pending_read_txn];
4090 while let Ok(pending_read_txn) = strict_serializable_reads_rx.try_recv() {
4091 pending_read_txns.push(pending_read_txn);
4092 }
4093 for (conn_id, pending_read_txn) in pending_read_txns {
4094 let prev = self
4095 .pending_linearize_read_txns
4096 .insert(conn_id, pending_read_txn);
4097 soft_assert_or_log!(
4098 prev.is_none(),
4099 "connections can not have multiple concurrent reads, prev: {prev:?}"
4100 )
4101 }
4102 messages.push(Message::LinearizeReads);
4103 }
4104 _ = self.advance_timelines_interval.tick() => {
4108 if self.controller.read_only() {
4112 messages.push(Message::AdvanceTimelines);
4113 } else {
4114 self.group_commit_tx.notify();
4115 }
4116 },
4117 () = linearize_reads_notified.as_mut() => {
4128 linearize_reads_notified.set(linearize_reads_notify.notified());
4129 messages.push(Message::LinearizeReads);
4130 }
4131
4132 timer = idle_rx.recv() => {
4137 timer.expect("does not drop").observe_duration();
4138 self.metrics
4139 .message_handling
4140 .with_label_values(&["watchdog"])
4141 .observe(0.0);
4142 continue;
4143 }
4144 };
4145
4146 message_batch.observe(f64::cast_lossy(messages.len()));
4148
4149 for msg in messages.drain(..) {
4150 let msg_kind = msg.kind();
4153 let span = span!(
4154 target: "mz_adapter::coord::handle_message_loop",
4155 Level::INFO,
4156 "coord::handle_message",
4157 kind = msg_kind
4158 );
4159 let otel_context = span.context().span().span_context().clone();
4160
4161 *last_message.lock().expect("poisoned") = LastMessage {
4165 kind: msg_kind,
4166 stmt: match &msg {
4167 Message::Command(
4168 _,
4169 Command::Execute {
4170 portal_name,
4171 session,
4172 ..
4173 },
4174 ) => session
4175 .get_portal_unverified(portal_name)
4176 .and_then(|p| p.stmt.as_ref().map(Arc::clone)),
4177 _ => None,
4178 },
4179 };
4180
4181 let start = Instant::now();
4182 self.handle_message(msg).instrument(span).await;
4183 let duration = start.elapsed();
4184
4185 self.metrics
4186 .message_handling
4187 .with_label_values(&[msg_kind])
4188 .observe(duration.as_secs_f64());
4189
4190 if duration > warn_threshold {
4192 let trace_id = otel_context.is_valid().then(|| otel_context.trace_id());
4193 tracing::error!(
4194 ?msg_kind,
4195 ?trace_id,
4196 ?duration,
4197 "very slow coordinator message"
4198 );
4199 }
4200 }
4201 }
4202
4203 if let Some(sweep) = self.hydration_history_sweep.take() {
4207 sweep.abort_and_wait().await;
4208 }
4209
4210 if let Some(catalog) = Arc::into_inner(self.catalog) {
4213 catalog.expire().await;
4214 }
4215 }
4216 .boxed_local()
4217 }
4218
4219 fn catalog(&self) -> &Catalog {
4221 &self.catalog
4222 }
4223
4224 fn owned_catalog(&self) -> Arc<Catalog> {
4227 Arc::clone(&self.catalog)
4228 }
4229
4230 fn optimizer_metrics(&self) -> OptimizerMetrics {
4233 self.optimizer_metrics.clone()
4234 }
4235
4236 fn catalog_mut(&mut self) -> &mut Catalog {
4238 Arc::make_mut(&mut self.catalog)
4246 }
4247
4248 async fn refill_user_id_pool(&mut self, min_count: u64) -> Result<(), AdapterError> {
4253 let batch_size = USER_ID_POOL_BATCH_SIZE.get(self.catalog().system_config().dyncfgs());
4254 let to_allocate = min_count.max(u64::from(batch_size));
4255 let id_ts = self.get_catalog_write_ts().await;
4256 let ids = self.catalog().allocate_user_ids(to_allocate, id_ts).await?;
4257 if let (Some((first_id, _)), Some((last_id, _))) = (ids.first(), ids.last()) {
4258 let start = match first_id {
4259 CatalogItemId::User(id) => *id,
4260 other => {
4261 return Err(AdapterError::Internal(format!(
4262 "expected User CatalogItemId, got {other:?}"
4263 )));
4264 }
4265 };
4266 let end = match last_id {
4267 CatalogItemId::User(id) => *id + 1, other => {
4269 return Err(AdapterError::Internal(format!(
4270 "expected User CatalogItemId, got {other:?}"
4271 )));
4272 }
4273 };
4274 self.user_id_pool.refill(start, end);
4275 } else {
4276 return Err(AdapterError::Internal(
4277 "catalog returned no user IDs".into(),
4278 ));
4279 }
4280 Ok(())
4281 }
4282
4283 async fn allocate_user_id(&mut self) -> Result<(CatalogItemId, GlobalId), AdapterError> {
4285 if let Some(id) = self.user_id_pool.allocate() {
4286 return Ok((CatalogItemId::User(id), GlobalId::User(id)));
4287 }
4288 self.refill_user_id_pool(1).await?;
4289 let id = self.user_id_pool.allocate().expect("ID pool just refilled");
4290 Ok((CatalogItemId::User(id), GlobalId::User(id)))
4291 }
4292
4293 async fn allocate_user_ids(
4295 &mut self,
4296 count: u64,
4297 ) -> Result<Vec<(CatalogItemId, GlobalId)>, AdapterError> {
4298 if self.user_id_pool.remaining() < count {
4299 self.refill_user_id_pool(count).await?;
4300 }
4301 let raw_ids = self
4302 .user_id_pool
4303 .allocate_many(count)
4304 .expect("pool has enough IDs after refill");
4305 Ok(raw_ids
4306 .into_iter()
4307 .map(|id| (CatalogItemId::User(id), GlobalId::User(id)))
4308 .collect())
4309 }
4310
4311 fn connection_context(&self) -> &ConnectionContext {
4313 self.controller.connection_context()
4314 }
4315
4316 fn secrets_reader(&self) -> &Arc<dyn SecretsReader> {
4318 &self.connection_context().secrets_reader
4319 }
4320
4321 #[allow(dead_code)]
4326 pub(crate) fn broadcast_notice(&self, notice: AdapterNotice) {
4327 for meta in self.active_conns.values() {
4328 let _ = meta.notice_tx.send(notice.clone());
4329 }
4330 }
4331
4332 pub(crate) fn broadcast_notice_tx(
4335 &self,
4336 ) -> Box<dyn FnOnce(AdapterNotice) -> () + Send + 'static> {
4337 let senders: Vec<_> = self
4338 .active_conns
4339 .values()
4340 .map(|meta| meta.notice_tx.clone())
4341 .collect();
4342 Box::new(move |notice| {
4343 for tx in senders {
4344 let _ = tx.send(notice.clone());
4345 }
4346 })
4347 }
4348
4349 pub(crate) fn active_conns(&self) -> &BTreeMap<ConnectionId, ConnMeta> {
4350 &self.active_conns
4351 }
4352
4353 #[instrument(level = "debug")]
4354 pub(crate) fn retire_execution(
4355 &mut self,
4356 reason: StatementEndedExecutionReason,
4357 ctx_extra: ExecuteContextExtra,
4358 ) {
4359 if let Some(uuid) = ctx_extra.retire() {
4360 let ended_at = self.now();
4361 self.end_statement_execution(uuid, reason, ended_at);
4362 }
4363 }
4364
4365 #[instrument(level = "debug")]
4367 pub fn dataflow_builder(&self, instance: ComputeInstanceId) -> DataflowBuilder<'_> {
4368 let compute = self
4369 .instance_snapshot(instance)
4370 .expect("compute instance does not exist");
4371 DataflowBuilder::new(self.catalog().state(), compute)
4372 }
4373
4374 pub fn instance_snapshot(
4376 &self,
4377 id: ComputeInstanceId,
4378 ) -> Result<ComputeInstanceSnapshot, InstanceMissing> {
4379 ComputeInstanceSnapshot::new(&self.controller, id)
4380 }
4381
4382 pub(crate) async fn ship_dataflow(
4389 &mut self,
4390 dataflow: LirDataflowDescription,
4391 instance: ComputeInstanceId,
4392 target_replica: Option<ReplicaId>,
4393 ) {
4394 self.try_ship_dataflow(dataflow, instance, target_replica)
4395 .await
4396 .unwrap_or_terminate("dataflow creation cannot fail");
4397 }
4398
4399 pub(crate) async fn try_ship_dataflow(
4402 &mut self,
4403 dataflow: LirDataflowDescription,
4404 instance: ComputeInstanceId,
4405 target_replica: Option<ReplicaId>,
4406 ) -> Result<(), DataflowCreationError> {
4407 let export_ids = dataflow.exported_index_ids().collect();
4410
4411 self.controller
4412 .compute
4413 .create_dataflow(instance, dataflow, target_replica)?;
4414
4415 self.initialize_compute_read_policies(export_ids, instance, CompactionWindow::Default)
4416 .await;
4417
4418 Ok(())
4419 }
4420
4421 pub(crate) fn allow_writes(&mut self, instance: ComputeInstanceId, id: GlobalId) {
4425 self.controller
4426 .compute
4427 .allow_writes(instance, id)
4428 .unwrap_or_terminate("allow_writes cannot fail");
4429 }
4430
4431 pub(crate) async fn ship_dataflow_and_notice_builtin_table_updates(
4433 &mut self,
4434 dataflow: LirDataflowDescription,
4435 instance: ComputeInstanceId,
4436 notice_builtin_updates_fut: Option<BuiltinTableAppendNotify>,
4437 target_replica: Option<ReplicaId>,
4438 ) {
4439 if let Some(notice_builtin_updates_fut) = notice_builtin_updates_fut {
4440 let ship_dataflow_fut = self.ship_dataflow(dataflow, instance, target_replica);
4441 let ((), ()) =
4442 futures::future::join(notice_builtin_updates_fut, ship_dataflow_fut).await;
4443 } else {
4444 self.ship_dataflow(dataflow, instance, target_replica).await;
4445 }
4446 }
4447
4448 pub fn install_compute_watch_set(
4452 &mut self,
4453 conn_id: ConnectionId,
4454 objects: BTreeSet<GlobalId>,
4455 t: Timestamp,
4456 state: WatchSetResponse,
4457 ) -> Result<(), CollectionLookupError> {
4458 let ws_id = self.controller.install_compute_watch_set(objects, t)?;
4459 self.connection_watch_sets
4460 .entry(conn_id.clone())
4461 .or_default()
4462 .insert(ws_id);
4463 self.installed_watch_sets.insert(ws_id, (conn_id, state));
4464 Ok(())
4465 }
4466
4467 pub fn install_storage_watch_set(
4471 &mut self,
4472 conn_id: ConnectionId,
4473 objects: BTreeSet<GlobalId>,
4474 t: Timestamp,
4475 state: WatchSetResponse,
4476 ) -> Result<(), CollectionMissing> {
4477 let ws_id = self.controller.install_storage_watch_set(objects, t)?;
4478 self.connection_watch_sets
4479 .entry(conn_id.clone())
4480 .or_default()
4481 .insert(ws_id);
4482 self.installed_watch_sets.insert(ws_id, (conn_id, state));
4483 Ok(())
4484 }
4485
4486 pub fn cancel_pending_watchsets(&mut self, conn_id: &ConnectionId) {
4488 if let Some(ws_ids) = self.connection_watch_sets.remove(conn_id) {
4489 for ws_id in ws_ids {
4490 self.installed_watch_sets.remove(&ws_id);
4491 }
4492 }
4493 }
4494
4495 pub async fn dump(&self) -> Result<serde_json::Value, anyhow::Error> {
4499 let global_timelines: BTreeMap<_, _> = self
4505 .global_timelines
4506 .iter()
4507 .map(|(timeline, state)| (timeline.to_string(), format!("{state:?}")))
4508 .collect();
4509 let active_conns: BTreeMap<_, _> = self
4510 .active_conns
4511 .iter()
4512 .map(|(id, meta)| (id.unhandled().to_string(), format!("{meta:?}")))
4513 .collect();
4514 let txn_read_holds: BTreeMap<_, _> = self
4515 .txn_read_holds
4516 .iter()
4517 .map(|(id, capability)| (id.unhandled().to_string(), format!("{capability:?}")))
4518 .collect();
4519 let pending_peeks: BTreeMap<_, _> = self
4520 .pending_peeks
4521 .iter()
4522 .map(|(id, peek)| (id.to_string(), format!("{peek:?}")))
4523 .collect();
4524 let client_pending_peeks: BTreeMap<_, _> = self
4525 .client_pending_peeks
4526 .iter()
4527 .map(|(id, peek)| {
4528 let peek: BTreeMap<_, _> = peek
4529 .iter()
4530 .map(|(uuid, storage_id)| (uuid.to_string(), storage_id))
4531 .collect();
4532 (id.to_string(), peek)
4533 })
4534 .collect();
4535 let pending_linearize_read_txns: BTreeMap<_, _> = self
4536 .pending_linearize_read_txns
4537 .iter()
4538 .map(|(id, read_txn)| (id.unhandled().to_string(), format!("{read_txn:?}")))
4539 .collect();
4540
4541 Ok(serde_json::json!({
4542 "global_timelines": global_timelines,
4543 "active_conns": active_conns,
4544 "txn_read_holds": txn_read_holds,
4545 "pending_peeks": pending_peeks,
4546 "client_pending_peeks": client_pending_peeks,
4547 "pending_linearize_read_txns": pending_linearize_read_txns,
4548 "controller": self.controller.dump().await?,
4549 }))
4550 }
4551
4552 async fn prune_storage_usage_events_on_startup(&self, retention_period: Duration) {
4566 let item_id = self
4567 .catalog()
4568 .resolve_builtin_table(&MZ_STORAGE_USAGE_BY_SHARD);
4569 let global_id = self.catalog.get_entry(&item_id).latest_global_id();
4570 let read_ts = self.get_local_read_ts().await;
4571 let current_contents_fut = self
4572 .controller
4573 .storage_collections
4574 .snapshot(global_id, read_ts);
4575 let internal_cmd_tx = self.internal_cmd_tx.clone();
4576 spawn(|| "storage_usage_prune", async move {
4577 let mut current_contents = current_contents_fut
4578 .await
4579 .unwrap_or_terminate("cannot fail to fetch snapshot");
4580 differential_dataflow::consolidation::consolidate(&mut current_contents);
4581
4582 let cutoff_ts = u128::from(read_ts).saturating_sub(retention_period.as_millis());
4583 let mut expired = Vec::new();
4584 for (row, diff) in current_contents {
4585 assert_eq!(
4586 diff, 1,
4587 "consolidated contents should not contain retractions: ({row:#?}, {diff:#?})"
4588 );
4589 let collection_timestamp = row
4591 .unpack()
4592 .get(3)
4593 .expect("definition of mz_storage_by_shard changed")
4594 .unwrap_timestamptz();
4595 let collection_timestamp = collection_timestamp.timestamp_millis();
4596 let collection_timestamp: u128 = collection_timestamp
4597 .try_into()
4598 .expect("all collections happen after Jan 1 1970");
4599 if collection_timestamp < cutoff_ts {
4600 debug!("pruning storage event {row:?}");
4601 let builtin_update = BuiltinTableUpdate::row(item_id, row, Diff::MINUS_ONE);
4602 expired.push(builtin_update);
4603 }
4604 }
4605
4606 let _ = internal_cmd_tx.send(Message::StorageUsagePrune(expired));
4608 });
4609 }
4610
4611 async fn prune_arrangement_sizes_history_on_startup(&self) {
4620 if self.controller.read_only() {
4622 return;
4623 }
4624
4625 let retention_period = mz_adapter_types::dyncfgs::ARRANGEMENT_SIZE_HISTORY_RETENTION_PERIOD
4626 .get(self.catalog().system_config().dyncfgs());
4627 let item_id = self
4628 .catalog()
4629 .resolve_builtin_table(&mz_catalog::builtin::MZ_OBJECT_ARRANGEMENT_SIZE_HISTORY);
4630 let global_id = self.catalog.get_entry(&item_id).latest_global_id();
4631 let read_ts = self.get_local_read_ts().await;
4632 let current_contents_fut = self
4633 .controller
4634 .storage_collections
4635 .snapshot(global_id, read_ts);
4636 let internal_cmd_tx = self.internal_cmd_tx.clone();
4637 spawn(|| "arrangement_sizes_history_prune", async move {
4638 let mut current_contents = current_contents_fut
4639 .await
4640 .unwrap_or_terminate("cannot fail to fetch snapshot");
4641 differential_dataflow::consolidation::consolidate(&mut current_contents);
4642
4643 let cutoff_ts = u128::from(read_ts).saturating_sub(retention_period.as_millis());
4644 let expired =
4645 arrangement_sizes_expired_retractions(current_contents, cutoff_ts, item_id);
4646
4647 let _ = internal_cmd_tx.send(Message::ArrangementSizesPrune(expired));
4651 });
4652 }
4653
4654 fn current_credit_consumption_rate(&self, exclude_cluster: Option<ClusterId>) -> Numeric {
4657 self.catalog()
4658 .user_cluster_replicas()
4659 .filter(|replica| Some(replica.cluster_id) != exclude_cluster)
4660 .filter_map(|replica| match &replica.config.location {
4661 ReplicaLocation::Managed(location) => Some(self.replica_credits_per_hour(location)),
4662 ReplicaLocation::Unmanaged(_) => None,
4663 })
4664 .sum()
4665 }
4666
4667 fn replica_credits_per_hour(&self, location: &ManagedReplicaLocation) -> Numeric {
4674 let size = location.size_for_billing();
4675 match self.catalog().cluster_replica_sizes().0.get(size) {
4676 Some(allocation) => allocation.credits_per_hour,
4677 None => {
4678 soft_panic_or_log!(
4679 "replica of size {:?} bills as unknown replica size {:?}, counting it as free",
4680 location.size,
4681 size,
4682 );
4683 Numeric::zero()
4684 }
4685 }
4686 }
4687}
4688
4689fn arrangement_sizes_expired_retractions(
4699 rows: impl IntoIterator<Item = (mz_repr::Row, i64)>,
4700 cutoff_ts: u128,
4701 item_id: CatalogItemId,
4702) -> Vec<BuiltinTableUpdate> {
4703 let mut expired = Vec::new();
4704 for (row, diff) in rows {
4705 if diff != 1 {
4706 soft_panic_or_log!(
4707 "consolidated contents should only contain diff 1: ({row:#?}, {diff:#?})"
4708 );
4709 }
4710 let collection_timestamp = row
4711 .unpack()
4712 .get(3)
4713 .expect("definition of mz_object_arrangement_size_history changed")
4714 .unwrap_timestamptz()
4715 .timestamp_millis();
4716 let collection_timestamp: u128 = collection_timestamp
4717 .try_into()
4718 .expect("all collections happen after Jan 1 1970");
4719 if collection_timestamp < cutoff_ts {
4720 expired.push(BuiltinTableUpdate::row(item_id, row, -Diff::from(diff)));
4721 }
4722 }
4723 expired
4724}
4725
4726#[cfg(test)]
4727impl Coordinator {
4728 #[allow(dead_code)]
4729 async fn verify_ship_dataflow_no_error(&mut self, dataflow: LirDataflowDescription) {
4730 let compute_instance = ComputeInstanceId::user(1).expect("1 is a valid ID");
4738
4739 let _: () = self.ship_dataflow(dataflow, compute_instance, None).await;
4740 }
4741}
4742
4743struct LastMessage {
4745 kind: &'static str,
4746 stmt: Option<Arc<Statement<Raw>>>,
4747}
4748
4749impl LastMessage {
4750 fn stmt_to_string(&self) -> Cow<'static, str> {
4752 self.stmt
4753 .as_ref()
4754 .map(|stmt| truncate_sql_for_logging(stmt.to_ast_string_redacted()).into())
4755 .unwrap_or(Cow::Borrowed("<none>"))
4756 }
4757}
4758
4759impl fmt::Debug for LastMessage {
4760 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
4761 f.debug_struct("LastMessage")
4762 .field("kind", &self.kind)
4763 .field("stmt", &self.stmt_to_string())
4764 .finish()
4765 }
4766}
4767
4768impl Drop for LastMessage {
4769 fn drop(&mut self) {
4770 if std::thread::panicking() {
4772 eprintln!("Coordinator panicking, dumping last message\n{self:?}",);
4774 }
4775 }
4776}
4777
4778pub fn serve(
4790 Config {
4791 controller_config,
4792 controller_envd_epoch,
4793 mut storage,
4794 timestamp_oracle_url,
4795 unsafe_mode,
4796 all_features,
4797 build_info,
4798 environment_id,
4799 metrics_registry,
4800 now,
4801 secrets_controller,
4802 cloud_resource_controller,
4803 cluster_replica_sizes,
4804 builtin_system_cluster_config,
4805 builtin_catalog_server_cluster_config,
4806 builtin_probe_cluster_config,
4807 builtin_support_cluster_config,
4808 builtin_analytics_cluster_config,
4809 system_parameter_defaults,
4810 availability_zones,
4811 storage_usage_client,
4812 storage_usage_collection_interval,
4813 storage_usage_retention_period,
4814 segment_client,
4815 egress_addresses,
4816 aws_account_id,
4817 aws_privatelink_availability_zones,
4818 connection_context,
4819 connection_limit_callback,
4820 remote_system_parameters,
4821 webhook_concurrency_limit,
4822 http_host_name,
4823 tracing_handle,
4824 read_only_controllers,
4825 caught_up_trigger: clusters_caught_up_trigger,
4826 helm_chart_version,
4827 license_key,
4828 external_login_password_mz_system,
4829 force_builtin_schema_migration,
4830 }: Config,
4831) -> BoxFuture<'static, Result<(Handle, Client), AdapterError>> {
4832 async move {
4833 let coord_start = Instant::now();
4834 info!("startup: coordinator init: beginning");
4835 info!("startup: coordinator init: preamble beginning");
4836
4837 let _builtins = LazyLock::force(&BUILTINS_STATIC);
4841
4842 let (cmd_tx, cmd_rx) = mpsc::unbounded_channel();
4843 let (internal_cmd_tx, internal_cmd_rx) = mpsc::unbounded_channel();
4844 let (strict_serializable_reads_tx, strict_serializable_reads_rx) =
4845 mpsc::unbounded_channel();
4846
4847 if !availability_zones.iter().all_unique() {
4849 coord_bail!("availability zones must be unique");
4850 }
4851
4852 let aws_principal_context = match (
4853 aws_account_id,
4854 connection_context.aws_external_id_prefix.clone(),
4855 ) {
4856 (Some(aws_account_id), Some(aws_external_id_prefix)) => Some(AwsPrincipalContext {
4857 aws_account_id,
4858 aws_external_id_prefix,
4859 }),
4860 _ => None,
4861 };
4862
4863 let aws_privatelink_availability_zones = aws_privatelink_availability_zones
4864 .map(|azs_vec| BTreeSet::from_iter(azs_vec.iter().cloned()));
4865
4866 info!(
4867 "startup: coordinator init: preamble complete in {:?}",
4868 coord_start.elapsed()
4869 );
4870 let oracle_init_start = Instant::now();
4871 info!("startup: coordinator init: timestamp oracle init beginning");
4872
4873 let timestamp_oracle_config = timestamp_oracle_url
4874 .map(|url| TimestampOracleConfig::from_url(&url, &metrics_registry))
4875 .transpose()?;
4876 let mut initial_timestamps =
4877 get_initial_oracle_timestamps(×tamp_oracle_config).await?;
4878
4879 initial_timestamps
4883 .entry(Timeline::EpochMilliseconds)
4884 .or_insert_with(mz_repr::Timestamp::minimum);
4885 let mut timestamp_oracles = BTreeMap::new();
4886 for (timeline, initial_timestamp) in initial_timestamps {
4887 Coordinator::ensure_timeline_state_with_initial_time(
4888 &timeline,
4889 initial_timestamp,
4890 now.clone(),
4891 timestamp_oracle_config.clone(),
4892 &mut timestamp_oracles,
4893 read_only_controllers,
4894 )
4895 .await;
4896 }
4897
4898 let catalog_upper = storage.current_upper().await;
4902 let epoch_millis_oracle = ×tamp_oracles
4908 .get(&Timeline::EpochMilliseconds)
4909 .expect("inserted above")
4910 .oracle;
4911
4912 let boot_now: mz_repr::Timestamp = (now)().into();
4917 if catalog_upper > timeline::write_ts_upper_bound(&boot_now) {
4918 tracing::error!(
4919 %catalog_upper, %boot_now,
4920 "catalog upper is far ahead of the wall clock, so writes and \
4921 strict-serializable reads on the EpochMilliseconds timeline will block \
4922 until the clock catches up",
4923 );
4924 }
4925
4926 let mut boot_ts = if read_only_controllers {
4927 let read_ts = epoch_millis_oracle.read_ts().await;
4928 std::cmp::max(read_ts, catalog_upper)
4929 } else {
4930 epoch_millis_oracle.apply_write(catalog_upper).await;
4933 epoch_millis_oracle.write_ts().await.timestamp
4934 };
4935
4936 info!(
4937 "startup: coordinator init: timestamp oracle init complete in {:?}",
4938 oracle_init_start.elapsed()
4939 );
4940
4941 let catalog_open_start = Instant::now();
4942 info!("startup: coordinator init: catalog open beginning");
4943 let persist_client = controller_config
4944 .persist_clients
4945 .open(controller_config.persist_location.clone())
4946 .await
4947 .context("opening persist client")?;
4948 let builtin_item_migration_config =
4949 BuiltinItemMigrationConfig {
4950 persist_client: persist_client.clone(),
4951 read_only: read_only_controllers,
4952 force_migration: force_builtin_schema_migration,
4953 }
4954 ;
4955 let OpenCatalogResult {
4956 mut catalog,
4957 last_seen_version,
4958 migrated_storage_collections_0dt,
4959 new_builtin_collections,
4960 builtin_table_updates,
4961 cached_global_exprs,
4962 uncached_local_exprs,
4963 } = Catalog::open(mz_catalog::config::Config {
4964 storage,
4965 metrics_registry: &metrics_registry,
4966 state: mz_catalog::config::StateConfig {
4967 unsafe_mode,
4968 all_features,
4969 build_info,
4970 environment_id: environment_id.clone(),
4971 read_only: read_only_controllers,
4972 now: now.clone(),
4973 boot_ts: boot_ts.clone(),
4974 skip_migrations: false,
4975 cluster_replica_sizes,
4976 builtin_system_cluster_config,
4977 builtin_catalog_server_cluster_config,
4978 builtin_probe_cluster_config,
4979 builtin_support_cluster_config,
4980 builtin_analytics_cluster_config,
4981 system_parameter_defaults,
4982 remote_system_parameters,
4983 availability_zones,
4984 egress_addresses,
4985 aws_principal_context,
4986 aws_privatelink_availability_zones,
4987 connection_context,
4988 http_host_name,
4989 builtin_item_migration_config,
4990 persist_client: persist_client.clone(),
4991 enable_expression_cache_override: None,
4992 helm_chart_version,
4993 external_login_password_mz_system,
4994 license_key: license_key.clone(),
4995 },
4996 })
4997 .await?;
4998
4999 let catalog_upper = catalog.current_upper().await;
5002 boot_ts = std::cmp::max(boot_ts, catalog_upper);
5003
5004 if !read_only_controllers {
5005 epoch_millis_oracle.apply_write(boot_ts).await;
5006 }
5007
5008 info!(
5009 "startup: coordinator init: catalog open complete in {:?}",
5010 catalog_open_start.elapsed()
5011 );
5012
5013 let hydrate_migrated_mvs = ENABLE_0DT_HYDRATE_MIGRATED_BUILTIN_MVS
5024 .get(catalog.system_config().dyncfgs())
5025 && last_seen_version
5026 .as_ref()
5027 .is_none_or(|version| *version >= MIN_LEADER_VERSION_FOR_MIGRATED_MV_WRITES);
5028
5029 let coord_thread_start = Instant::now();
5030 info!("startup: coordinator init: coordinator thread start beginning");
5031
5032 let session_id = catalog.config().session_id;
5033 let start_instant = catalog.config().start_instant;
5034
5035 let (bootstrap_tx, bootstrap_rx) = oneshot::channel();
5039 let handle = TokioHandle::current();
5040
5041 let metrics = Metrics::register_into(&metrics_registry);
5042 let metrics_clone = metrics.clone();
5043 let optimizer_metrics = OptimizerMetrics::register_into(
5044 &metrics_registry,
5045 catalog.system_config().optimizer_e2e_latency_warning_threshold(),
5046 );
5047 let segment_client_clone = segment_client.clone();
5048 let coord_now = now.clone();
5049 let advance_timelines_interval =
5050 tokio::time::interval(catalog.system_config().default_timestamp_interval());
5051
5052 let clusters_caught_up_check =
5053 clusters_caught_up_trigger.map(|trigger| {
5054 let mut exclude_collections: BTreeSet<GlobalId> =
5055 new_builtin_collections.iter().copied().collect();
5056
5057 let new_builtin_items = new_builtin_collections.iter().map(|global_id| {
5072 catalog
5073 .state()
5074 .try_get_entry_by_global_id(global_id)
5075 .expect("new builtin collections have catalog entries")
5076 .id()
5077 });
5078 let frozen_migrated_mvs = migrated_storage_collections_0dt
5079 .iter()
5080 .copied()
5081 .filter(|_| !hydrate_migrated_mvs)
5082 .filter(|id| catalog.state().get_entry(id).is_materialized_view());
5083 let mut todo: Vec<_> = new_builtin_items.chain(frozen_migrated_mvs).collect();
5084 while let Some(item_id) = todo.pop() {
5085 let entry = catalog.state().get_entry(&item_id);
5086 exclude_collections.extend(entry.global_ids());
5087 todo.extend_from_slice(entry.used_by());
5088 }
5089
5090 CaughtUpCheckContext {
5091 trigger,
5092 exclude_collections,
5093 }
5094 });
5095
5096 if let Some(TimestampOracleConfig::Postgres(pg_config)) =
5097 timestamp_oracle_config.as_ref()
5098 {
5099 let pg_timestamp_oracle_params =
5102 flags::timestamp_oracle_config(catalog.system_config());
5103 pg_timestamp_oracle_params.apply(pg_config);
5104 }
5105
5106 let connection_limit_callback: Arc<dyn Fn(&SystemVars) + Send + Sync> =
5109 Arc::new(move |system_vars: &SystemVars| {
5110 let limit: u64 = system_vars.max_connections().cast_into();
5111 let superuser_reserved: u64 =
5112 system_vars.superuser_reserved_connections().cast_into();
5113
5114 let superuser_reserved = if superuser_reserved >= limit {
5119 tracing::warn!(
5120 "superuser_reserved ({superuser_reserved}) is greater than max connections ({limit})!"
5121 );
5122 limit
5123 } else {
5124 superuser_reserved
5125 };
5126
5127 (connection_limit_callback)(limit, superuser_reserved);
5128 });
5129 catalog.system_config_mut().register_callback(
5130 &mz_sql::session::vars::MAX_CONNECTIONS,
5131 Arc::clone(&connection_limit_callback),
5132 );
5133 catalog.system_config_mut().register_callback(
5134 &mz_sql::session::vars::SUPERUSER_RESERVED_CONNECTIONS,
5135 connection_limit_callback,
5136 );
5137
5138 let (group_commit_tx, group_commit_rx) = appends::notifier();
5139
5140 let parent_span = tracing::Span::current();
5141 let thread = thread::Builder::new()
5142 .stack_size(3 * stack::STACK_SIZE)
5146 .name("coordinator".to_string())
5147 .spawn(move || {
5148 let span = info_span!(parent: parent_span, "coord::coordinator").entered();
5149
5150 let controller = handle
5151 .block_on({
5152 catalog.initialize_controller(
5153 controller_config,
5154 controller_envd_epoch,
5155 read_only_controllers,
5156 )
5157 })
5158 .unwrap_or_terminate("failed to initialize storage_controller");
5159 let catalog_upper = handle.block_on(catalog.current_upper());
5162 boot_ts = std::cmp::max(boot_ts, catalog_upper);
5163 if !read_only_controllers {
5164 let epoch_millis_oracle = ×tamp_oracles
5165 .get(&Timeline::EpochMilliseconds)
5166 .expect("inserted above")
5167 .oracle;
5168 handle.block_on(epoch_millis_oracle.apply_write(boot_ts));
5169 }
5170
5171 let catalog = Arc::new(catalog);
5172 let max_concurrent_occ_writes =
5173 usize::cast_from(catalog.system_config().max_concurrent_occ_writes());
5174
5175 let caching_secrets_reader = CachingSecretsReader::new(secrets_controller.reader());
5176 let (group_committer_tx, group_committer_rx) = mpsc::unbounded_channel();
5177 let mut coord = Coordinator {
5178 controller,
5179 catalog,
5180 internal_cmd_tx,
5181 group_commit_tx,
5182 reconcile_now: Arc::new(Notify::new()),
5183 group_committer_tx,
5184 strict_serializable_reads_tx,
5185 linearize_reads_notify: Arc::new(Notify::new()),
5186 global_timelines: timestamp_oracles,
5187 transient_id_gen: Arc::new(TransientIdGen::new()),
5188 active_conns: BTreeMap::new(),
5189 txn_read_holds: Default::default(),
5190 pending_peeks: BTreeMap::new(),
5191 client_pending_peeks: BTreeMap::new(),
5192 pending_linearize_read_txns: BTreeMap::new(),
5193 serialized_ddl: LockedVecDeque::new(),
5194 active_compute_sinks: BTreeMap::new(),
5195 active_webhooks: BTreeMap::new(),
5196 active_copies: BTreeMap::new(),
5197 connection_cancel_watches: BTreeMap::new(),
5198 introspection_subscribes: BTreeMap::new(),
5199 hydration_history_replica_cursor: None,
5200 hydration_history_sweep: None,
5201 metric_sinks: BTreeMap::new(),
5202 metric_sink_plans: BTreeMap::new(),
5203 deferred_plans: BTreeMap::new(),
5204 pending_writes: Vec::new(),
5205 occ_write_semaphore: Arc::new(Semaphore::new(max_concurrent_occ_writes)),
5206 advance_timelines_interval,
5207 secrets_controller,
5208 caching_secrets_reader,
5209 cloud_resource_controller,
5210 storage_usage_client,
5211 storage_usage_collection_interval,
5212 segment_client,
5213 metrics,
5214 catalog_info_metrics_registry: metrics_registry.clone(),
5215 scoped_frontend: None,
5216 optimizer_metrics,
5217 tracing_handle,
5218 statement_logging: StatementLogging::new(coord_now.clone()),
5219 webhook_concurrency_limit,
5220 timestamp_oracle_config,
5221 caught_up_check: clusters_caught_up_check,
5222 installed_watch_sets: BTreeMap::new(),
5223 connection_watch_sets: BTreeMap::new(),
5224 cluster_replica_statuses: ClusterReplicaStatuses::new(),
5225 read_only_controllers,
5226 buffered_builtin_table_updates: Some(Vec::new()),
5227 license_key,
5228 user_id_pool: IdPool::empty(),
5229 persist_client,
5230 };
5231
5232 handle.block_on(async {
5234 appends::spawn_group_committer(
5235 group_committer_rx,
5236 coord.get_local_timestamp_oracle(),
5237 coord.controller.storage.table_write_handle(),
5238 coord.catalog().upper_handle(),
5239 coord.internal_cmd_tx.clone(),
5240 coord.catalog().config().now.clone(),
5241 coord.metrics.clone(),
5242 coord.catalog().system_config().dyncfgs(),
5243 );
5244 });
5245
5246 let bootstrap = handle.block_on(async {
5247 coord
5248 .bootstrap(
5249 boot_ts,
5250 migrated_storage_collections_0dt,
5251 hydrate_migrated_mvs,
5252 builtin_table_updates,
5253 cached_global_exprs,
5254 uncached_local_exprs,
5255 )
5256 .await?;
5257 coord
5258 .controller
5259 .remove_orphaned_replicas(
5260 coord.catalog().get_next_user_replica_id().await?,
5261 coord.catalog().get_next_system_replica_id().await?,
5262 )
5263 .await
5264 .map_err(AdapterError::Orchestrator)?;
5265
5266 if let Some(retention_period) = storage_usage_retention_period {
5267 coord
5268 .prune_storage_usage_events_on_startup(retention_period)
5269 .await;
5270 }
5271
5272 coord.prune_arrangement_sizes_history_on_startup().await;
5273
5274 Ok(())
5275 });
5276 let ok = bootstrap.is_ok();
5277 drop(span);
5278 bootstrap_tx
5279 .send(bootstrap)
5280 .expect("bootstrap_rx is not dropped until it receives this message");
5281 if ok {
5282 handle.block_on(coord.serve(
5283 internal_cmd_rx,
5284 strict_serializable_reads_rx,
5285 cmd_rx,
5286 group_commit_rx,
5287 ));
5288 }
5289 })
5290 .expect("failed to create coordinator thread");
5291 match bootstrap_rx
5292 .await
5293 .expect("bootstrap_tx always sends a message or panics/halts")
5294 {
5295 Ok(()) => {
5296 info!(
5297 "startup: coordinator init: coordinator thread start complete in {:?}",
5298 coord_thread_start.elapsed()
5299 );
5300 info!(
5301 "startup: coordinator init: complete in {:?}",
5302 coord_start.elapsed()
5303 );
5304 let handle = Handle {
5305 session_id,
5306 start_instant,
5307 _thread: thread.join_on_drop(),
5308 };
5309 let client = Client::new(
5310 build_info,
5311 cmd_tx,
5312 metrics_clone,
5313 now,
5314 environment_id,
5315 segment_client_clone,
5316 );
5317 Ok((handle, client))
5318 }
5319 Err(e) => Err(e),
5320 }
5321 }
5322 .boxed()
5323}
5324
5325async fn get_initial_oracle_timestamps(
5339 timestamp_oracle_config: &Option<TimestampOracleConfig>,
5340) -> Result<BTreeMap<Timeline, Timestamp>, AdapterError> {
5341 let mut initial_timestamps = BTreeMap::new();
5342
5343 if let Some(config) = timestamp_oracle_config {
5344 let oracle_timestamps = config.get_all_timelines().await?;
5345
5346 let debug_msg = || {
5347 oracle_timestamps
5348 .iter()
5349 .map(|(timeline, ts)| format!("{:?} -> {}", timeline, ts))
5350 .join(", ")
5351 };
5352 info!(
5353 "current timestamps from the timestamp oracle: {}",
5354 debug_msg()
5355 );
5356
5357 for (timeline, ts) in oracle_timestamps {
5358 let entry = initial_timestamps
5359 .entry(Timeline::from_str(&timeline).expect("could not parse timeline"));
5360
5361 entry
5362 .and_modify(|current_ts| *current_ts = std::cmp::max(*current_ts, ts))
5363 .or_insert(ts);
5364 }
5365 } else {
5366 info!("no timestamp oracle configured!");
5367 };
5368
5369 let debug_msg = || {
5370 initial_timestamps
5371 .iter()
5372 .map(|(timeline, ts)| format!("{:?}: {}", timeline, ts))
5373 .join(", ")
5374 };
5375 info!("initial oracle timestamps: {}", debug_msg());
5376
5377 Ok(initial_timestamps)
5378}
5379
5380#[instrument]
5381pub async fn load_remote_system_parameters(
5382 storage: &mut Box<dyn OpenableDurableCatalogState>,
5383 system_parameter_sync_config: Option<SystemParameterSyncConfig>,
5384 system_parameter_sync_timeout: Duration,
5385) -> Result<RemoteSystemParameters, AdapterError> {
5386 if let Some(system_parameter_sync_config) = system_parameter_sync_config {
5387 tracing::info!("parameter sync on boot: start sync");
5388
5389 let mut params = SynchronizedParameters::new(SystemVars::default());
5429 let frontend_sync = async {
5430 let frontend =
5431 Arc::new(SystemParameterFrontend::from(&system_parameter_sync_config).await?);
5432 frontend.pull(&mut params);
5433 let ops = params
5434 .modified()
5435 .into_iter()
5436 .map(|param| {
5437 let name = param.name;
5438 let value = param.value;
5439 tracing::info!(name, value, initial = true, "sync parameter");
5440 (name, value)
5441 })
5442 .collect();
5443 tracing::info!("parameter sync on boot: end sync");
5444 Ok(RemoteSystemParameters {
5445 params: Some(ops),
5446 frontend: Some(frontend),
5447 })
5448 };
5449 if !storage.has_system_config_synced_once().await? {
5450 frontend_sync.await
5451 } else {
5452 match mz_ore::future::timeout(system_parameter_sync_timeout, frontend_sync).await {
5453 Ok(ops) => Ok(ops),
5454 Err(TimeoutError::Inner(e)) => Err(e),
5455 Err(TimeoutError::DeadlineElapsed) => {
5456 tracing::info!("parameter sync on boot: sync has timed out");
5457 Ok(RemoteSystemParameters::default())
5458 }
5459 }
5460 }
5461 } else {
5462 Ok(RemoteSystemParameters::default())
5463 }
5464}
5465
5466#[derive(Debug, Default)]
5468pub struct RemoteSystemParameters {
5469 pub params: Option<BTreeMap<String, String>>,
5472 pub frontend: Option<Arc<SystemParameterFrontend>>,
5474}
5475
5476#[derive(Debug)]
5477pub enum WatchSetResponse {
5478 StatementDependenciesReady(StatementLoggingId, StatementLifecycleEvent),
5479 AlterSinkReady(AlterSinkReadyContext),
5480 AlterMaterializedViewReady(AlterMaterializedViewReadyContext),
5481}
5482
5483#[derive(Debug)]
5484pub struct AlterSinkReadyContext {
5485 ctx: Option<ExecuteContext>,
5486 otel_ctx: OpenTelemetryContext,
5487 plan: AlterSinkPlan,
5488 plan_validity: PlanValidity,
5489 read_hold: ReadHolds,
5490}
5491
5492impl AlterSinkReadyContext {
5493 fn ctx(&mut self) -> &mut ExecuteContext {
5494 self.ctx.as_mut().expect("only cleared on drop")
5495 }
5496
5497 fn retire(mut self, result: Result<ExecuteResponse, AdapterError>) {
5498 self.ctx
5499 .take()
5500 .expect("only cleared on drop")
5501 .retire(result);
5502 }
5503}
5504
5505impl Drop for AlterSinkReadyContext {
5506 fn drop(&mut self) {
5507 if let Some(ctx) = self.ctx.take() {
5508 ctx.retire(Err(AdapterError::Canceled));
5509 }
5510 }
5511}
5512
5513#[derive(Debug)]
5514pub struct AlterMaterializedViewReadyContext {
5515 ctx: Option<ExecuteContext>,
5516 otel_ctx: OpenTelemetryContext,
5517 plan: plan::AlterMaterializedViewApplyReplacementPlan,
5518 plan_validity: PlanValidity,
5519}
5520
5521impl AlterMaterializedViewReadyContext {
5522 fn ctx(&mut self) -> &mut ExecuteContext {
5523 self.ctx.as_mut().expect("only cleared on drop")
5524 }
5525
5526 fn retire(mut self, result: Result<ExecuteResponse, AdapterError>) {
5527 self.ctx
5528 .take()
5529 .expect("only cleared on drop")
5530 .retire(result);
5531 }
5532}
5533
5534impl Drop for AlterMaterializedViewReadyContext {
5535 fn drop(&mut self) {
5536 if let Some(ctx) = self.ctx.take() {
5537 ctx.retire(Err(AdapterError::Canceled));
5538 }
5539 }
5540}
5541
5542#[derive(Debug)]
5545struct LockedVecDeque<T> {
5546 items: VecDeque<T>,
5547 lock: Arc<tokio::sync::Mutex<()>>,
5548}
5549
5550impl<T> LockedVecDeque<T> {
5551 pub fn new() -> Self {
5552 Self {
5553 items: VecDeque::new(),
5554 lock: Arc::new(tokio::sync::Mutex::new(())),
5555 }
5556 }
5557
5558 pub fn try_lock_owned(&self) -> Result<OwnedMutexGuard<()>, tokio::sync::TryLockError> {
5559 Arc::clone(&self.lock).try_lock_owned()
5560 }
5561
5562 pub fn is_empty(&self) -> bool {
5563 self.items.is_empty()
5564 }
5565
5566 pub fn push_back(&mut self, value: T) {
5567 self.items.push_back(value)
5568 }
5569
5570 pub fn pop_front(&mut self) -> Option<T> {
5571 self.items.pop_front()
5572 }
5573
5574 pub fn remove(&mut self, index: usize) -> Option<T> {
5575 self.items.remove(index)
5576 }
5577
5578 pub fn iter(&self) -> std::collections::vec_deque::Iter<'_, T> {
5579 self.items.iter()
5580 }
5581}
5582
5583#[derive(Debug)]
5584struct DeferredPlanStatement {
5585 ctx: ExecuteContext,
5586 ps: PlanStatement,
5587}
5588
5589#[derive(Debug)]
5590enum PlanStatement {
5591 Statement {
5592 stmt: Arc<Statement<Raw>>,
5593 params: Params,
5594 },
5595 Plan {
5596 plan: mz_sql::plan::Plan,
5597 resolved_ids: ResolvedIds,
5598 sql_impl_resolved_ids: ResolvedIds,
5599 },
5600}
5601
5602#[derive(Debug, Error)]
5603pub enum NetworkPolicyError {
5604 #[error("Access denied for address {0}")]
5605 AddressDenied(IpAddr),
5606 #[error("Access denied missing IP address")]
5607 MissingIp,
5608}
5609
5610pub(crate) fn validate_ip_with_policy_rules(
5611 ip: &IpAddr,
5612 rules: &Vec<NetworkPolicyRule>,
5613) -> Result<(), NetworkPolicyError> {
5614 if rules.iter().any(|r| r.address.0.contains(ip)) {
5617 Ok(())
5618 } else {
5619 Err(NetworkPolicyError::AddressDenied(ip.clone()))
5620 }
5621}
5622
5623pub(crate) fn infer_sql_type_for_catalog(
5624 hir_expr: &HirRelationExpr,
5625 mir_expr: &MirRelationExpr,
5626) -> SqlRelationType {
5627 let mut typ = hir_expr.top_level_typ();
5628 typ.backport_nullability_and_keys(&mir_expr.typ());
5629 typ
5630}
5631
5632#[cfg(test)]
5633mod execute_context_tests {
5634 use tokio::sync::{mpsc, oneshot};
5635
5636 use super::*;
5637 use crate::session::Session;
5638 use crate::util::ClientTransmitter;
5639
5640 #[mz_ore::test]
5643 fn test_retire_answers_client_when_runtime_shuts_down() {
5644 let runtime = tokio::runtime::Runtime::new().expect("can build runtime");
5645
5646 let (client_tx, mut client_rx) = oneshot::channel();
5647 let (internal_cmd_tx, _internal_cmd_rx) = mpsc::unbounded_channel();
5648
5649 runtime.block_on(async {
5650 let ctx = ExecuteContext::from_parts_with_response_barriers(
5651 ClientTransmitter::new(client_tx, internal_cmd_tx.clone()),
5652 internal_cmd_tx,
5653 Session::dummy(),
5654 ExecuteContextGuard::default(),
5655 vec![Box::pin(std::future::pending())],
5657 );
5658 ctx.retire(Ok(ExecuteResponse::StartedTransaction));
5659 });
5660
5661 drop(runtime);
5662
5663 let response = client_rx.try_recv().expect("client must be answered");
5664 assert!(
5665 matches!(response.result, Err(AdapterError::Internal(_))),
5666 "expected an internal error, got {:?}",
5667 response.result
5668 );
5669 }
5670}
5671
5672#[cfg(test)]
5673mod id_pool_tests {
5674 use super::IdPool;
5675
5676 #[mz_ore::test]
5677 fn test_empty_pool() {
5678 let mut pool = IdPool::empty();
5679 assert_eq!(pool.remaining(), 0);
5680 assert_eq!(pool.allocate(), None);
5681 assert_eq!(pool.allocate_many(1), None);
5682 }
5683
5684 #[mz_ore::test]
5685 fn test_allocate_single() {
5686 let mut pool = IdPool::empty();
5687 pool.refill(10, 13);
5688 assert_eq!(pool.remaining(), 3);
5689 assert_eq!(pool.allocate(), Some(10));
5690 assert_eq!(pool.allocate(), Some(11));
5691 assert_eq!(pool.allocate(), Some(12));
5692 assert_eq!(pool.remaining(), 0);
5693 assert_eq!(pool.allocate(), None);
5694 }
5695
5696 #[mz_ore::test]
5697 fn test_allocate_many() {
5698 let mut pool = IdPool::empty();
5699 pool.refill(100, 105);
5700 assert_eq!(pool.allocate_many(3), Some(vec![100, 101, 102]));
5701 assert_eq!(pool.remaining(), 2);
5702 assert_eq!(pool.allocate_many(3), None);
5704 assert_eq!(pool.allocate_many(2), Some(vec![103, 104]));
5706 assert_eq!(pool.remaining(), 0);
5707 }
5708
5709 #[mz_ore::test]
5710 fn test_allocate_many_zero() {
5711 let mut pool = IdPool::empty();
5712 pool.refill(1, 5);
5713 assert_eq!(pool.allocate_many(0), Some(vec![]));
5714 assert_eq!(pool.remaining(), 4);
5715 }
5716
5717 #[mz_ore::test]
5718 fn test_refill_resets_pool() {
5719 let mut pool = IdPool::empty();
5720 pool.refill(0, 2);
5721 assert_eq!(pool.allocate(), Some(0));
5722 pool.refill(50, 52);
5724 assert_eq!(pool.allocate(), Some(50));
5725 assert_eq!(pool.allocate(), Some(51));
5726 assert_eq!(pool.allocate(), None);
5727 }
5728
5729 #[mz_ore::test]
5730 fn test_mixed_allocate_and_allocate_many() {
5731 let mut pool = IdPool::empty();
5732 pool.refill(0, 10);
5733 assert_eq!(pool.allocate(), Some(0));
5734 assert_eq!(pool.allocate_many(3), Some(vec![1, 2, 3]));
5735 assert_eq!(pool.allocate(), Some(4));
5736 assert_eq!(pool.remaining(), 5);
5737 }
5738
5739 #[mz_ore::test]
5740 #[should_panic(expected = "invalid pool range")]
5741 fn test_refill_invalid_range_panics() {
5742 let mut pool = IdPool::empty();
5743 pool.refill(10, 5);
5744 }
5745}
5746
5747#[cfg(test)]
5748mod arrangement_sizes_pruner_tests {
5749 use mz_repr::catalog_item_id::CatalogItemId;
5750 use mz_repr::{Datum, Row};
5751
5752 use super::arrangement_sizes_expired_retractions;
5753
5754 fn history_row(ts_ms: i64) -> Row {
5758 let dt = mz_ore::now::to_datetime(ts_ms.try_into().expect("non-negative"));
5759 Row::pack_slice(&[
5760 Datum::String("r1"),
5761 Datum::String("u1"),
5762 Datum::Int64(123),
5763 Datum::TimestampTz(dt.try_into().expect("fits in TimestampTz")),
5764 ])
5765 }
5766
5767 fn item_id() -> CatalogItemId {
5768 CatalogItemId::User(42)
5770 }
5771
5772 #[mz_ore::test]
5773 fn empty_input_produces_no_retractions() {
5774 let out = arrangement_sizes_expired_retractions(Vec::new(), 1_000, item_id());
5775 assert!(out.is_empty());
5776 }
5777
5778 #[mz_ore::test]
5779 fn retracts_only_rows_strictly_before_cutoff() {
5780 let rows = vec![
5783 (history_row(100), 1),
5784 (history_row(500), 1),
5785 (history_row(1_000), 1), (history_row(5_000), 1),
5787 ];
5788 let out = arrangement_sizes_expired_retractions(rows, 1_000, item_id());
5789 assert_eq!(out.len(), 2);
5790 }
5791
5792 #[mz_ore::test]
5793 #[should_panic(expected = "consolidated contents should only contain diff 1")]
5794 fn retraction_in_input_panics() {
5795 let rows = vec![(history_row(100), -1)];
5796 let _ = arrangement_sizes_expired_retractions(rows, 1_000, item_id());
5797 }
5798}