mz_testdrive/action/postgres/
verify_slot.rs
1use std::cmp;
11use std::time::Duration;
12
13use anyhow::{Context, bail};
14use mz_ore::retry::Retry;
15
16use crate::action::{ControlFlow, State};
17use crate::parser::BuiltinCommand;
18use crate::util::postgres::postgres_client;
19
20pub async fn run_verify_slot(
21 mut cmd: BuiltinCommand,
22 state: &State,
23) -> Result<ControlFlow, anyhow::Error> {
24 let connection = cmd.args.string("connection")?;
25 let slot = cmd.args.string("slot")?;
26 let expect_active: bool = cmd.args.parse("active")?;
27 cmd.args.done()?;
28
29 let (client, conn_handle) = postgres_client(&connection, state.default_timeout).await?;
30
31 Retry::default()
32 .initial_backoff(Duration::from_millis(50))
33 .max_duration(cmp::max(state.default_timeout, Duration::from_secs(60)))
34 .retry_async_canceling(|_| async {
35 println!(">> checking for postgres replication slot {}", &slot);
36 let rows = client
37 .query(
38 "SELECT active_pid FROM pg_replication_slots WHERE slot_name LIKE $1::TEXT",
39 &[&slot],
40 )
41 .await
42 .context("querying postgres for replication slot")?;
43
44 if rows.len() != 1 {
45 bail!(
46 "expected entry for slot {} in pg_replication slots, found {}",
47 &slot,
48 rows.len()
49 );
50 }
51 let active_pid: Option<i32> = rows[0].get(0);
52 match (expect_active, active_pid) {
53 (true, None) => bail!("expected slot {slot} to be active, is inactive"),
54 (false, Some(pid)) => {
55 bail!("expected slot {slot} to be inactive, is active for pid {pid}")
56 }
57 _ => {}
58 };
59 Ok(())
60 })
61 .await?;
62
63 drop(client);
64 conn_handle
65 .await
66 .unwrap()
67 .context("postgres connection error")?;
68
69 Ok(ControlFlow::Continue)
70}