LCOV - code coverage report
Current view: top level - pageserver/src - config.rs (source / functions) Coverage Total Hit
Test: aca806cab4756d7eb6a304846130f4a73a5d5393.info Lines: 80.8 % 302 244
Test Date: 2025-04-24 20:31:15 Functions: 45.9 % 37 17

            Line data    Source code
       1              : //! Functions for handling page server configuration options
       2              : //!
       3              : //! Configuration options can be set in the pageserver.toml configuration
       4              : //! file, or on the command line.
       5              : //! See also `settings.md` for better description on every parameter.
       6              : 
       7              : pub mod ignored_fields;
       8              : 
       9              : use std::env;
      10              : use std::num::NonZeroUsize;
      11              : use std::sync::Arc;
      12              : use std::time::Duration;
      13              : 
      14              : use anyhow::{Context, bail, ensure};
      15              : use camino::{Utf8Path, Utf8PathBuf};
      16              : use once_cell::sync::OnceCell;
      17              : use pageserver_api::config::{DiskUsageEvictionTaskConfig, MaxVectoredReadBytes};
      18              : use pageserver_api::models::ImageCompressionAlgorithm;
      19              : use pageserver_api::shard::TenantShardId;
      20              : use pem::Pem;
      21              : use postgres_backend::AuthType;
      22              : use remote_storage::{RemotePath, RemoteStorageConfig};
      23              : use reqwest::Url;
      24              : use storage_broker::Uri;
      25              : use utils::id::{NodeId, TimelineId};
      26              : use utils::logging::{LogFormat, SecretString};
      27              : use utils::postgres_client::PostgresClientProtocol;
      28              : 
      29              : use crate::tenant::storage_layer::inmemory_layer::IndexEntry;
      30              : use crate::tenant::{TENANTS_SEGMENT_NAME, TIMELINES_SEGMENT_NAME};
      31              : use crate::virtual_file::io_engine;
      32              : use crate::{TENANT_HEATMAP_BASENAME, TENANT_LOCATION_CONFIG_NAME, virtual_file};
      33              : 
      34              : /// Global state of pageserver.
      35              : ///
      36              : /// It's mostly immutable configuration, but some semaphores and the
      37              : /// like crept in over time and the name stuck.
      38              : ///
      39              : /// Instantiated by deserializing `pageserver.toml` into  [`pageserver_api::config::ConfigToml`]
      40              : /// and passing that to [`PageServerConf::parse_and_validate`].
      41              : ///
      42              : /// # Adding a New Field
      43              : ///
      44              : /// 1. Add the field to `pageserver_api::config::ConfigToml`.
      45              : /// 2. Fix compiler errors (exhaustive destructuring will guide you).
      46              : ///
      47              : /// For fields that require additional validation or filling in of defaults at runtime,
      48              : /// check for examples in the [`PageServerConf::parse_and_validate`] method.
      49              : #[derive(Debug, Clone)]
      50              : pub struct PageServerConf {
      51              :     // Identifier of that particular pageserver so e g safekeepers
      52              :     // can safely distinguish different pageservers
      53              :     pub id: NodeId,
      54              : 
      55              :     /// Example (default): 127.0.0.1:64000
      56              :     pub listen_pg_addr: String,
      57              :     /// Example (default): 127.0.0.1:9898
      58              :     pub listen_http_addr: String,
      59              :     /// Example: 127.0.0.1:9899
      60              :     pub listen_https_addr: Option<String>,
      61              : 
      62              :     /// Path to a file with certificate's private key for https API.
      63              :     /// Default: server.key
      64              :     pub ssl_key_file: Utf8PathBuf,
      65              :     /// Path to a file with a X509 certificate for https API.
      66              :     /// Default: server.crt
      67              :     pub ssl_cert_file: Utf8PathBuf,
      68              :     /// Period to reload certificate and private key from files.
      69              :     /// Default: 60s.
      70              :     pub ssl_cert_reload_period: Duration,
      71              :     /// Trusted root CA certificates to use in https APIs in PEM format.
      72              :     pub ssl_ca_certs: Vec<Pem>,
      73              : 
      74              :     /// Current availability zone. Used for traffic metrics.
      75              :     pub availability_zone: Option<String>,
      76              : 
      77              :     // Timeout when waiting for WAL receiver to catch up to an LSN given in a GetPage@LSN call.
      78              :     pub wait_lsn_timeout: Duration,
      79              :     // How long to wait for WAL redo to complete.
      80              :     pub wal_redo_timeout: Duration,
      81              : 
      82              :     pub superuser: String,
      83              :     pub locale: String,
      84              : 
      85              :     pub page_cache_size: usize,
      86              :     pub max_file_descriptors: usize,
      87              : 
      88              :     // Repository directory, relative to current working directory.
      89              :     // Normally, the page server changes the current working directory
      90              :     // to the repository, and 'workdir' is always '.'. But we don't do
      91              :     // that during unit testing, because the current directory is global
      92              :     // to the process but different unit tests work on different
      93              :     // repositories.
      94              :     pub workdir: Utf8PathBuf,
      95              : 
      96              :     pub pg_distrib_dir: Utf8PathBuf,
      97              : 
      98              :     // Authentication
      99              :     /// authentication method for the HTTP mgmt API
     100              :     pub http_auth_type: AuthType,
     101              :     /// authentication method for libpq connections from compute
     102              :     pub pg_auth_type: AuthType,
     103              :     /// Path to a file or directory containing public key(s) for verifying JWT tokens.
     104              :     /// Used for both mgmt and compute auth, if enabled.
     105              :     pub auth_validation_public_key_path: Option<Utf8PathBuf>,
     106              : 
     107              :     pub remote_storage_config: Option<RemoteStorageConfig>,
     108              : 
     109              :     pub default_tenant_conf: pageserver_api::config::TenantConfigToml,
     110              : 
     111              :     /// Storage broker endpoints to connect to.
     112              :     pub broker_endpoint: Uri,
     113              :     pub broker_keepalive_interval: Duration,
     114              : 
     115              :     pub log_format: LogFormat,
     116              : 
     117              :     /// Number of tenants which will be concurrently loaded from remote storage proactively on startup or attach.
     118              :     ///
     119              :     /// A lower value implicitly deprioritizes loading such tenants, vs. other work in the system.
     120              :     pub concurrent_tenant_warmup: ConfigurableSemaphore,
     121              : 
     122              :     /// Number of concurrent [`TenantShard::gather_size_inputs`](crate::tenant::TenantShard::gather_size_inputs) allowed.
     123              :     pub concurrent_tenant_size_logical_size_queries: ConfigurableSemaphore,
     124              :     /// Limit of concurrent [`TenantShard::gather_size_inputs`] issued by module `eviction_task`.
     125              :     /// The number of permits is the same as `concurrent_tenant_size_logical_size_queries`.
     126              :     /// See the comment in `eviction_task` for details.
     127              :     ///
     128              :     /// [`TenantShard::gather_size_inputs`]: crate::tenant::TenantShard::gather_size_inputs
     129              :     pub eviction_task_immitated_concurrent_logical_size_queries: ConfigurableSemaphore,
     130              : 
     131              :     // How often to collect metrics and send them to the metrics endpoint.
     132              :     pub metric_collection_interval: Duration,
     133              :     // How often to send unchanged cached metrics to the metrics endpoint.
     134              :     pub metric_collection_endpoint: Option<Url>,
     135              :     pub metric_collection_bucket: Option<RemoteStorageConfig>,
     136              :     pub synthetic_size_calculation_interval: Duration,
     137              : 
     138              :     pub disk_usage_based_eviction: Option<DiskUsageEvictionTaskConfig>,
     139              : 
     140              :     pub test_remote_failures: u64,
     141              : 
     142              :     pub ondemand_download_behavior_treat_error_as_warn: bool,
     143              : 
     144              :     /// How long will background tasks be delayed at most after initial load of tenants.
     145              :     ///
     146              :     /// Our largest initialization completions are in the range of 100-200s, so perhaps 10s works
     147              :     /// as we now isolate initial loading, initial logical size calculation and background tasks.
     148              :     /// Smaller nodes will have background tasks "not running" for this long unless every timeline
     149              :     /// has it's initial logical size calculated. Not running background tasks for some seconds is
     150              :     /// not terrible.
     151              :     pub background_task_maximum_delay: Duration,
     152              : 
     153              :     pub control_plane_api: Option<Url>,
     154              : 
     155              :     /// JWT token for use with the control plane API.
     156              :     pub control_plane_api_token: Option<SecretString>,
     157              : 
     158              :     pub import_pgdata_upcall_api: Option<Url>,
     159              :     pub import_pgdata_upcall_api_token: Option<SecretString>,
     160              :     pub import_pgdata_aws_endpoint_url: Option<Url>,
     161              : 
     162              :     /// If true, pageserver will make best-effort to operate without a control plane: only
     163              :     /// for use in major incidents.
     164              :     pub control_plane_emergency_mode: bool,
     165              : 
     166              :     /// How many heatmap uploads may be done concurrency: lower values implicitly deprioritize
     167              :     /// heatmap uploads vs. other remote storage operations.
     168              :     pub heatmap_upload_concurrency: usize,
     169              : 
     170              :     /// How many remote storage downloads may be done for secondary tenants concurrently.  Implicitly
     171              :     /// deprioritises secondary downloads vs. remote storage operations for attached tenants.
     172              :     pub secondary_download_concurrency: usize,
     173              : 
     174              :     /// Maximum number of WAL records to be ingested and committed at the same time
     175              :     pub ingest_batch_size: u64,
     176              : 
     177              :     pub virtual_file_io_engine: virtual_file::IoEngineKind,
     178              : 
     179              :     pub max_vectored_read_bytes: MaxVectoredReadBytes,
     180              : 
     181              :     pub image_compression: ImageCompressionAlgorithm,
     182              : 
     183              :     /// Whether to offload archived timelines automatically
     184              :     pub timeline_offloading: bool,
     185              : 
     186              :     /// How many bytes of ephemeral layer content will we allow per kilobyte of RAM.  When this
     187              :     /// is exceeded, we start proactively closing ephemeral layers to limit the total amount
     188              :     /// of ephemeral data.
     189              :     ///
     190              :     /// Setting this to zero disables limits on total ephemeral layer size.
     191              :     pub ephemeral_bytes_per_memory_kb: usize,
     192              : 
     193              :     pub l0_flush: crate::l0_flush::L0FlushConfig,
     194              : 
     195              :     /// Direct IO settings
     196              :     pub virtual_file_io_mode: virtual_file::IoMode,
     197              : 
     198              :     /// Optionally disable disk syncs (unsafe!)
     199              :     pub no_sync: bool,
     200              : 
     201              :     pub wal_receiver_protocol: PostgresClientProtocol,
     202              : 
     203              :     pub page_service_pipelining: pageserver_api::config::PageServicePipeliningConfig,
     204              : 
     205              :     pub get_vectored_concurrent_io: pageserver_api::config::GetVectoredConcurrentIo,
     206              : 
     207              :     /// Enable read path debugging. If enabled, read key errors will print a backtrace of the layer
     208              :     /// files read.
     209              :     pub enable_read_path_debugging: bool,
     210              : 
     211              :     /// Interpreted protocol feature: if enabled, validate that the logical WAL received from
     212              :     /// safekeepers does not have gaps.
     213              :     pub validate_wal_contiguity: bool,
     214              : 
     215              :     /// When set, the previously written to disk heatmap is loaded on tenant attach and used
     216              :     /// to avoid clobbering the heatmap from new, cold, attached locations.
     217              :     pub load_previous_heatmap: bool,
     218              : 
     219              :     /// When set, include visible layers in the next uploaded heatmaps of an unarchived timeline.
     220              :     pub generate_unarchival_heatmap: bool,
     221              : 
     222              :     pub tracing: Option<pageserver_api::config::Tracing>,
     223              : 
     224              :     /// Enable TLS in page service API.
     225              :     /// Does not force TLS: the client negotiates TLS usage during the handshake.
     226              :     /// Uses key and certificate from ssl_key_file/ssl_cert_file.
     227              :     pub enable_tls_page_service_api: bool,
     228              : 
     229              :     /// Run in development mode, which disables certain safety checks
     230              :     /// such as authentication requirements for HTTP and PostgreSQL APIs.
     231              :     /// This is insecure and should only be used in development environments.
     232              :     pub dev_mode: bool,
     233              : }
     234              : 
     235              : /// Token for authentication to safekeepers
     236              : ///
     237              : /// We do not want to store this in a PageServerConf because the latter may be logged
     238              : /// and/or serialized at a whim, while the token is secret. Currently this token is the
     239              : /// same for accessing all tenants/timelines, but may become per-tenant/per-timeline in
     240              : /// the future, more tokens and auth may arrive for storage broker, completely changing the logic.
     241              : /// Hence, we resort to a global variable for now instead of passing the token from the
     242              : /// startup code to the connection code through a dozen layers.
     243              : pub static SAFEKEEPER_AUTH_TOKEN: OnceCell<Arc<String>> = OnceCell::new();
     244              : 
     245              : impl PageServerConf {
     246              :     //
     247              :     // Repository paths, relative to workdir.
     248              :     //
     249              : 
     250        47136 :     pub fn tenants_path(&self) -> Utf8PathBuf {
     251        47136 :         self.workdir.join(TENANTS_SEGMENT_NAME)
     252        47136 :     }
     253              : 
     254          432 :     pub fn deletion_prefix(&self) -> Utf8PathBuf {
     255          432 :         self.workdir.join("deletion")
     256          432 :     }
     257              : 
     258            0 :     pub fn metadata_path(&self) -> Utf8PathBuf {
     259            0 :         self.workdir.join("metadata.json")
     260            0 :     }
     261              : 
     262          168 :     pub fn deletion_list_path(&self, sequence: u64) -> Utf8PathBuf {
     263              :         // Encode a version in the filename, so that if we ever switch away from JSON we can
     264              :         // increment this.
     265              :         const VERSION: u8 = 1;
     266              : 
     267          168 :         self.deletion_prefix()
     268          168 :             .join(format!("{sequence:016x}-{VERSION:02x}.list"))
     269          168 :     }
     270              : 
     271          144 :     pub fn deletion_header_path(&self) -> Utf8PathBuf {
     272              :         // Encode a version in the filename, so that if we ever switch away from JSON we can
     273              :         // increment this.
     274              :         const VERSION: u8 = 1;
     275              : 
     276          144 :         self.deletion_prefix().join(format!("header-{VERSION:02x}"))
     277          144 :     }
     278              : 
     279        46812 :     pub fn tenant_path(&self, tenant_shard_id: &TenantShardId) -> Utf8PathBuf {
     280        46812 :         self.tenants_path().join(tenant_shard_id.to_string())
     281        46812 :     }
     282              : 
     283              :     /// Points to a place in pageserver's local directory,
     284              :     /// where certain tenant's LocationConf be stored.
     285            0 :     pub(crate) fn tenant_location_config_path(
     286            0 :         &self,
     287            0 :         tenant_shard_id: &TenantShardId,
     288            0 :     ) -> Utf8PathBuf {
     289            0 :         self.tenant_path(tenant_shard_id)
     290            0 :             .join(TENANT_LOCATION_CONFIG_NAME)
     291            0 :     }
     292              : 
     293         1392 :     pub(crate) fn tenant_heatmap_path(&self, tenant_shard_id: &TenantShardId) -> Utf8PathBuf {
     294         1392 :         self.tenant_path(tenant_shard_id)
     295         1392 :             .join(TENANT_HEATMAP_BASENAME)
     296         1392 :     }
     297              : 
     298        43992 :     pub fn timelines_path(&self, tenant_shard_id: &TenantShardId) -> Utf8PathBuf {
     299        43992 :         self.tenant_path(tenant_shard_id)
     300        43992 :             .join(TIMELINES_SEGMENT_NAME)
     301        43992 :     }
     302              : 
     303        41172 :     pub fn timeline_path(
     304        41172 :         &self,
     305        41172 :         tenant_shard_id: &TenantShardId,
     306        41172 :         timeline_id: &TimelineId,
     307        41172 :     ) -> Utf8PathBuf {
     308        41172 :         self.timelines_path(tenant_shard_id)
     309        41172 :             .join(timeline_id.to_string())
     310        41172 :     }
     311              : 
     312              :     /// Turns storage remote path of a file into its local path.
     313            0 :     pub fn local_path(&self, remote_path: &RemotePath) -> Utf8PathBuf {
     314            0 :         remote_path.with_base(&self.workdir)
     315            0 :     }
     316              : 
     317              :     //
     318              :     // Postgres distribution paths
     319              :     //
     320          144 :     pub fn pg_distrib_dir(&self, pg_version: u32) -> anyhow::Result<Utf8PathBuf> {
     321          144 :         let path = self.pg_distrib_dir.clone();
     322          144 : 
     323          144 :         #[allow(clippy::manual_range_patterns)]
     324          144 :         match pg_version {
     325          144 :             14 | 15 | 16 | 17 => Ok(path.join(format!("v{pg_version}"))),
     326            0 :             _ => bail!("Unsupported postgres version: {}", pg_version),
     327              :         }
     328          144 :     }
     329              : 
     330           72 :     pub fn pg_bin_dir(&self, pg_version: u32) -> anyhow::Result<Utf8PathBuf> {
     331           72 :         Ok(self.pg_distrib_dir(pg_version)?.join("bin"))
     332           72 :     }
     333           72 :     pub fn pg_lib_dir(&self, pg_version: u32) -> anyhow::Result<Utf8PathBuf> {
     334           72 :         Ok(self.pg_distrib_dir(pg_version)?.join("lib"))
     335           72 :     }
     336              : 
     337              :     /// Parse a configuration file (pageserver.toml) into a PageServerConf struct,
     338              :     /// validating the input and failing on errors.
     339              :     ///
     340              :     /// This leaves any options not present in the file in the built-in defaults.
     341         1500 :     pub fn parse_and_validate(
     342         1500 :         id: NodeId,
     343         1500 :         config_toml: pageserver_api::config::ConfigToml,
     344         1500 :         workdir: &Utf8Path,
     345         1500 :     ) -> anyhow::Result<Self> {
     346         1500 :         let pageserver_api::config::ConfigToml {
     347         1500 :             listen_pg_addr,
     348         1500 :             listen_http_addr,
     349         1500 :             listen_https_addr,
     350         1500 :             ssl_key_file,
     351         1500 :             ssl_cert_file,
     352         1500 :             ssl_cert_reload_period,
     353         1500 :             ssl_ca_file,
     354         1500 :             availability_zone,
     355         1500 :             wait_lsn_timeout,
     356         1500 :             wal_redo_timeout,
     357         1500 :             superuser,
     358         1500 :             locale,
     359         1500 :             page_cache_size,
     360         1500 :             max_file_descriptors,
     361         1500 :             pg_distrib_dir,
     362         1500 :             http_auth_type,
     363         1500 :             pg_auth_type,
     364         1500 :             auth_validation_public_key_path,
     365         1500 :             remote_storage,
     366         1500 :             broker_endpoint,
     367         1500 :             broker_keepalive_interval,
     368         1500 :             log_format,
     369         1500 :             metric_collection_interval,
     370         1500 :             metric_collection_endpoint,
     371         1500 :             metric_collection_bucket,
     372         1500 :             synthetic_size_calculation_interval,
     373         1500 :             disk_usage_based_eviction,
     374         1500 :             test_remote_failures,
     375         1500 :             ondemand_download_behavior_treat_error_as_warn,
     376         1500 :             background_task_maximum_delay,
     377         1500 :             control_plane_api,
     378         1500 :             control_plane_api_token,
     379         1500 :             control_plane_emergency_mode,
     380         1500 :             import_pgdata_upcall_api,
     381         1500 :             import_pgdata_upcall_api_token,
     382         1500 :             import_pgdata_aws_endpoint_url,
     383         1500 :             heatmap_upload_concurrency,
     384         1500 :             secondary_download_concurrency,
     385         1500 :             ingest_batch_size,
     386         1500 :             max_vectored_read_bytes,
     387         1500 :             image_compression,
     388         1500 :             timeline_offloading,
     389         1500 :             ephemeral_bytes_per_memory_kb,
     390         1500 :             l0_flush,
     391         1500 :             virtual_file_io_mode,
     392         1500 :             concurrent_tenant_warmup,
     393         1500 :             concurrent_tenant_size_logical_size_queries,
     394         1500 :             virtual_file_io_engine,
     395         1500 :             tenant_config,
     396         1500 :             no_sync,
     397         1500 :             wal_receiver_protocol,
     398         1500 :             page_service_pipelining,
     399         1500 :             get_vectored_concurrent_io,
     400         1500 :             enable_read_path_debugging,
     401         1500 :             validate_wal_contiguity,
     402         1500 :             load_previous_heatmap,
     403         1500 :             generate_unarchival_heatmap,
     404         1500 :             tracing,
     405         1500 :             enable_tls_page_service_api,
     406         1500 :             dev_mode,
     407         1500 :         } = config_toml;
     408              : 
     409         1500 :         let mut conf = PageServerConf {
     410              :             // ------------------------------------------------------------
     411              :             // fields that are already fully validated by the ConfigToml Deserialize impl
     412              :             // ------------------------------------------------------------
     413         1500 :             listen_pg_addr,
     414         1500 :             listen_http_addr,
     415         1500 :             listen_https_addr,
     416         1500 :             ssl_key_file,
     417         1500 :             ssl_cert_file,
     418         1500 :             ssl_cert_reload_period,
     419         1500 :             availability_zone,
     420         1500 :             wait_lsn_timeout,
     421         1500 :             wal_redo_timeout,
     422         1500 :             superuser,
     423         1500 :             locale,
     424         1500 :             page_cache_size,
     425         1500 :             max_file_descriptors,
     426         1500 :             http_auth_type,
     427         1500 :             pg_auth_type,
     428         1500 :             auth_validation_public_key_path,
     429         1500 :             remote_storage_config: remote_storage,
     430         1500 :             broker_endpoint,
     431         1500 :             broker_keepalive_interval,
     432         1500 :             log_format,
     433         1500 :             metric_collection_interval,
     434         1500 :             metric_collection_endpoint,
     435         1500 :             metric_collection_bucket,
     436         1500 :             synthetic_size_calculation_interval,
     437         1500 :             disk_usage_based_eviction,
     438         1500 :             test_remote_failures,
     439         1500 :             ondemand_download_behavior_treat_error_as_warn,
     440         1500 :             background_task_maximum_delay,
     441         1500 :             control_plane_api,
     442         1500 :             control_plane_emergency_mode,
     443         1500 :             heatmap_upload_concurrency,
     444         1500 :             secondary_download_concurrency,
     445         1500 :             ingest_batch_size,
     446         1500 :             max_vectored_read_bytes,
     447         1500 :             image_compression,
     448         1500 :             timeline_offloading,
     449         1500 :             ephemeral_bytes_per_memory_kb,
     450         1500 :             import_pgdata_upcall_api,
     451         1500 :             import_pgdata_upcall_api_token: import_pgdata_upcall_api_token.map(SecretString::from),
     452         1500 :             import_pgdata_aws_endpoint_url,
     453         1500 :             wal_receiver_protocol,
     454         1500 :             page_service_pipelining,
     455         1500 :             get_vectored_concurrent_io,
     456         1500 :             tracing,
     457         1500 :             enable_tls_page_service_api,
     458         1500 :             dev_mode,
     459         1500 : 
     460         1500 :             // ------------------------------------------------------------
     461         1500 :             // fields that require additional validation or custom handling
     462         1500 :             // ------------------------------------------------------------
     463         1500 :             workdir: workdir.to_owned(),
     464         1500 :             pg_distrib_dir: pg_distrib_dir.unwrap_or_else(|| {
     465           12 :                 std::env::current_dir()
     466           12 :                     .expect("current_dir() failed")
     467           12 :                     .try_into()
     468           12 :                     .expect("current_dir() is not a valid Utf8Path")
     469         1500 :             }),
     470         1500 :             control_plane_api_token: control_plane_api_token.map(SecretString::from),
     471         1500 :             id,
     472         1500 :             default_tenant_conf: tenant_config,
     473         1500 :             concurrent_tenant_warmup: ConfigurableSemaphore::new(concurrent_tenant_warmup),
     474         1500 :             concurrent_tenant_size_logical_size_queries: ConfigurableSemaphore::new(
     475         1500 :                 concurrent_tenant_size_logical_size_queries,
     476         1500 :             ),
     477         1500 :             eviction_task_immitated_concurrent_logical_size_queries: ConfigurableSemaphore::new(
     478         1500 :                 // re-use `concurrent_tenant_size_logical_size_queries`
     479         1500 :                 concurrent_tenant_size_logical_size_queries,
     480         1500 :             ),
     481         1500 :             virtual_file_io_engine: match virtual_file_io_engine {
     482            0 :                 Some(v) => v,
     483         1500 :                 None => match crate::virtual_file::io_engine_feature_test()
     484         1500 :                     .context("auto-detect virtual_file_io_engine")?
     485              :                 {
     486         1500 :                     io_engine::FeatureTestResult::PlatformPreferred(v) => v, // make no noise
     487            0 :                     io_engine::FeatureTestResult::Worse { engine, remark } => {
     488            0 :                         // TODO: bubble this up to the caller so we can tracing::warn! it.
     489            0 :                         eprintln!(
     490            0 :                             "auto-detected IO engine is not platform-preferred: engine={engine:?} remark={remark:?}"
     491            0 :                         );
     492            0 :                         engine
     493              :                     }
     494              :                 },
     495              :             },
     496         1500 :             l0_flush: l0_flush
     497         1500 :                 .map(crate::l0_flush::L0FlushConfig::from)
     498         1500 :                 .unwrap_or_default(),
     499         1500 :             virtual_file_io_mode: virtual_file_io_mode.unwrap_or(virtual_file::IoMode::preferred()),
     500         1500 :             no_sync: no_sync.unwrap_or(false),
     501         1500 :             enable_read_path_debugging: enable_read_path_debugging.unwrap_or(false),
     502         1500 :             validate_wal_contiguity: validate_wal_contiguity.unwrap_or(false),
     503         1500 :             load_previous_heatmap: load_previous_heatmap.unwrap_or(true),
     504         1500 :             generate_unarchival_heatmap: generate_unarchival_heatmap.unwrap_or(true),
     505         1500 :             ssl_ca_certs: match ssl_ca_file {
     506            0 :                 Some(ssl_ca_file) => {
     507            0 :                     let buf = std::fs::read(ssl_ca_file)?;
     508            0 :                     pem::parse_many(&buf)?
     509            0 :                         .into_iter()
     510            0 :                         .filter(|pem| pem.tag() == "CERTIFICATE")
     511            0 :                         .collect()
     512              :                 }
     513         1500 :                 None => Vec::new(),
     514              :             },
     515              :         };
     516              : 
     517              :         // ------------------------------------------------------------
     518              :         // custom validation code that covers more than one field in isolation
     519              :         // ------------------------------------------------------------
     520              : 
     521         1500 :         if conf.http_auth_type == AuthType::NeonJWT || conf.pg_auth_type == AuthType::NeonJWT {
     522            0 :             let auth_validation_public_key_path = conf
     523            0 :                 .auth_validation_public_key_path
     524            0 :                 .get_or_insert_with(|| workdir.join("auth_public_key.pem"));
     525            0 :             ensure!(
     526            0 :                 auth_validation_public_key_path.exists(),
     527            0 :                 format!(
     528            0 :                     "Can't find auth_validation_public_key at '{auth_validation_public_key_path}'",
     529            0 :                 )
     530              :             );
     531         1500 :         }
     532              : 
     533         1500 :         if let Some(tracing_config) = conf.tracing.as_ref() {
     534            0 :             let ratio = &tracing_config.sampling_ratio;
     535            0 :             ensure!(
     536            0 :                 ratio.denominator != 0 && ratio.denominator >= ratio.numerator,
     537            0 :                 format!(
     538            0 :                     "Invalid sampling ratio: {}/{}",
     539            0 :                     ratio.numerator, ratio.denominator
     540            0 :                 )
     541              :             );
     542         1500 :         }
     543              : 
     544         1500 :         IndexEntry::validate_checkpoint_distance(conf.default_tenant_conf.checkpoint_distance)
     545         1500 :             .map_err(anyhow::Error::msg)
     546         1500 :             .with_context(|| {
     547            0 :                 format!(
     548            0 :                     "effective checkpoint distance is unsupported: {}",
     549            0 :                     conf.default_tenant_conf.checkpoint_distance
     550            0 :                 )
     551         1500 :             })?;
     552              : 
     553         1500 :         Ok(conf)
     554         1500 :     }
     555              : 
     556              :     #[cfg(test)]
     557         1500 :     pub fn test_repo_dir(test_name: &str) -> Utf8PathBuf {
     558         1500 :         let test_output_dir = std::env::var("TEST_OUTPUT").unwrap_or("../tmp_check".into());
     559         1500 : 
     560         1500 :         let test_id = uuid::Uuid::new_v4();
     561         1500 :         Utf8PathBuf::from(format!("{test_output_dir}/test_{test_name}_{test_id}"))
     562         1500 :     }
     563              : 
     564         1488 :     pub fn dummy_conf(repo_dir: Utf8PathBuf) -> Self {
     565         1488 :         let pg_distrib_dir = Utf8PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../pg_install");
     566         1488 : 
     567         1488 :         let config_toml = pageserver_api::config::ConfigToml {
     568         1488 :             wait_lsn_timeout: Duration::from_secs(60),
     569         1488 :             wal_redo_timeout: Duration::from_secs(60),
     570         1488 :             pg_distrib_dir: Some(pg_distrib_dir),
     571         1488 :             metric_collection_interval: Duration::from_secs(60),
     572         1488 :             synthetic_size_calculation_interval: Duration::from_secs(60),
     573         1488 :             background_task_maximum_delay: Duration::ZERO,
     574         1488 :             load_previous_heatmap: Some(true),
     575         1488 :             generate_unarchival_heatmap: Some(true),
     576         1488 :             ..Default::default()
     577         1488 :         };
     578         1488 :         PageServerConf::parse_and_validate(NodeId(0), config_toml, &repo_dir).unwrap()
     579         1488 :     }
     580              : }
     581              : 
     582            0 : #[derive(serde::Deserialize, serde::Serialize)]
     583              : pub struct PageserverIdentity {
     584              :     pub id: NodeId,
     585              : }
     586              : 
     587              : /// Configurable semaphore permits setting.
     588              : ///
     589              : /// Does not allow semaphore permits to be zero, because at runtime initially zero permits and empty
     590              : /// semaphore cannot be distinguished, leading any feature using these to await forever (or until
     591              : /// new permits are added).
     592              : #[derive(Debug, Clone)]
     593              : pub struct ConfigurableSemaphore {
     594              :     initial_permits: NonZeroUsize,
     595              :     inner: std::sync::Arc<tokio::sync::Semaphore>,
     596              : }
     597              : 
     598              : impl ConfigurableSemaphore {
     599              :     /// Initializse using a non-zero amount of permits.
     600              :     ///
     601              :     /// Require a non-zero initial permits, because using permits == 0 is a crude way to disable a
     602              :     /// feature such as [`TenantShard::gather_size_inputs`]. Otherwise any semaphore using future will
     603              :     /// behave like [`futures::future::pending`], just waiting until new permits are added.
     604              :     ///
     605              :     /// [`TenantShard::gather_size_inputs`]: crate::tenant::TenantShard::gather_size_inputs
     606         4500 :     pub fn new(initial_permits: NonZeroUsize) -> Self {
     607         4500 :         ConfigurableSemaphore {
     608         4500 :             initial_permits,
     609         4500 :             inner: std::sync::Arc::new(tokio::sync::Semaphore::new(initial_permits.get())),
     610         4500 :         }
     611         4500 :     }
     612              : 
     613              :     /// Returns the configured amount of permits.
     614            0 :     pub fn initial_permits(&self) -> NonZeroUsize {
     615            0 :         self.initial_permits
     616            0 :     }
     617              : }
     618              : 
     619              : impl PartialEq for ConfigurableSemaphore {
     620            0 :     fn eq(&self, other: &Self) -> bool {
     621            0 :         // the number of permits can be increased at runtime, so we cannot really fulfill the
     622            0 :         // PartialEq value equality otherwise
     623            0 :         self.initial_permits == other.initial_permits
     624            0 :     }
     625              : }
     626              : 
     627              : impl Eq for ConfigurableSemaphore {}
     628              : 
     629              : impl ConfigurableSemaphore {
     630            0 :     pub fn inner(&self) -> &std::sync::Arc<tokio::sync::Semaphore> {
     631            0 :         &self.inner
     632            0 :     }
     633              : }
     634              : 
     635              : #[cfg(test)]
     636              : mod tests {
     637              : 
     638              :     use camino::Utf8PathBuf;
     639              :     use utils::id::NodeId;
     640              : 
     641              :     use super::PageServerConf;
     642              : 
     643              :     #[test]
     644           12 :     fn test_empty_config_toml_is_valid() {
     645           12 :         // we use Default impl of everything in this situation
     646           12 :         let input = r#"
     647           12 :         "#;
     648           12 :         let config_toml = toml_edit::de::from_str::<pageserver_api::config::ConfigToml>(input)
     649           12 :             .expect("empty config is valid");
     650           12 :         let workdir = Utf8PathBuf::from("/nonexistent");
     651           12 :         PageServerConf::parse_and_validate(NodeId(0), config_toml, &workdir)
     652           12 :             .expect("parse_and_validate");
     653           12 :     }
     654              : }
        

Generated by: LCOV version 2.1-beta