Skip to content

Commit b3fdf16

Browse files
committed
Add a warning about transaction semantics
1 parent 5e998f6 commit b3fdf16

File tree

2 files changed

+40
-19
lines changed

2 files changed

+40
-19
lines changed

tokio-postgres/src/lib.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,25 @@ impl Client {
248248
BatchExecute(self.0.batch_execute(query))
249249
}
250250

251-
pub fn transaction(&mut self) -> TransactionBuilder {
251+
/// A utility method to wrap a future in a database transaction.
252+
///
253+
/// The returned future will start a transaction and then run the provided future. If the future returns `Ok`, it
254+
/// will commit the transaction, and if it returns `Err`, it will roll the transaction back.
255+
///
256+
/// This is simply a convenience API; it's roughly equivalent to:
257+
///
258+
/// ```ignore
259+
/// client.batch_execute("BEGIN")
260+
/// .and_then(your_future)
261+
/// .and_then(client.batch_execute("COMMIT"))
262+
/// .or_else(|e| client.batch_execute("ROLLBACK").then(|_| Err(e)))
263+
/// ```
264+
///
265+
/// # Warning
266+
///
267+
/// Unlike the other futures created by a client, this future is *not* atomic with respect to other requests. If you
268+
/// attempt to execute it concurrently with other futures created by the same connection, they will interleave!
269+
pub fn build_transaction(&mut self) -> TransactionBuilder {
252270
TransactionBuilder(self.0.clone())
253271
}
254272

@@ -556,6 +574,7 @@ impl Stream for CopyOut {
556574
}
557575
}
558576

577+
/// A builder type which can wrap a future in a database transaction.
559578
pub struct TransactionBuilder(proto::Client);
560579

561580
impl TransactionBuilder {

tokio-postgres/tests/test/main.rs

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -478,7 +478,9 @@ fn transaction_commit() {
478478
.unwrap();
479479

480480
let f = client.batch_execute("INSERT INTO foo (name) VALUES ('steven')");
481-
runtime.block_on(client.transaction().build(f)).unwrap();
481+
runtime
482+
.block_on(client.build_transaction().build(f))
483+
.unwrap();
482484

483485
let rows = runtime
484486
.block_on(
@@ -514,7 +516,9 @@ fn transaction_abort() {
514516
.batch_execute("INSERT INTO foo (name) VALUES ('steven')")
515517
.map_err(|e| Box::new(e) as Box<dyn Error>)
516518
.and_then(|_| Err::<(), _>(Box::<dyn Error>::from("")));
517-
runtime.block_on(client.transaction().build(f)).unwrap_err();
519+
runtime
520+
.block_on(client.build_transaction().build(f))
521+
.unwrap_err();
518522

519523
let rows = runtime
520524
.block_on(
@@ -647,27 +651,25 @@ fn transaction_builder_around_moved_client() {
647651
let connection = connection.map_err(|e| panic!("{}", e));
648652
runtime.handle().spawn(connection).unwrap();
649653

650-
let transaction_builder = client.transaction();
651-
let work = future::lazy(move || {
652-
let execute = client.batch_execute(
654+
let transaction_builder = client.build_transaction();
655+
let work = client
656+
.batch_execute(
653657
"CREATE TEMPORARY TABLE transaction_foo (
654-
id SERIAL,
655-
name TEXT
656-
)",
657-
);
658-
659-
execute.and_then(move |_| {
658+
id SERIAL,
659+
name TEXT
660+
)",
661+
)
662+
.and_then(move |_| {
660663
client
661664
.prepare("INSERT INTO transaction_foo (name) VALUES ($1), ($2)")
662665
.map(|statement| (client, statement))
663666
})
664-
})
665-
.and_then(|(mut client, statement)| {
666-
client
667-
.query(&statement, &[&"jim", &"joe"])
668-
.collect()
669-
.map(|_res| client)
670-
});
667+
.and_then(|(mut client, statement)| {
668+
client
669+
.query(&statement, &[&"jim", &"joe"])
670+
.collect()
671+
.map(|_res| client)
672+
});
671673

672674
let transaction = transaction_builder.build(work);
673675
let mut client = runtime.block_on(transaction).unwrap();

0 commit comments

Comments
 (0)