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