fix: DeltaAppender cleanup on rollback#5421
Conversation
There was a problem hiding this comment.
Code Review
This pull request improves transaction management, error handling, and resource cleanup in the DuckDB storage engine. Key changes include early transaction registration with error handling, discarding appenders during rollback, wrapping database initialization in try-catch blocks with robust error checking, and adding test cases for batch disconnects and rollbacks. Feedback suggests wrapping the destructor's rollback call in a try-catch block to prevent potential server crashes from unhandled exceptions, and avoiding passing a std::string directly to a %s format specifier in exception constructors to prevent undefined behavior.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| ~DuckdbThdContext() | ||
| { | ||
| if (has_transaction()) | ||
| { | ||
| std::string error_msg; | ||
| duckdb_trans_rollback(error_msg); | ||
| } | ||
| std::string error_msg; | ||
| duckdb_trans_rollback(error_msg); | ||
| } |
There was a problem hiding this comment.
Destructors in C++ should not allow exceptions to propagate, as doing so will trigger std::terminate() and crash the server. Since DuckDB operations (such as those called within duckdb_trans_rollback) can throw exceptions (e.g., duckdb::Exception), it is safer to wrap the rollback call in a try-catch block to ensure any exceptions are caught and handled gracefully.
~DuckdbThdContext()
{
try
{
std::string error_msg;
duckdb_trans_rollback(error_msg);
}
catch (...)
{
// Ignore exceptions in destructor to prevent std::terminate
}
}There was a problem hiding this comment.
Pull request overview
This PR hardens the DuckDB storage engine integration by ensuring buffered batch writes are discarded (not flushed) on rollback/disconnect and by making DuckDB plugin initialization fail safely without leaking partially initialized state.
Changes:
- Add explicit discard paths for
DeltaAppenderbuffers, and invoke discard/reset before DuckDB rollback and appender removal. - Make transaction begin failures propagate back to MariaDB, and gate engine registration on a successful DuckDB
BEGIN. - Add new MTR tests covering rollback/disconnect behavior with pending batch inserts.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| storage/duckdb/runtime/duckdb_manager.cc | Wrap initialization steps in exception handling and clean up DuckDB state on failure. |
| storage/duckdb/runtime/duckdb_context.h | Reset/discard per-session appenders before rollback; propagate BEGIN error text. |
| storage/duckdb/runtime/delta_appender.h | Add discard() / discard_all() APIs to drop buffered rows without flushing. |
| storage/duckdb/runtime/delta_appender.cc | Implement discard logic and ensure discard occurs before appender removal/teardown. |
| storage/duckdb/mysql-test/duckdb/t/batch_rollback.test | Add MTR coverage for rollback with pending batched inserts. |
| storage/duckdb/mysql-test/duckdb/t/batch_disconnect.test | Add MTR coverage for disconnect with pending batched inserts. |
| storage/duckdb/ha_duckdb.cc | Ensure DuckDB transaction begins successfully before registering the handlerton in the transaction. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (result->HasError()) | ||
| throw duckdb::InvalidInputException( | ||
| "DuckDB initialization query failed: %s", result->GetError()); | ||
| }; |
| if (!m_con) | ||
| return true; |
e9efb13 to
f7d25ec
Compare
Fix DuckDB transaction cleanup and plugin initialization
What
Prevent buffered DuckDB writes from surviving rollback or connection teardown, and make plugin initialization fail safely instead of propagating exceptions into MariaDB.
Key changes
BEGINfailures and register the storage engine only after the transaction starts successfully.How to test
Run:
./mtr --suite=duckdb --force --forceExpected result: all enabled DuckDB tests pass, including
duckdb.batch_rollbackandduckdb.batch_disconnect.