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