Skip to content

Commit a78ce35

Browse files
author
Hirotaka Azuma
committed
Support keepalive interval and retries.
1 parent 5433118 commit a78ce35

File tree

9 files changed

+189
-20
lines changed

9 files changed

+189
-20
lines changed

postgres/src/config.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ use tokio_postgres::{Error, Socket};
4848
/// This option is ignored when connecting with Unix sockets. Defaults to on.
4949
/// * `keepalives_idle` - The number of seconds of inactivity after which a keepalive message is sent to the server.
5050
/// This option is ignored when connecting with Unix sockets. Defaults to 2 hours.
51+
/// * `keepalives_interval` - The time interval between TCP keepalive probes.
52+
/// This option is ignored when connecting with Unix sockets. Available on neither Redox nor Solaris.
53+
/// * `keepalives_retries` - The maximum number of TCP keepalive probes that will be sent before dropping a connection.
54+
/// This option is ignored when connecting with Unix sockets. Available on neither Redox, Solaris nor Windows.
5155
/// * `target_session_attrs` - Specifies requirements of the session. If set to `read-write`, the client will check that
5256
/// the `transaction_read_write` session parameter is set to `on`. This can be used to connect to the primary server
5357
/// in a database cluster as opposed to the secondary read-only mirrors. Defaults to `all`.
@@ -279,6 +283,45 @@ impl Config {
279283
self.config.get_keepalives_idle()
280284
}
281285

286+
/// Sets the time interval between TCP keepalive probes.
287+
/// On Windows, this sets the value of the tcp_keepalive struct’s keepaliveinterval field.
288+
///
289+
/// This is ignored for Unix domain sockets, or if the `keepalives` option is disabled.
290+
///
291+
/// Available on neither Redox nor Solaris.
292+
#[cfg(not(any(target_os = "redox", target_os = "solaris")))]
293+
pub fn keepalives_interval(&mut self, keepalives_interval: Duration) -> &mut Config {
294+
self.config.keepalives_interval(keepalives_interval);
295+
self
296+
}
297+
298+
/// Gets the time interval between TCP keepalive probes.
299+
///
300+
/// Available on neither Redox nor Solaris.
301+
#[cfg(not(any(target_os = "redox", target_os = "solaris")))]
302+
pub fn get_keepalives_interval(&self) -> Option<&Duration> {
303+
self.config.get_keepalives_interval()
304+
}
305+
306+
/// Sets the maximum number of TCP keepalive probes that will be sent before dropping a connection.
307+
///
308+
/// This is ignored for Unix domain sockets, or if the `keepalives` option is disabled.
309+
///
310+
/// Available on neither Redox, Solaris nor Windows.
311+
#[cfg(not(any(target_os = "redox", target_os = "solaris", target_os = "windows")))]
312+
pub fn keepalives_retries(&mut self, keepalives_retries: u32) -> &mut Config {
313+
self.config.keepalives_retries(keepalives_retries);
314+
self
315+
}
316+
317+
/// Gets the maximum number of TCP keepalive probes that will be sent before dropping a connection.
318+
///
319+
/// Available on neither Redox, Solaris nor Windows.
320+
#[cfg(not(any(target_os = "redox", target_os = "solaris", target_os = "windows")))]
321+
pub fn get_keepalives_retries(&self) -> Option<&u32> {
322+
self.config.get_keepalives_retries()
323+
}
324+
282325
/// Sets the requirements of the session.
283326
///
284327
/// This can be used to connect to the primary server in a clustered database rather than one of the read-only

tokio-postgres/src/cancel_query.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,7 @@ where
3838
&config.host,
3939
config.port,
4040
config.connect_timeout,
41-
config.keepalives,
42-
config.keepalives_idle,
41+
config.keepalive.as_ref(),
4342
)
4443
.await?;
4544

tokio-postgres/src/client.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use crate::config::Host;
44
use crate::config::SslMode;
55
use crate::connection::{Request, RequestMessages};
66
use crate::copy_out::CopyOutStream;
7+
use crate::keepalive::KeepaliveConfig;
78
use crate::query::RowStream;
89
use crate::simple_query::SimpleQueryStream;
910
#[cfg(feature = "runtime")]
@@ -154,8 +155,7 @@ pub(crate) struct SocketConfig {
154155
pub host: Host,
155156
pub port: u16,
156157
pub connect_timeout: Option<Duration>,
157-
pub keepalives: bool,
158-
pub keepalives_idle: Duration,
158+
pub keepalive: Option<KeepaliveConfig>,
159159
}
160160

