Skip to content

Commit 3f79953

Browse files
authored
refactor: use BackendPid instead of BackendKeyData where secret is not required (#1025)
Refactor the codebase by using BackendKeyData (contains a connection secret) only where the secret is required, while all other places will use BackendPid (wrapper around u32) to refer the connection or store it. The BackendKeyData becomes complex type without copy and hash and it should be cloned where needed instead and it makes visible the non-free operation of 256 bytes array copying. The performance benefits are not huge and I had to tweak the pgbench configuration with options `-t 10000 -c 100` to see the difference around the commit #2f70a7a70cc8eceefe577f994e89b5e943e0724d that added the support for the extended secret. <details> <summary>Benchmark comparisions</summary> <pre> ## commit before protocol change (1213bcc) ### 3.0 ``` progress: 1.0 s, 64284.8 tps, lat 1.077 ms stddev 1.546, 0 failed progress: 2.0 s, 103382.5 tps, lat 0.963 ms stddev 0.384, 0 failed progress: 3.0 s, 109524.8 tps, lat 0.910 ms stddev 0.354, 0 failed progress: 4.0 s, 104265.4 tps, lat 0.956 ms stddev 0.426, 0 failed progress: 5.0 s, 105109.7 tps, lat 0.948 ms stddev 0.686, 0 failed progress: 6.0 s, 106551.5 tps, lat 0.936 ms stddev 0.408, 0 failed progress: 7.0 s, 107288.2 tps, lat 0.928 ms stddev 0.458, 0 failed progress: 8.0 s, 105747.3 tps, lat 0.943 ms stddev 0.379, 0 failed progress: 9.0 s, 109564.1 tps, lat 0.910 ms stddev 0.352, 0 failed transaction type: <builtin: select only> scaling factor: 1 query mode: extended number of clients: 100 number of threads: 1 maximum number of tries: 1 number of transactions per client: 10000 number of transactions actually processed: 1000000/1000000 number of failed transactions: 0 (0.000%) latency average = 0.944 ms latency stddev = 0.579 ms initial connection time = 304.888 ms tps = 105265.939132 (without initial connection time) ``` ## commit with protocol change (2f70a7a) ### 3.0 ``` progress: 1.0 s, 58272.9 tps, lat 1.170 ms stddev 1.127, 0 failed progress: 2.0 s, 94177.8 tps, lat 1.058 ms stddev 0.427, 0 failed progress: 3.0 s, 97705.7 tps, lat 1.021 ms stddev 0.428, 0 failed progress: 4.0 s, 95059.2 tps, lat 1.049 ms stddev 0.451, 0 failed progress: 5.0 s, 95471.2 tps, lat 1.045 ms stddev 0.423, 0 failed progress: 6.0 s, 96092.2 tps, lat 1.038 ms stddev 0.431, 0 failed progress: 7.0 s, 94516.8 tps, lat 1.055 ms stddev 0.454, 0 failed progress: 8.0 s, 94852.2 tps, lat 1.052 ms stddev 0.466, 0 failed progress: 9.0 s, 93997.7 tps, lat 1.061 ms stddev 0.440, 0 failed progress: 10.0 s, 94333.2 tps, lat 1.057 ms stddev 0.427, 0 failed transaction type: <builtin: select only> scaling factor: 1 query mode: extended number of clients: 100 number of threads: 1 maximum number of tries: 1 number of transactions per client: 10000 number of transactions actually processed: 1000000/1000000 number of failed transactions: 0 (0.000%) latency average = 1.054 ms latency stddev = 0.509 ms initial connection time = 315.652 ms tps = 94268.307777 (without initial connection time) ``` ### 3.2 ``` progress: 1.0 s, 63850.8 tps, lat 1.096 ms stddev 0.569, 0 failed progress: 2.0 s, 93276.9 tps, lat 1.069 ms stddev 0.442, 0 failed progress: 3.0 s, 94956.4 tps, lat 1.051 ms stddev 0.442, 0 failed progress: 4.0 s, 92639.9 tps, lat 1.077 ms stddev 0.447, 0 failed progress: 5.0 s, 95421.7 tps, lat 1.045 ms stddev 0.437, 0 failed progress: 6.0 s, 95773.4 tps, lat 1.042 ms stddev 0.410, 0 failed progress: 7.0 s, 94532.0 tps, lat 1.055 ms stddev 0.407, 0 failed progress: 8.0 s, 91373.8 tps, lat 1.091 ms stddev 0.498, 0 failed progress: 9.0 s, 97471.6 tps, lat 1.024 ms stddev 0.442, 0 failed progress: 10.0 s, 96465.4 tps, lat 1.034 ms stddev 0.408, 0 failed transaction type: <builtin: select only> scaling factor: 1 query mode: extended number of clients: 100 number of threads: 1 maximum number of tries: 1 number of transactions per client: 10000 number of transactions actually processed: 1000000/1000000 number of failed transactions: 0 (0.000%) latency average = 1.058 ms latency stddev = 0.451 ms initial connection time = 297.277 ms tps = 94053.071327 (without initial connection time) ``` ## commit on main ### 3.0 ``` progress: 1.0 s, 63412.7 tps, lat 1.182 ms stddev 1.107, 0 failed progress: 2.0 s, 95552.5 tps, lat 1.043 ms stddev 0.432, 0 failed progress: 3.0 s, 95806.5 tps, lat 1.042 ms stddev 0.409, 0 failed progress: 4.0 s, 89794.0 tps, lat 1.111 ms stddev 0.474, 0 failed progress: 5.0 s, 96405.1 tps, lat 1.035 ms stddev 0.430, 0 failed progress: 6.0 s, 95296.9 tps, lat 1.047 ms stddev 0.462, 0 failed progress: 7.0 s, 90622.2 tps, lat 1.101 ms stddev 0.477, 0 failed progress: 8.0 s, 95969.6 tps, lat 1.039 ms stddev 0.421, 0 failed progress: 9.0 s, 98236.7 tps, lat 1.016 ms stddev 0.403, 0 failed progress: 10.0 s, 91498.0 tps, lat 1.090 ms stddev 0.505, 0 failed transaction type: <builtin: select only> scaling factor: 1 query mode: extended number of clients: 100 number of threads: 1 maximum number of tries: 1 number of transactions per client: 10000 number of transactions actually processed: 1000000/1000000 number of failed transactions: 0 (0.000%) latency average = 1.062 ms latency stddev = 0.515 ms initial connection time = 247.252 ms tps = 93552.326857 (without initial connection time) ``` ### 3.2 ``` progress: 1.0 s, 74672.9 tps, lat 1.046 ms stddev 0.450, 0 failed progress: 2.0 s, 92049.0 tps, lat 1.083 ms stddev 0.483, 0 failed progress: 3.0 s, 94283.1 tps, lat 1.058 ms stddev 0.479, 0 failed progress: 4.0 s, 97674.7 tps, lat 1.022 ms stddev 0.411, 0 failed progress: 5.0 s, 94013.9 tps, lat 1.061 ms stddev 0.458, 0 failed progress: 6.0 s, 97004.8 tps, lat 1.028 ms stddev 0.434, 0 failed progress: 7.0 s, 94271.1 tps, lat 1.059 ms stddev 0.438, 0 failed progress: 8.0 s, 92420.1 tps, lat 1.079 ms stddev 0.473, 0 failed progress: 9.0 s, 96882.3 tps, lat 1.030 ms stddev 0.455, 0 failed progress: 10.0 s, 95606.0 tps, lat 1.044 ms stddev 0.467, 0 failed transaction type: <builtin: select only> scaling factor: 1 query mode: extended number of clients: 100 number of threads: 1 maximum number of tries: 1 number of transactions per client: 10000 number of transactions actually processed: 1000000/1000000 number of failed transactions: 0 (0.000%) latency average = 1.051 ms latency stddev = 0.452 ms initial connection time = 214.759 ms tps = 94557.837152 (without initial connection time) ``` ## refactor ### 3.0 ``` progress: 1.0 s, 70670.2 tps, lat 1.048 ms stddev 1.346, 0 failed progress: 2.0 s, 107099.5 tps, lat 0.930 ms stddev 0.382, 0 failed progress: 3.0 s, 107923.4 tps, lat 0.924 ms stddev 0.388, 0 failed progress: 4.0 s, 103806.0 tps, lat 0.960 ms stddev 0.444, 0 failed progress: 5.0 s, 107500.9 tps, lat 0.927 ms stddev 0.349, 0 failed progress: 6.0 s, 110055.2 tps, lat 0.906 ms stddev 0.377, 0 failed progress: 7.0 s, 101593.1 tps, lat 0.981 ms stddev 0.424, 0 failed progress: 8.0 s, 108966.8 tps, lat 0.915 ms stddev 0.358, 0 failed progress: 9.0 s, 109519.1 tps, lat 0.911 ms stddev 0.411, 0 failed transaction type: <builtin: select only> scaling factor: 1 query mode: extended number of clients: 100 number of threads: 1 maximum number of tries: 1 number of transactions per client: 10000 number of transactions actually processed: 1000000/1000000 number of failed transactions: 0 (0.000%) latency average = 0.941 ms latency stddev = 0.527 ms initial connection time = 256.664 ms tps = 105492.454072 (without initial connection time) ``` ### 3.2 ``` progress: 1.0 s, 85192.0 tps, lat 0.922 ms stddev 0.411, 0 failed progress: 2.0 s, 107580.1 tps, lat 0.926 ms stddev 0.390, 0 failed progress: 3.0 s, 103631.8 tps, lat 0.962 ms stddev 0.414, 0 failed progress: 4.0 s, 108258.3 tps, lat 0.921 ms stddev 0.412, 0 failed progress: 5.0 s, 106677.5 tps, lat 0.934 ms stddev 0.350, 0 failed progress: 6.0 s, 105955.7 tps, lat 0.941 ms stddev 0.420, 0 failed progress: 7.0 s, 103849.4 tps, lat 0.960 ms stddev 0.396, 0 failed progress: 8.0 s, 109807.7 tps, lat 0.908 ms stddev 0.387, 0 failed progress: 9.0 s, 105221.1 tps, lat 0.947 ms stddev 0.427, 0 failed transaction type: <builtin: select only> scaling factor: 1 query mode: extended number of clients: 100 number of threads: 1 maximum number of tries: 1 number of transactions per client: 10000 number of transactions actually processed: 1000000/1000000 number of failed transactions: 0 (0.000%) latency average = 0.934 ms latency stddev = 0.402 ms initial connection time = 210.913 ms tps = 106376.709288 (without initial connection time) ``` ## refactor taken.rs ### 3.0 ``` progress: 1.0 s, 73139.4 tps, lat 1.026 ms stddev 1.544, 0 failed progress: 2.0 s, 106600.8 tps, lat 0.935 ms stddev 0.380, 0 failed progress: 3.0 s, 107713.6 tps, lat 0.925 ms stddev 0.406, 0 failed progress: 4.0 s, 105902.3 tps, lat 0.941 ms stddev 0.394, 0 failed progress: 5.0 s, 110257.9 tps, lat 0.905 ms stddev 0.361, 0 failed progress: 6.0 s, 104843.7 tps, lat 0.950 ms stddev 0.438, 0 failed progress: 7.0 s, 110620.3 tps, lat 0.901 ms stddev 0.372, 0 failed progress: 8.0 s, 109629.6 tps, lat 0.909 ms stddev 0.395, 0 failed progress: 9.0 s, 108185.1 tps, lat 0.922 ms stddev 0.402, 0 failed transaction type: <builtin: select only> scaling factor: 1 query mode: extended number of clients: 100 number of threads: 1 maximum number of tries: 1 number of transactions per client: 10000 number of transactions actually processed: 1000000/1000000 number of failed transactions: 0 (0.000%) latency average = 0.931 ms latency stddev = 0.568 ms initial connection time = 246.663 ms tps = 106630.076911 (without initial connection time) ``` ### 3.2 ``` progress: 1.0 s, 78327.4 tps, lat 0.989 ms stddev 0.422, 0 failed progress: 2.0 s, 111089.4 tps, lat 0.897 ms stddev 0.368, 0 failed progress: 3.0 s, 110143.4 tps, lat 0.905 ms stddev 0.384, 0 failed progress: 4.0 s, 104486.5 tps, lat 0.954 ms stddev 0.431, 0 failed progress: 5.0 s, 111117.3 tps, lat 0.897 ms stddev 0.370, 0 failed progress: 6.0 s, 110026.7 tps, lat 0.906 ms stddev 0.427, 0 failed progress: 7.0 s, 106161.7 tps, lat 0.939 ms stddev 0.424, 0 failed progress: 8.0 s, 109794.5 tps, lat 0.908 ms stddev 0.333, 0 failed progress: 9.0 s, 107956.2 tps, lat 0.923 ms stddev 0.391, 0 failed transaction type: <builtin: select only> scaling factor: 1 query mode: extended number of clients: 100 number of threads: 1 maximum number of tries: 1 number of transactions per client: 10000 number of transactions actually processed: 1000000/1000000 number of failed transactions: 0 (0.000%) latency average = 0.924 ms latency stddev = 0.398 ms initial connection time = 221.501 ms tps = 107541.255245 (without initial connection time) ``` ## use small vec ### 3.0 ``` progress: 1.0 s, 75604.0 tps, lat 0.992 ms stddev 0.829, 0 failed progress: 2.0 s, 105667.3 tps, lat 0.943 ms stddev 0.397, 0 failed progress: 3.0 s, 109196.7 tps, lat 0.913 ms stddev 0.390, 0 failed progress: 4.0 s, 105584.7 tps, lat 0.944 ms stddev 0.370, 0 failed progress: 5.0 s, 106206.0 tps, lat 0.939 ms stddev 0.441, 0 failed progress: 6.0 s, 108435.9 tps, lat 0.919 ms stddev 0.386, 0 failed progress: 7.0 s, 107940.5 tps, lat 0.924 ms stddev 0.434, 0 failed progress: 8.0 s, 104209.7 tps, lat 0.957 ms stddev 0.413, 0 failed progress: 9.0 s, 108662.8 tps, lat 0.917 ms stddev 0.385, 0 failed transaction type: <builtin: select only> scaling factor: 1 query mode: extended number of clients: 100 number of threads: 1 maximum number of tries: 1 number of transactions per client: 10000 number of transactions actually processed: 1000000/1000000 number of failed transactions: 0 (0.000%) latency average = 0.935 ms latency stddev = 0.451 ms initial connection time = 245.926 ms tps = 106242.580284 (without initial connection time) ``` ### 3.2 ``` progress: 1.0 s, 80461.8 tps, lat 0.971 ms stddev 0.388, 0 failed progress: 2.0 s, 107152.2 tps, lat 0.930 ms stddev 0.441, 0 failed progress: 3.0 s, 103553.7 tps, lat 0.963 ms stddev 0.430, 0 failed progress: 4.0 s, 113189.0 tps, lat 0.881 ms stddev 0.372, 0 failed progress: 5.0 s, 108662.4 tps, lat 0.918 ms stddev 0.412, 0 failed progress: 6.0 s, 104669.5 tps, lat 0.952 ms stddev 0.423, 0 failed progress: 7.0 s, 106442.4 tps, lat 0.937 ms stddev 0.398, 0 failed progress: 8.0 s, 106510.3 tps, lat 0.936 ms stddev 0.390, 0 failed progress: 9.0 s, 102740.4 tps, lat 0.970 ms stddev 0.451, 0 failed transaction type: <builtin: select only> scaling factor: 1 query mode: extended number of clients: 100 number of threads: 1 maximum number of tries: 1 number of transactions per client: 10000 number of transactions actually processed: 1000000/1000000 number of failed transactions: 0 (0.000%) latency average = 0.936 ms latency stddev = 0.409 ms initial connection time = 215.410 ms tps = 106187.514621 (without initial connection time) ``` </pre> </details> The performance difference is there. Integration test for cancellation has been added and some around refactoring have done.
1 parent f016ce9 commit 3f79953

48 files changed

Lines changed: 1371 additions & 480 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/CLIENT_CONNECTION.md

Lines changed: 375 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
use std::time::Duration;
2+
3+
use bytes::{BufMut, BytesMut};
4+
use rust::setup::{admin_tokio, connection_sqlx_direct};
5+
use sqlx::PgPool;
6+
use tokio::{io::AsyncWriteExt, net::TcpStream, task::JoinHandle, time::timeout};
7+
use tokio_postgres::{CancelToken, Error as PgError, NoTls, SimpleQueryMessage};
8+
9+
/// Returns whether `pid` has an active `pg_sleep` query visible in `pg_stat_activity`.
10+
/// Uses a direct PostgreSQL connection so the result bypasses pgdog completely.
11+
async fn is_sleeping(direct: &PgPool, pid: i32) -> bool {
12+
let count: i64 = sqlx::query_scalar(
13+
"SELECT COUNT(*) \
14+
FROM pg_stat_activity \
15+
WHERE pid = $1 \
16+
AND state = 'active' \
17+
AND query LIKE '%pg_sleep%'",
18+
)
19+
.bind(pid)
20+
.fetch_one(direct)
21+
.await
22+
.unwrap();
23+
count == 1
24+
}
25+
26+
/// Connect to pgdog, pin to a specific PG backend via BEGIN, capture the backend pid
27+
/// via `pg_backend_pid()`, and launch `SELECT pg_sleep(60)` in a background task.
28+
///
29+
/// `application_name` is embedded in the connection string so the caller can identify
30+
/// this connection in `SHOW CLIENTS` if needed.
31+
///
32+
/// Returns `(backend_pid, cancel_token, query_handle)`. The caller owns `cancel_token`
33+
/// and `query_handle`; both must be driven to completion to keep the test clean.
34+
async fn start_sleeping_connection(
35+
application_name: &str,
36+
) -> (
37+
i32,
38+
CancelToken,
39+
JoinHandle<Result<Vec<SimpleQueryMessage>, PgError>>,
40+
) {
41+
let (client, connection) = tokio_postgres::connect(
42+
&format!(
43+
"host=127.0.0.1 user=pgdog dbname=pgdog password=pgdog port=6432 application_name={application_name}"
44+
),
45+
NoTls,
46+
)
47+
.await
48+
.unwrap();
49+
50+
tokio::spawn(async move {
51+
if let Err(e) = connection.await {
52+
eprintln!("pgdog connection error: {}", e);
53+
}
54+
});
55+
56+
let cancel_token = client.cancel_token();
57+
58+
// BEGIN pins the client to one backend for the duration of the transaction.
59+
// Without this, transaction-mode pooling may assign a different backend to
60+
// pg_sleep than the one whose pid we captured.
61+
client.simple_query("BEGIN").await.unwrap();
62+
63+
let row = client
64+
.query_one("SELECT pg_backend_pid()", &[])
65+
.await
66+
.unwrap();
67+
let backend_pid: i32 = row.get(0);
68+
69+
let handle = tokio::spawn(async move { client.simple_query("SELECT pg_sleep(60)").await });
70+
71+
(backend_pid, cancel_token, handle)
72+
}
73+
74+
/// Assert that a query handle returned by `start_sleeping_connection` was cancelled:
75+
/// it must resolve to SQLSTATE 57014 (canceling statement due to user request).
76+
async fn assert_cancelled(
77+
handle: JoinHandle<Result<Vec<SimpleQueryMessage>, PgError>>,
78+
label: &str,
79+
) {
80+
let result = timeout(Duration::from_secs(5), handle)
81+
.await
82+
.expect(&format!(
83+
"{label}: cancelled query did not unblock within 5 seconds"
84+
))
85+
.expect(&format!("{label}: task panicked"));
86+
87+
let err = result.expect_err(&format!(
88+
"{label}: query should have been cancelled, but it succeeded"
89+
));
90+
let db_err = err.as_db_error().expect(&format!(
91+
"{label}: expected a PostgreSQL error, not a network error"
92+
));
93+
94+
assert_eq!(
95+
db_err.code().code(),
96+
"57014",
97+
"{label}: expected SQLSTATE 57014, got {}",
98+
db_err.code().code()
99+
);
100+
}
101+
102+
/// Verify that cancellation is precise: two independent connections both run a long
103+
/// query and each cancel request stops exactly one of them.
104+
///
105+
/// Steps:
106+
/// 1. Two clients connect through pgdog; each starts `SELECT pg_sleep(60)`.
107+
/// 2. Both queries are confirmed active on specific PG backends via `pg_stat_activity`.
108+
/// 3. Cancel connection 1 → only backend 1 stops; backend 2 remains active.
109+
/// 4. Cancel connection 2 → backend 2 stops.
110+
#[tokio::test]
111+
async fn test_cancel_query() {
112+
let direct = connection_sqlx_direct().await;
113+
114+
let (pid1, token1, handle1) = start_sleeping_connection("cancel_test").await;
115+
let (pid2, token2, handle2) = start_sleeping_connection("cancel_test").await;
116+
117+
// Give both queries time to reach their respective backends.
118+
tokio::time::sleep(Duration::from_millis(300)).await;
119+
120+
assert!(
121+
is_sleeping(&direct, pid1).await,
122+
"connection 1 (backend {pid1}) should be active before any cancel"
123+
);
124+
assert!(
125+
is_sleeping(&direct, pid2).await,
126+
"connection 2 (backend {pid2}) should be active before any cancel"
127+
);
128+
129+
// ── Cancel connection 1 ────────────────────────────────────────────────
130+
token1.cancel_query(NoTls).await.unwrap();
131+
132+
// Wait for the client to receive the cancellation error.
133+
// By the time the handle resolves, the backend has already stopped.
134+
assert_cancelled(handle1, "connection 1").await;
135+
136+
// Connection 1's backend is gone; connection 2 must still be running.
137+
tokio::time::sleep(Duration::from_millis(100)).await;
138+
assert!(
139+
!is_sleeping(&direct, pid1).await,
140+
"backend {pid1} should be idle after cancelling connection 1"
141+
);
142+
assert!(
143+
is_sleeping(&direct, pid2).await,
144+
"backend {pid2} should still be active after cancelling connection 1 only"
145+
);
146+
147+
// ── Cancel connection 2 ────────────────────────────────────────────────
148+
token2.cancel_query(NoTls).await.unwrap();
149+
150+
assert_cancelled(handle2, "connection 2").await;
151+
152+
tokio::time::sleep(Duration::from_millis(100)).await;
153+
assert!(
154+
!is_sleeping(&direct, pid2).await,
155+
"backend {pid2} should be idle after cancelling connection 2"
156+
);
157+
}
158+
159+
/// Verify that a cancel request carrying a wrong pid and secret is silently rejected:
160+
/// the running query is unaffected and the client does not receive a cancellation error.
161+
///
162+
/// pgdog's `verify_cancel` gate must reject the request before it reaches the pool,
163+
/// so the backend continues executing as if nothing happened.
164+
#[tokio::test]
165+
async fn test_cancel_query_wrong_secret() {
166+
let direct = connection_sqlx_direct().await;
167+
let app_name = "cancel_test_wrong_secret";
168+
let (backend_pid, real_cancel_token, query_handle) = start_sleeping_connection(app_name).await;
169+
170+
// Give the query time to reach the backend.
171+
tokio::time::sleep(Duration::from_millis(300)).await;
172+
173+
assert!(
174+
is_sleeping(&direct, backend_pid).await,
175+
"query should be running before wrong-secret cancel"
176+
);
177+
178+
// Look up the pgdog client pid from the admin interface.
179+
// SHOW CLIENTS exposes the pid (the 'id' column) that pgdog assigned during login —
180+
// the same value that was sent in the K message and that verify_cancel checks against.
181+
let admin = admin_tokio().await;
182+
let messages = admin.simple_query("SHOW CLIENTS").await.unwrap();
183+
let pgdog_pid: i32 = messages
184+
.iter()
185+
.filter_map(|m| match m {
186+
SimpleQueryMessage::Row(row) => Some(row),
187+
_ => None,
188+
})
189+
.find(|row| row.get("application_name") == Some(app_name))
190+
.expect("connection should appear in SHOW CLIENTS")
191+
.get("id")
192+
.expect("id column should be present")
193+
.parse()
194+
.expect("id should be a valid i32");
195+
196+
// Send a CancelRequest with the real pgdog client pid but a wrong secret.
197+
// pgdog will find the client in comms by pid, then reject it because
198+
// the secret doesn't match — verify_cancel returns false.
199+
let mut raw = TcpStream::connect("127.0.0.1:6432").await.unwrap();
200+
let mut buf = BytesMut::new();
201+
buf.put_i32(16); // total message length (including the length field)
202+
buf.put_i32(80877102); // CancelRequest magic code
203+
buf.put_i32(pgdog_pid); // correct pid
204+
buf.put_i32(0); // wrong secret
205+
raw.write_all(&buf).await.unwrap();
206+
// pgdog closes the connection silently after processing; no response is sent.
207+
drop(raw);
208+
209+
// Give pgdog enough time to receive and process the bogus cancel.
210+
tokio::time::sleep(Duration::from_millis(300)).await;
211+
212+
// The query must still be running — the secret mismatch was caught by verify_cancel.
213+
assert!(
214+
is_sleeping(&direct, backend_pid).await,
215+
"query should still be running after wrong-secret cancel — verify_cancel must have rejected it"
216+
);
217+
218+
// Clean up: cancel for real.
219+
real_cancel_token.cancel_query(NoTls).await.unwrap();
220+
assert_cancelled(query_handle, "wrong-secret test cleanup").await;
221+
}

integration/rust/tests/integration/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ pub mod auth;
33
pub mod auto_id;
44
pub mod avg;
55
pub mod ban;
6+
pub mod cancel;
67
pub mod client_ids;
78
pub mod connection_recovery;
89
pub mod cross_shard_disabled;

pgdog-postgres-types/src/interface.rs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,19 +48,22 @@ impl ToDataRowColumn for i64 {
4848
}
4949
}
5050

