Skip to content

Commit 87316f0

Browse files
author
Julius de Bruijn
committed
Configuration to disable internal stmt cache
This enables usage with pgBouncer's transaction mode. Typically when using the transaction mode, a client gets a new connection from pgBouncer for every new transaction. It's quite useful and allows one to use prepared statements in this mode. The workflow goes: ```sql -- start a new transaction BEGIN -- deallocate all stored statements from the server to prevent -- collisions DEALLOCATE ALL -- run the queries here -- .. -- .. COMMIT -- or ROLLBACK ``` Now in a case where the query uses custom types such as enums, what tokio-postgres does is it fetches the type info for the given type, stores the info to the cache and also caches the statements for fetching the info to the client. Now when we have two tables with different custom types in both of them, we can imagine the following workflow: ```rust // first query client.simple_query("BEGIN")?; client.simple_query("DEALLOCATE ALL")?; let stmt = client.prepare("SELECT \"public\".\"User\".\"id\", \"public\".\"User\".\"userType\" FROM \"public\".\"User\" WHERE 1=1 OFFSET $1")?; dbg!(client.query(&stmt, &[&0i64])?); client.simple_query("COMMIT")?; // second query client.simple_query("BEGIN")?; client.simple_query("DEALLOCATE ALL")?; let stmt = client.prepare("SELECT \"public\".\"Work\".\"id\", \"public\".\"Work\".\"workType\" FROM \"public\".\"Work\" WHERE 1=1 OFFSET $1")?; dbg!(client.query(&stmt, &[&0i64])?); client.simple_query("COMMIT")?; ``` The `userType` and `workType` are both enums, and the preparing of the second query will give an error `prepared statement "s1" does not exist`, where `s1` is the query to the `pg_catalog` for the type info. The change here gives an extra flag for the client to disable caching of statements.
1 parent f6620e6 commit 87316f0

File tree

4 files changed

+66
-7
lines changed

4 files changed

+66
-7
lines changed

postgres/src/config.rs

+15
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,21 @@ impl Config {
307307
self.config.get_channel_binding()
308308
}
309309

310+
/// When enabled, the client skips all internal caching for statements,
311+
/// allowing usage with pgBouncer's transaction mode and clearing of
312+
/// statements in the session with `DEALLOCATE ALL`.
313+
///
314+
/// Defaults to `false`.
315+
pub fn pgbouncer_mode(&mut self, enable: bool) -> &mut Config {
316+
self.config.pgbouncer_mode(enable);
317+
self
318+
}
319+
320+
/// Gets the pgBouncer mode status.
321+
pub fn get_pgbouncer_mode(&self) -> bool {
322+
self.config.get_pgbouncer_mode()
323+
}
324+
310325
/// Opens a connection to a PostgreSQL database.
311326
pub fn connect<T>(&self, tls: T) -> Result<Client, Error>
312327
where

tokio-postgres/src/client.rs

