Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
210 changes: 164 additions & 46 deletions test/sql/parallelism/interquery/concurrent_attach_detach.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
#include "duckdb/common/map.hpp"

#include <string>
#include <vector>
#include <thread>

using namespace duckdb;
using namespace std;

string test_dir_path;
const string prefix = "db_";
Expand All @@ -22,25 +22,42 @@ string getDBName(idx_t i) {
return prefix + to_string(i);
}

const idx_t dbCount = 10;
const idx_t workerCount = 40;
const idx_t iterationCount = 100;
atomic<bool> success;
const idx_t db_count = 10;
const idx_t worker_count = 40;
const idx_t iteration_count = 100;
const idx_t nr_initial_rows = 10000;

void execQuery(Connection &conn, const string &query) {
std::vector<string> logging;
mutex log_mutex;
atomic<bool> success {true};

void addLog(const string &msg) {
if (success) {
lock_guard<mutex> lock(log_mutex);
logging.push_back(msg);
}
}

duckdb::unique_ptr<MaterializedQueryResult> execQuery(Connection &conn, const string &query) {
auto result = conn.Query(query);
if (result->HasError()) {
Printer::Print(result->GetError());
success = false;
}
return result;
}

struct TableInfo {
idx_t size;
};

struct DBInfo {
mutex mu;
idx_t count = 0;
idx_t table_count = 0;
std::vector<TableInfo> tables;
};

DBInfo dbInfos[dbCount];
DBInfo db_infos[db_count];

class DBPoolMgr {
public:
Expand Down Expand Up @@ -74,76 +91,177 @@ class DBPoolMgr {
}
};

DBPoolMgr dbPool;
DBPoolMgr db_pool;

void lookup(Connection &conn, idx_t i) {
unique_lock<mutex> lock(dbInfos[i].mu);
auto maxTblId = dbInfos[i].count;
lock.unlock();
void createTbl(Connection &conn, idx_t db_id, idx_t worker_id) {
lock_guard<mutex> lock(db_infos[db_id].mu);
auto tbl_id = db_infos[db_id].table_count;
db_infos[db_id].tables.emplace_back(TableInfo {nr_initial_rows});
db_infos[db_id].table_count++;

if (maxTblId == 0) {
string query = "CREATE TABLE " + getDBName(db_id) + ".tbl_" + to_string(tbl_id) +
" AS SELECT "
"range::BIGINT AS i, "
"range::VARCHAR AS s, "
// note: timestamps in this test increment with 1 millisecond (i.e. 1000 microsecond)
"epoch_ms(range) AS ts, "
"{'key1': range::BIGINT, 'key2': range::VARCHAR} AS obj "
"FROM range(" +
to_string(nr_initial_rows) + ")";
addLog("thread: " + to_string(worker_id) + "; q: " + query);
execQuery(conn, query);
}

void lookup(Connection &conn, idx_t db_id, idx_t worker_id) {
unique_lock<mutex> lock(db_infos[db_id].mu);
auto max_tbl_id = db_infos[db_id].table_count;

if (max_tbl_id == 0) {
lock.unlock();
return;
}

auto tblId = std::rand() % maxTblId;
string query = "SELECT i, s FROM " + getDBName(i) + ".tbl_" + to_string(tblId) + " WHERE i = 2049";
execQuery(conn, query);
}
auto tbl_id = std::rand() % max_tbl_id;
auto expected_max_val = db_infos[db_id].tables[tbl_id].size - 1;
lock.unlock();

void createLookupTbl(Connection &conn, idx_t i) {
lock_guard<mutex> lock(dbInfos[i].mu);
auto tblId = dbInfos[i].count;
dbInfos[i].count++;
// run query
auto table_name = getDBName(db_id) + ".tbl_" + to_string(tbl_id);
string query = "SELECT i, s, ts, obj FROM " + table_name + " WHERE i = " + to_string(expected_max_val);
addLog("thread: " + to_string(worker_id) + "; q: " + query);
auto result = execQuery(conn, query);

string query = "CREATE TABLE " + getDBName(i) + ".tbl_" + to_string(tblId) +
" AS SELECT range AS i, range::VARCHAR AS s FROM range(10000)";
execQuery(conn, query);
REQUIRE(CHECK_COLUMN(result, 0, {Value::INTEGER(expected_max_val)}));
REQUIRE(CHECK_COLUMN(result, 1, {to_string(expected_max_val)}));
REQUIRE(CHECK_COLUMN(result, 2, {Value::TIMESTAMP(timestamp_t {static_cast<int64_t>(expected_max_val * 1000)})}));
REQUIRE(CHECK_COLUMN(
result, 3,
{Value::STRUCT({{"key1", Value::INTEGER(expected_max_val)}, {"key2", to_string(expected_max_val)}})}));
}

void workUnit(std::unique_ptr<Connection> conn) {
for (int i = 0; i < iterationCount; i++) {
idx_t scenarioId = std::rand() % 2;
idx_t dbId = std::rand() % dbCount;
void append(Connection &conn, idx_t db_id, idx_t worker_id, idx_t append_num_rows = STANDARD_VECTOR_SIZE) {
lock_guard<mutex> lock(db_infos[db_id].mu);
auto max_tbl_id = db_infos[db_id].table_count;
if (max_tbl_id == 0) {
return;
}
auto tbl_id = std::rand() % max_tbl_id;
auto current_num_rows = db_infos[db_id].tables[tbl_id].size;
db_infos[db_id].tables[tbl_id].size += append_num_rows;

dbPool.addWorker(*conn, dbId);
// set appender
auto tbl_str = "tbl_" + to_string(tbl_id);
addLog("thread: " + to_string(worker_id) + "; db: " + getDBName(db_id) + "; table: " + tbl_str + "; append rows");
try {
duckdb::Appender appender(conn, getDBName(db_id), DEFAULT_SCHEMA, tbl_str);
DataChunk chunk;

switch (scenarioId) {
case 0:
lookup(*conn, dbId);
break;
case 1:
createLookupTbl(*conn, dbId);
child_list_t<LogicalType> struct_children;
struct_children.emplace_back(make_pair("key1", LogicalTypeId::BIGINT));
struct_children.emplace_back(make_pair("key2", LogicalTypeId::VARCHAR));

const duckdb::vector<LogicalType> types = {LogicalType::BIGINT, LogicalType::VARCHAR, LogicalType::TIMESTAMP,
LogicalType::STRUCT(struct_children)};

// fill up datachunk
chunk.Initialize(*conn.context, types);
// int
auto &col_int = chunk.data[0];
auto data_int = FlatVector::GetData<int64_t>(col_int);
// varchar
auto &col_varchar = chunk.data[1];
auto data_varchar = FlatVector::GetData<string_t>(col_varchar);
// timestamp
auto &col_ts = chunk.data[2];
auto data_ts = FlatVector::GetData<timestamp_t>(col_ts);
// struct
auto &col_struct = chunk.data[3];
auto &data_struct_entries = StructVector::GetEntries(col_struct);
auto &entry_int = data_struct_entries[0];
entry_int->SetVectorType(VectorType::FLAT_VECTOR);
auto data_struct_int = FlatVector::GetData<int64_t>(*entry_int);
auto &entry_varchar = data_struct_entries[1];
entry_varchar->SetVectorType(VectorType::FLAT_VECTOR);
auto data_struct_varchar = FlatVector::GetData<string_t>(*entry_varchar);

for (idx_t row_idx = 0; row_idx < append_num_rows; row_idx++) {
data_int[row_idx] = current_num_rows + row_idx;
data_varchar[row_idx] = StringVector::AddString(col_varchar, to_string(current_num_rows + row_idx));
data_ts[row_idx] = timestamp_t {static_cast<int64_t>(1000 * (current_num_rows + row_idx))};
data_struct_int[row_idx] = current_num_rows + row_idx;
data_struct_varchar[row_idx] =
StringVector::AddString(*entry_varchar, to_string(current_num_rows + row_idx));
}

chunk.SetCardinality(append_num_rows);
appender.AppendDataChunk(chunk);
appender.Close();
} catch (const std::exception &e) {
addLog("Caught exception when using Appender: " + std::string(e.what()));
success = false;
} catch (...) {
addLog("Caught error when using Appender!");
success = false;
}
}

void workUnit(std::unique_ptr<Connection> conn, const idx_t &worker_id) {
for (int i = 0; i < iteration_count; i++) {
if (!success) {
break;
default:
throw runtime_error("invalid scenario");
}
try {
idx_t scenario_id = std::rand() % 3;
idx_t db_id = std::rand() % db_count;

db_pool.addWorker(*conn, db_id);

dbPool.removeWorker(*conn, dbId);
switch (scenario_id) {
case 0:
createTbl(*conn, db_id, worker_id);
break;
case 1:
lookup(*conn, db_id, worker_id);
break;
case 2:
append(*conn, db_id, worker_id);
break;
default:
addLog("invalid scenario: " + to_string(scenario_id));
success = false;
}

db_pool.removeWorker(*conn, db_id);
} catch (...) {
break;
}
}
}

TEST_CASE("Run a concurrent ATTACH/DETACH scenario", "[attach][.]") {
test_dir_path = TestDirectoryPath();

DuckDB db(nullptr);
Connection initConn(db);
Connection init_conn(db);

execQuery(initConn, "SET catalog_error_max_schemas = '0'");
execQuery(initConn, "SET threads = '1'");
execQuery(init_conn, "SET catalog_error_max_schemas = '0'");
execQuery(init_conn, "SET threads = '1'");
// execQuery(initConn, "SET default_block_size = '16384'");
// execQuery(initConn, "SET storage_compatibility_version = 'v1.3.2'");

success = true;
std::vector<thread> workers;
for (int i = 0; i < workerCount; i++) {
std::vector<std::thread> workers;
for (int i = 0; i < worker_count; i++) {
auto conn = make_uniq<Connection>(db);
workers.emplace_back(workUnit, std::move(conn));
workers.emplace_back(workUnit, std::move(conn), i);
}

for (auto &worker : workers) {
worker.join();
}
if (!success) {
for (auto msg : logging) {
Printer::Print(msg);
}
FAIL();
}
}
Loading