Skip to content

Commit b85a943

Browse files
authored
Merge pull request #164 from weiznich/instrumentation
Implement support for diesel::Instrumentation for all provided connec…
2 parents d2fc97d + 58b7f2e commit b85a943

File tree

13 files changed

+613
-133
lines changed

13 files changed

+613
-133
lines changed

.github/workflows/ci.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,10 @@ jobs:
4949
5050
- name: Set environment variables
5151
shell: bash
52-
if: matrix.rust == 'nightly'
52+
if: matrix.rust != 'nightly'
5353
run: |
54-
echo "RUSTFLAGS=--cap-lints=warn" >> $GITHUB_ENV
54+
echo "RUSTFLAGS=-D warnings" >> $GITHUB_ENV
55+
echo "RUSTDOCFLAGS=-D warnings" >> $GITHUB_ENV
5556
5657
- uses: ilammy/setup-nasm@v1
5758
if: matrix.backend == 'postgres' && matrix.os == 'windows-2019'
@@ -234,7 +235,7 @@ jobs:
234235
find ~/.cargo/registry -iname "*clippy.toml" -delete
235236
236237
- name: Run clippy
237-
run: cargo +stable clippy --all
238+
run: cargo +stable clippy --all --all-features
238239

239240
- name: Check formating
240241
run: cargo +stable fmt --all -- --check

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@ for Rust libraries in [RFC #1105](https://github.com/rust-lang/rfcs/blob/master/
77
## [Unreleased]
88

99
* Added type `diesel_async::pooled_connection::mobc::PooledConnection`
10-
* MySQL/MariaDB now use `CLIENT_FOUND_ROWS` capability to allow consistent behavior with PostgreSQL regarding return value of UPDATe commands.
10+
* MySQL/MariaDB now use `CLIENT_FOUND_ROWS` capability to allow consistent behaviour with PostgreSQL regarding return value of UPDATe commands.
1111
* The minimal supported rust version is now 1.78.0
12+
* Add a `SyncConnectionWrapper` type that turns a sync connection into an async one. This enables SQLite support for diesel-async
13+
* Add support for `diesel::connection::Instrumentation` to support logging and other instrumentation for any of the provided connection impls.
1214

1315
## [0.4.1] - 2023-09-01
1416

Cargo.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ cfg-if = "1"
4949
chrono = "0.4"
5050
diesel = { version = "2.2.0", default-features = false, features = ["chrono"] }
5151
diesel_migrations = "2.2.0"
52+
assert_matches = "1.0.1"
5253

5354
[features]
5455
default = []
@@ -83,8 +84,8 @@ features = [
8384
"r2d2",
8485
]
8586
no-default-features = true
86-
rustc-args = ["--cfg", "doc_cfg"]
87-
rustdoc-args = ["--cfg", "doc_cfg"]
87+
rustc-args = ["--cfg", "docsrs"]
88+
rustdoc-args = ["--cfg", "docsrs"]
8889

8990
[workspace]
9091
members = [

src/async_connection_wrapper.rs

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,6 @@ mod implementation {
107107
pub struct AsyncConnectionWrapper<C, B> {
108108
inner: C,
109109
runtime: B,
110-
instrumentation: Option<Box<dyn Instrumentation>>,
111110
}
112111

113112
impl<C, B> From<C> for AsyncConnectionWrapper<C, B>
@@ -119,7 +118,6 @@ mod implementation {
119118
Self {
120119
inner,
121120
runtime: B::get_runtime(),
122-
instrumentation: None,
123121
}
124122
}
125123
}
@@ -150,11 +148,7 @@ mod implementation {
150148
let runtime = B::get_runtime();
151149
let f = C::establish(database_url);
152150
let inner = runtime.block_on(f)?;
153-
Ok(Self {
154-
inner,
155-
runtime,
156-
instrumentation: None,
157-
})
151+
Ok(Self { inner, runtime })
158152
}
159153

160154
fn execute_returning_count<T>(&mut self, source: &T) -> diesel::QueryResult<usize>
@@ -165,18 +159,18 @@ mod implementation {
165159
self.runtime.block_on(f)
166160
}
167161

168-
fn transaction_state(
169-
&mut self,
162+
fn transaction_state(
163+
&mut self,
170164
) -> &mut <Self::TransactionManager as diesel::connection::TransactionManager<Self>>::TransactionStateData{
171165
self.inner.transaction_state()
172166
}
173167

174168
fn instrumentation(&mut self) -> &mut dyn Instrumentation {
175-
&mut self.instrumentation
169+
self.inner.instrumentation()
176170
}
177171

178172
fn set_instrumentation(&mut self, instrumentation: impl Instrumentation) {
179-
self.instrumentation = Some(Box::new(instrumentation));
173+
self.inner.set_instrumentation(instrumentation);
180174
}
181175
}
182176

src/lib.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#![cfg_attr(doc_cfg, feature(doc_cfg, doc_auto_cfg))]
1+
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
22
//! Diesel-async provides async variants of diesel related query functionality
33
//!
44
//! diesel-async is an extension to diesel itself. It is designed to be used together
@@ -69,6 +69,7 @@
6969
#![warn(missing_docs)]
7070

7171
use diesel::backend::Backend;
72+
use diesel::connection::Instrumentation;
7273
use diesel::query_builder::{AsQuery, QueryFragment, QueryId};
7374
use diesel::result::Error;
7475
use diesel::row::Row;
@@ -347,4 +348,10 @@ pub trait AsyncConnection: SimpleAsyncConnection + Sized + Send {
347348
fn _silence_lint_on_execute_future(_: Self::ExecuteFuture<'_, '_>) {}
348349
#[doc(hidden)]
349350
fn _silence_lint_on_load_future(_: Self::LoadFuture<'_, '_>) {}
351+
352+
#[doc(hidden)]
353+
fn instrumentation(&mut self) -> &mut dyn Instrumentation;
354+
355+
/// Set a specific [`Instrumentation`] implementation for this connection
356+
fn set_instrumentation(&mut self, instrumentation: impl Instrumentation);
350357
}

src/mysql/mod.rs

Lines changed: 107 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
use crate::stmt_cache::{PrepareCallback, StmtCache};
22
use crate::{AnsiTransactionManager, AsyncConnection, SimpleAsyncConnection};
33
use diesel::connection::statement_cache::{MaybeCached, StatementCacheKey};
4+
use diesel::connection::Instrumentation;
5+
use diesel::connection::InstrumentationEvent;
6+
use diesel::connection::StrQueryHelper;
47
use diesel::mysql::{Mysql, MysqlQueryBuilder, MysqlType};
58
use diesel::query_builder::QueryBuilder;
69
use diesel::query_builder::{bind_collector::RawBytesBindCollector, QueryFragment, QueryId};
@@ -26,12 +29,28 @@ pub struct AsyncMysqlConnection {
2629
conn: mysql_async::Conn,
2730
stmt_cache: StmtCache<Mysql, Statement>,
2831
transaction_manager: AnsiTransactionManager,
32+
instrumentation: std::sync::Mutex<Option<Box<dyn Instrumentation>>>,
2933
}
3034

3135
#[async_trait::async_trait]
3236
impl SimpleAsyncConnection for AsyncMysqlConnection {
3337
async fn batch_execute(&mut self, query: &str) -> diesel::QueryResult<()> {
34-
Ok(self.conn.query_drop(query).await.map_err(ErrorHelper)?)
38+
self.instrumentation()
39+
.on_connection_event(InstrumentationEvent::start_query(&StrQueryHelper::new(
40+
query,
41+
)));
42+
let result = self
43+
.conn
44+
.query_drop(query)
45+
.await
46+
.map_err(ErrorHelper)
47+
.map_err(Into::into);
48+
self.instrumentation()
49+
.on_connection_event(InstrumentationEvent::finish_query(
50+
&StrQueryHelper::new(query),
51+
result.as_ref().err(),
52+
));
53+
result
3554
}
3655
}
3756

@@ -53,20 +72,18 @@ impl AsyncConnection for AsyncMysqlConnection {
5372
type TransactionManager = AnsiTransactionManager;
5473

5574
async fn establish(database_url: &str) -> diesel::ConnectionResult<Self> {
56-
let opts = Opts::from_url(database_url)
57-
.map_err(|e| diesel::result::ConnectionError::InvalidConnectionUrl(e.to_string()))?;
58-
let builder = OptsBuilder::from_opts(opts)
59-
.init(CONNECTION_SETUP_QUERIES.to_vec())
60-
.stmt_cache_size(0) // We have our own cache
61-
.client_found_rows(true); // This allows a consistent behavior between MariaDB/MySQL and PostgreSQL (and is already set in `diesel`)
62-
63-
let conn = mysql_async::Conn::new(builder).await.map_err(ErrorHelper)?;
64-
65-
Ok(AsyncMysqlConnection {
66-
conn,
67-
stmt_cache: StmtCache::new(),
68-
transaction_manager: AnsiTransactionManager::default(),
69-
})
75+
let mut instrumentation = diesel::connection::get_default_instrumentation();
76+
instrumentation.on_connection_event(InstrumentationEvent::start_establish_connection(
77+
database_url,
78+
));
79+
let r = Self::establish_connection_inner(database_url).await;
80+
instrumentation.on_connection_event(InstrumentationEvent::finish_establish_connection(
81+
database_url,
82+
r.as_ref().err(),
83+
));
84+
let mut conn = r?;
85+
conn.instrumentation = std::sync::Mutex::new(instrumentation);
86+
Ok(conn)
7087
}
7188

7289
fn load<'conn, 'query, T>(&'conn mut self, source: T) -> Self::LoadFuture<'conn, 'query>
@@ -80,7 +97,10 @@ impl AsyncConnection for AsyncMysqlConnection {
8097
let stmt_for_exec = match stmt {
8198
MaybeCached::Cached(ref s) => (*s).clone(),
8299
MaybeCached::CannotCache(ref s) => s.clone(),
83-
_ => todo!(),
100+
_ => unreachable!(
101+
"Diesel has only two variants here at the time of writing.\n\
102+
If you ever see this error message please open in issue in the diesel-async issue tracker"
103+
),
84104
};
85105

86106
let (tx, rx) = futures_channel::mpsc::channel(0);
@@ -152,6 +172,19 @@ impl AsyncConnection for AsyncMysqlConnection {
152172
fn transaction_state(&mut self) -> &mut AnsiTransactionManager {
153173
&mut self.transaction_manager
154174
}
175+
176+
fn instrumentation(&mut self) -> &mut dyn Instrumentation {
177+
self.instrumentation
178+
.get_mut()
179+
.unwrap_or_else(|p| p.into_inner())
180+
}
181+
182+
fn set_instrumentation(&mut self, instrumentation: impl Instrumentation) {
183+
*self
184+
.instrumentation
185+
.get_mut()
186+
.unwrap_or_else(|p| p.into_inner()) = Some(Box::new(instrumentation));
187+
}
155188
}
156189

157190
#[inline(always)]
@@ -195,6 +228,9 @@ impl AsyncMysqlConnection {
195228
conn,
196229
stmt_cache: StmtCache::new(),
197230
transaction_manager: AnsiTransactionManager::default(),
231+
instrumentation: std::sync::Mutex::new(
232+
diesel::connection::get_default_instrumentation(),
233+
),
198234
};
199235

200236
for stmt in CONNECTION_SETUP_QUERIES {
@@ -219,6 +255,10 @@ impl AsyncMysqlConnection {
219255
T: QueryFragment<Mysql> + QueryId,
220256
F: Future<Output = QueryResult<R>> + Send,
221257
{
258+
self.instrumentation()
259+
.on_connection_event(InstrumentationEvent::start_query(&diesel::debug_query(
260+
&query,
261+
)));
222262
let mut bind_collector = RawBytesBindCollector::<Mysql>::new();
223263
let bind_collector = query
224264
.collect_binds(&mut bind_collector, &mut (), &Mysql)
@@ -228,6 +268,7 @@ impl AsyncMysqlConnection {
228268
ref mut conn,
229269
ref mut stmt_cache,
230270
ref mut transaction_manager,
271+
ref mut instrumentation,
231272
..
232273
} = self;
233274

@@ -242,28 +283,37 @@ impl AsyncMysqlConnection {
242283
} = bind_collector?;
243284
let is_safe_to_cache_prepared = is_safe_to_cache_prepared?;
244285
let sql = sql?;
245-
let cache_key = if let Some(query_id) = query_id {
246-
StatementCacheKey::Type(query_id)
247-
} else {
248-
StatementCacheKey::Sql {
249-
sql: sql.clone(),
250-
bind_types: metadata.clone(),
251-
}
286+
let inner = async {
287+
let cache_key = if let Some(query_id) = query_id {
288+
StatementCacheKey::Type(query_id)
289+
} else {
290+
StatementCacheKey::Sql {
291+
sql: sql.clone(),
292+
bind_types: metadata.clone(),
293+
}
294+
};
295+
296+
let (stmt, conn) = stmt_cache
297+
.cached_prepared_statement(
298+
cache_key,
299+
sql.clone(),
300+
is_safe_to_cache_prepared,
301+
&metadata,
302+
conn,
303+
instrumentation,
304+
)
305+
.await?;
306+
callback(conn, stmt, ToSqlHelper { metadata, binds }).await
252307
};
253-
254-
let (stmt, conn) = stmt_cache
255-
.cached_prepared_statement(
256-
cache_key,
257-
sql,
258-
is_safe_to_cache_prepared,
259-
&metadata,
260-
conn,
261-
)
262-
.await?;
263-
update_transaction_manager_status(
264-
callback(conn, stmt, ToSqlHelper { metadata, binds }).await,
265-
transaction_manager,
266-
)
308+
let r = update_transaction_manager_status(inner.await, transaction_manager);
309+
instrumentation
310+
.get_mut()
311+
.unwrap_or_else(|p| p.into_inner())
312+
.on_connection_event(InstrumentationEvent::finish_query(
313+
&StrQueryHelper::new(&sql),
314+
r.as_ref().err(),
315+
));
316+
r
267317
}
268318
.boxed()
269319
}
@@ -300,6 +350,26 @@ impl AsyncMysqlConnection {
300350

301351
Ok(())
302352
}
353+
354+
async fn establish_connection_inner(
355+
database_url: &str,
356+
) -> Result<AsyncMysqlConnection, ConnectionError> {
357+
let opts = Opts::from_url(database_url)
358+
.map_err(|e| diesel::result::ConnectionError::InvalidConnectionUrl(e.to_string()))?;
359+
let builder = OptsBuilder::from_opts(opts)
360+
.init(CONNECTION_SETUP_QUERIES.to_vec())
361+
.stmt_cache_size(0) // We have our own cache
362+
.client_found_rows(true); // This allows a consistent behavior between MariaDB/MySQL and PostgreSQL (and is already set in `diesel`)
363+
364+
let conn = mysql_async::Conn::new(builder).await.map_err(ErrorHelper)?;
365+
366+
Ok(AsyncMysqlConnection {
367+
conn,
368+
stmt_cache: StmtCache::new(),
369+
transaction_manager: AnsiTransactionManager::default(),
370+
instrumentation: std::sync::Mutex::new(None),
371+
})
372+
}
303373
}
304374

305375
#[cfg(any(

0 commit comments

Comments
 (0)