+27-6
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ struct State {
6464
pub struct InnerClient {
6565
sender: mpsc::UnboundedSender<Request>,
6666
state: Mutex<State>,
67+
pgbouncer_mode: bool,
6768
}
6869

6970
impl InnerClient {
@@ -81,27 +82,45 @@ impl InnerClient {
8182
}
8283

8384
pub fn typeinfo(&self) -> Option<Statement> {
84-
self.state.lock().typeinfo.clone()
85+
if self.pgbouncer_mode {
86+
None
87+
} else {
88+
self.state.lock().typeinfo.clone()
89+
}
8590
}
8691

8792
pub fn set_typeinfo(&self, statement: &Statement) {
88-
self.state.lock().typeinfo = Some(statement.clone());
93+
if !self.pgbouncer_mode {
94+
self.state.lock().typeinfo = Some(statement.clone());
95+
}
8996
}
9097

9198
pub fn typeinfo_composite(&self) -> Option<Statement> {
92-
self.state.lock().typeinfo_composite.clone()
99+
if self.pgbouncer_mode {
100+
None
101+
} else {
102+
self.state.lock().typeinfo_composite.clone()
103+
}
93104
}
94105

95106
pub fn set_typeinfo_composite(&self, statement: &Statement) {
96-
self.state.lock().typeinfo_composite = Some(statement.clone());
107+
if !self.pgbouncer_mode {
108+
self.state.lock().typeinfo_composite = Some(statement.clone());
109+
}
97110
}
98111

99112
pub fn typeinfo_enum(&self) -> Option<Statement> {
100-
self.state.lock().typeinfo_enum.clone()
113+
if self.pgbouncer_mode {
114+
None
115+
} else {
116+
self.state.lock().typeinfo_enum.clone()
117+
}
101118
}
102119

103120
pub fn set_typeinfo_enum(&self, statement: &Statement) {
104-
self.state.lock().typeinfo_enum = Some(statement.clone());
121+
if !self.pgbouncer_mode {
122+
self.state.lock().typeinfo_enum = Some(statement.clone());
123+
}
105124
}
106125

107126
pub fn type_(&self, oid: Oid) -> Option<Type> {
@@ -151,6 +170,7 @@ impl Client {
151170
ssl_mode: SslMode,
152171
process_id: i32,
153172
secret_key: i32,
173+
pgbouncer_mode: bool,
154174
) -> Client {
155175
Client {
156176
inner: Arc::new(InnerClient {
@@ -162,6 +182,7 @@ impl Client {
162182
types: HashMap::new(),
163183
buf: BytesMut::new(),
164184
}),
185+
pgbouncer_mode,
165186
}),
166187
#[cfg(feature = "runtime")]
167188
socket_config: None,

tokio-postgres/src/config.rs

+17
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ pub struct Config {
159159
pub(crate) keepalives_idle: Duration,
160160
pub(crate) target_session_attrs: TargetSessionAttrs,
161161
pub(crate) channel_binding: ChannelBinding,
162+
pub(crate) pgbouncer_mode: bool,
162163
}
163164

164165
impl Default for Config {
@@ -184,6 +185,7 @@ impl Config {
184185
keepalives_idle: Duration::from_secs(2 * 60 * 60),
185186
target_session_attrs: TargetSessionAttrs::Any,
186187
channel_binding: ChannelBinding::Prefer,
188+
pgbouncer_mode: false,
187189
}
188190
}
189191

@@ -387,6 +389,21 @@ impl Config {
387389
self.channel_binding
388390
}
389391

392+
/// When enabled, the client skips all internal caching for statements,
393+
/// allowing usage with pgBouncer's transaction mode and clearing of
394+
/// statements in the session with `DEALLOCATE ALL`.
395+
///
396+
/// Defaults to `false`.
397+
pub fn pgbouncer_mode(&mut self, enable: bool) -> &mut Config {
398+
self.pgbouncer_mode = enable;
399+
self
400+
}
401+
402+
/// Gets the pgBouncer mode status.
403+
pub fn get_pgbouncer_mode(&self) -> bool {
404+
self.pgbouncer_mode
405+
}
406+
390407
fn param(&mut self, key: &str, value: &str) -> Result<(), Error> {
391408
match key {
392409
"user" => {

tokio-postgres/src/connect_raw.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,13 @@ where
100100
let (process_id, secret_key, parameters) = read_info(&mut stream).await?;
101101

102102
let (sender, receiver) = mpsc::unbounded();
103-
let client = Client::new(sender, config.ssl_mode, process_id, secret_key);
103+
let client = Client::new(
104+
sender,
105+
config.ssl_mode,
106+
process_id,
107+
secret_key,
108+
config.pgbouncer_mode,
109+
);
104110
let connection = Connection::new(stream.inner, stream.delayed, parameters, receiver);
105111

106112
Ok((client, connection))

0 commit comments

Comments
 (0)