161161
/// An asynchronous PostgreSQL client.

tokio-postgres/src/config.rs

Lines changed: 86 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#[cfg(feature = "runtime")]
44
use crate::connect::connect;
55
use crate::connect_raw::connect_raw;
6+
use crate::keepalive::KeepaliveConfig;
67
#[cfg(feature = "runtime")]
78
use crate::tls::MakeTlsConnect;
89
use crate::tls::TlsConnect;
@@ -99,6 +100,10 @@ pub enum Host {
99100
/// This option is ignored when connecting with Unix sockets. Defaults to on.
100101
/// * `keepalives_idle` - The number of seconds of inactivity after which a keepalive message is sent to the server.
101102
/// This option is ignored when connecting with Unix sockets. Defaults to 2 hours.
103+
/// * `keepalives_interval` - The time interval between TCP keepalive probes.
104+
/// This option is ignored when connecting with Unix sockets. Available on neither Redox nor Solaris.
105+
/// * `keepalives_retries` - The maximum number of TCP keepalive probes that will be sent before dropping a connection.
106+
/// This option is ignored when connecting with Unix sockets. Available on neither Redox, Solaris nor Windows.
102107
/// * `target_session_attrs` - Specifies requirements of the session. If set to `read-write`, the client will check that
103108
/// the `transaction_read_write` session parameter is set to `on`. This can be used to connect to the primary server
104109
/// in a database cluster as opposed to the secondary read-only mirrors. Defaults to `all`.
@@ -156,7 +161,7 @@ pub struct Config {
156161
pub(crate) port: Vec<u16>,
157162
pub(crate) connect_timeout: Option<Duration>,
158163
pub(crate) keepalives: bool,
159-
pub(crate) keepalives_idle: Duration,
164+
pub(crate) keepalive_config: KeepaliveConfig,
160165
pub(crate) target_session_attrs: TargetSessionAttrs,
161166
pub(crate) channel_binding: ChannelBinding,
162167
}
@@ -170,6 +175,13 @@ impl Default for Config {
170175
impl Config {
171176
/// Creates a new configuration.
172177
pub fn new() -> Config {
178+
let keepalive_config = KeepaliveConfig {
179+
idle: Duration::from_secs(2 * 60 * 60),
180+
#[cfg(not(any(target_os = "redox", target_os = "solaris")))]
181+
interval: None,
182+
#[cfg(not(any(target_os = "redox", target_os = "solaris", target_os = "windows")))]
183+
retries: None,
184+
};
173185
Config {
174186
user: None,
175187
password: None,
@@ -181,7 +193,7 @@ impl Config {
181193
port: vec![],
182194
connect_timeout: None,
183195
keepalives: true,
184-
keepalives_idle: Duration::from_secs(2 * 60 * 60),
196+
keepalive_config,
185197
target_session_attrs: TargetSessionAttrs::Any,
186198
channel_binding: ChannelBinding::Prefer,
187199
}
@@ -347,14 +359,53 @@ impl Config {
347359
///
348360
/// This is ignored for Unix domain sockets, or if the `keepalives` option is disabled. Defaults to 2 hours.
349361
pub fn keepalives_idle(&mut self, keepalives_idle: Duration) -> &mut Config {
350-
self.keepalives_idle = keepalives_idle;
362+
self.keepalive_config.idle = keepalives_idle;
351363
self
352364
}
353365

354366
/// Gets the configured amount of idle time before a keepalive packet will
355367
/// be sent on the connection.
356368
pub fn get_keepalives_idle(&self) -> Duration {
357-
self.keepalives_idle
369+
self.keepalive_config.idle
370+
}
371+
372+
/// Sets the time interval between TCP keepalive probes.
373+
/// On Windows, this sets the value of the tcp_keepalive struct’s keepaliveinterval field.
374+
///
375+
/// This is ignored for Unix domain sockets, or if the `keepalives` option is disabled.
376+
///
377+
/// Available on neither Redox nor Solaris.
378+
#[cfg(not(any(target_os = "redox", target_os = "solaris")))]
379+
pub fn keepalives_interval(&mut self, keepalives_interval: Duration) -> &mut Config {
380+
self.keepalive_config.interval = Some(keepalives_interval);
381+
self
382+
}
383+
384+
/// Gets the time interval between TCP keepalive probes.
385+
///
386+
/// Available on neither Redox nor Solaris.
387+
#[cfg(not(any(target_os = "redox", target_os = "solaris")))]
388+
pub fn get_keepalives_interval(&self) -> Option<&Duration> {
389+
self.keepalive_config.interval.as_ref()
390+
}
391+
392+
/// Sets the maximum number of TCP keepalive probes that will be sent before dropping a connection.
393+
///
394+
/// This is ignored for Unix domain sockets, or if the `keepalives` option is disabled.
395+
///
396+
/// Available on neither Redox, Solaris nor Windows.
397+
#[cfg(not(any(target_os = "redox", target_os = "solaris", target_os = "windows")))]
398+
pub fn keepalives_retries(&mut self, keepalives_retries: u32) -> &mut Config {
399+
self.keepalive_config.retries = Some(keepalives_retries);
400+
self
401+
}
402+
403+
/// Gets the maximum number of TCP keepalive probes that will be sent before dropping a connection.
404+
///
405+
/// Available on neither Redox, Solaris nor Windows.
406+
#[cfg(not(any(target_os = "redox", target_os = "solaris", target_os = "windows")))]
407+
pub fn get_keepalives_retries(&self) -> Option<&u32> {
408+
self.keepalive_config.retries.as_ref()
358409
}
359410

360411
/// Sets the requirements of the session.
@@ -451,6 +502,22 @@ impl Config {
451502
self.keepalives_idle(Duration::from_secs(keepalives_idle as u64));
452503
}
453504
}
505+
#[cfg(not(any(target_os = "redox", target_os = "solaris")))]
506+
"keepalives_interval" => {
507+
let keepalives_interval = value.parse::<i64>().map_err(|_| {
508+
Error::config_parse(Box::new(InvalidValue("keepalives_interval")))
509+
})?;
510+
if keepalives_interval > 0 {
511+
self.keepalives_interval(Duration::from_secs(keepalives_interval as u64));
512+
}
513+
}
514+
#[cfg(not(any(target_os = "redox", target_os = "solaris", target_os = "windows")))]
515+
"keepalives_retries" => {
516+
let keepalives_retries = value.parse::<u32>().map_err(|_| {
517+
Error::config_parse(Box::new(InvalidValue("keepalives_retries")))
518+
})?;
519+
self.keepalives_retries(keepalives_retries);
520+
}
454521
"target_session_attrs" => {
455522
let target_session_attrs = match value {
456523
"any" => TargetSessionAttrs::Any,
@@ -534,8 +601,8 @@ impl fmt::Debug for Config {
534601
}
535602
}
536603

537-
f.debug_struct("Config")
538-
.field("user", &self.user)
604+
let mut ds = f.debug_struct("Config");
605+
ds.field("user", &self.user)
539606
.field("password", &self.password.as_ref().map(|_| Redaction {}))
540607
.field("dbname", &self.dbname)
541608
.field("options", &self.options)
@@ -545,8 +612,19 @@ impl fmt::Debug for Config {
545612
.field("port", &self.port)
546613
.field("connect_timeout", &self.connect_timeout)
547614
.field("keepalives", &self.keepalives)
548-
.field("keepalives_idle", &self.keepalives_idle)
549-
.field("target_session_attrs", &self.target_session_attrs)
615+
.field("keepalives_idle", &self.keepalive_config.idle);
616+
617+
#[cfg(not(any(target_os = "redox", target_os = "solaris")))]
618+
{
619+
ds.field("keepalives_interval", &self.keepalive_config.interval);
620+
}
621+
622+
#[cfg(not(any(target_os = "redox", target_os = "solaris", target_os = "windows")))]
623+
{
624+
ds.field("keepalives_retries", &self.keepalive_config.retries);
625+
}
626+
627+
ds.field("target_session_attrs", &self.target_session_attrs)
550628
.field("channel_binding", &self.channel_binding)
551629
.finish()
552630
}

tokio-postgres/src/connect.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,11 @@ where
6565
host,
6666
port,
6767
config.connect_timeout,
68-
config.keepalives,
69-
config.keepalives_idle,
68+
if config.keepalives {
69+
Some(&config.keepalive_config)
70+
} else {
71+
None
72+
},
7073
)
7174
.await?;
7275
let (mut client, mut connection) = connect_raw(socket, tls, config).await?;
@@ -115,8 +118,11 @@ where
115118
host: host.clone(),
116119
port,
117120
connect_timeout: config.connect_timeout,
118-
keepalives: config.keepalives,
119-
keepalives_idle: config.keepalives_idle,
121+
keepalive: if config.keepalives {
122+
Some(config.keepalive_config.clone())
123+
} else {
124+
None
125+
},
120126
});
121127

122128
Ok((client, connection))

tokio-postgres/src/connect_socket.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::config::Host;
2+
use crate::keepalive::KeepaliveConfig;
23
use crate::{Error, Socket};
34
use socket2::{SockRef, TcpKeepalive};
45
use std::future::Future;
@@ -13,8 +14,7 @@ pub(crate) async fn connect_socket(
1314
host: &Host,
1415
port: u16,
1516
connect_timeout: Option<Duration>,
16-
keepalives: bool,
17-
keepalives_idle: Duration,
17+
keepalive_config: Option<&KeepaliveConfig>,
1818
) -> Result<Socket, Error> {
1919
match host {
2020
Host::Tcp(host) => {
@@ -35,9 +35,9 @@ pub(crate) async fn connect_socket(
3535
};
3636

3737
stream.set_nodelay(true).map_err(Error::connect)?;
38-
if keepalives {
38+
if let Some(keepalive_config) = keepalive_config {
3939
SockRef::from(&stream)
40-
.set_tcp_keepalive(&TcpKeepalive::new().with_time(keepalives_idle))
40+
.set_tcp_keepalive(&TcpKeepalive::from(keepalive_config))
4141
.map_err(Error::connect)?;
4242
}
4343

tokio-postgres/src/keepalive.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
use socket2::TcpKeepalive;
2+
use std::time::Duration;
3+
4+
#[derive(Clone, PartialEq, Eq)]
5+
pub(crate) struct KeepaliveConfig {
6+
pub idle: Duration,
7+
#[cfg(not(any(target_os = "redox", target_os = "solaris")))]
8+
pub interval: Option<Duration>,
9+
#[cfg(not(any(target_os = "redox", target_os = "solaris", target_os = "windows")))]
10+
pub retries: Option<u32>,
11+
}
12+
13+
impl From<&KeepaliveConfig> for TcpKeepalive {
14+
fn from(keepalive_config: &KeepaliveConfig) -> Self {
15+
let mut tcp_keepalive = Self::new().with_time(keepalive_config.idle);
16+
17+
#[cfg(not(any(target_os = "redox", target_os = "solaris")))]
18+
if let Some(interval) = keepalive_config.interval {
19+
tcp_keepalive = tcp_keepalive.with_interval(interval);
20+
}
21+
22+
#[cfg(not(any(target_os = "redox", target_os = "solaris", target_os = "windows")))]
23+
if let Some(retries) = keepalive_config.retries {
24+
tcp_keepalive = tcp_keepalive.with_retries(retries);
25+
}
26+
27+
tcp_keepalive
28+
}
29+
}

tokio-postgres/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ mod copy_in;
163163
mod copy_out;
164164
pub mod error;
165165
mod generic_client;
166+
mod keepalive;
166167
mod maybe_tls_stream;
167168
mod portal;
168169
mod prepare;

tokio-postgres/tests/test/parse.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,19 @@ fn settings() {
3636
);
3737
}
3838

39+
#[test]
40+
#[cfg(not(any(target_os = "redox", target_os = "solaris", target_os = "windows")))]
41+
fn keepalive_settings() {
42+
check(
43+
"keepalives=1 keepalives_idle=15 keepalives_interval=5 keepalives_retries=9",
44+
Config::new()
45+
.keepalives(true)
46+
.keepalives_idle(Duration::from_secs(15))
47+
.keepalives_interval(Duration::from_secs(5))
48+
.keepalives_retries(9),
49+
);
50+
}
51+
3952
#[test]
4053
fn url() {
4154
check("postgresql://", &Config::new());

0 commit comments

Comments
 (0)