chore(proxy): disallow unwrap and unimplemented (#10142)

As the title says, I updated the lint rules to no longer allow unwrap or
unimplemented.

Three special cases:
* Tests are allowed to use them
* std::sync::Mutex lock().unwrap() is common because it's usually
correct to continue panicking on poison
* `tokio::spawn_blocking(...).await.unwrap()` is common because it will
only error if the blocking fn panics, so continuing the panic is also
correct

I've introduced two extension traits to help with these last two, that
are a bit more explicit so they don't need an expect message every time.
This commit is contained in:
Conrad Ludgate
2024-12-16 16:37:15 +00:00
committed by GitHub
parent 2e4c9c5704
commit 59b7ff8988
39 changed files with 178 additions and 113 deletions
+5 -1
View File
@@ -268,7 +268,11 @@ impl PoolingBackend {
if !self.local_pool.initialized(&conn_info) {
// only install and grant usage one at a time.
let _permit = local_backend.initialize.acquire().await.unwrap();
let _permit = local_backend
.initialize
.acquire()
.await
.expect("semaphore should never be closed");
// check again for race
if !self.local_pool.initialized(&conn_info) {
+13 -21
View File
@@ -186,8 +186,8 @@ impl ClientDataRemote {
}
#[cfg(test)]
#[expect(clippy::unwrap_used)]
mod tests {
use std::mem;
use std::sync::atomic::AtomicBool;
use super::*;
@@ -269,39 +269,33 @@ mod tests {
assert_eq!(0, pool.get_global_connections_count());
}
{
let mut client = Client::new(create_inner(), conn_info.clone(), ep_pool.clone());
client.do_drop().unwrap()();
mem::forget(client); // drop the client
let client = Client::new(create_inner(), conn_info.clone(), ep_pool.clone());
drop(client);
assert_eq!(1, pool.get_global_connections_count());
}
{
let mut closed_client = Client::new(
let closed_client = Client::new(
create_inner_with(MockClient::new(true)),
conn_info.clone(),
ep_pool.clone(),
);
closed_client.do_drop().unwrap()();
mem::forget(closed_client); // drop the client
// The closed client shouldn't be added to the pool.
drop(closed_client);
assert_eq!(1, pool.get_global_connections_count());
}
let is_closed: Arc<AtomicBool> = Arc::new(false.into());
{
let mut client = Client::new(
let client = Client::new(
create_inner_with(MockClient(is_closed.clone())),
conn_info.clone(),
ep_pool.clone(),
);
client.do_drop().unwrap()();
mem::forget(client); // drop the client
drop(client);
// The client should be added to the pool.
assert_eq!(2, pool.get_global_connections_count());
}
{
let mut client = Client::new(create_inner(), conn_info, ep_pool);
client.do_drop().unwrap()();
mem::forget(client); // drop the client
let client = Client::new(create_inner(), conn_info, ep_pool);
drop(client);
// The client shouldn't be added to the pool. Because the ep-pool is full.
assert_eq!(2, pool.get_global_connections_count());
@@ -319,15 +313,13 @@ mod tests {
&pool.get_or_create_endpoint_pool(&conn_info.endpoint_cache_key().unwrap()),
);
{
let mut client = Client::new(create_inner(), conn_info.clone(), ep_pool.clone());
client.do_drop().unwrap()();
mem::forget(client); // drop the client
let client = Client::new(create_inner(), conn_info.clone(), ep_pool.clone());
drop(client);
assert_eq!(3, pool.get_global_connections_count());
}
{
let mut client = Client::new(create_inner(), conn_info.clone(), ep_pool.clone());
client.do_drop().unwrap()();
mem::forget(client); // drop the client
let client = Client::new(create_inner(), conn_info.clone(), ep_pool.clone());
drop(client);
// The client shouldn't be added to the pool. Because the global pool is full.
assert_eq!(3, pool.get_global_connections_count());
+21 -24
View File
@@ -187,19 +187,22 @@ impl<C: ClientInnerExt> EndpointConnPool<C> {
pub(crate) fn put(pool: &RwLock<Self>, conn_info: &ConnInfo, client: ClientInnerCommon<C>) {
let conn_id = client.get_conn_id();
let pool_name = pool.read().get_name().to_string();
let (max_conn, conn_count, pool_name) = {
let pool = pool.read();
(
pool.global_pool_size_max_conns,
pool.global_connections_count
.load(atomic::Ordering::Relaxed),
pool.get_name().to_string(),
)
};
if client.inner.is_closed() {
info!(%conn_id, "{}: throwing away connection '{conn_info}' because connection is closed", pool_name);
return;
}
let global_max_conn = pool.read().global_pool_size_max_conns;
if pool
.read()
.global_connections_count
.load(atomic::Ordering::Relaxed)
>= global_max_conn
{
if conn_count >= max_conn {
info!(%conn_id, "{}: throwing away connection '{conn_info}' because pool is full", pool_name);
return;
}
@@ -633,35 +636,29 @@ impl<C: ClientInnerExt> Client<C> {
}
pub(crate) fn metrics(&self) -> Arc<MetricCounter> {
let aux = &self.inner.as_ref().unwrap().aux;
let aux = &self
.inner
.as_ref()
.expect("client inner should not be removed")
.aux;
USAGE_METRICS.register(Ids {
endpoint_id: aux.endpoint_id,
branch_id: aux.branch_id,
})
}
}
pub(crate) fn do_drop(&mut self) -> Option<impl FnOnce() + use<C>> {
impl<C: ClientInnerExt> Drop for Client<C> {
fn drop(&mut self) {
let conn_info = self.conn_info.clone();
let client = self
.inner
.take()
.expect("client inner should not be removed");
if let Some(conn_pool) = std::mem::take(&mut self.pool).upgrade() {
let current_span = self.span.clone();
let _current_span = self.span.enter();
// return connection to the pool
return Some(move || {
let _span = current_span.enter();
EndpointConnPool::put(&conn_pool, &conn_info, client);
});
}
None
}
}
impl<C: ClientInnerExt> Drop for Client<C> {
fn drop(&mut self) {
if let Some(drop) = self.do_drop() {
tokio::task::spawn_blocking(drop);
EndpointConnPool::put(&conn_pool, &conn_info, client);
}
}
}
+7 -4
View File
@@ -81,11 +81,14 @@ impl HttpErrorBody {
.header(http::header::CONTENT_TYPE, "application/json")
// we do not have nested maps with non string keys so serialization shouldn't fail
.body(
Full::new(Bytes::from(serde_json::to_string(self).unwrap()))
.map_err(|x| match x {})
.boxed(),
Full::new(Bytes::from(
serde_json::to_string(self)
.expect("serialising HttpErrorBody should never fail"),
))
.map_err(|x| match x {})
.boxed(),
)
.unwrap()
.expect("content-type header should be valid")
}
}
+5 -1
View File
@@ -204,7 +204,10 @@ fn pg_array_parse_inner(
if c == '\\' {
escaped = true;
(i, c) = pg_array_chr.next().unwrap();
let Some(x) = pg_array_chr.next() else {
return Err(JsonConversionError::UnbalancedArray);
};
(i, c) = x;
}
match c {
@@ -253,6 +256,7 @@ fn pg_array_parse_inner(
}
#[cfg(test)]
#[expect(clippy::unwrap_used)]
mod tests {
use serde_json::json;
+4 -7
View File
@@ -179,7 +179,6 @@ pub(crate) fn poll_client<C: ClientInnerExt>(
info!(cold_start_info = cold_start_info.as_str(), %conn_info, %session_id, "new connection");
});
let pool = Arc::downgrade(&global_pool);
let pool_clone = pool.clone();
let db_user = conn_info.db_and_user();
let idle = global_pool.get_idle_timeout();
@@ -273,11 +272,7 @@ pub(crate) fn poll_client<C: ClientInnerExt>(
}),
};
Client::new(
inner,
conn_info,
Arc::downgrade(&pool_clone.upgrade().unwrap().global_pool),
)
Client::new(inner, conn_info, Arc::downgrade(&global_pool.global_pool))
}
impl ClientInnerCommon<postgres_client::Client> {
@@ -321,7 +316,8 @@ fn resign_jwt(sk: &SigningKey, payload: &[u8], jti: u64) -> Result<String, HttpC
let mut buffer = itoa::Buffer::new();
// encode the jti integer to a json rawvalue
let jti = serde_json::from_str::<&RawValue>(buffer.format(jti)).unwrap();
let jti = serde_json::from_str::<&RawValue>(buffer.format(jti))
.expect("itoa formatted integer should be guaranteed valid json");
// update the jti in-place
let payload =
@@ -368,6 +364,7 @@ fn sign_jwt(sk: &SigningKey, payload: &[u8]) -> String {
}
#[cfg(test)]
#[expect(clippy::unwrap_used)]
mod tests {
use p256::ecdsa::SigningKey;
use typed_json::json;
+3 -2
View File
@@ -46,6 +46,7 @@ use utils::http::error::ApiError;
use crate::cancellation::CancellationHandlerMain;
use crate::config::{ProxyConfig, ProxyProtocolV2};
use crate::context::RequestContext;
use crate::ext::TaskExt;
use crate::metrics::Metrics;
use crate::protocol2::{read_proxy_protocol, ChainRW, ConnectHeader, ConnectionInfo};
use crate::proxy::run_until_cancelled;
@@ -84,7 +85,7 @@ pub async fn task_main(
cancellation_token.cancelled().await;
tokio::task::spawn_blocking(move || conn_pool.shutdown())
.await
.unwrap();
.propagate_task_panic();
}
});
@@ -104,7 +105,7 @@ pub async fn task_main(
cancellation_token.cancelled().await;
tokio::task::spawn_blocking(move || http_conn_pool.shutdown())
.await
.unwrap();
.propagate_task_panic();
}
});
+1
View File
@@ -1110,6 +1110,7 @@ impl Discard<'_> {
}
#[cfg(test)]
#[expect(clippy::unwrap_used)]
mod tests {
use super::*;
+1
View File
@@ -178,6 +178,7 @@ pub(crate) async fn serve_websocket(
}
#[cfg(test)]
#[expect(clippy::unwrap_used)]
mod tests {
use std::pin::pin;