51-
impl ToDataRowColumn for Option<i64> {
51+
impl ToDataRowColumn for i32 {
5252
fn to_data_row_column(&self) -> Data {
53-
match self {
54-
Some(value) => ToDataRowColumn::to_data_row_column(value),
55-
None => Data::null(),
56-
}
53+
Bytes::copy_from_slice(self.to_string().as_bytes()).into()
54+
}
55+
}
56+
57+
impl ToDataRowColumn for u32 {
58+
fn to_data_row_column(&self) -> Data {
59+
Bytes::copy_from_slice(self.to_string().as_bytes()).into()
5760
}
5861
}
5962

60-
impl ToDataRowColumn for Option<String> {
63+
impl<T: ToDataRowColumn> ToDataRowColumn for Option<T> {
6164
fn to_data_row_column(&self) -> Data {
6265
match self {
63-
Some(value) => ToDataRowColumn::to_data_row_column(value),
66+
Some(value) => value.to_data_row_column(),
6467
None => Data::null(),
6568
}
6669
}

pgdog/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ azure_identity = "0.34.0"
7777
azure_core = "0.34.0"
7878
crc32c = "0.6.8"
7979
bit-vec = "0.8"
80+
smallvec = "1"
8081
reqwest = { version = "0.12", default-features = false, features = ["rustls-tls-webpki-roots-no-provider"] }
8182
hex = "0.4"
8283
x509-parser = "0.18"

pgdog/src/admin/server.rs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ use tracing::debug;
99
use crate::frontend::ClientRequest;
1010
use crate::net::messages::command_complete::CommandComplete;
1111
use crate::net::messages::{ErrorResponse, FromBytes, Protocol, Query, ReadyForQuery};
12+
use crate::net::ProtocolMessage;
1213
use crate::net::ToBytes;
13-
use crate::net::{BackendKeyData, ProtocolMessage};
1414

1515
use super::parser::Parser;
1616
use super::prelude::Message;
@@ -63,11 +63,6 @@ impl AdminServer {
6363
self.messages.extend(messages);
6464
self.messages.push_back(ReadyForQuery::idle().message()?);
6565

66-
self.messages = std::mem::take(&mut self.messages)
67-
.into_iter()
68-
.map(|m| m.backend(BackendKeyData::default()))
69-
.collect();
70-
7166
Ok(())
7267
}
7368

pgdog/src/admin/show_client_memory.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ impl Command for ShowClientMemory {
4242
let user = client.paramters.get_default("user", "postgres");
4343
let database = client.paramters.get_default("database", user);
4444

45-
row.add(client.id.pid as i64)
45+
row.add(client.key.pid())
4646
.add(database)
4747
.add(user)
4848
.add(client.addr.ip().to_string().as_str())

pgdog/src/admin/show_clients.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ impl Command for ShowClients {
7777
let row = self
7878
.filter
7979
.clone()
80-
.add("id", client.id.pid as i64)
80+
.add("id", client.key.pid())
8181
.add("user", user)
8282
.add("database", client.paramters.get_default("database", user))
8383
.add("addr", client.addr.ip().to_string())

pgdog/src/admin/show_server_memory.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ impl Command for ShowServerMemory {
3636
let mut messages = vec![rd.message()?];
3737

3838
let stats = stats();
39-
for (_, server) in stats {
39+
for server in stats {
4040
let mut row = DataRow::new();
4141
let memory = &server.stats.memory;
4242

@@ -45,7 +45,7 @@ impl Command for ShowServerMemory {
4545
.add(server.addr.user.as_str())
4646
.add(server.addr.host.as_str())
4747
.add(server.addr.port as i64)
48-
.add(server.stats.id.pid as i64)
48+
.add(server.stats.id)
4949
.add(memory.buffer.reallocs as i64)
5050
.add(memory.buffer.reclaims as i64)
5151
.add(memory.buffer.bytes_used as i64)

0 commit comments

Comments
 (0)