Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit be2ca03

Browse files
committed
tokio-postgres query cancellation
1 parent c2fb9c6 commit be2ca03

File tree

5 files changed

+151
-2
lines changed

5 files changed

+151
-2
lines changed

tokio-postgres/src/lib.rs

+16
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ pub enum TlsMode {
6363
Require(Box<TlsConnect>),
6464
}
6565

66+
pub fn cancel_query(params: ConnectParams, tls: TlsMode, cancel_data: CancelData) -> CancelQuery {
67+
CancelQuery(proto::CancelFuture::new(params, tls, cancel_data))
68+
}
69+
6670
pub fn connect(params: ConnectParams, tls: TlsMode) -> Handshake {
6771
Handshake(proto::HandshakeFuture::new(params, tls))
6872
}
@@ -110,6 +114,18 @@ impl Future for Connection {
110114
}
111115
}
112116

117+
#[must_use = "futures do nothing unless polled"]
118+
pub struct CancelQuery(proto::CancelFuture);
119+
120+
impl Future for CancelQuery {
121+
type Item = ();
122+
type Error = Error;
123+
124+
fn poll(&mut self) -> Poll<(), Error> {
125+
self.0.poll()
126+
}
127+
}
128+
113129
#[must_use = "futures do nothing unless polled"]
114130
pub struct Handshake(proto::HandshakeFuture);
115131

tokio-postgres/src/proto/cancel.rs

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
use futures::{Future, Poll};
2+
use postgres_protocol::message::frontend;
3+
use state_machine_future::RentToOwn;
4+
use tokio_io::io::{self, Flush, WriteAll};
5+
6+
use error::Error;
7+
use params::ConnectParams;
8+
use proto::connect::ConnectFuture;
9+
use tls::TlsStream;
10+
use {CancelData, TlsMode};
11+
12+
#[derive(StateMachineFuture)]
13+
pub enum Cancel {
14+
#[state_machine_future(start, transitions(SendingCancel))]
15+
Start {
16+
future: ConnectFuture,
17+
cancel_data: CancelData,
18+
},
19+
#[state_machine_future(transitions(FlushingCancel))]
20+
SendingCancel {
21+
future: WriteAll<Box<TlsStream>, Vec<u8>>,
22+
},
23+
#[state_machine_future(transitions(Finished))]
24+
FlushingCancel { future: Flush<Box<TlsStream>> },
25+
#[state_machine_future(ready)]
26+
Finished(()),
27+
#[state_machine_future(error)]
28+
Failed(Error),
29+
}
30+
31+
impl PollCancel for Cancel {
32+
fn poll_start<'a>(state: &'a mut RentToOwn<'a, Start>) -> Poll<AfterStart, Error> {
33+
let stream = try_ready!(state.future.poll());
34+
35+
let mut buf = vec![];
36+
frontend::cancel_request(
37+
state.cancel_data.process_id,
38+
state.cancel_data.secret_key,
39+
&mut buf,
40+
);
41+
42+
transition!(SendingCancel {
43+
future: io::write_all(stream, buf),
44+
})
45+
}
46+
47+
fn poll_sending_cancel<'a>(
48+
state: &'a mut RentToOwn<'a, SendingCancel>,
49+
) -> Poll<AfterSendingCancel, Error> {
50+
let (stream, _) = try_ready!(state.future.poll());
51+
52+
transition!(FlushingCancel {
53+
future: io::flush(stream),
54+
})
55+
}
56+
57+
fn poll_flushing_cancel<'a>(
58+
state: &'a mut RentToOwn<'a, FlushingCancel>,
59+
) -> Poll<AfterFlushingCancel, Error> {
60+
try_ready!(state.future.poll());
61+
transition!(Finished(()))
62+
}
63+
}
64+
65+
impl CancelFuture {
66+
pub fn new(params: ConnectParams, mode: TlsMode, cancel_data: CancelData) -> CancelFuture {
67+
Cancel::start(ConnectFuture::new(params, mode), cancel_data)
68+
}
69+
}

tokio-postgres/src/proto/connect.rs

+22-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use std::io;
77
use std::net::{SocketAddr, ToSocketAddrs};
88
use std::time::{Duration, Instant};
99
use std::vec;
10-
use tokio_io::io::{read_exact, write_all, ReadExact, WriteAll};
10+
use tokio_io::io::{flush, read_exact, write_all, Flush, ReadExact, WriteAll};
1111
use tokio_tcp::{self, TcpStream};
1212
use tokio_timer::Delay;
1313

