Skip to content

Commit

Permalink
Add query abort injection test utility (facebookincubator#8948)
Browse files Browse the repository at this point in the history
Summary:
Add a global scoped query abort injection utility to inject abort
during assert query results. Uses it in the SharedAbitrationTest
to make some query abort during memory arbitration.
The `TestScopedAbortInjection` is inspired by the PR facebookincubator#8921 .

Pull Request resolved: facebookincubator#8948

Reviewed By: tanjialiang

Differential Revision: D54569827

Pulled By: xiaoxmeng

fbshipit-source-id: 6251f84ba45e848636a8bb787f9c4fe05f488b5c
  • Loading branch information
duanmeng authored and facebook-github-bot committed Mar 6, 2024
1 parent 021d714 commit 3b6ce71
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 1 deletion.
4 changes: 3 additions & 1 deletion velox/common/memory/tests/SharedArbitratorTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1073,6 +1073,7 @@ TEST_F(SharedArbitrationTest, concurrentArbitration) {
const int maxNumZombieTasks = 8;
std::vector<std::thread> queryThreads;
queryThreads.reserve(numThreads);
TestScopedAbortInjection testScopedAbortInjection(10, numThreads);
for (int i = 0; i < numThreads; ++i) {
queryThreads.emplace_back([&, i]() {
std::shared_ptr<Task> task;
Expand Down Expand Up @@ -1131,7 +1132,8 @@ TEST_F(SharedArbitrationTest, concurrentArbitration) {
} catch (const VeloxException& e) {
if (e.errorCode() != error_code::kMemCapExceeded.c_str() &&
e.errorCode() != error_code::kMemAborted.c_str() &&
e.errorCode() != error_code::kMemAllocError.c_str()) {
e.errorCode() != error_code::kMemAllocError.c_str() &&
(e.message() != "Aborted for external error")) {
std::rethrow_exception(std::current_exception());
}
}
Expand Down
39 changes: 39 additions & 0 deletions velox/exec/tests/utils/QueryAssertions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

#include "duckdb/common/types.hpp" // @manual
#include "velox/duckdb/conversion/DuckConversion.h"
#include "velox/exec/tests/utils/ArbitratorTestUtil.h"
#include "velox/exec/tests/utils/Cursor.h"
#include "velox/exec/tests/utils/QueryAssertions.h"
#include "velox/vector/VectorTypeUtils.h"
Expand Down Expand Up @@ -1316,6 +1317,43 @@ void assertResultsOrdered(
}
}

tsan_atomic<int32_t>& testingAbortPct() {
static tsan_atomic<int32_t> abortPct = 0;
return abortPct;
}

tsan_atomic<int32_t>& testingAbortCounter() {
static tsan_atomic<int32_t> counter = 0;
return counter;
}

TestScopedAbortInjection::TestScopedAbortInjection(
int32_t abortPct,
int32_t maxInjections) {
testingAbortPct() = abortPct;
testingAbortCounter() = maxInjections;
}

TestScopedAbortInjection::~TestScopedAbortInjection() {
testingAbortPct() = 0;
testingAbortCounter() = 0;
}

bool testingMaybeTriggerAbort(exec::Task* task) {
if (testingAbortPct() <= 0 || testingAbortCounter() <= 0) {
return false;
}

if ((folly::Random::rand32() % 100) < testingAbortPct()) {
if (testingAbortCounter()-- > 0) {
task->requestAbort();
return true;
}
}

return false;
}

std::pair<std::unique_ptr<TaskCursor>, std::vector<RowVectorPtr>> readCursor(
const CursorParameters& params,
std::function<void(exec::Task*)> addSplits,
Expand All @@ -1329,6 +1367,7 @@ std::pair<std::unique_ptr<TaskCursor>, std::vector<RowVectorPtr>> readCursor(
while (cursor->moveNext()) {
result.push_back(cursor->current());
addSplits(task);
testingMaybeTriggerAbort(task);
}

if (!waitForTaskCompletion(task, maxWaitMicros)) {
Expand Down
21 changes: 21 additions & 0 deletions velox/exec/tests/utils/QueryAssertions.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,27 @@ class DuckDbQueryRunner {
std::function<void(std::vector<MaterializedRow>&)> resultCallback);
};

/// Scoped abort percentage utility that allows user to trigger abort during the
/// query execution.
/// 'abortPct' specifies the probability of of triggering abort. 100% means
/// abort will always be triggered.
/// 'maxInjections' indicates the max number of actual triggering, e.g. when
/// 'abortPct' is 20 and 'maxInjections' is 10, continuous calls to
/// testingMaybeTriggerAbort() will keep rolling the dice that has a chance of
/// 20% triggering until 10 triggers have been invoked.
class TestScopedAbortInjection {
public:
explicit TestScopedAbortInjection(
int32_t abortPct,
int32_t maxInjections = std::numeric_limits<int32_t>::max());

~TestScopedAbortInjection();
};

/// Test utility that might trigger task abort. The function returns true if
/// abortion triggers otherwise false.
bool testingMaybeTriggerAbort(exec::Task* task);

std::pair<std::unique_ptr<TaskCursor>, std::vector<RowVectorPtr>> readCursor(
const CursorParameters& params,
std::function<void(exec::Task*)> addSplits,
Expand Down

0 comments on commit 3b6ce71

Please sign in to comment.