1mod codec;
20mod dyncfgs;
21
22use std::collections::BTreeMap;
23use std::net::{IpAddr, SocketAddr};
24use std::path::PathBuf;
25use std::pin::Pin;
26use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
27use std::sync::{Arc, Mutex};
28use std::time::{Duration, Instant};
29
30use anyhow::Context;
31use axum::{Router, routing};
32use bytes::BytesMut;
33use futures::TryFutureExt;
34use futures::stream::BoxStream;
35use hickory_resolver::config::LookupIpStrategy;
36use hickory_resolver::lookup_ip::LookupIp;
37use hickory_resolver::net::NetError;
38use hickory_resolver::net::runtime::TokioRuntimeProvider;
39use hickory_resolver::proto::rr::{Name, RData, RecordType};
40use hickory_resolver::system_conf::read_system_conf;
41use hickory_resolver::{Resolver, TokioResolver};
42use hyper::StatusCode;
43use hyper_util::rt::TokioIo;
44use launchdarkly_server_sdk as ld;
45use mz_build_info::{BuildInfo, build_info};
46use mz_dyncfg::ConfigSet;
47use mz_frontegg_auth::Authenticator as FronteggAuthentication;
48use mz_orchestrator_tracing::NO_SENTRY_TARGET;
49use mz_ore::cast::CastFrom;
50use mz_ore::id_gen::conn_id_org_uuid;
51use mz_ore::metrics::{
52 ComputedGauge, ComputedIntGauge, ComputedUIntGauge, IntCounter, IntGauge, MetricsRegistry,
53};
54use mz_ore::netio::AsyncReady;
55use mz_ore::now::{NowFn, SYSTEM_TIME, epoch_to_uuid_v7};
56use mz_ore::stats::histogram_seconds_buckets;
57use mz_ore::task::{JoinSetExt, spawn};
58use mz_ore::tracing::TracingHandle;
59use mz_ore::{metric, netio};
60use mz_pgwire_common::{
61 ACCEPT_SSL_ENCRYPTION, CONN_UUID_KEY, Conn, ErrorResponse, FrontendMessage,
62 FrontendStartupMessage, MAX_STARTUP_FRAME_SIZE, MZ_FORWARDED_FOR_KEY, REJECT_ENCRYPTION,
63 VERSION_3, decode_startup,
64};
65use mz_server_core::{
66 Connection, ConnectionStream, ListenerHandle, ReloadTrigger, ReloadingSslContext,
67 ReloadingTlsConfig, ServeConfig, ServeDyncfg, TlsCertConfig, TlsMode, listen,
68};
69use openssl::error::ErrorStack;
70use openssl::ssl::{NameType, Ssl, SslConnector, SslMethod, SslVerifyMode};
71use prometheus::{Histogram, HistogramVec, IntCounterVec, IntGaugeVec};
72use proxy_header::{ProxiedAddress, ProxyHeader};
73use semver::Version;
74use tokio::io::{self, AsyncRead, AsyncWrite, AsyncWriteExt};
75use tokio::net::TcpStream;
76use tokio::sync::oneshot;
77use tokio::task::JoinSet;
78use tokio_metrics::TaskMetrics;
79use tokio_openssl::SslStream;
80use tokio_postgres::error::SqlState;
81use tower::Service;
82use tracing::{debug, error, info, warn};
83use uuid::Uuid;
84
85use crate::codec::{BackendMessage, FramedConn};
86use crate::dyncfgs::{
87 CONNECTION_HIGH_WATERMARK, CONNECTION_LOW_WATERMARK, INJECT_PROXY_PROTOCOL_HEADER_HTTP,
88 MAX_CONNECTIONS, PRE_RESOLVED_TIMEOUT, SIGTERM_CONNECTION_WAIT, SIGTERM_LISTEN_WAIT,
89 has_tracing_config_update, tracing_config,
90};
91
92pub const BUILD_INFO: BuildInfo = build_info!();
94
95pub struct BalancerConfig {
96 build_version: Version,
98 internal_http_listen_addr: SocketAddr,
100 pgwire_listen_addr: SocketAddr,
102 https_listen_addr: SocketAddr,
104 cancellation_resolver: CancellationResolver,
106 resolver: BalancerResolver,
108 https_sni_addr_template: String,
109 tls: Option<TlsCertConfig>,
110 internal_tls: bool,
111 metrics_registry: MetricsRegistry,
112 reload_certs: BoxStream<'static, Option<oneshot::Sender<Result<(), anyhow::Error>>>>,
113 launchdarkly_sdk_key: Option<String>,
114 config_sync_file_path: Option<PathBuf>,
115 config_sync_timeout: Duration,
116 config_sync_loop_interval: Option<Duration>,
117 cloud_provider: Option<String>,
118 cloud_provider_region: Option<String>,
119 tracing_handle: TracingHandle,
120 default_configs: Vec<(String, String)>,
121}
122
123impl BalancerConfig {
124 pub fn new(
125 build_info: &BuildInfo,
126 internal_http_listen_addr: SocketAddr,
127 pgwire_listen_addr: SocketAddr,
128 https_listen_addr: SocketAddr,
129 cancellation_resolver: CancellationResolver,
130 resolver: BalancerResolver,
131 https_sni_addr_template: String,
132 tls: Option<TlsCertConfig>,
133 internal_tls: bool,
134 metrics_registry: MetricsRegistry,
135 reload_certs: ReloadTrigger,
136 launchdarkly_sdk_key: Option<String>,
137 config_sync_file: Option<PathBuf>,
138 config_sync_timeout: Duration,
139 config_sync_loop_interval: Option<Duration>,
140 cloud_provider: Option<String>,
141 cloud_provider_region: Option<String>,
142 tracing_handle: TracingHandle,
143 default_configs: Vec<(String, String)>,
144 ) -> Self {
145 Self {
146 build_version: build_info.semver_version(),
147 internal_http_listen_addr,
148 pgwire_listen_addr,
149 https_listen_addr,
150 cancellation_resolver,
151 resolver,
152 https_sni_addr_template,
153 tls,
154 internal_tls,
155 metrics_registry,
156 reload_certs,
157 launchdarkly_sdk_key,
158 config_sync_file_path: config_sync_file,
159 config_sync_timeout,
160 config_sync_loop_interval,
161 cloud_provider,
162 cloud_provider_region,
163 tracing_handle,
164 default_configs,
165 }
166 }
167}
168
169#[derive(Debug)]
171pub struct BalancerMetrics {
172 _uptime: ComputedGauge,
173}
174
175impl BalancerMetrics {
176 pub fn new(cfg: &BalancerConfig) -> Self {
178 let start = Instant::now();
179 let uptime = cfg.metrics_registry.register_computed_gauge(
180 metric!(
181 name: "mz_balancer_metadata_seconds",
182 help: "server uptime, labels are build metadata",
183 const_labels: {
184 "version" => cfg.build_version,
185 "build_type" => if cfg!(release) { "release" } else { "debug" }
186 },
187 ),
188 move || start.elapsed().as_secs_f64(),
189 );
190 BalancerMetrics { _uptime: uptime }
191 }
192}
193
194pub struct BalancerService {
195 cfg: BalancerConfig,
196 pub pgwire: (ListenerHandle, Pin<Box<dyn ConnectionStream>>),
197 pub https: (ListenerHandle, Pin<Box<dyn ConnectionStream>>),
198 pub internal_http: (ListenerHandle, Pin<Box<dyn ConnectionStream>>),
199 _metrics: BalancerMetrics,
200 configs: ConfigSet,
201}
202
203impl BalancerService {
204 pub async fn new(cfg: BalancerConfig) -> Result<Self, anyhow::Error> {
205 let pgwire = listen(&cfg.pgwire_listen_addr)?;
206 let https = listen(&cfg.https_listen_addr)?;
207 let internal_http = listen(&cfg.internal_http_listen_addr)?;
208 let metrics = BalancerMetrics::new(&cfg);
209 let mut configs = ConfigSet::default();
210 configs = dyncfgs::all_dyncfgs(configs);
211 dyncfgs::set_defaults(&configs, cfg.default_configs.clone())?;
212 let tracing_handle = cfg.tracing_handle.clone();
213 match (
215 cfg.launchdarkly_sdk_key.as_deref(),
216 cfg.config_sync_file_path.as_deref(),
217 ) {
218 (Some(key), None) => {
219 let _ = mz_dyncfg_launchdarkly::sync_launchdarkly_to_configset(
220 configs.clone(),
221 &BUILD_INFO,
222 |builder| {
223 let region = cfg
224 .cloud_provider_region
225 .clone()
226 .unwrap_or_else(|| String::from("unknown"));
227 if let Some(provider) = cfg.cloud_provider.clone() {
228 builder.add_context(
229 ld::ContextBuilder::new(format!(
230 "{}/{}/{}",
231 provider, region, cfg.build_version
232 ))
233 .kind("balancer")
234 .set_string("provider", provider)
235 .set_string("region", region)
236 .set_string("version", cfg.build_version.to_string())
237 .build()
238 .map_err(|e| anyhow::anyhow!(e))?,
239 );
240 } else {
241 builder.add_context(
242 ld::ContextBuilder::new(format!(
243 "{}/{}/{}",
244 "unknown", region, cfg.build_version
245 ))
246 .anonymous(true) .kind("balancer")
248 .set_string("provider", "unknown")
249 .set_string("region", region)
250 .set_string("version", cfg.build_version.to_string())
251 .build()
252 .map_err(|e| anyhow::anyhow!(e))?,
253 );
254 }
255 Ok(())
256 },
257 Some(key),
258 cfg.config_sync_timeout,
259 cfg.config_sync_loop_interval,
260 move |updates, configs| {
261 if has_tracing_config_update(updates) {
262 match tracing_config(configs) {
263 Ok(parameters) => parameters.apply(&tracing_handle),
264 Err(err) => warn!("unable to update tracing: {err}"),
265 }
266 }
267 },
268 )
269 .await
270 .inspect_err(|e| warn!("LaunchDarkly sync error: {e}"));
271 }
272 (None, Some(path)) => {
273 let _ = mz_dyncfg_file::sync_file_to_configset(
274 configs.clone(),
275 path,
276 cfg.config_sync_timeout,
277 cfg.config_sync_loop_interval,
278 move |updates, configs| {
279 if has_tracing_config_update(updates) {
280 match tracing_config(configs) {
281 Ok(parameters) => parameters.apply(&tracing_handle),
282 Err(err) => warn!("unable to update tracing: {err}"),
283 }
284 }
285 },
286 )
287 .await
288 .inspect_err(|e| warn!("File config sync error: {e}"));
294 }
295 (Some(_), Some(_)) => panic!(
296 "must provide either config_sync_file_path or launchdarkly_sdk_key for config syncing",
297 ),
298 (None, None) => {}
299 };
300 Ok(Self {
301 cfg,
302 pgwire,
303 https,
304 internal_http,
305 _metrics: metrics,
306 configs,
307 })
308 }
309
310 pub async fn serve(self) -> Result<(), anyhow::Error> {
311 let (pgwire_tls, https_tls) = match &self.cfg.tls {
312 Some(tls) => {
313 let context = tls.reloading_context(self.cfg.reload_certs)?;
314 (
315 Some(ReloadingTlsConfig {
316 context: context.clone(),
317 mode: TlsMode::Require,
318 }),
319 Some(context),
320 )
321 }
322 None => (None, None),
323 };
324
325 let internal_tls = self
328 .cfg
329 .internal_tls
330 .then(internal_tls_connector)
331 .transpose()
332 .context("building internal TLS connector")?;
333
334 let metrics = ServerMetricsConfig::register_into(&self.cfg.metrics_registry);
335 let limiter = ConnectionLimiter::new(&self.cfg.metrics_registry, self.configs.clone());
336
337 let mut set = JoinSet::new();
338 let mut server_handles = Vec::new();
339 let pgwire_addr = self.pgwire.0.local_addr();
340 let https_addr = self.https.0.local_addr();
341 let internal_http_addr = self.internal_http.0.local_addr();
342
343 let shared_dns = match self.cfg.resolver.shared_dns() {
348 Some(dns) => dns,
349 None => Arc::new(TenantDnsResolver::new()?),
350 };
351
352 {
353 let pgwire = PgwireBalancer {
354 resolver: Arc::new(self.cfg.resolver),
355 cancellation_resolver: Arc::new(self.cfg.cancellation_resolver),
356 tls: pgwire_tls,
357 internal_tls: internal_tls.clone(),
358 metrics: ServerMetrics::new(metrics.clone(), "pgwire"),
359 limiter: Arc::clone(&limiter),
360 configs: self.configs.clone(),
361 now: SYSTEM_TIME.clone(),
362 };
363 let (handle, stream) = self.pgwire;
364 server_handles.push(handle);
365 set.spawn_named(|| "pgwire_stream", {
366 let config_set = self.configs.clone();
367 async move {
368 mz_server_core::serve(ServeConfig {
369 server: pgwire,
370 conns: stream,
371 dyncfg: Some(ServeDyncfg {
372 config_set,
373 sigterm_wait_config: &SIGTERM_CONNECTION_WAIT,
374 }),
375 })
376 .await;
377 warn!("pgwire server exited");
378 }
379 });
380 }
381 {
382 let Some((addr, port)) = self.cfg.https_sni_addr_template.split_once(':') else {
383 panic!("expected port in https_addr_template");
384 };
385 let port: u16 = port.parse().expect("unexpected port");
386
387 let https = HttpsBalancer {
388 resolver: shared_dns,
389 tls: https_tls,
390 resolve_template: Arc::from(addr),
391 port,
392 metrics: Arc::from(ServerMetrics::new(metrics, "https")),
393 limiter: Arc::clone(&limiter),
394 configs: self.configs.clone(),
395 internal_tls,
396 };
397 let (handle, stream) = self.https;
398 server_handles.push(handle);
399 set.spawn_named(|| "https_stream", {
400 let config_set = self.configs.clone();
401 async move {
402 mz_server_core::serve(ServeConfig {
403 server: https,
404 conns: stream,
405 dyncfg: Some(ServeDyncfg {
406 config_set,
407 sigterm_wait_config: &SIGTERM_CONNECTION_WAIT,
408 }),
409 })
410 .await;
411 warn!("https server exited");
412 }
413 });
414 }
415 {
416 let router = Router::new()
417 .route(
418 "/metrics",
419 routing::get(move |headers: axum::http::HeaderMap| async move {
420 mz_http_util::handle_prometheus(&self.cfg.metrics_registry, headers).await
421 }),
422 )
423 .route(
424 "/api/livez",
425 routing::get(mz_http_util::handle_liveness_check),
426 )
427 .route("/api/readyz", {
428 let limiter = Arc::clone(&limiter);
429 routing::get(move || async move {
430 if limiter.evaluate_watermarks() {
431 (StatusCode::OK, "ready")
432 } else {
433 (
434 StatusCode::SERVICE_UNAVAILABLE,
435 "at the connection high watermark",
436 )
437 }
438 })
439 });
440 let internal_http = InternalHttpServer { router };
441 let (handle, stream) = self.internal_http;
442 server_handles.push(handle);
443 set.spawn_named(|| "internal_http_stream", async move {
444 mz_server_core::serve(ServeConfig {
445 server: internal_http,
446 conns: stream,
447 dyncfg: None,
450 })
451 .await;
452 warn!("internal_http server exited");
453 });
454 }
455 spawn(|| "connection_watermark_tick", async move {
458 let mut tick = tokio::time::interval(Duration::from_secs(1));
459 loop {
460 tick.tick().await;
461 limiter.evaluate_watermarks();
462 }
463 });
464 #[cfg(unix)]
465 {
466 let mut sigterm =
467 tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate())?;
468 set.spawn_named(|| "sigterm_handler", async move {
469 sigterm.recv().await;
470 let wait = SIGTERM_LISTEN_WAIT.get(&self.configs);
471 warn!("received signal TERM - delaying for {:?}!", wait);
472 tokio::time::sleep(wait).await;
473 warn!("sigterm delay complete, dropping server handles");
474 drop(server_handles);
475 });
476 }
477
478 println!("balancerd {} listening...", BUILD_INFO.human_version(None));
479 println!(" TLS enabled: {}", self.cfg.tls.is_some());
480 println!(" pgwire address: {}", pgwire_addr);
481 println!(" HTTPS address: {}", https_addr);
482 println!(" internal HTTP address: {}", internal_http_addr);
483
484 while let Some(res) = set.join_next().await {
486 if let Err(err) = res {
487 error!("serving task failed: {err}")
488 }
489 }
490 Ok(())
491 }
492}
493
494struct InternalHttpServer {
495 router: Router,
496}
497
498impl mz_server_core::Server for InternalHttpServer {
499 const NAME: &'static str = "internal_http";
500
501 fn handle_connection(
503 &self,
504 conn: Connection,
505 _tokio_metrics_intervals: impl Iterator<Item = TaskMetrics> + Send + 'static,
506 ) -> mz_server_core::ConnectionHandler {
507 let router = self.router.clone();
508 let service = hyper::service::service_fn(move |req| router.clone().call(req));
509 let conn = TokioIo::new(conn);
510
511 Box::pin(async {
512 let http = hyper::server::conn::http1::Builder::new();
513 http.serve_connection(conn, service).err_into().await
514 })
515 }
516}
517
518fn internal_tls_connector() -> Result<SslConnector, ErrorStack> {
520 let mut builder = SslConnector::builder(SslMethod::tls())?;
521 builder.set_verify(SslVerifyMode::NONE);
523 Ok(builder.build())
524}
525
526struct GaugeGuard {
529 gauge: IntGauge,
530}
531
532impl From<IntGauge> for GaugeGuard {
533 fn from(gauge: IntGauge) -> Self {
534 let _self = Self { gauge };
535 _self.gauge.inc();
536 _self
537 }
538}
539
540impl Drop for GaugeGuard {
541 fn drop(&mut self) {
542 self.gauge.dec();
543 }
544}
545
546#[derive(Clone, Debug)]
547struct ServerMetricsConfig {
548 connection_status: IntCounterVec,
549 connection_setup_duration: HistogramVec,
550 upstream_connect_duration: HistogramVec,
551 connection_duration: HistogramVec,
552 active_connections: IntGaugeVec,
553 pre_resolved_connections: IntGaugeVec,
554 pre_resolved_timeouts: IntCounterVec,
555 tls_handshake_failures: IntCounterVec,
556 tenant_connections: IntGaugeVec,
557 tenant_connection_rx: IntCounterVec,
558 tenant_connection_tx: IntCounterVec,
559 tenant_pgwire_sni_count: IntCounterVec,
560}
561
562impl ServerMetricsConfig {
563 fn register_into(registry: &MetricsRegistry) -> Self {
564 let connection_status = registry.register(metric!(
565 name: "mz_balancer_connection_status",
566 help: "Count of completed network connections, by status. `error` means balancerd or \
567 its backend could not serve the connection. Connections the client ended or \
568 failed count as `success`.",
569 var_labels: ["source", "status"],
570 ));
571 let connection_setup_duration = registry.register(metric!(
572 name: "mz_balancer_connection_setup_duration_seconds",
573 help: "Time from accepting a connection until proxying it to environmentd begins, \
574 for connections that reach proxying.",
575 var_labels: ["source"],
576 buckets: histogram_seconds_buckets(0.001, 64.0),
577 ));
578 let upstream_connect_duration = registry.register(metric!(
579 name: "mz_balancer_upstream_connect_duration_seconds",
580 help: "Time to open a TCP connection to environmentd, for connections that succeed.",
581 var_labels: ["source"],
582 buckets: histogram_seconds_buckets(0.000_128, 16.0),
583 ));
584 let connection_duration = registry.register(metric!(
585 name: "mz_balancer_connection_duration_seconds",
586 help: "Time from accepting a connection until it closes, for connections that reach \
587 proxying.",
588 var_labels: ["source"],
589 buckets: histogram_seconds_buckets(1.0, 8192.0),
590 ));
591 let active_connections = registry.register(metric!(
592 name: "mz_balancer_connection_active",
593 help: "Count of currently open network connections.",
594 var_labels: ["source"],
595 ));
596 let pre_resolved_connections = registry.register(metric!(
597 name: "mz_balancer_pre_resolved_connection_active",
598 help: "Count of open network connections that have not yet resolved a backend.",
599 var_labels: ["source"],
600 ));
601 let pre_resolved_timeouts = registry.register(metric!(
602 name: "mz_balancer_pre_resolved_timeout_total",
603 help: "Count of connections closed for not resolving a backend within the pre-resolved timeout.",
604 var_labels: ["source"],
605 ));
606 let tls_handshake_failures = registry.register(metric!(
607 name: "mz_balancer_tls_handshake_failures_total",
608 help: "Count of client TLS handshakes that failed. Rises when clients reject the \
609 certificate balancerd presents, as well as with scanners and misconfigured \
610 clients, none of which count as an `error` connection status.",
611 var_labels: ["source"],
612 ));
613 let tenant_connections = registry.register(metric!(
614 name: "mz_balancer_tenant_connection_active",
615 help: "Count of opened network connections by tenant.",
616 var_labels: ["source", "tenant"]
617 ));
618 let tenant_connection_rx = registry.register(metric!(
619 name: "mz_balancer_tenant_connection_rx",
620 help: "Number of bytes received from a client for a tenant.",
621 var_labels: ["source", "tenant"],
622 ));
623 let tenant_connection_tx = registry.register(metric!(
624 name: "mz_balancer_tenant_connection_tx",
625 help: "Number of bytes sent to a client for a tenant.",
626 var_labels: ["source", "tenant"],
627 ));
628 let tenant_pgwire_sni_count = registry.register(metric!(
629 name: "mz_balancer_tenant_pgwire_sni_count",
630 help: "Count of pgwire connections that have and do not have SNI available per tenant.",
631 var_labels: ["tenant", "has_sni"],
632 ));
633 Self {
634 connection_status,
635 connection_setup_duration,
636 upstream_connect_duration,
637 connection_duration,
638 active_connections,
639 pre_resolved_connections,
640 pre_resolved_timeouts,
641 tls_handshake_failures,
642 tenant_connections,
643 tenant_connection_rx,
644 tenant_connection_tx,
645 tenant_pgwire_sni_count,
646 }
647 }
648}
649
650#[derive(Clone, Debug)]
651struct ServerMetrics {
652 inner: ServerMetricsConfig,
653 source: &'static str,
654}
655
656impl ServerMetrics {
657 fn new(inner: ServerMetricsConfig, source: &'static str) -> Self {
658 let self_ = Self { inner, source };
659
660 self_.connection_status(false);
663 self_.connection_status(true);
664 self_.connection_setup_duration();
665 self_.upstream_connect_duration();
666 self_.connection_duration();
667 self_.tls_handshake_failures();
668 drop(self_.active_connections());
669
670 self_
671 }
672
673 fn connection_status(&self, is_ok: bool) -> IntCounter {
674 self.inner
675 .connection_status
676 .with_label_values(&[self.source, Self::status_label(is_ok)])
677 }
678
679 fn connection_setup_duration(&self) -> Histogram {
680 self.inner
681 .connection_setup_duration
682 .with_label_values(&[self.source])
683 }
684
685 fn upstream_connect_duration(&self) -> Histogram {
686 self.inner
687 .upstream_connect_duration
688 .with_label_values(&[self.source])
689 }
690
691 fn connection_duration(&self) -> Histogram {
692 self.inner
693 .connection_duration
694 .with_label_values(&[self.source])
695 }
696
697 fn active_connections(&self) -> GaugeGuard {
698 self.inner
699 .active_connections
700 .with_label_values(&[self.source])
701 .into()
702 }
703
704 fn pre_resolved_connections(&self) -> GaugeGuard {
705 self.inner
706 .pre_resolved_connections
707 .with_label_values(&[self.source])
708 .into()
709 }
710
711 fn pre_resolved_timeout(&self) -> IntCounter {
712 self.inner
713 .pre_resolved_timeouts
714 .with_label_values(&[self.source])
715 }
716
717 fn tls_handshake_failures(&self) -> IntCounter {
718 self.inner
719 .tls_handshake_failures
720 .with_label_values(&[self.source])
721 }
722
723 fn tenant_connections(&self, tenant: &str) -> GaugeGuard {
724 self.inner
725 .tenant_connections
726 .with_label_values(&[self.source, tenant])
727 .into()
728 }
729
730 fn tenant_connections_rx(&self, tenant: &str) -> IntCounter {
731 self.inner
732 .tenant_connection_rx
733 .with_label_values(&[self.source, tenant])
734 }
735
736 fn tenant_connections_tx(&self, tenant: &str) -> IntCounter {
737 self.inner
738 .tenant_connection_tx
739 .with_label_values(&[self.source, tenant])
740 }
741
742 fn tenant_pgwire_sni_count(&self, tenant: &str, has_sni: bool) -> IntCounter {
743 self.inner
744 .tenant_pgwire_sni_count
745 .with_label_values(&[tenant, &has_sni.to_string()])
746 }
747
748 fn status_label(is_ok: bool) -> &'static str {
749 if is_ok { "success" } else { "error" }
750 }
751}
752
753#[derive(Debug)]
760struct ConnectionLimiter {
761 configs: ConfigSet,
762 active: AtomicU32,
763 limited: AtomicBool,
766 accepting: Arc<AtomicBool>,
768 watermark_state: Mutex<WatermarkState>,
771 rejected: IntCounter,
772 watermark_transitions: IntCounter,
773 _limit: ComputedUIntGauge,
774 _accepting: ComputedIntGauge,
775 _high_watermark: ComputedUIntGauge,
776 _low_watermark: ComputedUIntGauge,
777}
778
779#[derive(Debug, Default)]
780struct WatermarkState {
781 out_of_rotation: bool,
782 last_warning: Option<String>,
784}
785
786#[derive(Debug, Clone, Copy)]
788struct Watermarks {
789 low: usize,
790 high: usize,
791}
792
793fn configured_watermarks(configs: &ConfigSet) -> Result<Option<Watermarks>, String> {
800 let low = CONNECTION_LOW_WATERMARK.get(configs);
801 let high = CONNECTION_HIGH_WATERMARK.get(configs);
802 let max = MAX_CONNECTIONS.get(configs);
803 let reason = match (low, high) {
804 (0, 0) => return Ok(None),
805 (0, _) | (_, 0) => "both watermarks must be set",
806 (low, high) if low >= high => "the low watermark must be below the high watermark",
807 _ if max == 0 => "balancerd_max_connections must be enabled",
808 (_, high) if high >= usize::cast_from(max) => {
809 "the high watermark must be below balancerd_max_connections"
810 }
811 (low, high) => return Ok(Some(Watermarks { low, high })),
812 };
813 Err(format!(
814 "{reason} (low={low}, high={high}, max_connections={max})"
815 ))
816}
817
818fn watermark_metric(configs: &ConfigSet, pick: fn(Watermarks) -> usize) -> u64 {
820 configured_watermarks(configs)
821 .ok()
822 .flatten()
823 .map_or(0, |w| u64::cast_from(pick(w)))
824}
825
826impl ConnectionLimiter {
827 fn new(registry: &MetricsRegistry, configs: ConfigSet) -> Arc<Self> {
828 let rejected = registry.register(metric!(
829 name: "mz_balancer_connection_rejected_total",
830 help: "Count of connections refused because the connection limit was reached.",
831 ));
832 let limit = registry.register_computed_gauge(
833 metric!(
834 name: "mz_balancer_connection_limit",
835 help: "Maximum number of connections proxied at once, 0 if unlimited.",
836 ),
837 {
838 let configs = configs.clone();
839 move || u64::from(MAX_CONNECTIONS.get(&configs))
840 },
841 );
842 let watermark_transitions = registry.register(metric!(
843 name: "mz_balancer_connection_watermark_transitions_total",
844 help: "Count of times balancerd left or returned to load balancer rotation because \
845 of the connection watermarks.",
846 ));
847 let accepting = Arc::new(AtomicBool::new(true));
848 let accepting_gauge: ComputedIntGauge = registry.register_computed_gauge(
849 metric!(
850 name: "mz_balancer_connection_accepting",
851 help: "Whether /api/readyz reports ready: 1 in load balancer rotation, 0 out.",
852 ),
853 {
854 let accepting = Arc::clone(&accepting);
855 move || i64::from(accepting.load(Ordering::Relaxed))
856 },
857 );
858 let high_watermark = registry.register_computed_gauge(
860 metric!(
861 name: "mz_balancer_connection_high_watermark",
862 help: "Connections at which balancerd leaves load balancer rotation, 0 if disabled.",
863 ),
864 {
865 let configs = configs.clone();
866 move || watermark_metric(&configs, |w| w.high)
867 },
868 );
869 let low_watermark = registry.register_computed_gauge(
870 metric!(
871 name: "mz_balancer_connection_low_watermark",
872 help: "Connections below which balancerd returns to load balancer rotation, 0 if \
873 disabled.",
874 ),
875 {
876 let configs = configs.clone();
877 move || watermark_metric(&configs, |w| w.low)
878 },
879 );
880 Arc::new(ConnectionLimiter {
881 configs,
882 active: AtomicU32::new(0),
883 limited: AtomicBool::new(false),
884 accepting,
885 watermark_state: Mutex::new(WatermarkState::default()),
886 rejected,
887 watermark_transitions,
888 _limit: limit,
889 _accepting: accepting_gauge,
890 _high_watermark: high_watermark,
891 _low_watermark: low_watermark,
892 })
893 }
894
895 #[cfg(test)]
896 fn accepting(&self) -> bool {
897 self.accepting.load(Ordering::Relaxed)
898 }
899
900 fn evaluate_watermarks(&self) -> bool {
912 let mut state = self.watermark_state.lock().expect("lock poisoned");
913 let watermarks = self.watermarks(&mut state.last_warning);
914 let active = usize::cast_from(self.active.load(Ordering::SeqCst));
915 let accepting = match (watermarks, state.out_of_rotation) {
916 (Some(w), false) if active >= w.high => {
917 warn!(
918 "leaving load balancer rotation: {active} active connections reached the \
919 high watermark of {}",
920 w.high
921 );
922 false
923 }
924 (Some(w), true) if active < w.low => {
925 info!(
926 "returning to load balancer rotation: {active} active connections, below \
927 the low watermark of {}",
928 w.low
929 );
930 true
931 }
932 (None, true) => {
935 info!(
936 "returning to load balancer rotation: connection watermarks no longer in effect"
937 );
938 true
939 }
940 _ => return !state.out_of_rotation,
941 };
942 state.out_of_rotation = !accepting;
943 self.accepting.store(accepting, Ordering::Relaxed);
944 self.watermark_transitions.inc();
945 accepting
946 }
947
948 fn watermarks(&self, last: &mut Option<String>) -> Option<Watermarks> {
949 match configured_watermarks(&self.configs) {
950 Ok(watermarks) => {
951 *last = None;
952 watermarks
953 }
954 Err(reason) => {
955 if last.as_deref() != Some(&reason) {
956 warn!("ignoring connection watermarks: {reason}");
957 *last = Some(reason);
958 }
959 None
960 }
961 }
962 }
963
964 fn acquire(self: &Arc<Self>) -> Option<ConnectionGuard> {
967 let limit = MAX_CONNECTIONS.get(&self.configs);
968 let unlimited = limit == 0;
969 match self
970 .active
971 .try_update(Ordering::SeqCst, Ordering::SeqCst, |active| {
972 (unlimited || active < limit).then(|| active + 1)
973 }) {
974 Ok(prev) => {
975 if unlimited || prev + 1 <= limit - limit / 10 {
978 if self.limited.swap(false, Ordering::Relaxed) {
979 info!("accepting new connections again, limit is {limit}");
980 }
981 }
982 Some(ConnectionGuard(Arc::clone(self)))
983 }
984 Err(active) => {
985 self.rejected.inc();
986 if !self.limited.swap(true, Ordering::Relaxed) {
987 warn!(
988 "refusing new connections: at the limit of {limit} connections \
989 ({active} active)"
990 );
991 }
992 None
993 }
994 }
995 }
996}
997
998struct ConnectionGuard(Arc<ConnectionLimiter>);
1000
1001impl Drop for ConnectionGuard {
1002 fn drop(&mut self) {
1003 self.0.active.fetch_sub(1, Ordering::SeqCst);
1004 }
1005}
1006
1007async fn reject<A>(conn: &mut FramedConn<A>, err: ErrorResponse) -> Result<(), io::Error>
1014where
1015 A: AsyncRead + AsyncWrite + Unpin,
1016{
1017 conn.send(err).await?;
1018 conn.flush().await
1019}
1020
1021#[derive(Debug, Default)]
1037struct ConnectionFault {
1038 server: AtomicBool,
1039 in_server_work: AtomicBool,
1040}
1041
1042impl ConnectionFault {
1043 fn server(&self) {
1045 self.server.store(true, Ordering::Relaxed);
1046 }
1047
1048 async fn server_work<F: Future>(&self, fut: F) -> F::Output {
1054 self.in_server_work.store(true, Ordering::Relaxed);
1055 let output = fut.await;
1056 self.in_server_work.store(false, Ordering::Relaxed);
1057 output
1058 }
1059
1060 fn timed_out(&self) {
1063 if self.in_server_work.load(Ordering::Relaxed) {
1064 self.server();
1065 }
1066 }
1067
1068 fn is_ok(&self) -> bool {
1069 !self.server.load(Ordering::Relaxed)
1070 }
1071}
1072
1073async fn under_pre_resolved_timeout<F, T, E>(
1079 timeout: Duration,
1080 metrics: &ServerMetrics,
1081 fault: &ConnectionFault,
1082 fut: F,
1083) -> Result<T, E>
1084where
1085 F: Future<Output = Result<T, E>>,
1086 E: From<io::Error>,
1087{
1088 if timeout.is_zero() {
1089 return fut.await;
1090 }
1091 match tokio::time::timeout(timeout, fut).await {
1092 Ok(result) => result,
1093 Err(_) => {
1094 metrics.pre_resolved_timeout().inc();
1095 fault.timed_out();
1096 Err(io::Error::new(
1097 io::ErrorKind::TimedOut,
1098 "timed out before resolving a backend",
1099 )
1100 .into())
1101 }
1102 }
1103}
1104
1105pub enum CancellationResolver {
1106 Directory(PathBuf),
1107 Static(String),
1108}
1109
1110struct PgwireBalancer {
1111 configs: ConfigSet,
1113 tls: Option<ReloadingTlsConfig>,
1114 internal_tls: Option<SslConnector>,
1115 cancellation_resolver: Arc<CancellationResolver>,
1116 resolver: Arc<BalancerResolver>,
1117 metrics: ServerMetrics,
1118 limiter: Arc<ConnectionLimiter>,
1119 now: NowFn,
1120}
1121
1122impl PgwireBalancer {
1123 async fn pre_resolve(
1128 conn: Connection,
1129 tls: Option<ReloadingTlsConfig>,
1130 resolver: &BalancerResolver,
1131 cancellation_resolver: Arc<CancellationResolver>,
1132 conn_uuid: Uuid,
1133 metrics: &ServerMetrics,
1134 fault: &ConnectionFault,
1135 ) -> Result<
1136 Option<(
1137 FramedConn<Connection>,
1138 ResolvedAddr,
1139 BTreeMap<String, String>,
1140 )>,
1141 anyhow::Error,
1142 > {
1143 let peer_addr = conn.peer_addr();
1144 let mut conn = Conn::Unencrypted(conn);
1145 loop {
1146 let message = decode_startup(&mut conn, MAX_STARTUP_FRAME_SIZE).await?;
1147 conn = match message {
1148 None => return Ok(None),
1152
1153 Some(FrontendStartupMessage::Startup {
1154 version,
1155 mut params,
1156 }) => {
1157 let mut conn = FramedConn::new(conn);
1158 let rejected = SqlState::SQLSERVER_REJECTED_ESTABLISHMENT_OF_SQLCONNECTION;
1159 let peer_addr = match peer_addr {
1160 Ok(addr) => addr.ip(),
1161 Err(e) => {
1162 error!("Invalid peer_addr {:?}", e);
1163 fault.server();
1164 reject(
1165 &mut conn,
1166 ErrorResponse::fatal(rejected, "invalid peer address"),
1167 )
1168 .await?;
1169 return Ok(None);
1170 }
1171 };
1172 debug!(
1173 %conn_uuid, %peer_addr,
1174 "starting new pgwire connection in balancer",
1175 );
1176 let prev = params.insert(CONN_UUID_KEY.to_string(), conn_uuid.to_string());
1177 if prev.is_some() {
1178 reject(
1179 &mut conn,
1180 ErrorResponse::fatal(
1181 rejected,
1182 format!("invalid parameter '{CONN_UUID_KEY}'"),
1183 ),
1184 )
1185 .await?;
1186 return Ok(None);
1187 }
1188
1189 let forwarded_for = params.insert(
1190 MZ_FORWARDED_FOR_KEY.to_string(),
1191 peer_addr.to_string().clone(),
1192 );
1193 if let Some(_) = forwarded_for {
1194 reject(
1195 &mut conn,
1196 ErrorResponse::fatal(
1197 rejected,
1198 format!("invalid parameter '{MZ_FORWARDED_FOR_KEY}'"),
1199 ),
1200 )
1201 .await?;
1202 return Ok(None);
1203 };
1204
1205 let Some(resolved) = Self::resolve_destination(
1206 &mut conn,
1207 version,
1208 ¶ms,
1209 resolver,
1210 tls.map(|tls| tls.mode),
1211 metrics,
1212 fault,
1213 )
1214 .await?
1215 else {
1216 return Ok(None);
1217 };
1218 return Ok(Some((conn, resolved, params)));
1219 }
1220
1221 Some(FrontendStartupMessage::CancelRequest {
1222 conn_id,
1223 secret_key,
1224 }) => {
1225 spawn(|| "cancel request", async move {
1226 cancel_request(conn_id, secret_key, &cancellation_resolver).await;
1227 });
1228 return Ok(None);
1231 }
1232
1233 Some(FrontendStartupMessage::SslRequest) => match (conn, &tls) {
1234 (Conn::Unencrypted(mut conn), Some(tls)) => {
1235 conn.write_all(&[ACCEPT_SSL_ENCRYPTION]).await?;
1236 let mut ssl_stream = Ssl::new(&tls.context.get())
1237 .and_then(|ssl| SslStream::new(ssl, conn))
1238 .inspect_err(|_| fault.server())?;
1239 if let Err(e) = Pin::new(&mut ssl_stream).accept().await {
1240 metrics.tls_handshake_failures().inc();
1241 let _ = ssl_stream.get_mut().shutdown().await;
1242 return Err(e.into());
1243 }
1244 Conn::Ssl(ssl_stream)
1245 }
1246 (mut conn, _) => {
1247 conn.write_all(&[REJECT_ENCRYPTION]).await?;
1248 conn
1249 }
1250 },
1251
1252 Some(FrontendStartupMessage::GssEncRequest) => {
1253 conn.write_all(&[REJECT_ENCRYPTION]).await?;
1254 conn
1255 }
1256 }
1257 }
1258 }
1259
1260 #[mz_ore::instrument(level = "debug")]
1261 async fn resolve_destination<'a, A>(
1265 conn: &'a mut FramedConn<A>,
1266 version: i32,
1267 params: &BTreeMap<String, String>,
1268 resolver: &BalancerResolver,
1269 tls_mode: Option<TlsMode>,
1270 metrics: &ServerMetrics,
1271 fault: &ConnectionFault,
1272 ) -> Result<Option<ResolvedAddr>, io::Error>
1273 where
1274 A: AsyncRead + AsyncWrite + AsyncReady + Send + Sync + Unpin,
1275 {
1276 if version != VERSION_3 {
1277 reject(
1278 conn,
1279 ErrorResponse::fatal(
1280 SqlState::SQLSERVER_REJECTED_ESTABLISHMENT_OF_SQLCONNECTION,
1281 "server does not support the client's requested protocol version",
1282 ),
1283 )
1284 .await?;
1285 return Ok(None);
1286 }
1287
1288 let Some(user) = params.get("user") else {
1289 reject(
1290 conn,
1291 ErrorResponse::fatal(
1292 SqlState::SQLSERVER_REJECTED_ESTABLISHMENT_OF_SQLCONNECTION,
1293 "user parameter required",
1294 ),
1295 )
1296 .await?;
1297 return Ok(None);
1298 };
1299
1300 if let Err(err) = conn.inner().ensure_tls_compatibility(&tls_mode) {
1301 reject(conn, err).await?;
1302 return Ok(None);
1303 }
1304
1305 let resolved = match resolver.resolve(conn, user, metrics, fault).await {
1306 Ok(v) => v,
1307 Err(err) => {
1308 let sql_state = match &err {
1309 ResolveError::InvalidPassword => SqlState::INVALID_PASSWORD,
1310 ResolveError::Client(details) => {
1311 warn!("client-caused connection failure: {details:#}");
1312 SqlState::SQLSERVER_REJECTED_ESTABLISHMENT_OF_SQLCONNECTION
1313 }
1314 ResolveError::Upstream(details) => {
1315 warn!("upstream not available: {details:#}");
1316 if !names_unknown_host(details) {
1317 fault.server();
1318 }
1319 SqlState::SQLSERVER_REJECTED_ESTABLISHMENT_OF_SQLCONNECTION
1320 }
1321 ResolveError::Internal(details) => {
1322 error!("resolving connection destination: {details:#}");
1323 fault.server();
1324 SqlState::SQLSERVER_REJECTED_ESTABLISHMENT_OF_SQLCONNECTION
1325 }
1326 };
1327 reject(conn, ErrorResponse::fatal(sql_state, err.to_string())).await?;
1328 return Ok(None);
1329 }
1330 };
1331
1332 Ok(Some(resolved))
1333 }
1334
1335 async fn proxy<A>(
1340 mut conn: FramedConn<A>,
1341 resolved: ResolvedAddr,
1342 params: BTreeMap<String, String>,
1343 internal_tls: Option<&SslConnector>,
1344 metrics: &ServerMetrics,
1345 fault: &ConnectionFault,
1346 accepted: Instant,
1347 ) -> Result<(), io::Error>
1348 where
1349 A: AsyncRead + AsyncWrite + AsyncReady + Send + Sync + Unpin,
1350 {
1351 let _active_guard = resolved
1352 .tenant
1353 .as_ref()
1354 .map(|tenant| metrics.tenant_connections(tenant));
1355 let mut mz_stream = match Self::init_stream(
1356 &mut conn,
1357 resolved.addr,
1358 resolved.password,
1359 params,
1360 internal_tls,
1361 metrics,
1362 )
1363 .await
1364 {
1365 Ok(stream) => stream,
1366 Err(e) => {
1367 error!("failed to connect to upstream server: {e}");
1368 fault.server();
1369 return reject(
1370 &mut conn,
1371 ErrorResponse::fatal(
1372 SqlState::SQLSERVER_REJECTED_ESTABLISHMENT_OF_SQLCONNECTION,
1373 "upstream server not available",
1374 ),
1375 )
1376 .await;
1377 }
1378 };
1379
1380 let (mut client, unread) = conn.into_parts();
1384 if !unread.is_empty() {
1385 mz_stream
1386 .write_all(&unread)
1387 .await
1388 .inspect_err(|_| fault.server())?;
1389 }
1390 drop(unread);
1391 metrics
1392 .connection_setup_duration()
1393 .observe(accepted.elapsed().as_secs_f64());
1394
1395 let mut client_counter = CountingConn::new(&mut client);
1396
1397 let res = tokio::io::copy_bidirectional(&mut client_counter, &mut mz_stream).await;
1400 metrics
1401 .connection_duration()
1402 .observe(accepted.elapsed().as_secs_f64());
1403 if let Some(tenant) = &resolved.tenant {
1404 metrics
1405 .tenant_connections_tx(tenant)
1406 .inc_by(u64::cast_from(client_counter.written));
1407 metrics
1408 .tenant_connections_rx(tenant)
1409 .inc_by(u64::cast_from(client_counter.read));
1410 }
1411 res?;
1412
1413 Ok(())
1414 }
1415
1416 #[mz_ore::instrument(level = "debug")]
1417 async fn init_stream<'a, A>(
1418 conn: &'a mut FramedConn<A>,
1419 envd_addr: SocketAddr,
1420 password: Option<String>,
1421 params: BTreeMap<String, String>,
1422 internal_tls: Option<&SslConnector>,
1423 metrics: &ServerMetrics,
1424 ) -> Result<Conn<netio::Stream>, anyhow::Error>
1425 where
1426 A: AsyncRead + AsyncWrite + AsyncReady + Send + Sync + Unpin,
1427 {
1428 let mut mz_stream = connect_upstream(envd_addr, metrics).await?;
1433 let mut buf = BytesMut::new();
1434
1435 let mut mz_stream = if let Some(internal_tls) = internal_tls {
1436 FrontendStartupMessage::SslRequest.encode(&mut buf)?;
1437 mz_stream.write_all(&buf).await?;
1438 buf.clear();
1439 let mut maybe_ssl_request_response = [0u8; 1];
1440 let nread =
1441 netio::read_exact_or_eof(&mut mz_stream, &mut maybe_ssl_request_response).await?;
1442 if nread == 1 && maybe_ssl_request_response == [ACCEPT_SSL_ENCRYPTION] {
1443 let mut ssl = internal_tls.configure()?.into_ssl(&envd_addr.to_string())?;
1445 ssl.set_connect_state();
1446 Conn::Ssl(SslStream::new(ssl, mz_stream)?)
1447 } else {
1448 Conn::Unencrypted(mz_stream)
1449 }
1450 } else {
1451 Conn::Unencrypted(mz_stream)
1452 };
1453
1454 let startup = FrontendStartupMessage::Startup {
1456 version: VERSION_3,
1457 params,
1458 };
1459 startup.encode(&mut buf)?;
1460 mz_stream.write_all(&buf).await?;
1461 let client_stream = conn.inner_mut();
1462
1463 if password.is_none() {
1479 return Ok(mz_stream);
1480 }
1481
1482 let mut maybe_auth_frame = [0; 1 + 4 + 4];
1486 let nread = netio::read_exact_or_eof(&mut mz_stream, &mut maybe_auth_frame).await?;
1487 const AUTH_PASSWORD_CLEARTEXT: [u8; 9] = [b'R', 0, 0, 0, 8, 0, 0, 0, 3];
1490 if nread == AUTH_PASSWORD_CLEARTEXT.len()
1491 && maybe_auth_frame == AUTH_PASSWORD_CLEARTEXT
1492 && password.is_some()
1493 {
1494 let Some(password) = password else {
1496 unreachable!("verified some above");
1497 };
1498 let password = FrontendMessage::Password { password };
1499 buf.clear();
1500 password.encode(&mut buf)?;
1501 mz_stream.write_all(&buf).await?;
1502 mz_stream.flush().await?;
1503 } else {
1504 client_stream.write_all(&maybe_auth_frame[0..nread]).await?;
1507 }
1508
1509 Ok(mz_stream)
1510 }
1511}
1512
1513impl mz_server_core::Server for PgwireBalancer {
1514 const NAME: &'static str = "pgwire_balancer";
1515
1516 fn handle_connection(
1517 &self,
1518 conn: Connection,
1519 _tokio_metrics_intervals: impl Iterator<Item = TaskMetrics> + Send + 'static,
1520 ) -> mz_server_core::ConnectionHandler {
1521 let tls = self.tls.clone();
1522 let internal_tls = self.internal_tls.clone();
1523 let resolver = Arc::clone(&self.resolver);
1524 let inner_metrics = self.metrics.clone();
1525 let outer_metrics = self.metrics.clone();
1526 let limiter = Arc::clone(&self.limiter);
1527 let pre_resolved_timeout = PRE_RESOLVED_TIMEOUT.get(&self.configs);
1528 let cancellation_resolver = Arc::clone(&self.cancellation_resolver);
1529 let conn_uuid = epoch_to_uuid_v7(&(self.now)());
1530 conn.uuid_handle().set(conn_uuid);
1531 Box::pin(async move {
1532 let accepted = Instant::now();
1535 let fault = ConnectionFault::default();
1536 let fault_ref = &fault;
1537 let active_guard = outer_metrics.active_connections();
1538 let _: Result<(), anyhow::Error> = async move {
1539 let Some(_conn_guard) = limiter.acquire() else {
1540 fault_ref.server();
1541 return Ok(());
1542 };
1543 let pre_resolved = inner_metrics.pre_resolved_connections();
1544
1545 let destination = under_pre_resolved_timeout(
1549 pre_resolved_timeout,
1550 &inner_metrics,
1551 fault_ref,
1552 Box::pin(Self::pre_resolve(
1553 conn,
1554 tls,
1555 &resolver,
1556 cancellation_resolver,
1557 conn_uuid,
1558 &inner_metrics,
1559 fault_ref,
1560 )),
1561 )
1562 .await?;
1563
1564 let Some((conn, resolved, params)) = destination else {
1565 return Ok(());
1566 };
1567 drop(pre_resolved);
1568 if let Some(tenant) = &resolved.tenant {
1573 info!(
1574 target: NO_SENTRY_TARGET,
1575 %conn_uuid,
1576 client_addr = params.get(MZ_FORWARDED_FOR_KEY).map_or("", String::as_str),
1577 %tenant,
1578 authenticated = resolved.password.is_some(),
1579 "pgwire connection resolved",
1580 );
1581 }
1582
1583 PgwireBalancer::proxy(
1584 conn,
1585 resolved,
1586 params,
1587 internal_tls.as_ref(),
1588 &inner_metrics,
1589 fault_ref,
1590 accepted,
1591 )
1592 .await?;
1593 Ok(())
1594 }
1595 .await;
1596 drop(active_guard);
1597 outer_metrics.connection_status(fault.is_ok()).inc();
1598 Ok(())
1599 })
1600 }
1601}
1602
1603struct CountingConn<C> {
1605 inner: C,
1606 read: usize,
1607 written: usize,
1608}
1609
1610impl<C> CountingConn<C> {
1611 fn new(inner: C) -> Self {
1612 CountingConn {
1613 inner,
1614 read: 0,
1615 written: 0,
1616 }
1617 }
1618}
1619
1620impl<C> AsyncRead for CountingConn<C>
1621where
1622 C: AsyncRead + Unpin,
1623{
1624 fn poll_read(
1625 self: Pin<&mut Self>,
1626 cx: &mut std::task::Context<'_>,
1627 buf: &mut io::ReadBuf<'_>,
1628 ) -> std::task::Poll<std::io::Result<()>> {
1629 let counter = self.get_mut();
1630 let pin = Pin::new(&mut counter.inner);
1631 let bytes = buf.filled().len();
1632 let poll = pin.poll_read(cx, buf);
1633 let bytes = buf.filled().len() - bytes;
1634 if let std::task::Poll::Ready(Ok(())) = poll {
1635 counter.read += bytes
1636 }
1637 poll
1638 }
1639}
1640
1641impl<C> AsyncWrite for CountingConn<C>
1642where
1643 C: AsyncWrite + Unpin,
1644{
1645 fn poll_write(
1646 self: Pin<&mut Self>,
1647 cx: &mut std::task::Context<'_>,
1648 buf: &[u8],
1649 ) -> std::task::Poll<Result<usize, std::io::Error>> {
1650 let counter = self.get_mut();
1651 let pin = Pin::new(&mut counter.inner);
1652 let poll = pin.poll_write(cx, buf);
1653 if let std::task::Poll::Ready(Ok(bytes)) = poll {
1654 counter.written += bytes
1655 }
1656 poll
1657 }
1658
1659 fn poll_flush(
1660 self: Pin<&mut Self>,
1661 cx: &mut std::task::Context<'_>,
1662 ) -> std::task::Poll<Result<(), std::io::Error>> {
1663 let counter = self.get_mut();
1664 let pin = Pin::new(&mut counter.inner);
1665 pin.poll_flush(cx)
1666 }
1667
1668 fn poll_shutdown(
1669 self: Pin<&mut Self>,
1670 cx: &mut std::task::Context<'_>,
1671 ) -> std::task::Poll<Result<(), std::io::Error>> {
1672 let counter = self.get_mut();
1673 let pin = Pin::new(&mut counter.inner);
1674 pin.poll_shutdown(cx)
1675 }
1676}
1677
1678async fn cancel_request(
1695 conn_id: u32,
1696 secret_key: u32,
1697 cancellation_resolver: &CancellationResolver,
1698) {
1699 let suffix = conn_id_org_uuid(conn_id);
1700 let contents = match cancellation_resolver {
1701 CancellationResolver::Directory(dir) => {
1702 let path = dir.join(&suffix);
1703 match std::fs::read_to_string(&path) {
1704 Ok(contents) => contents,
1705 Err(err) => {
1706 error!("could not read cancel file {path:?}: {err}");
1707 return;
1708 }
1709 }
1710 }
1711 CancellationResolver::Static(addr) => addr.to_owned(),
1712 };
1713 let mut all_ips = Vec::new();
1714 for addr in contents.lines() {
1715 let addr = addr.trim();
1716 if addr.is_empty() {
1717 continue;
1718 }
1719 match tokio::net::lookup_host(addr).await {
1720 Ok(ips) => all_ips.extend(ips),
1721 Err(err) => {
1722 error!("{addr} failed resolution: {err}");
1723 }
1724 }
1725 }
1726 let mut buf = BytesMut::with_capacity(16);
1727 let msg = FrontendStartupMessage::CancelRequest {
1728 conn_id,
1729 secret_key,
1730 };
1731 msg.encode(&mut buf).expect("must encode");
1732 let buf = buf.freeze();
1733 for ip in all_ips {
1734 debug!("cancelling {suffix} to {ip}");
1735 let buf = buf.clone();
1736 spawn(|| "cancel request for ip", async move {
1737 let send = async {
1738 let mut stream = TcpStream::connect(&ip).await?;
1739 stream.write_all(&buf).await?;
1740 stream.shutdown().await?;
1741 Ok::<_, io::Error>(())
1742 };
1743 if let Err(err) = send.await {
1744 error!("error mirroring cancel to {ip}: {err}");
1745 }
1746 });
1747 }
1748}
1749
1750async fn connect_upstream(addr: SocketAddr, metrics: &ServerMetrics) -> io::Result<netio::Stream> {
1752 let start = Instant::now();
1753 let stream = netio::Stream::connect(addr).await?;
1754 metrics
1755 .upstream_connect_duration()
1756 .observe(start.elapsed().as_secs_f64());
1757 Ok(stream)
1758}
1759
1760async fn send_http_error(client_stream: &mut Box<dyn ClientStream>, status: &str, body: &str) {
1765 let response = format!(
1766 "HTTP/1.1 {status}\r\n\
1767 Content-Type: text/plain\r\n\
1768 Content-Length: {}\r\n\
1769 Connection: close\r\n\
1770 \r\n\
1771 {body}",
1772 body.len(),
1773 );
1774 let _ = client_stream.write_all(response.as_bytes()).await;
1775 let _ = client_stream.shutdown().await;
1776}
1777
1778struct HttpsBalancer {
1779 resolver: Arc<TenantDnsResolver>,
1780 tls: Option<ReloadingSslContext>,
1781 resolve_template: Arc<str>,
1782 port: u16,
1783 metrics: Arc<ServerMetrics>,
1784 limiter: Arc<ConnectionLimiter>,
1785 configs: ConfigSet,
1786 internal_tls: Option<SslConnector>,
1787}
1788
1789impl HttpsBalancer {
1790 async fn pre_resolve(
1792 conn: Connection,
1793 tls_context: Option<ReloadingSslContext>,
1794 resolver: &TenantDnsResolver,
1795 resolve_template: &str,
1796 port: u16,
1797 metrics: &ServerMetrics,
1798 fault: &ConnectionFault,
1799 ) -> Result<(Box<dyn ClientStream>, ResolvedAddr, SocketAddr), anyhow::Error> {
1800 let peer_addr = conn
1801 .peer_addr()
1802 .context("fetching peer addr")
1803 .inspect_err(|_| fault.server())?;
1804 let (client_stream, servername): (Box<dyn ClientStream>, Option<String>) = match tls_context
1805 {
1806 Some(tls_context) => {
1807 let mut ssl_stream = Ssl::new(&tls_context.get())
1808 .and_then(|ssl| SslStream::new(ssl, conn))
1809 .inspect_err(|_| fault.server())?;
1810 if let Err(e) = Pin::new(&mut ssl_stream).accept().await {
1811 metrics.tls_handshake_failures().inc();
1812 let _ = ssl_stream.get_mut().shutdown().await;
1813 return Err(e.into());
1814 }
1815 let servername: Option<String> =
1816 ssl_stream.ssl().servername(NameType::HOST_NAME).map(|sn| {
1817 match sn.split_once('.') {
1818 Some((left, _right)) => left,
1819 None => sn,
1820 }
1821 .into()
1822 });
1823 debug!("Found sni servername: {servername:?} (https)");
1824 (Box::new(ssl_stream), servername)
1825 }
1826 _ => (Box::new(conn), None),
1827 };
1828 let resolved = fault
1829 .server_work(Self::resolve(
1830 resolver,
1831 resolve_template,
1832 port,
1833 servername.as_deref(),
1834 ))
1835 .await
1836 .inspect_err(|e| {
1837 if !names_unknown_host(e) {
1838 fault.server();
1839 }
1840 })?;
1841 Ok((client_stream, resolved, peer_addr))
1842 }
1843
1844 async fn resolve(
1845 resolver: &TenantDnsResolver,
1846 resolve_template: &str,
1847 port: u16,
1848 servername: Option<&str>,
1849 ) -> Result<ResolvedAddr, anyhow::Error> {
1850 let (addr, tenant) = match servername {
1851 Some(sni) => resolver.resolve_sni(resolve_template, port, sni).await?,
1852 None if resolve_template.contains("{}") => return Err(UnknownHost::NoSni.into()),
1855 None => {
1856 debug!("https hostname (no SNI): {}:{}", resolve_template, port);
1857 resolver.resolve(resolve_template, port, false).await?
1858 }
1859 };
1860
1861 Ok(ResolvedAddr {
1862 addr,
1863 password: None,
1864 tenant,
1865 })
1866 }
1867}
1868
1869fn extract_tenant_from_cname(cname: &str) -> Option<String> {
1882 let mut parts = cname.split('.');
1883 let _service = parts.next();
1884 let Some(namespace) = parts.next() else {
1885 return None;
1886 };
1887 let Some((_, namespace)) = namespace.split_once('-') else {
1889 return None;
1890 };
1891 let Some((tenant, _)) = namespace.rsplit_once('-') else {
1893 return None;
1894 };
1895 let Ok(tenant) = Uuid::parse_str(tenant) else {
1898 error!("cname tenant not a uuid: {tenant}");
1899 return None;
1900 };
1901 Some(tenant.to_string())
1902}
1903
1904fn strip_ipv6_brackets(host: &str) -> &str {
1909 host.strip_prefix('[')
1910 .and_then(|h| h.strip_suffix(']'))
1911 .unwrap_or(host)
1912}
1913
1914impl mz_server_core::Server for HttpsBalancer {
1915 const NAME: &'static str = "https_balancer";
1916
1917 fn handle_connection(
1919 &self,
1920 conn: Connection,
1921 _tokio_metrics_intervals: impl Iterator<Item = TaskMetrics> + Send + 'static,
1922 ) -> mz_server_core::ConnectionHandler {
1923 let tls_context = self.tls.clone();
1924 let internal_tls = self.internal_tls.clone();
1925 let resolver = Arc::clone(&self.resolver);
1926 let resolve_template = Arc::clone(&self.resolve_template);
1927 let port = self.port;
1928 let inner_metrics = Arc::clone(&self.metrics);
1929 let outer_metrics = Arc::clone(&self.metrics);
1930 let limiter = Arc::clone(&self.limiter);
1931 let pre_resolved_timeout = PRE_RESOLVED_TIMEOUT.get(&self.configs);
1932 let inject_proxy_headers = INJECT_PROXY_PROTOCOL_HEADER_HTTP.get(&self.configs);
1933 Box::pin(async move {
1934 let accepted = Instant::now();
1935 let fault = ConnectionFault::default();
1936 let fault_ref = &fault;
1937 let active_guard = inner_metrics.active_connections();
1938 let result: Result<_, anyhow::Error> = Box::pin(async move {
1939 let Some(_conn_guard) = limiter.acquire() else {
1940 fault_ref.server();
1941 return Ok(());
1942 };
1943 let pre_resolved = inner_metrics.pre_resolved_connections();
1944
1945 let (mut client_stream, resolved, peer_addr) = under_pre_resolved_timeout(
1946 pre_resolved_timeout,
1947 &inner_metrics,
1948 fault_ref,
1949 Self::pre_resolve(
1950 conn,
1951 tls_context,
1952 &resolver,
1953 &resolve_template,
1954 port,
1955 &inner_metrics,
1956 fault_ref,
1957 ),
1958 )
1959 .await?;
1960 drop(pre_resolved);
1961 if let Some(tenant) = &resolved.tenant {
1964 info!(
1965 target: NO_SENTRY_TARGET,
1966 client_addr = %peer_addr.ip(),
1967 %tenant,
1968 authenticated = false,
1969 "https connection resolved",
1970 );
1971 }
1972 let inner_active_guard = resolved
1973 .tenant
1974 .as_ref()
1975 .map(|tenant| inner_metrics.tenant_connections(tenant));
1976 let mut mz_stream = match connect_upstream(resolved.addr, &inner_metrics).await {
1977 Ok(stream) => stream,
1978 Err(e) => {
1979 error!("failed to connect to upstream server: {e}");
1980 fault_ref.server();
1981 send_http_error(
1982 &mut client_stream,
1983 "502 Bad Gateway",
1984 "upstream server not available",
1985 )
1986 .await;
1987 return Ok(());
1988 }
1989 };
1990
1991 let mz_stream = async {
1994 if inject_proxy_headers {
1995 let addrs = ProxiedAddress::stream(peer_addr, resolved.addr);
1997 let header = ProxyHeader::with_address(addrs);
1998 let mut buf = [0u8; 1024];
1999 let len = header.encode_to_slice_v2(&mut buf)?;
2000 mz_stream.write_all(&buf[..len]).await?;
2001 }
2002
2003 let mz_stream = if let Some(internal_tls) = &internal_tls {
2004 let mut ssl = internal_tls
2006 .configure()?
2007 .into_ssl(&resolved.addr.to_string())?;
2008 ssl.set_connect_state();
2009 Conn::Ssl(SslStream::new(ssl, mz_stream)?)
2010 } else {
2011 Conn::Unencrypted(mz_stream)
2012 };
2013 Ok::<_, anyhow::Error>(mz_stream)
2014 }
2015 .await;
2016 let mut mz_stream = mz_stream.inspect_err(|_| fault_ref.server())?;
2017 inner_metrics
2018 .connection_setup_duration()
2019 .observe(accepted.elapsed().as_secs_f64());
2020
2021 let mut client_counter = CountingConn::new(client_stream);
2022
2023 let _ = tokio::io::copy_bidirectional(&mut client_counter, &mut mz_stream).await;
2027 inner_metrics
2028 .connection_duration()
2029 .observe(accepted.elapsed().as_secs_f64());
2030 if let Some(tenant) = &resolved.tenant {
2031 inner_metrics
2032 .tenant_connections_tx(tenant)
2033 .inc_by(u64::cast_from(client_counter.written));
2034 inner_metrics
2035 .tenant_connections_rx(tenant)
2036 .inc_by(u64::cast_from(client_counter.read));
2037 }
2038 drop(inner_active_guard);
2039 Ok(())
2040 })
2041 .await;
2042 drop(active_guard);
2043 outer_metrics.connection_status(fault.is_ok()).inc();
2044 if let Err(e) = result {
2045 debug!("connection error: {e}");
2046 }
2047 Ok(())
2048 })
2049 }
2050}
2051
2052#[derive(Debug)]
2055pub struct SniTemplate {
2056 pub template: String,
2057 pub port: u16,
2058}
2059
2060trait ClientStream: AsyncRead + AsyncWrite + Unpin + Send {}
2061impl<T: AsyncRead + AsyncWrite + Unpin + Send> ClientStream for T {}
2062
2063#[derive(Debug)]
2064pub enum BalancerResolver {
2065 Static(String),
2066 MultiTenant {
2067 dns: Arc<TenantDnsResolver>,
2068 frontegg: FronteggResolver,
2069 sni: Option<SniTemplate>,
2070 },
2071}
2072
2073#[derive(Debug, thiserror::Error)]
2079enum ResolveError {
2080 #[error("invalid password")]
2081 InvalidPassword,
2082 #[error("internal error")]
2087 Client(#[source] anyhow::Error),
2088 #[error("upstream server not available")]
2095 Upstream(#[source] anyhow::Error),
2096 #[error("internal error")]
2098 Internal(#[from] anyhow::Error),
2099}
2100
2101impl From<io::Error> for ResolveError {
2102 fn from(e: io::Error) -> Self {
2103 ResolveError::Internal(e.into())
2104 }
2105}
2106
2107impl BalancerResolver {
2108 pub fn shared_dns(&self) -> Option<Arc<TenantDnsResolver>> {
2111 match self {
2112 BalancerResolver::Static(_) => None,
2113 BalancerResolver::MultiTenant { dns, .. } => Some(Arc::clone(dns)),
2114 }
2115 }
2116
2117 async fn resolve<A>(
2118 &self,
2119 conn: &mut FramedConn<A>,
2120 user: &str,
2121 metrics: &ServerMetrics,
2122 fault: &ConnectionFault,
2123 ) -> Result<ResolvedAddr, ResolveError>
2124 where
2125 A: AsyncRead + AsyncWrite + Unpin,
2126 {
2127 match self {
2128 BalancerResolver::MultiTenant {
2129 dns: dns_resolver,
2130 frontegg:
2131 FronteggResolver {
2132 auth,
2133 addr_template,
2134 },
2135 sni: sni_resolver,
2136 } => {
2137 let servername = match conn.inner() {
2138 Conn::Ssl(ssl_stream) => {
2139 ssl_stream.ssl().servername(NameType::HOST_NAME).map(|sn| {
2140 match sn.split_once('.') {
2141 Some((left, _right)) => left,
2142 None => sn,
2143 }
2144 })
2145 }
2146 Conn::Unencrypted(_) => None,
2147 };
2148 let has_sni = servername.is_some();
2149 let resolved_addr = match (servername, sni_resolver.as_ref()) {
2150 (Some(servername), Some(SniTemplate { template, port })) => {
2151 let (addr, tenant) = fault
2156 .server_work(dns_resolver.resolve_sni(template, *port, servername))
2157 .await
2158 .map_err(ResolveError::Upstream)?;
2159 debug!("pgwire SNI resolved tenant: {:?}", tenant);
2160 ResolvedAddr {
2161 addr,
2162 password: None,
2163 tenant,
2164 }
2165 }
2166 _ => {
2167 let client_io = |e: io::Error| ResolveError::Client(e.into());
2168 conn.send(BackendMessage::AuthenticationCleartextPassword)
2169 .await
2170 .map_err(client_io)?;
2171 conn.flush().await.map_err(client_io)?;
2172 let password = match conn.recv().await.map_err(client_io)? {
2173 Some(FrontendMessage::Password { password }) => password,
2174 _ => {
2175 return Err(ResolveError::Client(anyhow::anyhow!(
2176 "expected Password message"
2177 )));
2178 }
2179 };
2180
2181 let auth_response = fault
2185 .server_work(auth.authenticate(user, &password, None))
2186 .await;
2187 let auth_session = match auth_response {
2188 Ok((auth_session, _)) => auth_session,
2189 Err(e) => {
2190 warn!("pgwire connection failed authentication: {}", e);
2191 if !e.is_credential_rejection() {
2193 fault.server();
2194 }
2195 return Err(ResolveError::InvalidPassword);
2196 }
2197 };
2198
2199 let hostname_with_port =
2200 addr_template.replace("{}", &auth_session.tenant_id().to_string());
2201 let (hostname, port_str) = hostname_with_port
2202 .rsplit_once(':')
2203 .ok_or_else(|| anyhow::anyhow!("port required in addr_template"))?;
2204 let port: u16 = port_str.parse().with_context(|| {
2205 format!("invalid port in addr_template: {}", port_str)
2206 })?;
2207 let addr = fault
2211 .server_work(dns_resolver.resolve_addr(hostname, port))
2212 .await
2213 .map_err(ResolveError::Upstream)?;
2214 let tenant = auth_session.tenant_id().to_string();
2215 debug!("Frontegg resolved tenant: {}", tenant);
2216 ResolvedAddr {
2217 addr,
2218 password: Some(password),
2219 tenant: Some(tenant),
2220 }
2221 }
2222 };
2223 metrics
2224 .tenant_pgwire_sni_count(
2225 resolved_addr.tenant.as_deref().unwrap_or("unknown"),
2226 has_sni,
2227 )
2228 .inc();
2229
2230 Ok(resolved_addr)
2231 }
2232 BalancerResolver::Static(addr) => {
2233 let Some(addr) = fault
2236 .server_work(tokio::net::lookup_host(addr))
2237 .await?
2238 .next()
2239 else {
2240 return Err(anyhow::anyhow!("{addr} did not resolve to any addresses").into());
2241 };
2242
2243 Ok(ResolvedAddr {
2244 addr,
2245 password: None,
2246 tenant: None,
2247 })
2248 }
2249 }
2250 }
2251}
2252
2253fn create_resolver() -> Result<TokioResolver, anyhow::Error> {
2261 let (config, mut opts) = read_system_conf().context("reading system DNS configuration")?;
2262 opts.cache_size = 0;
2263 opts.ip_strategy = LookupIpStrategy::Ipv4thenIpv6;
2265
2266 Ok(
2267 Resolver::builder_with_config(config, TokioRuntimeProvider::default())
2268 .with_options(opts)
2269 .build()?,
2270 )
2271}
2272
2273#[derive(Debug)]
2279pub struct TenantDnsResolver {
2280 resolver: TokioResolver,
2281}
2282
2283impl TenantDnsResolver {
2284 pub fn new() -> Result<Self, anyhow::Error> {
2287 Ok(Self {
2288 resolver: create_resolver()?,
2289 })
2290 }
2291
2292 async fn resolve_cname(&self, hostname: &str) -> Option<String> {
2294 match self.resolver.lookup(hostname, RecordType::CNAME).await {
2295 Ok(cname_response) => {
2296 if let Some(cname_record) = cname_response.answers().first() {
2297 if let RData::CNAME(cname_data) = &cname_record.data {
2298 let cname = cname_data.to_string();
2299 debug!("CNAME for {}: {}", hostname, cname);
2300 return Some(cname);
2301 }
2302 }
2303 None
2304 }
2305 Err(e) => {
2306 debug!("CNAME lookup failed for {}: {}", hostname, e);
2307 None
2308 }
2309 }
2310 }
2311
2312 async fn resolve_a(&self, hostname: &str) -> Result<LookupIp, anyhow::Error> {
2314 self.resolver
2315 .lookup_ip(hostname)
2316 .await
2317 .with_context(|| format!("resolving A records for {}", hostname))
2318 }
2319
2320 pub async fn resolve_sni(
2331 &self,
2332 template: &str,
2333 port: u16,
2334 servername: &str,
2335 ) -> Result<(SocketAddr, Option<String>), anyhow::Error> {
2336 let hostname = template.replace("{}", servername);
2337 debug!("SNI hostname: {}", hostname);
2338 let client_chosen = template.contains("{}");
2341 if client_chosen && Name::from_utf8(&hostname).is_err() {
2343 return Err(UnknownHost::Named(hostname).into());
2344 }
2345 self.resolve(&hostname, port, client_chosen).await
2346 }
2347
2348 async fn resolve_addr(&self, host: &str, port: u16) -> Result<SocketAddr, anyhow::Error> {
2351 let host = strip_ipv6_brackets(host);
2352 if let Ok(ip) = host.parse::<IpAddr>() {
2356 return Ok(SocketAddr::new(ip, port));
2357 }
2358 Self::first_addr(self.resolve_a(host).await?, port)
2359 }
2360
2361 async fn resolve(
2366 &self,
2367 host: &str,
2368 port: u16,
2369 client_chosen: bool,
2370 ) -> Result<(SocketAddr, Option<String>), anyhow::Error> {
2371 let host = strip_ipv6_brackets(host);
2372 if let Ok(ip) = host.parse::<IpAddr>() {
2374 return Ok((SocketAddr::new(ip, port), None));
2375 }
2376
2377 let (ips, tenant) = if let Some(cname) = self.resolve_cname(host).await {
2381 let tenant = extract_tenant_from_cname(&cname);
2382 (self.resolve_a(&cname).await?, tenant)
2383 } else {
2384 let ips = self.resolve_a(host).await.map_err(|e| {
2385 let no_records = e
2386 .downcast_ref::<NetError>()
2387 .is_some_and(NetError::is_no_records_found);
2388 if client_chosen && no_records {
2389 e.context(UnknownHost::Named(host.to_owned()))
2390 } else {
2391 e
2392 }
2393 })?;
2394 (ips, None)
2395 };
2396
2397 Ok((Self::first_addr(ips, port)?, tenant))
2398 }
2399
2400 fn first_addr(ips: LookupIp, port: u16) -> Result<SocketAddr, anyhow::Error> {
2402 ips.iter()
2403 .next()
2404 .map(|ip| SocketAddr::new(ip, port))
2405 .ok_or_else(|| anyhow::anyhow!("no A records found in DNS response"))
2406 }
2407}
2408
2409#[derive(Debug, thiserror::Error)]
2412enum UnknownHost {
2413 #[error("no SNI to route by")]
2414 NoSni,
2415 #[error("no tenant at {0}")]
2417 Named(String),
2418}
2419
2420fn names_unknown_host(err: &anyhow::Error) -> bool {
2422 err.downcast_ref::<UnknownHost>().is_some()
2423}
2424
2425#[derive(Debug)]
2426pub struct FronteggResolver {
2427 pub auth: FronteggAuthentication,
2428 pub addr_template: String,
2429}
2430
2431#[derive(Debug)]
2432struct ResolvedAddr {
2433 addr: SocketAddr,
2434 password: Option<String>,
2435 tenant: Option<String>,
2436}
2437
2438#[cfg(test)]
2439mod tests {
2440 use mz_dyncfg::ConfigUpdates;
2441
2442 use super::*;
2443
2444 #[mz_ore::test]
2445 fn test_tenant() {
2446 let tests = vec![
2447 ("", None),
2448 (
2449 "environmentd.environment-58cd23ff-a4d7-4bd0-ad85-a6ff29cc86c3-0.svc.cluster.local",
2450 Some("58cd23ff-a4d7-4bd0-ad85-a6ff29cc86c3"),
2451 ),
2452 (
2453 "environmentd.environment-58cd23ff-a4d7-4bd0-ad85-a6ff29cc86c3-0.svc.cluster.local.",
2456 Some("58cd23ff-a4d7-4bd0-ad85-a6ff29cc86c3"),
2457 ),
2458 (
2459 "service.something-58cd23ff-a4d7-4bd0-ad85-a6ff29cc86c3-0.ssvvcc.cloister.faraway",
2461 Some("58cd23ff-a4d7-4bd0-ad85-a6ff29cc86c3"),
2462 ),
2463 (
2464 "environmentd.environment-58cd23ffa4d74bd0ad85a6ff29cc86c3-0.svc.cluster.local",
2466 Some("58cd23ff-a4d7-4bd0-ad85-a6ff29cc86c3"),
2467 ),
2468 (
2469 "environmentd.environment-58cd23ff-a4d7-4bd0-ad85-a6ff29cc86c3-1234.svc.cluster.local",
2471 Some("58cd23ff-a4d7-4bd0-ad85-a6ff29cc86c3"),
2472 ),
2473 (
2474 "environmentd.environment-58CD23FF-A4D7-4BD0-AD85-A6FF29CC86C3-0.svc.cluster.local",
2476 Some("58cd23ff-a4d7-4bd0-ad85-a6ff29cc86c3"),
2477 ),
2478 (
2479 "environmentd.environment-58cd23ff-a4d7-4bd0-ad85-a6ff29cc86c3.svc.cluster.local",
2481 None,
2482 ),
2483 (
2484 "environment-58cd23ff-a4d7-4bd0-ad85-a6ff29cc86c3-0.svc.cluster.local",
2486 None,
2487 ),
2488 (
2489 "environmentd.environment-8cd23ff-a4d7-4bd0-ad85-a6ff29cc86c3-0.svc.cluster.local",
2491 None,
2492 ),
2493 ];
2494 for (name, expect) in tests {
2495 let cname = extract_tenant_from_cname(name);
2496 assert_eq!(
2497 cname.as_deref(),
2498 expect,
2499 "{name} got {cname:?} expected {expect:?}"
2500 );
2501 }
2502 }
2503
2504 #[mz_ore::test]
2505 fn test_strip_ipv6_brackets() {
2506 assert_eq!(strip_ipv6_brackets("[::1]"), "::1");
2507 assert_eq!(strip_ipv6_brackets("[2001:db8::1]"), "2001:db8::1");
2508 assert_eq!(strip_ipv6_brackets("127.0.0.1"), "127.0.0.1");
2510 assert_eq!(strip_ipv6_brackets("host.example.com"), "host.example.com");
2511 assert_eq!(strip_ipv6_brackets("[unclosed"), "[unclosed");
2512 assert_eq!(strip_ipv6_brackets("unopened]"), "unopened]");
2513 }
2514
2515 #[mz_ore::test]
2516 fn test_connection_limiter() {
2517 let configs = dyncfgs::all_dyncfgs(ConfigSet::default());
2518 let set_max = |max: u32| {
2519 let mut updates = ConfigUpdates::default();
2520 updates.add(&MAX_CONNECTIONS, max);
2521 updates.apply(&configs);
2522 };
2523
2524 set_max(2);
2525 let limiter = ConnectionLimiter::new(&MetricsRegistry::new(), configs.clone());
2526 let first = limiter.acquire().expect("under the limit");
2527 let second = limiter.acquire().expect("at the limit");
2528 assert!(limiter.acquire().is_none());
2529 assert_eq!(limiter.rejected.get(), 1);
2530
2531 drop(second);
2533 let _third = limiter.acquire().expect("capacity freed");
2534 drop(first);
2535
2536 set_max(1);
2539 assert!(limiter.acquire().is_none());
2540
2541 set_max(0);
2543 assert!(limiter.acquire().is_some());
2544 }
2545
2546 #[mz_ore::test(tokio::test)]
2549 #[cfg_attr(miri, ignore)] async fn test_dyncfgs_sync_from_launchdarkly() {
2551 mz_dyncfg_launchdarkly::sync_launchdarkly_to_configset(
2552 dyncfgs::all_dyncfgs(ConfigSet::default()),
2553 &BUILD_INFO,
2554 |_| Ok(()),
2555 None,
2556 Duration::ZERO,
2557 None,
2558 |_, _| {},
2559 )
2560 .await
2561 .expect("every balancerd dyncfg converts to a LaunchDarkly flag");
2562 }
2563
2564 #[mz_ore::test]
2565 fn test_connection_watermarks() {
2566 let configs = dyncfgs::all_dyncfgs(ConfigSet::default());
2567 let set = |low: usize, high: usize, max: u32| {
2568 let mut updates = ConfigUpdates::default();
2569 updates.add(&CONNECTION_LOW_WATERMARK, low);
2570 updates.add(&CONNECTION_HIGH_WATERMARK, high);
2571 updates.add(&MAX_CONNECTIONS, max);
2572 updates.apply(&configs);
2573 };
2574 let limiter = ConnectionLimiter::new(&MetricsRegistry::new(), configs.clone());
2575 let _first = limiter.acquire().expect("under the limit");
2576 let second = limiter.acquire().expect("under the limit");
2577 let third = limiter.acquire().expect("under the limit");
2578
2579 for (low, high, max) in [
2581 (0, 0, 5),
2582 (1, 0, 5),
2583 (0, 3, 5),
2584 (3, 3, 5),
2585 (4, 3, 5),
2586 (1, 5, 5),
2587 (1, 3, 0),
2588 ] {
2589 set(low, high, max);
2590 limiter.evaluate_watermarks();
2591 assert!(
2592 limiter.accepting(),
2593 "watermarks low={low} high={high} max={max} must be ignored",
2594 );
2595 }
2596 assert_eq!(limiter.watermark_transitions.get(), 0);
2597
2598 set(2, 3, 5);
2599 limiter.evaluate_watermarks();
2600 assert!(!limiter.accepting(), "at the high watermark");
2601 assert_eq!(limiter.watermark_transitions.get(), 1);
2602
2603 drop(third);
2604 limiter.evaluate_watermarks();
2605 assert!(
2606 !limiter.accepting(),
2607 "between the watermarks the state is kept"
2608 );
2609 drop(second);
2610 limiter.evaluate_watermarks();
2611 assert!(
2612 limiter.accepting(),
2613 "below the low watermark readiness returns"
2614 );
2615 assert_eq!(limiter.watermark_transitions.get(), 2);
2616
2617 let _fourth = limiter.acquire().expect("under the limit");
2619 let _fifth = limiter.acquire().expect("under the limit");
2620 limiter.evaluate_watermarks();
2621 assert!(!limiter.accepting(), "at the high watermark again");
2622 set(0, 0, 5);
2623 limiter.evaluate_watermarks();
2624 assert!(
2625 limiter.accepting(),
2626 "disabling the watermarks restores readiness"
2627 );
2628 assert_eq!(limiter.watermark_transitions.get(), 4);
2629 }
2630
2631 #[mz_ore::test(tokio::test(start_paused = true))]
2633 async fn test_pre_resolved_timeout_zero_disables() {
2634 let metrics = ServerMetrics::new(
2635 ServerMetricsConfig::register_into(&MetricsRegistry::new()),
2636 "pgwire",
2637 );
2638
2639 let slow = async {
2641 tokio::time::sleep(Duration::from_secs(60 * 60)).await;
2642 Ok::<(), io::Error>(())
2643 };
2644 let fault = ConnectionFault::default();
2645 assert!(
2646 under_pre_resolved_timeout(Duration::ZERO, &metrics, &fault, slow)
2647 .await
2648 .is_ok()
2649 );
2650 assert_eq!(metrics.pre_resolved_timeout().get(), 0);
2651
2652 let stalled = std::future::pending::<Result<(), io::Error>>();
2654 let err = under_pre_resolved_timeout(Duration::from_secs(30), &metrics, &fault, stalled)
2655 .await
2656 .expect_err("a phase that never finishes must be cut short");
2657 assert_eq!(err.kind(), io::ErrorKind::TimedOut);
2658 assert_eq!(metrics.pre_resolved_timeout().get(), 1);
2659 }
2660
2661 #[mz_ore::test(tokio::test)]
2663 #[cfg_attr(miri, ignore)] async fn test_sni_naming_no_tenant() {
2665 let resolver = TenantDnsResolver::new().expect("system DNS configuration is readable");
2666 for servername in ["bad!name".to_owned(), "a".repeat(64)] {
2667 let err = resolver
2668 .resolve_sni("blncr-{}.invalid", 6876, &servername)
2669 .await
2670 .expect_err("not a valid hostname");
2671 assert!(names_unknown_host(&err), "{servername}: {err:#}");
2672 }
2673
2674 let err = HttpsBalancer::resolve(&resolver, "blncr-{}.invalid", 6876, None)
2675 .await
2676 .expect_err("no SNI to route by");
2677 assert!(names_unknown_host(&err), "{err:#}");
2678 }
2679
2680 #[mz_ore::test(tokio::test(start_paused = true))]
2682 async fn test_pre_resolved_timeout_fault() {
2683 let metrics = ServerMetrics::new(
2684 ServerMetricsConfig::register_into(&MetricsRegistry::new()),
2685 "pgwire",
2686 );
2687
2688 let waiting_on_client = ConnectionFault::default();
2689 let stalled = std::future::pending::<Result<(), io::Error>>();
2690 under_pre_resolved_timeout(
2691 Duration::from_secs(30),
2692 &metrics,
2693 &waiting_on_client,
2694 stalled,
2695 )
2696 .await
2697 .expect_err("a phase that never finishes must be cut short");
2698 assert!(waiting_on_client.is_ok());
2699
2700 let resolving = ConnectionFault::default();
2701 let stalled = resolving.server_work(std::future::pending::<Result<(), io::Error>>());
2702 under_pre_resolved_timeout(Duration::from_secs(30), &metrics, &resolving, stalled)
2703 .await
2704 .expect_err("a phase that never finishes must be cut short");
2705 assert!(!resolving.is_ok());
2706
2707 let resolved = ConnectionFault::default();
2709 let phase = async {
2710 resolved.server_work(async {}).await;
2711 std::future::pending::<Result<(), io::Error>>().await
2712 };
2713 under_pre_resolved_timeout(Duration::from_secs(30), &metrics, &resolved, phase)
2714 .await
2715 .expect_err("a phase that never finishes must be cut short");
2716 assert!(resolved.is_ok());
2717 }
2718}