Skip to content

Commit 7c6572d

Browse files
committed
Add a way to opt-out of pooled connection reset
1 parent 32c6f2a commit 7c6572d

File tree

4 files changed

+122
-7
lines changed

4 files changed

+122
-7
lines changed

src/conn/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,13 @@ impl Conn {
229229
self.inner.last_ok_packet.as_ref()
230230
}
231231

232+
/// Turns on/off automatic connection reset (see [`crate::PoolOpts::with_reset_connection`]).
233+
///
234+
/// Only makes sense for pooled connections.
235+
pub fn reset_connection(&mut self, reset_connection: bool) {
236+
self.inner.reset_upon_returning_to_a_pool = reset_connection;
237+
}
238+
232239
pub(crate) fn stream_mut(&mut self) -> Result<&mut Stream> {
233240
self.inner.stream_mut()
234241
}

src/conn/pool/mod.rs

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,8 @@ impl Pool {
232232

233233
/// Async function that resolves to `Conn`.
234234
pub fn get_conn(&self) -> GetConn {
235-
GetConn::new(self, true)
235+
let reset_connection = self.opts.pool_opts().reset_connection();
236+
GetConn::new(self, reset_connection)
236237
}
237238

238239
/// Starts a new transaction.
@@ -382,7 +383,6 @@ mod test {
382383
future::{join_all, select, select_all, try_join_all},
383384
try_join, FutureExt,
384385
};
385-
use mysql_common::row::Row;
386386
use tokio::time::{sleep, timeout};
387387

388388
use std::{
@@ -396,7 +396,7 @@ mod test {
396396
opts::PoolOpts,
397397
prelude::*,
398398
test_misc::get_opts,
399-
PoolConstraints, TxOpts,
399+
PoolConstraints, Row, TxOpts, Value,
400400
};
401401

402402
macro_rules! conn_ex_field {
@@ -411,6 +411,55 @@ mod test {
411411
};
412412
}
413413

414+
#[tokio::test]
415+
async fn should_opt_out_of_connection_reset() -> super::Result<()> {
416+
let pool_opts = PoolOpts::new().with_constraints(PoolConstraints::new(1, 1).unwrap());
417+
let opts = get_opts().pool_opts(pool_opts.clone());
418+
419+
let pool = Pool::new(opts.clone());
420+
421+
let mut conn = pool.get_conn().await.unwrap();
422+
assert_eq!(
423+
conn.query_first::<Value, _>("SELECT @foo").await?.unwrap(),
424+
Value::NULL
425+
);
426+
conn.query_drop("SET @foo = 'foo'").await?;
427+
assert_eq!(
428+
conn.query_first::<String, _>("SELECT @foo").await?.unwrap(),
429+
"foo",
430+
);
431+
drop(conn);
432+
433+
conn = pool.get_conn().await.unwrap();
434+
assert_eq!(
435+
conn.query_first::<Value, _>("SELECT @foo").await?.unwrap(),
436+
Value::NULL
437+
);
438+
conn.query_drop("SET @foo = 'foo'").await?;
439+
conn.reset_connection(false);
440+
drop(conn);
441+
442+
conn = pool.get_conn().await.unwrap();
443+
assert_eq!(
444+
conn.query_first::<String, _>("SELECT @foo").await?.unwrap(),
445+
"foo",
446+
);
447+
drop(conn);
448+
pool.disconnect().await.unwrap();
449+
450+
let pool = Pool::new(opts.pool_opts(pool_opts.with_reset_connection(false)));
451+
conn = pool.get_conn().await.unwrap();
452+
conn.query_drop("SET @foo = 'foo'").await?;
453+
drop(conn);
454+
conn = pool.get_conn().await.unwrap();
455+
assert_eq!(
456+
conn.query_first::<String, _>("SELECT @foo").await?.unwrap(),
457+
"foo",
458+
);
459+
drop(conn);
460+
pool.disconnect().await
461+
}
462+
414463
#[test]
415464
fn should_not_hang() -> super::Result<()> {
416465
pub struct Database {

src/conn/routines/change_user.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use crate::Conn;
1111

1212
use super::Routine;
1313

14-
/// A routine that performs `COM_RESET_CONNECTION`.
14+
/// A routine that performs `COM_CHANGE_USER`.
1515
#[derive(Debug, Copy, Clone)]
1616
pub struct ChangeUser;
1717

src/opts/mod.rs

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -209,9 +209,15 @@ pub struct PoolOpts {
209209
constraints: PoolConstraints,
210210
inactive_connection_ttl: Duration,
211211
ttl_check_interval: Duration,
212+
reset_connection: bool,
212213
}
213214

214215
impl PoolOpts {
216+
/// Calls `Self::default`.
217+
pub fn new() -> Self {
218+
Self::default()
219+
}
220+
215221
/// Creates the default [`PoolOpts`] with the given constraints.
216222
pub fn with_constraints(mut self, constraints: PoolConstraints) -> Self {
217223
self.constraints = constraints;
@@ -223,6 +229,50 @@ impl PoolOpts {
223229
self.constraints
224230
}
225231

232+
/// Sets whether to reset connection upon returning it to a pool (defaults to `true`).
233+
///
234+
/// Default behavior increases reliability but comes with cons:
235+
///
236+
/// * reset procedure removes all prepared statements, i.e. kills prepared statements cache
237+
/// * connection reset is quite fast but requires additional client-server roundtrip
238+
/// (might also requires requthentication for older servers)
239+
///
240+
/// The purpose of the reset procedure is to:
241+
///
242+
/// * rollback any opened transactions (`mysql_async` is able to do this without explicit reset)
243+
/// * reset transaction isolation level
244+
/// * reset session variables
245+
/// * delete user variables
246+
/// * remove temporary tables
247+
/// * remove all PREPARE statement (this action kills prepared statements cache)
248+
///
249+
/// So to encrease overall performance you can safely opt-out of the default behavior
250+
/// if you are not willing to change the session state in an unpleasant way.
251+
///
252+
/// It is also possible to selectively opt-in/out using [`Conn::reset_connection`].
253+
///
254+
/// # Connection URL
255+
///
256+
/// You can use `reset_connection` URL parameter to set this value. E.g.
257+
///
258+
/// ```
259+
/// # use mysql_async::*;
260+
/// # use std::time::Duration;
261+
/// # fn main() -> Result<()> {
262+
/// let opts = Opts::from_url("mysql://localhost/db?reset_connection=false")?;
263+
/// assert_eq!(opts.pool_opts().reset_connection(), false);
264+
/// # Ok(()) }
265+
/// ```
266+
pub fn with_reset_connection(mut self, reset_connection: bool) -> Self {
267+
self.reset_connection = reset_connection;
268+
self
269+
}
270+
271+
/// Returns the `reset_connection` value (see [`PoolOpts::with_reset_connection`]).
272+
pub fn reset_connection(&self) -> bool {
273+
self.reset_connection
274+
}
275+
226276
/// Pool will recycle inactive connection if it is outside of the lower bound of the pool
227277
/// and if it is idling longer than this value (defaults to
228278
/// [`DEFAULT_INACTIVE_CONNECTION_TTL`]).
@@ -309,6 +359,7 @@ impl Default for PoolOpts {
309359
constraints: DEFAULT_POOL_CONSTRAINTS,
310360
inactive_connection_ttl: DEFAULT_INACTIVE_CONNECTION_TTL,
311361
ttl_check_interval: DEFAULT_TTL_CHECK_INTERVAL,
362+
reset_connection: true,
312363
}
313364
}
314365
}
@@ -1340,7 +1391,6 @@ fn mysqlopts_from_url(url: &Url) -> std::result::Result<MysqlOpts, UrlError> {
13401391
Ok(value) => {
13411392
opts.pool_opts = opts
13421393
.pool_opts
1343-
.clone()
13441394
.with_inactive_connection_ttl(Duration::from_secs(value))
13451395
}
13461396
_ => {
@@ -1355,7 +1405,6 @@ fn mysqlopts_from_url(url: &Url) -> std::result::Result<MysqlOpts, UrlError> {
13551405
Ok(value) => {
13561406
opts.pool_opts = opts
13571407
.pool_opts
1358-
.clone()
13591408
.with_ttl_check_interval(Duration::from_secs(value))
13601409
}
13611410
_ => {
@@ -1421,6 +1470,16 @@ fn mysqlopts_from_url(url: &Url) -> std::result::Result<MysqlOpts, UrlError> {
14211470
});
14221471
}
14231472
}
1473+
} else if key == "reset_connection" {
1474+
match bool::from_str(&*value) {
1475+
Ok(parsed) => opts.pool_opts = opts.pool_opts.with_reset_connection(parsed),
1476+
Err(_) => {
1477+
return Err(UrlError::InvalidParamValue {
1478+
param: key.to_string(),
1479+
value,
1480+
});
1481+
}
1482+
}
14241483
} else if key == "tcp_nodelay" {
14251484
match bool::from_str(&*value) {
14261485
Ok(value) => opts.tcp_nodelay = value,
@@ -1538,7 +1597,7 @@ fn mysqlopts_from_url(url: &Url) -> std::result::Result<MysqlOpts, UrlError> {
15381597
}
15391598

15401599
if let Some(pool_constraints) = PoolConstraints::new(pool_min, pool_max) {
1541-
opts.pool_opts = opts.pool_opts.clone().with_constraints(pool_constraints);
1600+
opts.pool_opts = opts.pool_opts.with_constraints(pool_constraints);
15421601
} else {
15431602
return Err(UrlError::InvalidPoolConstraints {
15441603
min: pool_min,

0 commit comments

Comments
 (0)