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