From 6899b2428aac734d1a2d4106076017edae0beb93 Mon Sep 17 00:00:00 2001 From: an-tao Date: Mon, 18 May 2026 15:36:22 +0800 Subject: [PATCH 1/3] Fix doBegin --- orm_lib/src/TransactionImpl.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/orm_lib/src/TransactionImpl.cc b/orm_lib/src/TransactionImpl.cc index 471b6a893a..a936f1e2c3 100644 --- a/orm_lib/src/TransactionImpl.cc +++ b/orm_lib/src/TransactionImpl.cc @@ -314,8 +314,8 @@ void TransactionImpl::doBegin() {}, {}, [](const Result &) { LOG_TRACE << "Transaction begin!"; }, - [thisPtr](const std::exception_ptr &) { - LOG_ERROR << "Error occurred in transaction begin"; + [thisPtr](const std::exception_ptr &e) { + LOG_ERROR << "Error occurred in transaction begin:" << e.what(); thisPtr->isCommitedOrRolledback_ = true; thisPtr->isWorking_ = false; thisPtr->thisPtr_.reset(); From 9e98abfb140c5cdddabd0ba32c350467b2534858 Mon Sep 17 00:00:00 2001 From: an-tao Date: Mon, 18 May 2026 15:46:15 +0800 Subject: [PATCH 2/3] rethrow --- orm_lib/src/TransactionImpl.cc | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/orm_lib/src/TransactionImpl.cc b/orm_lib/src/TransactionImpl.cc index a936f1e2c3..1cf13b6908 100644 --- a/orm_lib/src/TransactionImpl.cc +++ b/orm_lib/src/TransactionImpl.cc @@ -314,8 +314,16 @@ void TransactionImpl::doBegin() {}, {}, [](const Result &) { LOG_TRACE << "Transaction begin!"; }, - [thisPtr](const std::exception_ptr &e) { - LOG_ERROR << "Error occurred in transaction begin:" << e.what(); + [thisPtr](const std::exception_ptr &ePtr) { + try + { + std::rethrow_exception(ePtr); + } + catch (const std::exception &e) + { + LOG_ERROR << "Error occurred in transaction begin:" + << e.what(); + } thisPtr->isCommitedOrRolledback_ = true; thisPtr->isWorking_ = false; thisPtr->thisPtr_.reset(); From 494d5834b7ce0eb70db71174c2fc139cab6a1fc6 Mon Sep 17 00:00:00 2001 From: an-tao Date: Tue, 19 May 2026 18:57:35 +0800 Subject: [PATCH 3/3] Add SQLite locking test change --- orm_lib/tests/db_test.cc | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/orm_lib/tests/db_test.cc b/orm_lib/tests/db_test.cc index 65c779ecd3..b855f89742 100644 --- a/orm_lib/tests/db_test.cc +++ b/orm_lib/tests/db_test.cc @@ -4175,31 +4175,36 @@ DROGON_TEST(SQLite3TransactionTypeTest) // write). DROGON_TEST(SQLite3TransactionTypeLockingTest) { - // A pool of 2 connections to a shared file-based database gives us two - // independent SQLite connections that observe each other's locks. + // Two pools (one connection each) to a shared file-based database give us + // deterministic connection ownership for lock checks. const auto nonce = std::chrono::steady_clock::now().time_since_epoch().count(); const auto dbPath = "drogon_trans_type_lock_test_" + std::to_string(nonce) + ".db"; std::remove(dbPath.c_str()); - auto pool = DbClient::newSqlite3Client("filename=" + dbPath, 2); + { + auto setupClient = DbClient::newSqlite3Client("filename=" + dbPath, 1); + setupClient->execSqlSync("PRAGMA journal_mode=WAL"); + setupClient->execSqlSync( + "CREATE TABLE IF NOT EXISTS lock_test (id INTEGER PRIMARY KEY)"); + } + + auto poolA = DbClient::newSqlite3Client("filename=" + dbPath, 1); + auto poolB = DbClient::newSqlite3Client("filename=" + dbPath, 1); + // WAL mode is required: it changes BEGIN IMMEDIATE from acquiring a // RESERVED lock to acquiring the WAL write lock. This matches production // usage and makes the busy semantics more predictable — only one writer // is ever permitted and SQLITE_BUSY is returned immediately (no timeout // retry) when a second BEGIN IMMEDIATE is attempted. - pool->execSqlSync("PRAGMA journal_mode=WAL"); - // No retry delay: SQLITE_BUSY must surface as an exception immediately. - pool->execSqlSync("PRAGMA busy_timeout=0"); - pool->execSqlSync( - "CREATE TABLE IF NOT EXISTS lock_test (id INTEGER PRIMARY KEY)"); + poolA->execSqlSync("PRAGMA journal_mode=WAL"); std::shared_ptr transA; // Hold an IMMEDIATE transaction on connection A. try { - transA = pool->newTransaction(TransactionType::Immediate); + transA = poolA->newTransaction(TransactionType::Immediate); // doBegin() is asynchronous — the BEGIN IMMEDIATE is queued to the // connection's event loop. Run a synchronous query through the // transaction to flush the queue; once execSqlSync returns, the @@ -4214,12 +4219,15 @@ DROGON_TEST(SQLite3TransactionTypeLockingTest) return; } + // Disable retry on B so SQLITE_BUSY surfaces immediately. + poolB->execSqlSync("PRAGMA busy_timeout=0"); + // Connection B attempting BEGIN IMMEDIATE must fail because A already - // holds the RESERVED lock. SQLite's default busy_timeout is 0. + // holds the RESERVED lock. bool gotBusy = false; try { - auto transB = pool->newTransaction(TransactionType::Immediate); + auto transB = poolB->newTransaction(TransactionType::Immediate); transB->execSqlSync("SELECT 1"); transB->rollback(); } @@ -4229,6 +4237,7 @@ DROGON_TEST(SQLite3TransactionTypeLockingTest) } transA->rollback(); + poolA->execSqlSync("SELECT 1"); std::remove(dbPath.c_str()); if (gotBusy)