Files
email-tracker/external/duckdb/test/sql/parallelism/interquery/test_concurrentappend.cpp
2025-10-24 19:21:19 -05:00

115 lines
3.9 KiB
C++

#include "catch.hpp"
#include "duckdb/common/value_operations/value_operations.hpp"
#include "test_helpers.hpp"
#include <atomic>
#include <thread>
using namespace duckdb;
using namespace std;
static constexpr int CONCURRENT_APPEND_THREAD_COUNT = 10;
static constexpr int CONCURRENT_APPEND_INSERT_ELEMENTS = 1000;
TEST_CASE("Sequential append", "[interquery][.]") {
duckdb::unique_ptr<MaterializedQueryResult> result;
DuckDB db(nullptr);
Connection con(db);
duckdb::vector<duckdb::unique_ptr<Connection>> connections;
// enable detailed profiling
con.Query("PRAGMA enable_profiling");
auto detailed_profiling_output = TestCreatePath("detailed_profiling_output");
con.Query("PRAGMA profiling_output='" + detailed_profiling_output + "'");
con.Query("PRAGMA profiling_mode = detailed");
// initialize the database
REQUIRE_NO_FAIL(con.Query("CREATE TABLE integers(i INTEGER);"));
for (size_t i = 0; i < CONCURRENT_APPEND_THREAD_COUNT; i++) {
connections.push_back(make_uniq<Connection>(db));
connections[i]->Query("BEGIN TRANSACTION;");
}
for (size_t i = 0; i < CONCURRENT_APPEND_THREAD_COUNT; i++) {
result = connections[i]->Query("SELECT COUNT(*) FROM integers");
D_ASSERT(result->RowCount() > 0);
Value count = result->GetValue(0, 0);
REQUIRE(count == 0);
for (size_t j = 0; j < CONCURRENT_APPEND_INSERT_ELEMENTS; j++) {
connections[i]->Query("INSERT INTO integers VALUES (3)");
result = connections[i]->Query("SELECT COUNT(*) FROM integers");
Value new_count = result->GetValue(0, 0);
REQUIRE(new_count == j + 1);
count = new_count;
}
}
for (size_t i = 0; i < CONCURRENT_APPEND_THREAD_COUNT; i++) {
connections[i]->Query("COMMIT;");
}
result = con.Query("SELECT COUNT(*) FROM integers");
Value count = result->GetValue(0, 0);
REQUIRE(count == CONCURRENT_APPEND_THREAD_COUNT * CONCURRENT_APPEND_INSERT_ELEMENTS);
}
static volatile std::atomic<int> append_finished_threads;
static void insert_random_elements(DuckDB *db, bool *correct, int threadnr) {
correct[threadnr] = true;
Connection con(*db);
// initial count
con.Query("BEGIN TRANSACTION;");
auto result = con.Query("SELECT COUNT(*) FROM integers");
Value count = result->GetValue(0, 0);
auto start_count = count.GetValue<int64_t>();
for (size_t i = 0; i < CONCURRENT_APPEND_INSERT_ELEMENTS; i++) {
// count should increase by one for every append we do
con.Query("INSERT INTO integers VALUES (3)");
result = con.Query("SELECT COUNT(*) FROM integers");
Value new_count = result->GetValue(0, 0);
if (new_count != start_count + i + 1) {
correct[threadnr] = false;
}
count = new_count;
}
append_finished_threads++;
while (append_finished_threads != CONCURRENT_APPEND_THREAD_COUNT)
;
con.Query("COMMIT;");
}
TEST_CASE("Concurrent append", "[interquery][.]") {
duckdb::unique_ptr<QueryResult> result;
DuckDB db(nullptr);
Connection con(db);
// enable detailed profiling
con.Query("PRAGMA enable_profiling");
auto detailed_profiling_output = TestCreatePath("detailed_profiling_output");
con.Query("PRAGMA profiling_output='" + detailed_profiling_output + "'");
con.Query("PRAGMA profiling_mode = detailed");
// initialize the database
REQUIRE_NO_FAIL(con.Query("CREATE TABLE integers(i INTEGER);"));
append_finished_threads = 0;
bool correct[CONCURRENT_APPEND_THREAD_COUNT];
thread threads[CONCURRENT_APPEND_THREAD_COUNT];
for (size_t i = 0; i < CONCURRENT_APPEND_THREAD_COUNT; i++) {
threads[i] = thread(insert_random_elements, &db, correct, i);
}
for (size_t i = 0; i < CONCURRENT_APPEND_THREAD_COUNT; i++) {
threads[i].join();
REQUIRE(correct[i]);
}
result = con.Query("SELECT COUNT(*), SUM(i) FROM integers");
REQUIRE(
CHECK_COLUMN(result, 0, {Value::BIGINT(CONCURRENT_APPEND_THREAD_COUNT * CONCURRENT_APPEND_INSERT_ELEMENTS)}));
REQUIRE(CHECK_COLUMN(result, 1,
{Value::BIGINT(3 * CONCURRENT_APPEND_THREAD_COUNT * CONCURRENT_APPEND_INSERT_ELEMENTS)}));
}