Line data Source code
1 : use std::time::Duration;
2 :
3 : use pageserver_api::models::detach_ancestor::AncestorDetached;
4 : use pageserver_api::models::{
5 : DetachBehavior, LocationConfig, LocationConfigListResponse, LsnLease, PageserverUtilization,
6 : SecondaryProgress, TenantScanRemoteStorageResponse, TenantShardSplitRequest,
7 : TenantShardSplitResponse, TenantWaitLsnRequest, TimelineArchivalConfigRequest,
8 : TimelineCreateRequest, TimelineInfo, TopTenantShardsRequest, TopTenantShardsResponse,
9 : };
10 : use pageserver_api::shard::TenantShardId;
11 : use pageserver_client::BlockUnblock;
12 : use pageserver_client::mgmt_api::{Client, Result};
13 : use reqwest::StatusCode;
14 : use utils::id::{NodeId, TenantId, TimelineId};
15 : use utils::lsn::Lsn;
16 :
17 : /// Thin wrapper around [`pageserver_client::mgmt_api::Client`]. It allows the storage
18 : /// controller to collect metrics in a non-intrusive manner.
19 : #[derive(Debug, Clone)]
20 : pub(crate) struct PageserverClient {
21 : inner: Client,
22 : node_id_label: String,
23 : }
24 :
25 : macro_rules! measured_request {
26 : ($name:literal, $method:expr, $node_id: expr, $invoke:expr) => {{
27 : let labels = crate::metrics::PageserverRequestLabelGroup {
28 : pageserver_id: $node_id,
29 : path: $name,
30 : method: $method,
31 : };
32 :
33 : let latency = &crate::metrics::METRICS_REGISTRY
34 : .metrics_group
35 : .storage_controller_pageserver_request_latency;
36 : let _timer_guard = latency.start_timer(labels.clone());
37 :
38 : let res = $invoke;
39 :
40 : if res.is_err() {
41 : let error_counters = &crate::metrics::METRICS_REGISTRY
42 : .metrics_group
43 : .storage_controller_pageserver_request_error;
44 : error_counters.inc(labels)
45 : }
46 :
47 : res
48 : }};
49 : }
50 :
51 : impl PageserverClient {
52 0 : pub(crate) fn new(
53 0 : node_id: NodeId,
54 0 : raw_client: reqwest::Client,
55 0 : mgmt_api_endpoint: String,
56 0 : jwt: Option<&str>,
57 0 : ) -> Self {
58 0 : Self {
59 0 : inner: Client::new(raw_client, mgmt_api_endpoint, jwt),
60 0 : node_id_label: node_id.0.to_string(),
61 0 : }
62 0 : }
63 :
64 0 : pub(crate) async fn tenant_delete(&self, tenant_shard_id: TenantShardId) -> Result<StatusCode> {
65 0 : measured_request!(
66 0 : "tenant",
67 0 : crate::metrics::Method::Delete,
68 0 : &self.node_id_label,
69 0 : self.inner.tenant_delete(tenant_shard_id).await
70 : )
71 0 : }
72 :
73 0 : pub(crate) async fn tenant_time_travel_remote_storage(
74 0 : &self,
75 0 : tenant_shard_id: TenantShardId,
76 0 : timestamp: &str,
77 0 : done_if_after: &str,
78 0 : ) -> Result<()> {
79 0 : measured_request!(
80 0 : "tenant_time_travel_remote_storage",
81 0 : crate::metrics::Method::Put,
82 0 : &self.node_id_label,
83 0 : self.inner
84 0 : .tenant_time_travel_remote_storage(tenant_shard_id, timestamp, done_if_after)
85 0 : .await
86 : )
87 0 : }
88 :
89 : /* BEGIN_HADRON */
90 0 : pub(crate) async fn tenant_timeline_describe(
91 0 : &self,
92 0 : tenant_shard_id: &TenantShardId,
93 0 : timeline_id: &TimelineId,
94 0 : ) -> Result<TimelineInfo> {
95 0 : measured_request!(
96 0 : "tenant_timeline_describe",
97 0 : crate::metrics::Method::Get,
98 0 : &self.node_id_label,
99 0 : self.inner
100 0 : .tenant_timeline_describe(tenant_shard_id, timeline_id,)
101 0 : .await
102 : )
103 0 : }
104 : /* END_HADRON */
105 :
106 0 : pub(crate) async fn tenant_scan_remote_storage(
107 0 : &self,
108 0 : tenant_id: TenantId,
109 0 : ) -> Result<TenantScanRemoteStorageResponse> {
110 0 : measured_request!(
111 0 : "tenant_scan_remote_storage",
112 0 : crate::metrics::Method::Get,
113 0 : &self.node_id_label,
114 0 : self.inner.tenant_scan_remote_storage(tenant_id).await
115 : )
116 0 : }
117 :
118 0 : pub(crate) async fn tenant_secondary_download(
119 0 : &self,
120 0 : tenant_id: TenantShardId,
121 0 : wait: Option<std::time::Duration>,
122 0 : ) -> Result<(StatusCode, SecondaryProgress)> {
123 0 : measured_request!(
124 0 : "tenant_secondary_download",
125 0 : crate::metrics::Method::Post,
126 0 : &self.node_id_label,
127 0 : self.inner.tenant_secondary_download(tenant_id, wait).await
128 : )
129 0 : }
130 :
131 0 : pub(crate) async fn tenant_secondary_status(
132 0 : &self,
133 0 : tenant_shard_id: TenantShardId,
134 0 : ) -> Result<SecondaryProgress> {
135 0 : measured_request!(
136 0 : "tenant_secondary_status",
137 0 : crate::metrics::Method::Get,
138 0 : &self.node_id_label,
139 0 : self.inner.tenant_secondary_status(tenant_shard_id).await
140 : )
141 0 : }
142 :
143 0 : pub(crate) async fn tenant_heatmap_upload(&self, tenant_id: TenantShardId) -> Result<()> {
144 0 : measured_request!(
145 0 : "tenant_heatmap_upload",
146 0 : crate::metrics::Method::Post,
147 0 : &self.node_id_label,
148 0 : self.inner.tenant_heatmap_upload(tenant_id).await
149 : )
150 0 : }
151 :
152 0 : pub(crate) async fn location_config(
153 0 : &self,
154 0 : tenant_shard_id: TenantShardId,
155 0 : config: LocationConfig,
156 0 : flush_ms: Option<std::time::Duration>,
157 0 : lazy: bool,
158 0 : ) -> Result<()> {
159 0 : measured_request!(
160 0 : "location_config",
161 0 : crate::metrics::Method::Put,
162 0 : &self.node_id_label,
163 0 : self.inner
164 0 : .location_config(tenant_shard_id, config, flush_ms, lazy)
165 0 : .await
166 : )
167 0 : }
168 :
169 0 : pub(crate) async fn list_location_config(&self) -> Result<LocationConfigListResponse> {
170 0 : measured_request!(
171 0 : "location_configs",
172 0 : crate::metrics::Method::Get,
173 0 : &self.node_id_label,
174 0 : self.inner.list_location_config().await
175 : )
176 0 : }
177 :
178 0 : pub(crate) async fn get_location_config(
179 0 : &self,
180 0 : tenant_shard_id: TenantShardId,
181 0 : ) -> Result<Option<LocationConfig>> {
182 0 : measured_request!(
183 0 : "location_config",
184 0 : crate::metrics::Method::Get,
185 0 : &self.node_id_label,
186 0 : self.inner.get_location_config(tenant_shard_id).await
187 : )
188 0 : }
189 :
190 0 : pub(crate) async fn timeline_create(
191 0 : &self,
192 0 : tenant_shard_id: TenantShardId,
193 0 : req: &TimelineCreateRequest,
194 0 : ) -> Result<TimelineInfo> {
195 0 : measured_request!(
196 0 : "timeline",
197 0 : crate::metrics::Method::Post,
198 0 : &self.node_id_label,
199 0 : self.inner.timeline_create(tenant_shard_id, req).await
200 : )
201 0 : }
202 :
203 0 : pub(crate) async fn timeline_delete(
204 0 : &self,
205 0 : tenant_shard_id: TenantShardId,
206 0 : timeline_id: TimelineId,
207 0 : ) -> Result<StatusCode> {
208 0 : measured_request!(
209 0 : "timeline",
210 0 : crate::metrics::Method::Delete,
211 0 : &self.node_id_label,
212 0 : self.inner
213 0 : .timeline_delete(tenant_shard_id, timeline_id)
214 0 : .await
215 : )
216 0 : }
217 :
218 0 : pub(crate) async fn timeline_lease_lsn(
219 0 : &self,
220 0 : tenant_shard_id: TenantShardId,
221 0 : timeline_id: TimelineId,
222 0 : lsn: Lsn,
223 0 : ) -> Result<LsnLease> {
224 0 : measured_request!(
225 0 : "timeline_lease_lsn",
226 0 : crate::metrics::Method::Post,
227 0 : &self.node_id_label,
228 0 : self.inner
229 0 : .timeline_init_lsn_lease(tenant_shard_id, timeline_id, lsn)
230 0 : .await
231 : )
232 0 : }
233 :
234 : #[allow(unused)]
235 0 : pub(crate) async fn timeline_detail(
236 0 : &self,
237 0 : tenant_shard_id: TenantShardId,
238 0 : timeline_id: TimelineId,
239 0 : ) -> Result<TimelineInfo> {
240 0 : measured_request!(
241 0 : "timeline_detail",
242 0 : crate::metrics::Method::Get,
243 0 : &self.node_id_label,
244 0 : self.inner
245 0 : .timeline_detail(tenant_shard_id, timeline_id)
246 0 : .await
247 : )
248 0 : }
249 :
250 0 : pub(crate) async fn tenant_shard_split(
251 0 : &self,
252 0 : tenant_shard_id: TenantShardId,
253 0 : req: TenantShardSplitRequest,
254 0 : ) -> Result<TenantShardSplitResponse> {
255 0 : measured_request!(
256 0 : "tenant_shard_split",
257 0 : crate::metrics::Method::Put,
258 0 : &self.node_id_label,
259 0 : self.inner.tenant_shard_split(tenant_shard_id, req).await
260 : )
261 0 : }
262 :
263 0 : pub(crate) async fn timeline_list(
264 0 : &self,
265 0 : tenant_shard_id: &TenantShardId,
266 0 : ) -> Result<Vec<TimelineInfo>> {
267 0 : measured_request!(
268 0 : "timelines",
269 0 : crate::metrics::Method::Get,
270 0 : &self.node_id_label,
271 0 : self.inner.timeline_list(tenant_shard_id).await
272 : )
273 0 : }
274 :
275 0 : pub(crate) async fn timeline_archival_config(
276 0 : &self,
277 0 : tenant_shard_id: TenantShardId,
278 0 : timeline_id: TimelineId,
279 0 : req: &TimelineArchivalConfigRequest,
280 0 : ) -> Result<()> {
281 0 : measured_request!(
282 0 : "timeline_archival_config",
283 0 : crate::metrics::Method::Put,
284 0 : &self.node_id_label,
285 0 : self.inner
286 0 : .timeline_archival_config(tenant_shard_id, timeline_id, req)
287 0 : .await
288 : )
289 0 : }
290 :
291 0 : pub(crate) async fn timeline_detach_ancestor(
292 0 : &self,
293 0 : tenant_shard_id: TenantShardId,
294 0 : timeline_id: TimelineId,
295 0 : behavior: Option<DetachBehavior>,
296 0 : ) -> Result<AncestorDetached> {
297 0 : measured_request!(
298 0 : "timeline_detach_ancestor",
299 0 : crate::metrics::Method::Put,
300 0 : &self.node_id_label,
301 0 : self.inner
302 0 : .timeline_detach_ancestor(tenant_shard_id, timeline_id, behavior)
303 0 : .await
304 : )
305 0 : }
306 :
307 0 : pub(crate) async fn timeline_block_unblock_gc(
308 0 : &self,
309 0 : tenant_shard_id: TenantShardId,
310 0 : timeline_id: TimelineId,
311 0 : dir: BlockUnblock,
312 0 : ) -> Result<()> {
313 : // measuring these makes no sense because we synchronize with the gc loop and remote
314 : // storage on block_gc so there should be huge outliers
315 0 : measured_request!(
316 0 : "timeline_block_unblock_gc",
317 0 : crate::metrics::Method::Post,
318 0 : &self.node_id_label,
319 0 : self.inner
320 0 : .timeline_block_unblock_gc(tenant_shard_id, timeline_id, dir)
321 0 : .await
322 : )
323 0 : }
324 :
325 0 : pub(crate) async fn timeline_download_heatmap_layers(
326 0 : &self,
327 0 : tenant_shard_id: TenantShardId,
328 0 : timeline_id: TimelineId,
329 0 : concurrency: Option<usize>,
330 0 : recurse: bool,
331 0 : ) -> Result<()> {
332 0 : measured_request!(
333 0 : "download_heatmap_layers",
334 0 : crate::metrics::Method::Post,
335 0 : &self.node_id_label,
336 0 : self.inner
337 0 : .timeline_download_heatmap_layers(
338 0 : tenant_shard_id,
339 0 : timeline_id,
340 0 : concurrency,
341 0 : recurse
342 0 : )
343 0 : .await
344 : )
345 0 : }
346 :
347 0 : pub(crate) async fn get_utilization(&self) -> Result<PageserverUtilization> {
348 0 : measured_request!(
349 0 : "utilization",
350 0 : crate::metrics::Method::Get,
351 0 : &self.node_id_label,
352 0 : self.inner.get_utilization().await
353 : )
354 0 : }
355 :
356 0 : pub(crate) async fn top_tenant_shards(
357 0 : &self,
358 0 : request: TopTenantShardsRequest,
359 0 : ) -> Result<TopTenantShardsResponse> {
360 0 : measured_request!(
361 0 : "top_tenants",
362 0 : crate::metrics::Method::Post,
363 0 : &self.node_id_label,
364 0 : self.inner.top_tenant_shards(request).await
365 : )
366 0 : }
367 :
368 0 : pub(crate) async fn wait_lsn(
369 0 : &self,
370 0 : tenant_shard_id: TenantShardId,
371 0 : request: TenantWaitLsnRequest,
372 0 : ) -> Result<StatusCode> {
373 0 : measured_request!(
374 0 : "wait_lsn",
375 0 : crate::metrics::Method::Post,
376 0 : &self.node_id_label,
377 0 : self.inner.wait_lsn(tenant_shard_id, request).await
378 : )
379 0 : }
380 :
381 0 : pub(crate) async fn activate_post_import(
382 0 : &self,
383 0 : tenant_shard_id: TenantShardId,
384 0 : timeline_id: TimelineId,
385 0 : timeline_activate_timeout: Duration,
386 0 : ) -> Result<TimelineInfo> {
387 0 : measured_request!(
388 0 : "activate_post_import",
389 0 : crate::metrics::Method::Put,
390 0 : &self.node_id_label,
391 0 : self.inner
392 0 : .activate_post_import(tenant_shard_id, timeline_id, timeline_activate_timeout)
393 0 : .await
394 : )
395 0 : }
396 :
397 0 : pub(crate) async fn update_feature_flag_spec(&self, spec: String) -> Result<()> {
398 0 : measured_request!(
399 0 : "update_feature_flag_spec",
400 0 : crate::metrics::Method::Post,
401 0 : &self.node_id_label,
402 0 : self.inner.update_feature_flag_spec(spec).await
403 : )
404 0 : }
405 : }
|