1//! Keep-alive pings on the backend's connection (contract §2): they go 2//! out while a scan runs, only then, and a ping left unanswered closes 3//! the connection, so the request on it is retried on a new one. 4 5use std::sync::atomic::{AtomicUsize, Ordering}; 6use std::time::Duration; 7 8use support::mock_jev::{MockJev, Reply}; 9use support::{jev_instance_with, noul}; 10 11const SELECT: &str = "SELECT jev_prob(t, 'Urgent?') FROM tickets t"; 12 13#[tokio::test(flavor = "multi_thread")] 14async fn pings_go_out_while_a_scan_waits_and_stop_after_it() { 15 let mock = MockJev::start(|_| Reply::json(200, noul(0.95)).after(Duration::from_secs(1))).await; 16 let (_pg, client) = jev_instance_with( 17 &mock, 18 &[("jev.keepalive_interval", "100ms"), ("jev.keepalive_timeout", "5s")], 19 ) 20 .await; 21 22 client.query_one(SELECT, &[]).await.expect("answered"); 23 // A second of silence from the server at a 100 ms interval. 24 let during = mock.pings(); 25 assert!(during >= 5, "{during} pings while the answer stalled"); 26 27 // Nothing runs the executor between statements, so nothing pings. 28 tokio::time::sleep(Duration::from_millis(100)).await; 29 let after = mock.pings(); 30 tokio::time::sleep(Duration::from_millis(500)).await; 31 assert_eq!(mock.pings(), after, "no pings between statements"); 32 assert_eq!(mock.connections(), 1); 33} 34 35#[tokio::test(flavor = "multi_thread")] 36async fn an_unanswered_ping_closes_the_connection_and_the_request_is_retried() { 37 let calls = AtomicUsize::new(0); 38 let mock = MockJev::start(move |_| match calls.fetch_add(1, Ordering::SeqCst) { 39 0 => Reply::json(200, noul(0.10)).after(Duration::from_secs(30)), 40 _ => Reply::json(200, noul(0.95)), 41 }) 42 .await; 43 mock.ignore_pings_on_next(1); 44 let (_pg, client) = jev_instance_with( 45 &mock, 46 &[("jev.keepalive_interval", "100ms"), ("jev.keepalive_timeout", "200ms")], 47 ) 48 .await; 49 50 let row = client.query_one(SELECT, &[]).await.expect("answered on a new connection"); 51 assert_eq!(row.get::<_, f64>(0), 0.95); 52 53 // The 10 s attempt timeout would retry on the same connection; only 54 // the keep-alive closes it. 55 assert_eq!(mock.connections(), 2, "the silent connection was replaced"); 56 let requests = mock.requests(); 57 assert_eq!(requests.len(), 2); 58 // It may have been billed, so it is a retry, not a redial. 59 assert_eq!(requests[1].headers["x-typesafe-retry-count"], "1"); 60 assert_eq!(requests[0].body, requests[1].body); 61}