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

Generated by: LCOV version 2.1-beta