@@ -59,13 +59,20 @@ pub enum Connect {
5959
params: ConnectParams,
6060
tls: TlsMode,
6161
},
62-
#[state_machine_future(transitions(ReadingSsl))]
62+
#[state_machine_future(transitions(FlushingSsl))]
6363
SendingSsl {
6464
future: WriteAll<Socket, Vec<u8>>,
6565
params: ConnectParams,
6666
connector: Box<TlsConnect>,
6767
required: bool,
6868
},
69+
#[state_machine_future(transitions(ReadingSsl))]
70+
FlushingSsl {
71+
future: Flush<Socket>,
72+
params: ConnectParams,
73+
connector: Box<TlsConnect>,
74+
required: bool,
75+
},
6976
#[state_machine_future(transitions(ConnectingTls, Ready))]
7077
ReadingSsl {
7178
future: ReadExact<Socket, [u8; 1]>,
@@ -228,6 +235,19 @@ impl PollConnect for Connect {
228235
) -> Poll<AfterSendingSsl, Error> {
229236
let (stream, _) = try_ready!(state.future.poll());
230237
let state = state.take();
238+
transition!(FlushingSsl {
239+
future: flush(stream),
240+
params: state.params,
241+
connector: state.connector,
242+
required: state.required,
243+
})
244+
}
245+
246+
fn poll_flushing_ssl<'a>(
247+
state: &'a mut RentToOwn<'a, FlushingSsl>,
248+
) -> Poll<AfterFlushingSsl, Error> {
249+
let stream = try_ready!(state.future.poll());
250+
let state = state.take();
231251
transition!(ReadingSsl {
232252
future: read_exact(stream, [0]),
233253
params: state.params,

tokio-postgres/src/proto/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ macro_rules! try_receive {
88
};
99
}
1010

11+
mod cancel;
1112
mod client;
1213
mod codec;
1314
mod connect;
@@ -20,6 +21,7 @@ mod row;
2021
mod socket;
2122
mod statement;
2223

24+
pub use proto::cancel::CancelFuture;
2325
pub use proto::client::Client;
2426
pub use proto::codec::PostgresCodec;
2527
pub use proto::connection::Connection;

tokio-postgres/tests/test.rs

+42
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
extern crate env_logger;
2+
extern crate futures;
23
extern crate tokio;
34
extern crate tokio_postgres;
45

6+
use std::time::{Duration, Instant};
57
use tokio::prelude::*;
68
use tokio::runtime::current_thread::Runtime;
9+
use tokio::timer::Delay;
710
use tokio_postgres::error::SqlState;
811
use tokio_postgres::types::Type;
912
use tokio_postgres::TlsMode;
@@ -214,3 +217,42 @@ fn insert_select() {
214217
let tests = insert.join(select);
215218
runtime.block_on(tests).unwrap();
216219
}
220+
221+
#[test]
222+
fn cancel_query() {
223+
let _ = env_logger::try_init();
224+
let mut runtime = Runtime::new().unwrap();
225+
226+
let handshake = tokio_postgres::connect(
227+
"postgres://postgres@localhost:5433".parse().unwrap(),
228+
TlsMode::None,
229+
);
230+
let (mut client, connection) = runtime.block_on(handshake).unwrap();
231+
let cancel_data = connection.cancel_data();
232+
let connection = connection.map_err(|e| panic!("{}", e));
233+
runtime.handle().spawn(connection).unwrap();
234+
235+
let sleep = client.prepare("SELECT pg_sleep(100)");
236+
let sleep = runtime.block_on(sleep).unwrap();
237+
238+
let sleep = client.execute(&sleep, &[]).then(|r| match r {
239+
Ok(_) => panic!("unexpected success"),
240+
Err(ref e) if e.code() == Some(&SqlState::QUERY_CANCELED) => Ok::<(), ()>(()),
241+
Err(e) => panic!("unexpected error {}", e),
242+
});
243+
let cancel = Delay::new(Instant::now() + Duration::from_millis(100))
244+
.then(|r| {
245+
r.unwrap();
246+
tokio_postgres::cancel_query(
247+
"postgres://postgres@localhost:5433".parse().unwrap(),
248+
TlsMode::None,
249+
cancel_data,
250+
)
251+
})
252+
.then(|r| {
253+
r.unwrap();
254+
Ok::<(), ()>(())
255+
});
256+
257+
let ((), ()) = runtime.block_on(sleep.join(cancel)).unwrap();
258+
}

0 commit comments

Comments
 (0)