deadpool_diesel/
manager.rs1use std::{borrow::Cow, fmt, marker::PhantomData, sync::Arc};
2
3use deadpool::{
4 managed::{self, Metrics, RecycleError, RecycleResult},
5 Runtime,
6};
7use deadpool_sync::SyncWrapper;
8use diesel::{query_builder::QueryFragment, IntoSql, RunQueryDsl};
9
10use crate::Error;
11
12pub struct Manager<C> {
19 database_url: String,
20 runtime: Runtime,
21 manager_config: Arc<ManagerConfig<C>>,
22 _marker: PhantomData<fn() -> C>,
23}
24
25pub type RecycleCheckCallback<C> = dyn Fn(&mut C) -> Result<(), Error> + Send + Sync;
27
28pub enum RecyclingMethod<C> {
30 Fast,
36 Verified,
40 CustomQuery(Cow<'static, str>),
42 CustomFunction(Box<RecycleCheckCallback<C>>),
46}
47
48impl<C> Default for RecyclingMethod<C> {
52 fn default() -> Self {
53 Self::Fast
54 }
55}
56
57#[derive(Debug)]
64pub struct ManagerConfig<C> {
65 pub recycling_method: RecyclingMethod<C>,
67}
68
69impl<C> Default for ManagerConfig<C> {
70 fn default() -> Self {
71 Self {
72 recycling_method: Default::default(),
73 }
74 }
75}
76
77impl<C: fmt::Debug> fmt::Debug for RecyclingMethod<C> {
78 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79 match self {
80 Self::Fast => write!(f, "Fast"),
81 Self::Verified => write!(f, "Verified"),
82 Self::CustomQuery(arg0) => f.debug_tuple("CustomQuery").field(arg0).finish(),
83 Self::CustomFunction(_) => f.debug_tuple("CustomFunction").finish(),
84 }
85 }
86}
87
88impl<C> fmt::Debug for Manager<C> {
90 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91 f.debug_struct("Manager")
92 .field("database_url", &self.database_url)
93 .field("runtime", &self.runtime)
94 .field("_marker", &self._marker)
95 .finish()
96 }
97}
98
99impl<C> Manager<C>
100where
101 C: diesel::Connection,
102{
103 #[must_use]
108 pub fn new<S: Into<String>>(database_url: S, runtime: Runtime) -> Self {
109 Self::from_config(database_url, runtime, Default::default())
110 }
111
112 #[must_use]
117 pub fn from_config(
118 database_url: impl Into<String>,
119 runtime: Runtime,
120 manager_config: ManagerConfig<C>,
121 ) -> Self {
122 Manager {
123 database_url: database_url.into(),
124 runtime,
125 manager_config: Arc::new(manager_config),
126 _marker: PhantomData,
127 }
128 }
129}
130
131impl<C> managed::Manager for Manager<C>
132where
133 C: diesel::Connection + 'static,
134 diesel::dsl::BareSelect<diesel::dsl::AsExprOf<i32, diesel::sql_types::Integer>>:
135 QueryFragment<C::Backend>,
136 diesel::query_builder::SqlQuery: QueryFragment<C::Backend>,
137{
138 type Type = crate::Connection<C>;
139 type Error = Error;
140
141 async fn create(&self) -> Result<Self::Type, Self::Error> {
142 let database_url = self.database_url.clone();
143 SyncWrapper::new(self.runtime, move || {
144 C::establish(&database_url).map_err(Into::into)
145 })
146 .await
147 }
148
149 async fn recycle(&self, obj: &mut Self::Type, _: &Metrics) -> RecycleResult<Self::Error> {
150 if obj.is_mutex_poisoned() {
151 return Err(RecycleError::message(
152 "Mutex is poisoned. Connection is considered unusable.",
153 ));
154 }
155 let config = Arc::clone(&self.manager_config);
156 obj.interact(move |conn| config.recycling_method.perform_recycle_check(conn))
157 .await
158 .map_err(|e| RecycleError::message(format!("Panic: {:?}", e)))
159 .and_then(|r| r.map_err(RecycleError::Backend))
160 }
161}
162
163impl<C> RecyclingMethod<C>
164where
165 C: diesel::Connection,
166 diesel::dsl::BareSelect<diesel::dsl::AsExprOf<i32, diesel::sql_types::Integer>>:
167 QueryFragment<C::Backend>,
168 diesel::query_builder::SqlQuery: QueryFragment<C::Backend>,
169{
170 fn perform_recycle_check(&self, conn: &mut C) -> Result<(), Error> {
171 use diesel::connection::TransactionManager;
172
173 if C::TransactionManager::is_broken_transaction_manager(conn) {
177 return Err(Error::BrokenTransactionManger);
178 }
179 match self {
180 RecyclingMethod::Fast => {}
182 RecyclingMethod::Verified => {
186 let _ = diesel::select(1.into_sql::<diesel::sql_types::Integer>())
187 .execute(conn)
188 .map_err(Error::Ping)?;
189 }
190 RecyclingMethod::CustomQuery(query) => {
192 let _ = diesel::sql_query(query.as_ref())
193 .execute(conn)
194 .map_err(Error::Ping)?;
195 }
196 RecyclingMethod::CustomFunction(check) => check(conn)?,
198 }
199 Ok(())
200 }
201}