LCOV - code coverage report
Current view: top level - libs/compute_api/src - responses.rs (source / functions) Coverage Total Hit
Test: 1d5975439f3c9882b18414799141ebf9a3922c58.info Lines: 0.0 % 60 0
Test Date: 2025-07-31 15:59:03 Functions: 0.0 % 64 0

            Line data    Source code
       1              : //! Structs representing the JSON formats used in the compute_ctl's HTTP API.
       2              : 
       3              : use chrono::{DateTime, Utc};
       4              : use jsonwebtoken::jwk::JwkSet;
       5              : use serde::{Deserialize, Serialize, Serializer};
       6              : use std::fmt::Display;
       7              : 
       8              : use crate::privilege::Privilege;
       9              : use crate::spec::{ComputeSpec, Database, ExtVersion, PgIdent, Role};
      10              : 
      11            0 : #[derive(Serialize, Debug, Deserialize)]
      12              : pub struct GenericAPIError {
      13              :     pub error: String,
      14              : }
      15              : 
      16              : /// All configuration parameters necessary for a compute. When
      17              : /// [`ComputeConfig::spec`] is provided, it means that the compute is attached
      18              : /// to a tenant. [`ComputeConfig::compute_ctl_config`] will always be provided
      19              : /// and contains parameters necessary for operating `compute_ctl` independently
      20              : /// of whether a tenant is attached to the compute or not.
      21              : ///
      22              : /// This also happens to be the body of `compute_ctl`'s /configure request.
      23            0 : #[derive(Debug, Deserialize, Serialize)]
      24              : pub struct ComputeConfig {
      25              :     /// The compute spec
      26              :     pub spec: Option<ComputeSpec>,
      27              : 
      28              :     /// The compute_ctl configuration
      29              :     #[allow(dead_code)]
      30              :     pub compute_ctl_config: ComputeCtlConfig,
      31              : }
      32              : 
      33              : impl From<ControlPlaneConfigResponse> for ComputeConfig {
      34            0 :     fn from(value: ControlPlaneConfigResponse) -> Self {
      35            0 :         Self {
      36            0 :             spec: value.spec,
      37            0 :             compute_ctl_config: value.compute_ctl_config,
      38            0 :         }
      39            0 :     }
      40              : }
      41              : 
      42              : #[derive(Debug, Clone, Serialize)]
      43              : pub struct ExtensionInstallResponse {
      44              :     pub extension: PgIdent,
      45              :     pub version: ExtVersion,
      46              : }
      47              : 
      48              : /// Status of the LFC prewarm process. The same state machine is reused for
      49              : /// both autoprewarm (prewarm after compute/Postgres start using the previously
      50              : /// stored LFC state) and explicit prewarming via API.
      51              : #[derive(Serialize, Default, Debug, Clone)]
      52              : #[serde(tag = "status", rename_all = "snake_case")]
      53              : pub enum LfcPrewarmState {
      54              :     /// Default value when compute boots up.
      55              :     #[default]
      56              :     NotPrewarmed,
      57              :     /// Prewarming thread is active and loading pages into LFC.
      58              :     Prewarming,
      59              :     /// We found requested LFC state in the endpoint storage and
      60              :     /// completed prewarming successfully.
      61              :     Completed {
      62              :         total: i32,
      63              :         prewarmed: i32,
      64              :         skipped: i32,
      65              :         state_download_time_ms: u32,
      66              :         uncompress_time_ms: u32,
      67              :         prewarm_time_ms: u32,
      68              :     },
      69              :     /// Unexpected error happened during prewarming. Note, `Not Found 404`
      70              :     /// response from the endpoint storage is explicitly excluded here
      71              :     /// because it can normally happen on the first compute start,
      72              :     /// since LFC state is not available yet.
      73              :     Failed { error: String },
      74              :     /// We tried to fetch the corresponding LFC state from the endpoint storage,
      75              :     /// but received `Not Found 404`. This should normally happen only during the
      76              :     /// first endpoint start after creation with `autoprewarm: true`.
      77              :     /// This may also happen if LFC is turned off or not initialized
      78              :     ///
      79              :     /// During the orchestrated prewarm via API, when a caller explicitly
      80              :     /// provides the LFC state key to prewarm from, it's the caller responsibility
      81              :     /// to handle this status as an error state in this case.
      82              :     Skipped,
      83              :     /// LFC prewarm was cancelled. Some pages in LFC cache may be prewarmed if query
      84              :     /// has started working before cancellation
      85              :     Cancelled,
      86              : }
      87              : 
      88              : impl Display for LfcPrewarmState {
      89            0 :     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
      90            0 :         match self {
      91            0 :             LfcPrewarmState::NotPrewarmed => f.write_str("NotPrewarmed"),
      92            0 :             LfcPrewarmState::Prewarming => f.write_str("Prewarming"),
      93            0 :             LfcPrewarmState::Completed { .. } => f.write_str("Completed"),
      94            0 :             LfcPrewarmState::Skipped => f.write_str("Skipped"),
      95            0 :             LfcPrewarmState::Failed { error } => write!(f, "Error({error})"),
      96            0 :             LfcPrewarmState::Cancelled => f.write_str("Cancelled"),
      97              :         }
      98            0 :     }
      99              : }
     100              : 
     101              : #[derive(Serialize, Default, Debug, Clone)]
     102              : #[serde(tag = "status", rename_all = "snake_case")]
     103              : pub enum LfcOffloadState {
     104              :     #[default]
     105              :     NotOffloaded,
     106              :     Offloading,
     107              :     Completed {
     108              :         state_query_time_ms: u32,
     109              :         compress_time_ms: u32,
     110              :         state_upload_time_ms: u32,
     111              :     },
     112              :     Failed {
     113              :         error: String,
     114              :     },
     115              :     /// LFC state was empty so it wasn't offloaded
     116              :     Skipped,
     117              : }
     118              : 
     119              : #[derive(Serialize, Debug, Clone)]
     120              : #[serde(tag = "status", rename_all = "snake_case")]
     121              : pub enum PromoteState {
     122              :     NotPromoted,
     123              :     Completed {
     124              :         lsn_wait_time_ms: u32,
     125              :         pg_promote_time_ms: u32,
     126              :         reconfigure_time_ms: u32,
     127              :     },
     128              :     Failed {
     129              :         error: String,
     130              :     },
     131              : }
     132              : 
     133            0 : #[derive(Deserialize, Default, Debug)]
     134              : #[serde(rename_all = "snake_case")]
     135              : pub struct PromoteConfig {
     136              :     pub spec: ComputeSpec,
     137              :     pub wal_flush_lsn: utils::lsn::Lsn,
     138              : }
     139              : 
     140              : /// Response of the /status API
     141            0 : #[derive(Serialize, Debug, Deserialize)]
     142              : #[serde(rename_all = "snake_case")]
     143              : pub struct ComputeStatusResponse {
     144              :     pub start_time: DateTime<Utc>,
     145              :     pub tenant: Option<String>,
     146              :     pub timeline: Option<String>,
     147              :     pub status: ComputeStatus,
     148              :     #[serde(serialize_with = "rfc3339_serialize")]
     149              :     pub last_active: Option<DateTime<Utc>>,
     150              :     pub error: Option<String>,
     151              : }
     152              : 
     153            0 : #[derive(Serialize, Clone, Copy, Debug, Deserialize, PartialEq, Eq, Default)]
     154              : #[serde(rename_all = "snake_case")]
     155              : pub enum TerminateMode {
     156              :     #[default]
     157              :     /// wait 30s till returning from /terminate to allow control plane to get the error
     158              :     Fast,
     159              :     /// return from /terminate immediately as soon as all components are terminated
     160              :     Immediate,
     161              : }
     162              : 
     163              : impl From<TerminateMode> for ComputeStatus {
     164            0 :     fn from(mode: TerminateMode) -> Self {
     165            0 :         match mode {
     166            0 :             TerminateMode::Fast => ComputeStatus::TerminationPendingFast,
     167            0 :             TerminateMode::Immediate => ComputeStatus::TerminationPendingImmediate,
     168              :         }
     169            0 :     }
     170              : }
     171              : 
     172            0 : #[derive(Serialize, Clone, Copy, Debug, Deserialize, PartialEq, Eq)]
     173              : #[serde(rename_all = "snake_case")]
     174              : pub enum ComputeStatus {
     175              :     // Spec wasn't provided at start, waiting for it to be
     176              :     // provided by control-plane.
     177              :     Empty,
     178              :     // Compute configuration was requested.
     179              :     ConfigurationPending,
     180              :     // Compute node has spec and initial startup and
     181              :     // configuration is in progress.
     182              :     Init,
     183              :     // Compute is configured and running.
     184              :     Running,
     185              :     // New spec is being applied.
     186              :     Configuration,
     187              :     // Either startup or configuration failed,
     188              :     // compute will exit soon or is waiting for
     189              :     // control-plane to terminate it.
     190              :     Failed,
     191              :     // Termination requested
     192              :     TerminationPendingFast,
     193              :     // Termination requested, without waiting 30s before returning from /terminate
     194              :     TerminationPendingImmediate,
     195              :     // Terminated Postgres
     196              :     Terminated,
     197              :     // A spec refresh is being requested
     198              :     RefreshConfigurationPending,
     199              :     // A spec refresh is being applied. We cannot refresh configuration again until the current
     200              :     // refresh is done, i.e., signal_refresh_configuration() will return 500 error.
     201              :     RefreshConfiguration,
     202              : }
     203              : 
     204            0 : #[derive(Deserialize, Serialize)]
     205              : pub struct TerminateResponse {
     206              :     pub lsn: Option<utils::lsn::Lsn>,
     207              : }
     208              : 
     209              : impl Display for ComputeStatus {
     210            0 :     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
     211            0 :         match self {
     212            0 :             ComputeStatus::Empty => f.write_str("empty"),
     213            0 :             ComputeStatus::ConfigurationPending => f.write_str("configuration-pending"),
     214            0 :             ComputeStatus::RefreshConfiguration => f.write_str("refresh-configuration"),
     215              :             ComputeStatus::RefreshConfigurationPending => {
     216            0 :                 f.write_str("refresh-configuration-pending")
     217              :             }
     218            0 :             ComputeStatus::Init => f.write_str("init"),
     219            0 :             ComputeStatus::Running => f.write_str("running"),
     220            0 :             ComputeStatus::Configuration => f.write_str("configuration"),
     221            0 :             ComputeStatus::Failed => f.write_str("failed"),
     222            0 :             ComputeStatus::TerminationPendingFast => f.write_str("termination-pending-fast"),
     223              :             ComputeStatus::TerminationPendingImmediate => {
     224            0 :                 f.write_str("termination-pending-immediate")
     225              :             }
     226            0 :             ComputeStatus::Terminated => f.write_str("terminated"),
     227              :         }
     228            0 :     }
     229              : }
     230              : 
     231            0 : pub fn rfc3339_serialize<S>(x: &Option<DateTime<Utc>>, s: S) -> Result<S::Ok, S::Error>
     232            0 : where
     233            0 :     S: Serializer,
     234              : {
     235            0 :     if let Some(x) = x {
     236            0 :         x.to_rfc3339().serialize(s)
     237              :     } else {
     238            0 :         s.serialize_none()
     239              :     }
     240            0 : }
     241              : 
     242              : /// Response of the /metrics.json API
     243              : #[derive(Clone, Debug, Default, Serialize)]
     244              : pub struct ComputeMetrics {
     245              :     /// Time spent waiting in pool
     246              :     pub wait_for_spec_ms: u64,
     247              : 
     248              :     /// Time spent checking if safekeepers are synced
     249              :     pub sync_sk_check_ms: u64,
     250              : 
     251              :     /// Time spent syncing safekeepers (walproposer.c).
     252              :     /// In most cases this should be zero.
     253              :     pub sync_safekeepers_ms: u64,
     254              : 
     255              :     /// Time it took to establish a pg connection to the pageserver.
     256              :     /// This is two roundtrips, so it's a good proxy for compute-pageserver
     257              :     /// latency. The latency is usually 0.2ms, but it's not safe to assume
     258              :     /// that.
     259              :     pub pageserver_connect_micros: u64,
     260              : 
     261              :     /// Time to get basebackup from pageserver and write it to disk.
     262              :     pub basebackup_ms: u64,
     263              : 
     264              :     /// Compressed size of basebackup received.
     265              :     pub basebackup_bytes: u64,
     266              : 
     267              :     /// Time spent starting potgres. This includes initialization of shared
     268              :     /// buffers, preloading extensions, and other pg operations.
     269              :     pub start_postgres_ms: u64,
     270              : 
     271              :     /// Time spent applying pg catalog updates that were made in the console
     272              :     /// UI. This should be 0 when startup time matters, since cplane tries
     273              :     /// to do these updates eagerly, and passes the skip_pg_catalog_updates
     274              :     /// when it's safe to skip this step.
     275              :     pub config_ms: u64,
     276              : 
     277              :     /// Total time, from when we receive the spec to when we're ready to take
     278              :     /// pg connections.
     279              :     pub total_startup_ms: u64,
     280              :     pub load_ext_ms: u64,
     281              :     pub num_ext_downloaded: u64,
     282              :     pub largest_ext_size: u64, // these are measured in bytes
     283              :     pub total_ext_download_size: u64,
     284              : }
     285              : 
     286              : #[derive(Clone, Debug, Default, Serialize)]
     287              : pub struct CatalogObjects {
     288              :     pub roles: Vec<Role>,
     289              :     pub databases: Vec<Database>,
     290              : }
     291              : 
     292            0 : #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
     293              : pub struct ComputeCtlConfig {
     294              :     /// Set of JSON web keys that the compute can use to authenticate
     295              :     /// communication from the control plane.
     296              :     pub jwks: JwkSet,
     297              :     pub tls: Option<TlsConfig>,
     298              : }
     299              : 
     300              : impl Default for ComputeCtlConfig {
     301            0 :     fn default() -> Self {
     302            0 :         Self {
     303            0 :             jwks: JwkSet {
     304            0 :                 keys: Vec::default(),
     305            0 :             },
     306            0 :             tls: None,
     307            0 :         }
     308            0 :     }
     309              : }
     310              : 
     311            0 : #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
     312              : pub struct TlsConfig {
     313              :     pub key_path: String,
     314              :     pub cert_path: String,
     315              : }
     316              : 
     317              : /// Response of the `/computes/{compute_id}/spec` control-plane API.
     318            0 : #[derive(Deserialize, Debug)]
     319              : pub struct ControlPlaneConfigResponse {
     320              :     pub spec: Option<ComputeSpec>,
     321              :     pub status: ControlPlaneComputeStatus,
     322              :     pub compute_ctl_config: ComputeCtlConfig,
     323              : }
     324              : 
     325            0 : #[derive(Deserialize, Clone, Copy, Debug, PartialEq, Eq)]
     326              : #[serde(rename_all = "snake_case")]
     327              : pub enum ControlPlaneComputeStatus {
     328              :     // Compute is known to control-plane, but it's not
     329              :     // yet attached to any timeline / endpoint.
     330              :     Empty,
     331              :     // Compute is attached to some timeline / endpoint and
     332              :     // should be able to start with provided spec.
     333              :     Attached,
     334              : }
     335              : 
     336              : #[derive(Clone, Debug, Default, Serialize)]
     337              : pub struct InstalledExtension {
     338              :     pub extname: String,
     339              :     pub version: String,
     340              :     pub n_databases: u32, // Number of databases using this extension
     341              :     pub owned_by_superuser: String,
     342              : }
     343              : 
     344              : #[derive(Clone, Debug, Default, Serialize)]
     345              : pub struct InstalledExtensions {
     346              :     pub extensions: Vec<InstalledExtension>,
     347              : }
     348              : 
     349              : #[derive(Clone, Debug, Default, Serialize)]
     350              : pub struct ExtensionInstallResult {
     351              :     pub extension: PgIdent,
     352              :     pub version: ExtVersion,
     353              : }
     354              : #[derive(Clone, Debug, Default, Serialize)]
     355              : pub struct SetRoleGrantsResponse {
     356              :     pub database: PgIdent,
     357              :     pub schema: PgIdent,
     358              :     pub privileges: Vec<Privilege>,
     359              :     pub role: PgIdent,
     360              : }
        

Generated by: LCOV version 2.1-beta