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