Line data Source code
1 : // rustc lints/lint groups
2 : // https://doc.rust-lang.org/rustc/lints/groups.html
3 : #![deny(
4 : deprecated,
5 : future_incompatible,
6 : let_underscore,
7 : nonstandard_style,
8 : rust_2024_compatibility
9 : )]
10 : #![warn(clippy::all, clippy::pedantic, clippy::cargo)]
11 : // List of denied lints from the clippy::restriction group.
12 : // https://rust-lang.github.io/rust-clippy/master/index.html#?groups=restriction
13 : #![warn(
14 : clippy::undocumented_unsafe_blocks,
15 : // TODO: Enable once all individual checks are enabled.
16 : //clippy::as_conversions,
17 : clippy::dbg_macro,
18 : clippy::empty_enum_variants_with_brackets,
19 : clippy::exit,
20 : clippy::float_cmp_const,
21 : clippy::lossy_float_literal,
22 : clippy::macro_use_imports,
23 : clippy::manual_ok_or,
24 : // TODO: consider clippy::map_err_ignore
25 : // TODO: consider clippy::mem_forget
26 : clippy::rc_mutex,
27 : clippy::rest_pat_in_fully_bound_structs,
28 : clippy::string_add,
29 : clippy::string_to_string,
30 : clippy::todo,
31 : // TODO: consider clippy::unimplemented
32 : // TODO: consider clippy::unwrap_used
33 : )]
34 : // List of permanently allowed lints.
35 : #![allow(
36 : // It's ok to cast bool to u8, etc.
37 : clippy::cast_lossless,
38 : // Seems unavoidable.
39 : clippy::multiple_crate_versions,
40 : // While #[must_use] is a great feature this check is too noisy.
41 : clippy::must_use_candidate,
42 : // Inline consts, structs, fns, imports, etc. are ok if they're used by
43 : // the following statement(s).
44 : clippy::items_after_statements,
45 : )]
46 : // List of temporarily allowed lints.
47 : // TODO: fix code and reduce list or move to permanent list above.
48 : #![expect(
49 : clippy::cargo_common_metadata,
50 : clippy::cast_possible_truncation,
51 : clippy::cast_possible_wrap,
52 : clippy::cast_precision_loss,
53 : clippy::cast_sign_loss,
54 : clippy::doc_markdown,
55 : clippy::inline_always,
56 : clippy::match_same_arms,
57 : clippy::match_wild_err_arm,
58 : clippy::missing_errors_doc,
59 : clippy::missing_panics_doc,
60 : clippy::module_name_repetitions,
61 : clippy::needless_pass_by_value,
62 : clippy::redundant_closure_for_method_calls,
63 : clippy::similar_names,
64 : clippy::single_match_else,
65 : clippy::struct_excessive_bools,
66 : clippy::struct_field_names,
67 : clippy::too_many_lines,
68 : clippy::unused_self
69 : )]
70 : #![cfg_attr(
71 : any(test, feature = "testing"),
72 : allow(
73 : clippy::needless_raw_string_hashes,
74 : clippy::unreadable_literal,
75 : clippy::unused_async,
76 : )
77 : )]
78 : // List of temporarily allowed lints to unblock beta/nightly.
79 : #![allow(unknown_lints)]
80 :
81 : use std::convert::Infallible;
82 :
83 : use anyhow::{bail, Context};
84 : use intern::{EndpointIdInt, EndpointIdTag, InternId};
85 : use tokio::task::JoinError;
86 : use tokio_util::sync::CancellationToken;
87 : use tracing::warn;
88 :
89 : pub mod auth;
90 : pub mod cache;
91 : pub mod cancellation;
92 : pub mod compute;
93 : pub mod compute_ctl;
94 : pub mod config;
95 : pub mod console_redirect_proxy;
96 : pub mod context;
97 : pub mod control_plane;
98 : pub mod error;
99 : pub mod http;
100 : pub mod intern;
101 : pub mod jemalloc;
102 : pub mod logging;
103 : pub mod metrics;
104 : pub mod parse;
105 : pub mod protocol2;
106 : pub mod proxy;
107 : pub mod rate_limiter;
108 : pub mod redis;
109 : pub mod sasl;
110 : pub mod scram;
111 : pub mod serverless;
112 : pub mod stream;
113 : pub mod url;
114 : pub mod usage_metrics;
115 : pub mod waiters;
116 :
117 : /// Handle unix signals appropriately.
118 0 : pub async fn handle_signals<F>(
119 0 : token: CancellationToken,
120 0 : mut refresh_config: F,
121 0 : ) -> anyhow::Result<Infallible>
122 0 : where
123 0 : F: FnMut(),
124 0 : {
125 : use tokio::signal::unix::{signal, SignalKind};
126 :
127 0 : let mut hangup = signal(SignalKind::hangup())?;
128 0 : let mut interrupt = signal(SignalKind::interrupt())?;
129 0 : let mut terminate = signal(SignalKind::terminate())?;
130 :
131 : loop {
132 0 : tokio::select! {
133 : // Hangup is commonly used for config reload.
134 0 : _ = hangup.recv() => {
135 0 : warn!("received SIGHUP");
136 0 : refresh_config();
137 : }
138 : // Shut down the whole application.
139 0 : _ = interrupt.recv() => {
140 0 : warn!("received SIGINT, exiting immediately");
141 0 : bail!("interrupted");
142 : }
143 0 : _ = terminate.recv() => {
144 0 : warn!("received SIGTERM, shutting down once all existing connections have closed");
145 0 : token.cancel();
146 : }
147 : }
148 : }
149 0 : }
150 :
151 : /// Flattens `Result<Result<T>>` into `Result<T>`.
152 0 : pub fn flatten_err<T>(r: Result<anyhow::Result<T>, JoinError>) -> anyhow::Result<T> {
153 0 : r.context("join error").and_then(|x| x)
154 0 : }
155 :
156 : macro_rules! smol_str_wrapper {
157 : ($name:ident) => {
158 : #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
159 : pub struct $name(smol_str::SmolStr);
160 :
161 : impl $name {
162 : #[allow(unused)]
163 0 : pub(crate) fn as_str(&self) -> &str {
164 0 : self.0.as_str()
165 0 : }
166 : }
167 :
168 : impl std::fmt::Display for $name {
169 3 : fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
170 3 : self.0.fmt(f)
171 3 : }
172 : }
173 :
174 : impl<T> std::cmp::PartialEq<T> for $name
175 : where
176 : smol_str::SmolStr: std::cmp::PartialEq<T>,
177 : {
178 19 : fn eq(&self, other: &T) -> bool {
179 19 : self.0.eq(other)
180 19 : }
181 : }
182 :
183 : impl<T> From<T> for $name
184 : where
185 : smol_str::SmolStr: From<T>,
186 : {
187 166 : fn from(x: T) -> Self {
188 166 : Self(x.into())
189 166 : }
190 : }
191 :
192 : impl AsRef<str> for $name {
193 10 : fn as_ref(&self) -> &str {
194 10 : self.0.as_ref()
195 10 : }
196 : }
197 :
198 : impl std::ops::Deref for $name {
199 : type Target = str;
200 167 : fn deref(&self) -> &str {
201 167 : &*self.0
202 167 : }
203 : }
204 :
205 : impl<'de> serde::de::Deserialize<'de> for $name {
206 0 : fn deserialize<D: serde::de::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
207 0 : <smol_str::SmolStr as serde::de::Deserialize<'de>>::deserialize(d).map(Self)
208 0 : }
209 : }
210 :
211 : impl serde::Serialize for $name {
212 0 : fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
213 0 : self.0.serialize(s)
214 0 : }
215 : }
216 : };
217 : }
218 :
219 : const POOLER_SUFFIX: &str = "-pooler";
220 :
221 : impl EndpointId {
222 3 : fn normalize(&self) -> Self {
223 3 : if let Some(stripped) = self.as_ref().strip_suffix(POOLER_SUFFIX) {
224 0 : stripped.into()
225 : } else {
226 3 : self.clone()
227 : }
228 3 : }
229 :
230 0 : fn normalize_intern(&self) -> EndpointIdInt {
231 0 : if let Some(stripped) = self.as_ref().strip_suffix(POOLER_SUFFIX) {
232 0 : EndpointIdTag::get_interner().get_or_intern(stripped)
233 : } else {
234 0 : self.into()
235 : }
236 0 : }
237 : }
238 :
239 : // 90% of role name strings are 20 characters or less.
240 : smol_str_wrapper!(RoleName);
241 : // 50% of endpoint strings are 23 characters or less.
242 : smol_str_wrapper!(EndpointId);
243 : // 50% of branch strings are 23 characters or less.
244 : smol_str_wrapper!(BranchId);
245 : // 90% of project strings are 23 characters or less.
246 : smol_str_wrapper!(ProjectId);
247 :
248 : // will usually equal endpoint ID
249 : smol_str_wrapper!(EndpointCacheKey);
250 :
251 : smol_str_wrapper!(DbName);
252 :
253 : // postgres hostname, will likely be a port:ip addr
254 : smol_str_wrapper!(Host);
255 :
256 : // Endpoints are a bit tricky. Rare they might be branches or projects.
257 : impl EndpointId {
258 0 : pub(crate) fn is_endpoint(&self) -> bool {
259 0 : self.0.starts_with("ep-")
260 0 : }
261 0 : pub(crate) fn is_branch(&self) -> bool {
262 0 : self.0.starts_with("br-")
263 0 : }
264 : // pub(crate) fn is_project(&self) -> bool {
265 : // !self.is_endpoint() && !self.is_branch()
266 : // }
267 0 : pub(crate) fn as_branch(&self) -> BranchId {
268 0 : BranchId(self.0.clone())
269 0 : }
270 0 : pub(crate) fn as_project(&self) -> ProjectId {
271 0 : ProjectId(self.0.clone())
272 0 : }
273 : }
|