Skip to content

future: Add regression test for double callback#470

Merged
wprzytula merged 1 commit into
scylladb:masterfrom
wprzytula:double-callback-bug-regression-test
Jun 2, 2026
Merged

future: Add regression test for double callback#470
wprzytula merged 1 commit into
scylladb:masterfrom
wprzytula:double-callback-bug-regression-test

Conversation

@wprzytula

@wprzytula wprzytula commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Refs: #468

Add a probabilistic test (authored by Claude Opus 4.6) that verifies the callback is invoked exactly once when set_callback races with future resolution. The test creates a future whose resolution is synchronized via a barrier, then calls set_callback at the same time, checking that the atomic callback counter never exceeds 1.

The natural race window is unfortunately too narrow to hit reliably without instrumentation. Manual testing on my machine showed the bug in ~20-40% runs, so this is enough to be hit in the CI from time to time.

Pre-review checklist

  • I have split my patch into logically separate commits.
  • All commit messages clearly explain what they change and why.
  • PR description sums up the changes and reasons why they should be introduced.
  • I have implemented Rust unit tests for the features/changes introduced.
  • [ ] I have enabled appropriate tests in Makefile in {SCYLLA,CASSANDRA}_(NO_VALGRIND_)TEST_FILTER.
  • [ ] I added appropriate Fixes: annotations to PR description.

@coderabbitai

coderabbitai Bot commented Jun 2, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 37a916f3-829a-4395-89f0-720e7c7b63e5

📥 Commits

Reviewing files that changed from the base of the PR and between 872285d and 86331f3.

📒 Files selected for processing (1)
  • scylla-rust-wrapper/src/future.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • scylla-rust-wrapper/src/future.rs

📝 Walkthrough

Walkthrough

This PR adds a regression test test_cass_future_callback_invoked_exactly_once that races cass_future_set_callback against future resolution across 10,000 iterations on a multi-threaded Tokio runtime. Each iteration uses a Barrier and tokio::task::spawn_blocking to maximize the race window, counts callback invocations with an AtomicU32, waits with cass_future_wait, and asserts the callback was invoked exactly once. No production or public API code was changed.

Sequence Diagram(s)

sequenceDiagram
  participant TestThread as TestThread
  participant Tokio as TokioRuntime
  participant Worker as spawn_blocking
  participant CassFuture as CassFuture
  participant Callback as counting_cb

  TestThread->>Tokio: create multi-threaded runtime
  TestThread->>Tokio: submit iteration task
  Tokio->>Worker: spawn_blocking (create & resolve future with Barrier)
  Worker->>CassFuture: resolve future (after Barrier)
  TestThread->>CassFuture: cass_future_set_callback(counting_cb)
  CassFuture->>Callback: invoke counting_cb (once)
  TestThread->>CassFuture: cass_future_wait()
Loading

Possibly related PRs

Suggested labels

P1, area/Driver_-_cpp-rs-driver

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding a regression test for a double callback bug, which aligns with the changeset's core objective.
Description check ✅ Passed The PR description addresses the main purpose, references the issue (#468), explains the test rationale, and the author completed most pre-review checklist items (4 of 6 checked). Two items remain unchecked but are marked as not applicable.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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


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

@wprzytula
wprzytula requested review from Lorak-mmk and Copilot June 2, 2026 09:32
@wprzytula wprzytula self-assigned this Jun 2, 2026
@wprzytula wprzytula added the area/testing Related to unit/integration testing label Jun 2, 2026
@wprzytula wprzytula added this to the 1.1.0 milestone Jun 2, 2026
@coderabbitai coderabbitai Bot added P1 P1 priority item - very important area/Driver_-_cpp-rs-driver labels Jun 2, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Adds a Rust unit regression test to guard against the previously fixed race where a CassFuture callback could be invoked twice when cass_future_set_callback races with future resolution (refs #468 / issue #468).

Changes:

  • Introduces a new multi-threaded, contention-based test that attempts to race set_callback against resolution and asserts the callback counter never exceeds 1.
  • Uses a barrier + blocking task to widen the race window and repeats the scenario many times to increase the chance of exercising the interleaving.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

/// (but not yet acquired the state lock to store it) by the time
/// `set_callback` is called, creating lock contention.
#[test]
#[ntest::timeout(30000)]
Comment thread scylla-rust-wrapper/src/future.rs Outdated
.into(),
);

for _ in 0..10_000 {
Comment on lines +911 to +913
/// To trigger this, we ensure the async task has already produced its result
/// (but not yet acquired the state lock to store it) by the time
/// `set_callback` is called, creating lock contention.
Comment thread scylla-rust-wrapper/src/future.rs Outdated
unsafe { cass_future_wait(cass_fut.borrow()) };

// Spin-wait for the callback to be invoked at least once.
let deadline = std::time::Instant::now() + Duration::from_millis(100);
Add a probabilistic test that verifies the callback is invoked
exactly once when set_callback races with future resolution.
The test creates a future whose resolution is synchronized via
a barrier, then calls set_callback at the same time, checking
that the atomic callback counter never exceeds 1.

The natural race window is unfortunately too narrow to hit reliably
without instrumentation. Manual testing on my machine showed the bug
in ~20-40% runs, so this is enough to be hit in the CI from time
to time.
@wprzytula
wprzytula force-pushed the double-callback-bug-regression-test branch from 872285d to 86331f3 Compare June 2, 2026 10:29
@wprzytula
wprzytula merged commit ab2ce69 into scylladb:master Jun 2, 2026
9 checks passed
@wprzytula
wprzytula deleted the double-callback-bug-regression-test branch June 2, 2026 11:00
@wprzytula wprzytula mentioned this pull request Jun 2, 2026
wprzytula added a commit that referenced this pull request Jun 2, 2026
Refs: #468

Add a probabilistic test (authored by Claude Opus 4.6) that verifies the
callback is invoked exactly once when set_callback races with future
resolution. The test creates a future whose resolution is synchronized
via a barrier, then calls set_callback at the same time, checking that
the atomic callback counter never exceeds 1.

The natural race window is unfortunately too narrow to hit reliably
without instrumentation. Manual testing on my machine showed the bug in
~20-40% runs, so this is enough to be hit in the CI from time to time.

(cherry picked from commit ab2ce69)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/Driver_-_cpp-rs-driver area/testing Related to unit/integration testing P1 P1 priority item - very important

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants