Skip to content

Conversation

@tobyhede
Copy link
Contributor

@tobyhede tobyhede commented Feb 12, 2026

Add three integration tests that prove shared mutex contention exists across concurrent proxy connections. Tests measure scaling factor, per-connection latency under concurrency, and cross-connection blocking. Assertions are set to fail under current shared-client architecture and will pass after the per-connection cipher fix.

Summary by CodeRabbit

  • Tests
    • Added multitenant performance and contention validation tests to measure scaling efficiency and per-connection latency characteristics of concurrent encrypted database operations, and verify tenant isolation behavior.

@coderabbitai
Copy link

coderabbitai bot commented Feb 12, 2026

📝 Walkthrough

Walkthrough

A new test module for multitenant contention is introduced with three tests that measure mutex contention characteristics, concurrent insert performance scaling, and isolation between concurrent tenant connections using a PostgreSQL database.

Changes

Cohort / File(s) Summary
Multitenant Contention Tests
packages/cipherstash-proxy-integration/src/multitenant/contention.rs
New test module with three integration tests: concurrent encrypted inserts scaling measurement, per-connection latency under concurrency, and slow connection isolation. Tests use environment-driven tenant keyset IDs, TLS connections, timing assertions, and concurrent operations via JoinSet.
Module Declaration
packages/cipherstash-proxy-integration/src/multitenant/mod.rs
Added mod contention; declaration to expose the new contention test module.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Suggested reviewers

  • freshtonic

Poem

🐰 Hop-hop, the tests now measure stride,
With tenants dancing side by side,
Mutexes tested, latencies scaled,
No blocked connections, contention failed!
Concurrency reigns, let the queries fly free! 🚀

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding contention validation tests for a scoped cipher in the integration test suite.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch connection-scoped-cipher

No actionable comments were generated in the recent review. 🎉

🧹 Recent nitpick comments
packages/cipherstash-proxy-integration/src/multitenant/contention.rs (1)

26-31: Consider using expect() for clearer error messages on missing env vars.

The unwrap() calls will panic with a generic error if the environment variables are not set. Using expect() would provide a clearer diagnostic message for developers debugging test failures.

Suggested improvement
         let keysets = [
-            std::env::var("CS_TENANT_KEYSET_ID_1").unwrap(),
-            std::env::var("CS_TENANT_KEYSET_ID_2").unwrap(),
-            std::env::var("CS_TENANT_KEYSET_ID_3").unwrap(),
+            std::env::var("CS_TENANT_KEYSET_ID_1")
+                .expect("CS_TENANT_KEYSET_ID_1 must be set for multitenant tests"),
+            std::env::var("CS_TENANT_KEYSET_ID_2")
+                .expect("CS_TENANT_KEYSET_ID_2 must be set for multitenant tests"),
+            std::env::var("CS_TENANT_KEYSET_ID_3")
+                .expect("CS_TENANT_KEYSET_ID_3 must be set for multitenant tests"),
         ];

Comment @coderabbitai help to get the list of available commands and usage tips.

…ped cipher

Add three integration tests that prove shared mutex contention exists
across concurrent proxy connections. Tests measure scaling factor,
per-connection latency under concurrency, and cross-connection blocking.
Assertions are set to fail under current shared-client architecture and
will pass after the per-connection cipher fix.
@tobyhede tobyhede force-pushed the connection-scoped-cipher branch from 3b293b1 to cd98c26 Compare February 12, 2026 04:15
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@packages/cipherstash-proxy-integration/src/contention.rs`:
- Around line 185-186: The 50ms hardcoded delay in the tokio::time::sleep call
is timing-sensitive and can let task A not yet be in pg_sleep when B starts;
replace this fragile sleep with a deterministic synchronization mechanism (e.g.,
a oneshot or Barrier) or, if you prefer a simpler change, increase the delay and
add a clarifying comment explaining why the longer wait is required; locate the
tokio::time::sleep(std::time::Duration::from_millis(50)).await invocation in
contention.rs (the section that sets up connections A and B and expects A to be
in pg_sleep) and update it to wait on an explicit signal from A (or use a larger
duration with a comment) so the test is reliable under CI/cold-TLS conditions.
🧹 Nitpick comments (1)
packages/cipherstash-proxy-integration/src/contention.rs (1)

11-28: Consider using execute() instead of query() for INSERT statements.

Since INSERT doesn't return meaningful rows here, execute() would be more idiomatic and clearer in intent.

♻️ Suggested change
-            client
-                .query(
-                    "INSERT INTO encrypted (id, encrypted_text) VALUES ($1, $2)",
-                    &[&id, &val],
-                )
-                .await
-                .unwrap();
+            client
+                .execute(
+                    "INSERT INTO encrypted (id, encrypted_text) VALUES ($1, $2)",
+                    &[&id, &val],
+                )
+                .await
+                .unwrap();

Comment on lines 185 to 186
// Small delay so A is likely in-flight before B starts
tokio::time::sleep(std::time::Duration::from_millis(50)).await;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Timing sensitivity: 50ms delay may not guarantee A is in pg_sleep.

The 50ms delay assumes connection A has completed the INSERT and entered pg_sleep before B starts. Under slow conditions (CI load, cold TLS handshake), A might still be setting up, reducing test reliability.

Consider adding a synchronization mechanism or increasing the delay with a comment explaining the rationale.

💡 Alternative: use a longer delay with documentation
         // Small delay so A is likely in-flight before B starts
-        tokio::time::sleep(std::time::Duration::from_millis(50)).await;
+        // Delay to allow A to complete INSERT and enter pg_sleep.
+        // 200ms accounts for TLS handshake + INSERT under CI load.
+        tokio::time::sleep(std::time::Duration::from_millis(200)).await;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Small delay so A is likely in-flight before B starts
tokio::time::sleep(std::time::Duration::from_millis(50)).await;
// Delay to allow A to complete INSERT and enter pg_sleep.
// 200ms accounts for TLS handshake + INSERT under CI load.
tokio::time::sleep(std::time::Duration::from_millis(200)).await;
🤖 Prompt for AI Agents
In `@packages/cipherstash-proxy-integration/src/contention.rs` around lines 185 -
186, The 50ms hardcoded delay in the tokio::time::sleep call is timing-sensitive
and can let task A not yet be in pg_sleep when B starts; replace this fragile
sleep with a deterministic synchronization mechanism (e.g., a oneshot or
Barrier) or, if you prefer a simpler change, increase the delay and add a
clarifying comment explaining why the longer wait is required; locate the
tokio::time::sleep(std::time::Duration::from_millis(50)).await invocation in
contention.rs (the section that sets up connections A and B and expects A to be
in pg_sleep) and update it to wait on an explicit signal from A (or use a larger
duration with a comment) so the test is reliable under CI/cold-TLS conditions.

Contention tests must run in the multitenant phase where different
tenant keysets compete for the shared ZerokmsClient mutexes — the
actual customer scenario. Tests now use SET CIPHERSTASH.KEYSET_ID
with 3 tenant keysets, separate connection setup from timing, and
increase volume to 50 inserts per tenant to surface contention.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant