Line data Source code
1 : use pageserver_api::{
2 : models::{
3 : detach_ancestor::AncestorDetached, LocationConfig, LocationConfigListResponse,
4 : PageserverUtilization, SecondaryProgress, TenantScanRemoteStorageResponse,
5 : TenantShardSplitRequest, TenantShardSplitResponse, TimelineArchivalConfigRequest,
6 : TimelineCreateRequest, TimelineInfo, TopTenantShardsRequest, TopTenantShardsResponse,
7 : },
8 : shard::TenantShardId,
9 : };
10 : use pageserver_client::{
11 : mgmt_api::{Client, Result},
12 : BlockUnblock,
13 : };
14 : use reqwest::StatusCode;
15 : use utils::id::{NodeId, TenantId, TimelineId};
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(node_id: NodeId, mgmt_api_endpoint: String, jwt: Option<&str>) -> Self {
53 0 : Self {
54 0 : inner: Client::from_client(reqwest::Client::new(), mgmt_api_endpoint, jwt),
55 0 : node_id_label: node_id.0.to_string(),
56 0 : }
57 0 : }
58 :
59 0 : pub(crate) fn from_client(
60 0 : node_id: NodeId,
61 0 : raw_client: reqwest::Client,
62 0 : mgmt_api_endpoint: String,
63 0 : jwt: Option<&str>,
64 0 : ) -> Self {
65 0 : Self {
66 0 : inner: Client::from_client(raw_client, mgmt_api_endpoint, jwt),
67 0 : node_id_label: node_id.0.to_string(),
68 0 : }
69 0 : }
70 :
71 0 : pub(crate) async fn tenant_delete(&self, tenant_shard_id: TenantShardId) -> Result<StatusCode> {
72 0 : measured_request!(
73 0 : "tenant",
74 0 : crate::metrics::Method::Delete,
75 0 : &self.node_id_label,
76 0 : self.inner.tenant_delete(tenant_shard_id).await
77 : )
78 0 : }
79 :
80 0 : pub(crate) async fn tenant_time_travel_remote_storage(
81 0 : &self,
82 0 : tenant_shard_id: TenantShardId,
83 0 : timestamp: &str,
84 0 : done_if_after: &str,
85 0 : ) -> Result<()> {
86 0 : measured_request!(
87 0 : "tenant_time_travel_remote_storage",
88 0 : crate::metrics::Method::Put,
89 0 : &self.node_id_label,
90 0 : self.inner
91 0 : .tenant_time_travel_remote_storage(tenant_shard_id, timestamp, done_if_after)
92 0 : .await
93 : )
94 0 : }
95 :
96 0 : pub(crate) async fn tenant_scan_remote_storage(
97 0 : &self,
98 0 : tenant_id: TenantId,
99 0 : ) -> Result<TenantScanRemoteStorageResponse> {
100 0 : measured_request!(
101 0 : "tenant_scan_remote_storage",
102 0 : crate::metrics::Method::Get,
103 0 : &self.node_id_label,
104 0 : self.inner.tenant_scan_remote_storage(tenant_id).await
105 : )
106 0 : }
107 :
108 0 : pub(crate) async fn tenant_secondary_download(
109 0 : &self,
110 0 : tenant_id: TenantShardId,
111 0 : wait: Option<std::time::Duration>,
112 0 : ) -> Result<(StatusCode, SecondaryProgress)> {
113 0 : measured_request!(
114 0 : "tenant_secondary_download",
115 0 : crate::metrics::Method::Post,
116 0 : &self.node_id_label,
117 0 : self.inner.tenant_secondary_download(tenant_id, wait).await
118 : )
119 0 : }
120 :
121 0 : pub(crate) async fn tenant_secondary_status(
122 0 : &self,
123 0 : tenant_shard_id: TenantShardId,
124 0 : ) -> Result<SecondaryProgress> {
125 0 : measured_request!(
126 0 : "tenant_secondary_status",
127 0 : crate::metrics::Method::Get,
128 0 : &self.node_id_label,
129 0 : self.inner.tenant_secondary_status(tenant_shard_id).await
130 : )
131 0 : }
132 :
133 0 : pub(crate) async fn tenant_heatmap_upload(&self, tenant_id: TenantShardId) -> Result<()> {
134 0 : measured_request!(
135 0 : "tenant_heatmap_upload",
136 0 : crate::metrics::Method::Post,
137 0 : &self.node_id_label,
138 0 : self.inner.tenant_heatmap_upload(tenant_id).await
139 : )
140 0 : }
141 :
142 0 : pub(crate) async fn location_config(
143 0 : &self,
144 0 : tenant_shard_id: TenantShardId,
145 0 : config: LocationConfig,
146 0 : flush_ms: Option<std::time::Duration>,
147 0 : lazy: bool,
148 0 : ) -> Result<()> {
149 0 : measured_request!(
150 0 : "location_config",
151 0 : crate::metrics::Method::Put,
152 0 : &self.node_id_label,
153 0 : self.inner
154 0 : .location_config(tenant_shard_id, config, flush_ms, lazy)
155 0 : .await
156 : )
157 0 : }
158 :
159 0 : pub(crate) async fn list_location_config(&self) -> Result<LocationConfigListResponse> {
160 0 : measured_request!(
161 0 : "location_configs",
162 0 : crate::metrics::Method::Get,
163 0 : &self.node_id_label,
164 0 : self.inner.list_location_config().await
165 : )
166 0 : }
167 :
168 0 : pub(crate) async fn get_location_config(
169 0 : &self,
170 0 : tenant_shard_id: TenantShardId,
171 0 : ) -> Result<Option<LocationConfig>> {
172 0 : measured_request!(
173 0 : "location_config",
174 0 : crate::metrics::Method::Get,
175 0 : &self.node_id_label,
176 0 : self.inner.get_location_config(tenant_shard_id).await
177 : )
178 0 : }
179 :
180 0 : pub(crate) async fn timeline_create(
181 0 : &self,
182 0 : tenant_shard_id: TenantShardId,
183 0 : req: &TimelineCreateRequest,
184 0 : ) -> Result<TimelineInfo> {
185 0 : measured_request!(
186 0 : "timeline",
187 0 : crate::metrics::Method::Post,
188 0 : &self.node_id_label,
189 0 : self.inner.timeline_create(tenant_shard_id, req).await
190 : )
191 0 : }
192 :
193 0 : pub(crate) async fn timeline_delete(
194 0 : &self,
195 0 : tenant_shard_id: TenantShardId,
196 0 : timeline_id: TimelineId,
197 0 : ) -> Result<StatusCode> {
198 0 : measured_request!(
199 0 : "timeline",
200 0 : crate::metrics::Method::Delete,
201 0 : &self.node_id_label,
202 0 : self.inner
203 0 : .timeline_delete(tenant_shard_id, timeline_id)
204 0 : .await
205 : )
206 0 : }
207 :
208 0 : pub(crate) async fn tenant_shard_split(
209 0 : &self,
210 0 : tenant_shard_id: TenantShardId,
211 0 : req: TenantShardSplitRequest,
212 0 : ) -> Result<TenantShardSplitResponse> {
213 0 : measured_request!(
214 0 : "tenant_shard_split",
215 0 : crate::metrics::Method::Put,
216 0 : &self.node_id_label,
217 0 : self.inner.tenant_shard_split(tenant_shard_id, req).await
218 : )
219 0 : }
220 :
221 0 : pub(crate) async fn timeline_list(
222 0 : &self,
223 0 : tenant_shard_id: &TenantShardId,
224 0 : ) -> Result<Vec<TimelineInfo>> {
225 0 : measured_request!(
226 0 : "timelines",
227 0 : crate::metrics::Method::Get,
228 0 : &self.node_id_label,
229 0 : self.inner.timeline_list(tenant_shard_id).await
230 : )
231 0 : }
232 :
233 0 : pub(crate) async fn timeline_archival_config(
234 0 : &self,
235 0 : tenant_shard_id: TenantShardId,
236 0 : timeline_id: TimelineId,
237 0 : req: &TimelineArchivalConfigRequest,
238 0 : ) -> Result<()> {
239 0 : measured_request!(
240 0 : "timeline_archival_config",
241 0 : crate::metrics::Method::Post,
242 0 : &self.node_id_label,
243 0 : self.inner
244 0 : .timeline_archival_config(tenant_shard_id, timeline_id, req)
245 0 : .await
246 : )
247 0 : }
248 :
249 0 : pub(crate) async fn timeline_detach_ancestor(
250 0 : &self,
251 0 : tenant_shard_id: TenantShardId,
252 0 : timeline_id: TimelineId,
253 0 : ) -> Result<AncestorDetached> {
254 0 : measured_request!(
255 0 : "timeline_detach_ancestor",
256 0 : crate::metrics::Method::Put,
257 0 : &self.node_id_label,
258 0 : self.inner
259 0 : .timeline_detach_ancestor(tenant_shard_id, timeline_id)
260 0 : .await
261 : )
262 0 : }
263 :
264 0 : pub(crate) async fn timeline_block_unblock_gc(
265 0 : &self,
266 0 : tenant_shard_id: TenantShardId,
267 0 : timeline_id: TimelineId,
268 0 : dir: BlockUnblock,
269 0 : ) -> Result<()> {
270 0 : // measuring these makes no sense because we synchronize with the gc loop and remote
271 0 : // storage on block_gc so there should be huge outliers
272 0 : measured_request!(
273 0 : "timeline_block_unblock_gc",
274 0 : crate::metrics::Method::Post,
275 0 : &self.node_id_label,
276 0 : self.inner
277 0 : .timeline_block_unblock_gc(tenant_shard_id, timeline_id, dir)
278 0 : .await
279 : )
280 0 : }
281 :
282 0 : pub(crate) async fn get_utilization(&self) -> Result<PageserverUtilization> {
283 0 : measured_request!(
284 0 : "utilization",
285 0 : crate::metrics::Method::Get,
286 0 : &self.node_id_label,
287 0 : self.inner.get_utilization().await
288 : )
289 0 : }
290 :
291 0 : pub(crate) async fn top_tenant_shards(
292 0 : &self,
293 0 : request: TopTenantShardsRequest,
294 0 : ) -> Result<TopTenantShardsResponse> {
295 0 : measured_request!(
296 0 : "top_tenants",
297 0 : crate::metrics::Method::Post,
298 0 : &self.node_id_label,
299 0 : self.inner.top_tenant_shards(request).await
300 : )
301 0 : }
302 : }
|