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 : #[derive(Serialize, Default, Debug, Clone, PartialEq)]
50 : #[serde(tag = "status", rename_all = "snake_case")]
51 : pub enum LfcPrewarmState {
52 : #[default]
53 : NotPrewarmed,
54 : Prewarming,
55 : Completed,
56 : Failed {
57 : error: String,
58 : },
59 : }
60 :
61 : impl Display for LfcPrewarmState {
62 0 : fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63 0 : match self {
64 0 : LfcPrewarmState::NotPrewarmed => f.write_str("NotPrewarmed"),
65 0 : LfcPrewarmState::Prewarming => f.write_str("Prewarming"),
66 0 : LfcPrewarmState::Completed => f.write_str("Completed"),
67 0 : LfcPrewarmState::Failed { error } => write!(f, "Error({error})"),
68 : }
69 0 : }
70 : }
71 :
72 : #[derive(Serialize, Default, Debug, Clone, PartialEq)]
73 : #[serde(tag = "status", rename_all = "snake_case")]
74 : pub enum LfcOffloadState {
75 : #[default]
76 : NotOffloaded,
77 : Offloading,
78 : Completed,
79 : Failed {
80 : error: String,
81 : },
82 : }
83 :
84 : #[derive(Serialize, Debug, Clone, PartialEq)]
85 : #[serde(tag = "status", rename_all = "snake_case")]
86 : /// Response of /promote
87 : pub enum PromoteState {
88 : NotPromoted,
89 : Completed,
90 : Failed { error: String },
91 : }
92 :
93 0 : #[derive(Deserialize, Serialize, Default, Debug, Clone)]
94 : #[serde(rename_all = "snake_case")]
95 : /// Result of /safekeepers_lsn
96 : pub struct SafekeepersLsn {
97 : pub safekeepers: String,
98 : pub wal_flush_lsn: utils::lsn::Lsn,
99 : }
100 :
101 : /// Response of the /status API
102 0 : #[derive(Serialize, Debug, Deserialize)]
103 : #[serde(rename_all = "snake_case")]
104 : pub struct ComputeStatusResponse {
105 : pub start_time: DateTime<Utc>,
106 : pub tenant: Option<String>,
107 : pub timeline: Option<String>,
108 : pub status: ComputeStatus,
109 : #[serde(serialize_with = "rfc3339_serialize")]
110 : pub last_active: Option<DateTime<Utc>>,
111 : pub error: Option<String>,
112 : }
113 :
114 0 : #[derive(Serialize, Clone, Copy, Debug, Deserialize, PartialEq, Eq, Default)]
115 : #[serde(rename_all = "snake_case")]
116 : pub enum TerminateMode {
117 : #[default]
118 : /// wait 30s till returning from /terminate to allow control plane to get the error
119 : Fast,
120 : /// return from /terminate immediately as soon as all components are terminated
121 : Immediate,
122 : }
123 :
124 : impl From<TerminateMode> for ComputeStatus {
125 0 : fn from(mode: TerminateMode) -> Self {
126 0 : match mode {
127 0 : TerminateMode::Fast => ComputeStatus::TerminationPendingFast,
128 0 : TerminateMode::Immediate => ComputeStatus::TerminationPendingImmediate,
129 : }
130 0 : }
131 : }
132 :
133 0 : #[derive(Serialize, Clone, Copy, Debug, Deserialize, PartialEq, Eq)]
134 : #[serde(rename_all = "snake_case")]
135 : pub enum ComputeStatus {
136 : // Spec wasn't provided at start, waiting for it to be
137 : // provided by control-plane.
138 : Empty,
139 : // Compute configuration was requested.
140 : ConfigurationPending,
141 : // Compute node has spec and initial startup and
142 : // configuration is in progress.
143 : Init,
144 : // Compute is configured and running.
145 : Running,
146 : // New spec is being applied.
147 : Configuration,
148 : // Either startup or configuration failed,
149 : // compute will exit soon or is waiting for
150 : // control-plane to terminate it.
151 : Failed,
152 : // Termination requested
153 : TerminationPendingFast,
154 : // Termination requested, without waiting 30s before returning from /terminate
155 : TerminationPendingImmediate,
156 : // Terminated Postgres
157 : Terminated,
158 : }
159 :
160 0 : #[derive(Deserialize, Serialize)]
161 : pub struct TerminateResponse {
162 : pub lsn: Option<utils::lsn::Lsn>,
163 : }
164 :
165 : impl Display for ComputeStatus {
166 0 : fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
167 0 : match self {
168 0 : ComputeStatus::Empty => f.write_str("empty"),
169 0 : ComputeStatus::ConfigurationPending => f.write_str("configuration-pending"),
170 0 : ComputeStatus::Init => f.write_str("init"),
171 0 : ComputeStatus::Running => f.write_str("running"),
172 0 : ComputeStatus::Configuration => f.write_str("configuration"),
173 0 : ComputeStatus::Failed => f.write_str("failed"),
174 0 : ComputeStatus::TerminationPendingFast => f.write_str("termination-pending-fast"),
175 : ComputeStatus::TerminationPendingImmediate => {
176 0 : f.write_str("termination-pending-immediate")
177 : }
178 0 : ComputeStatus::Terminated => f.write_str("terminated"),
179 : }
180 0 : }
181 : }
182 :
183 0 : pub fn rfc3339_serialize<S>(x: &Option<DateTime<Utc>>, s: S) -> Result<S::Ok, S::Error>
184 0 : where
185 0 : S: Serializer,
186 : {
187 0 : if let Some(x) = x {
188 0 : x.to_rfc3339().serialize(s)
189 : } else {
190 0 : s.serialize_none()
191 : }
192 0 : }
193 :
194 : /// Response of the /metrics.json API
195 : #[derive(Clone, Debug, Default, Serialize)]
196 : pub struct ComputeMetrics {
197 : /// Time spent waiting in pool
198 : pub wait_for_spec_ms: u64,
199 :
200 : /// Time spent checking if safekeepers are synced
201 : pub sync_sk_check_ms: u64,
202 :
203 : /// Time spent syncing safekeepers (walproposer.c).
204 : /// In most cases this should be zero.
205 : pub sync_safekeepers_ms: u64,
206 :
207 : /// Time it took to establish a pg connection to the pageserver.
208 : /// This is two roundtrips, so it's a good proxy for compute-pageserver
209 : /// latency. The latency is usually 0.2ms, but it's not safe to assume
210 : /// that.
211 : pub pageserver_connect_micros: u64,
212 :
213 : /// Time to get basebackup from pageserver and write it to disk.
214 : pub basebackup_ms: u64,
215 :
216 : /// Compressed size of basebackup received.
217 : pub basebackup_bytes: u64,
218 :
219 : /// Time spent starting potgres. This includes initialization of shared
220 : /// buffers, preloading extensions, and other pg operations.
221 : pub start_postgres_ms: u64,
222 :
223 : /// Time spent applying pg catalog updates that were made in the console
224 : /// UI. This should be 0 when startup time matters, since cplane tries
225 : /// to do these updates eagerly, and passes the skip_pg_catalog_updates
226 : /// when it's safe to skip this step.
227 : pub config_ms: u64,
228 :
229 : /// Total time, from when we receive the spec to when we're ready to take
230 : /// pg connections.
231 : pub total_startup_ms: u64,
232 : pub load_ext_ms: u64,
233 : pub num_ext_downloaded: u64,
234 : pub largest_ext_size: u64, // these are measured in bytes
235 : pub total_ext_download_size: u64,
236 : }
237 :
238 : #[derive(Clone, Debug, Default, Serialize)]
239 : pub struct CatalogObjects {
240 : pub roles: Vec<Role>,
241 : pub databases: Vec<Database>,
242 : }
243 :
244 0 : #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
245 : pub struct ComputeCtlConfig {
246 : /// Set of JSON web keys that the compute can use to authenticate
247 : /// communication from the control plane.
248 : pub jwks: JwkSet,
249 : pub tls: Option<TlsConfig>,
250 : }
251 :
252 : impl Default for ComputeCtlConfig {
253 0 : fn default() -> Self {
254 0 : Self {
255 0 : jwks: JwkSet {
256 0 : keys: Vec::default(),
257 0 : },
258 0 : tls: None,
259 0 : }
260 0 : }
261 : }
262 :
263 0 : #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
264 : pub struct TlsConfig {
265 : pub key_path: String,
266 : pub cert_path: String,
267 : }
268 :
269 : /// Response of the `/computes/{compute_id}/spec` control-plane API.
270 0 : #[derive(Deserialize, Debug)]
271 : pub struct ControlPlaneConfigResponse {
272 : pub spec: Option<ComputeSpec>,
273 : pub status: ControlPlaneComputeStatus,
274 : pub compute_ctl_config: ComputeCtlConfig,
275 : }
276 :
277 0 : #[derive(Deserialize, Clone, Copy, Debug, PartialEq, Eq)]
278 : #[serde(rename_all = "snake_case")]
279 : pub enum ControlPlaneComputeStatus {
280 : // Compute is known to control-plane, but it's not
281 : // yet attached to any timeline / endpoint.
282 : Empty,
283 : // Compute is attached to some timeline / endpoint and
284 : // should be able to start with provided spec.
285 : Attached,
286 : }
287 :
288 : #[derive(Clone, Debug, Default, Serialize)]
289 : pub struct InstalledExtension {
290 : pub extname: String,
291 : pub version: String,
292 : pub n_databases: u32, // Number of databases using this extension
293 : pub owned_by_superuser: String,
294 : }
295 :
296 : #[derive(Clone, Debug, Default, Serialize)]
297 : pub struct InstalledExtensions {
298 : pub extensions: Vec<InstalledExtension>,
299 : }
300 :
301 : #[derive(Clone, Debug, Default, Serialize)]
302 : pub struct ExtensionInstallResult {
303 : pub extension: PgIdent,
304 : pub version: ExtVersion,
305 : }
306 : #[derive(Clone, Debug, Default, Serialize)]
307 : pub struct SetRoleGrantsResponse {
308 : pub database: PgIdent,
309 : pub schema: PgIdent,
310 : pub privileges: Vec<Privilege>,
311 : pub role: PgIdent,
312 : }
|