Skip to content

Commit 4cc7ffd

Browse files
committed
fix(integration): use relative threshold in slow-connection contention test
The absolute 0.5s threshold failed in CI where 10 encrypted inserts take ~1.0s baseline. Replace with solo baseline measurement and relative assertion (contention_ratio < 2.0), matching the approach used by the other two passing contention tests.
1 parent c39464e commit 4cc7ffd

1 file changed

Lines changed: 38 additions & 26 deletions

File tree

  • packages/cipherstash-proxy-integration/src/multitenant

packages/cipherstash-proxy-integration/src/multitenant/contention.rs

Lines changed: 38 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -212,23 +212,38 @@ mod tests {
212212
);
213213
}
214214

215+
/// Number of encrypted inserts for the slow-connection test.
216+
const SLOW_CONN_INSERTS: usize = 10;
217+
215218
/// Verifies that a slow tenant connection does not block other tenants.
216219
///
217-
/// Tenant A: encrypted insert, signals readiness, then pg_sleep(0.5).
218-
/// Tenant B (different keyset): waits for A's signal, then does 10 encrypted inserts, timed.
220+
/// First measures a solo baseline: one tenant doing N encrypted inserts alone.
221+
/// Then runs the contention scenario:
222+
/// Tenant A: encrypted insert, signals readiness, then pg_sleep(2).
223+
/// Tenant B (different keyset): waits for A's signal, then does N encrypted inserts, timed.
224+
///
225+
/// Asserts that B's time under contention is within 2x of the solo baseline,
226+
/// proving B is not blocked by A's sleep. Uses a relative comparison instead
227+
/// of an absolute threshold to avoid CI environment speed sensitivity.
219228
///
220229
/// Connection setup is excluded from timing. A `Notify` ensures B starts only after
221230
/// A has completed its encrypted insert and entered pg_sleep, avoiding timing fragility.
222-
///
223-
/// With shared mutex contention, B may be blocked while A holds a lock.
224-
/// After per-connection cipher fix, B should complete independently of A's sleep.
225231
#[tokio::test]
226232
async fn multitenant_slow_connection_does_not_block_other_tenants() {
227233
trace();
228234
clear().await;
229235

230236
let keyset_ids = tenant_keyset_ids(2);
231237

238+
// --- Solo baseline: measure how long N inserts take with no contention ---
239+
let baseline_client = connect_as_tenant(&keyset_ids[1]).await;
240+
let baseline_duration =
241+
do_encrypted_inserts(&baseline_client, SLOW_CONN_INSERTS).await;
242+
drop(baseline_client);
243+
244+
clear().await;
245+
246+
// --- Contention scenario ---
232247
// Establish both connections before timing
233248
let client_a = connect_as_tenant(&keyset_ids[0]).await;
234249
let client_b = connect_as_tenant(&keyset_ids[1]).await;
@@ -237,7 +252,7 @@ mod tests {
237252
let a_ready = Arc::new(Notify::new());
238253
let a_ready_tx = a_ready.clone();
239254

240-
// Tenant A: encrypted insert, signal, then sleep
255+
// Tenant A: encrypted insert, signal, then sleep (2s to be clearly longer than inserts)
241256
let a_handle = tokio::spawn(async move {
242257
let id = random_id();
243258
let val = random_string();
@@ -252,47 +267,44 @@ mod tests {
252267
// Signal that the encrypted insert is done; A is now entering pg_sleep
253268
a_ready_tx.notify_one();
254269

255-
// Hold this connection busy with a sleep
256-
client_a.simple_query("SELECT pg_sleep(0.5)").await.unwrap();
270+
// Hold this connection busy with a long sleep
271+
client_a.simple_query("SELECT pg_sleep(2)").await.unwrap();
257272
});
258273

259274
// Wait for A to complete its encrypted insert before starting B
260275
a_ready.notified().await;
261276

262277
// Tenant B: encrypted inserts, timed
263278
let b_handle = tokio::spawn(async move {
264-
let start = Instant::now();
265-
for _ in 0..10 {
266-
let id = random_id();
267-
let val = random_string();
268-
client_b
269-
.query(
270-
"INSERT INTO encrypted (id, encrypted_text) VALUES ($1, $2)",
271-
&[&id, &val],
272-
)
273-
.await
274-
.unwrap();
275-
}
276-
start.elapsed()
279+
do_encrypted_inserts(&client_b, SLOW_CONN_INSERTS).await
277280
});
278281

279282
// Wait for both
280283
let b_duration = b_handle.await.unwrap();
281284
a_handle.await.unwrap();
282285

283286
// --- Diagnostics ---
287+
let contention_ratio = b_duration.as_secs_f64() / baseline_duration.as_secs_f64();
288+
284289
eprintln!("=== multitenant_slow_connection_does_not_block_other_tenants ===");
285290
eprintln!(
286-
" Tenant B (10 encrypted inserts while Tenant A sleeps): {:.3}s",
291+
" Solo baseline ({SLOW_CONN_INSERTS} encrypted inserts): {:.3}s",
292+
baseline_duration.as_secs_f64()
293+
);
294+
eprintln!(
295+
" Tenant B ({SLOW_CONN_INSERTS} encrypted inserts while A sleeps): {:.3}s",
287296
b_duration.as_secs_f64()
288297
);
289-
eprintln!(" (After fix: expect B completes well under 0.5s, independent of A's sleep)");
298+
eprintln!(" Contention ratio (B / baseline): {contention_ratio:.3}");
299+
eprintln!(" (After fix: expect ratio < 2.0, B completes independently of A's sleep)");
290300
eprintln!("=================================================================");
291301

292302
assert!(
293-
b_duration.as_secs_f64() < 0.5,
294-
"Tenant B should not be blocked by Tenant A's sleep, took {:.3}s",
295-
b_duration.as_secs_f64()
303+
contention_ratio < 2.0,
304+
"Tenant B should not be blocked by Tenant A's sleep, \
305+
contention ratio={contention_ratio:.3} (B={:.3}s, baseline={:.3}s)",
306+
b_duration.as_secs_f64(),
307+
baseline_duration.as_secs_f64()
296308
);
297309
}
298310
}

0 commit comments

Comments
 (0)