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 : }
|