NAME
DBIO::ClickHouse::Storage::TxnScopeGuard - Scope guard for a storage that has nothing to roll back
VERSION
version 0.900004
DESCRIPTION
The scope guard "txn_scope_guard" in DBIO::ClickHouse::Storage hands out. It is DBIO::Storage::TxnScopeGuard with one method replaced: "DESTROY" carps instead of rolling back, because on ClickHouse there is nothing to roll back (docs/adr/0003-no-transactions-policy.md).
You do not instantiate this yourself. Core takes a guard on your behalf in delete_all, update_all, multi-create, cascade delete and non-void populate; this class is what those get.
Construction, "commit" in DBIO::Storage::TxnScopeGuard and "rollback" in DBIO::Storage::TxnScopeGuard are inherited unchanged, which is the point -- they already land on the right answer:
newcallstxn_begin, a no-op that keeps core'stransaction_depthbookkeeping ("_exec_txn_begin" in DBIO::ClickHouse::Storage).$guard->commitcallstxn_commit, likewise a no-op.$guard->rollbackcalls "txn_rollback" in DBIO::ClickHouse::Storage and therefore throws. That is a caller stating that work was undone, and it is as untrue when written on a guard as it is when written on the schema.
METHODS
DESTROY
Unwinds transaction_depth by one and carps that nothing was rolled back. Never throws, and never touches the in-flight exception.
Core's implementation would call txn_rollback here, which on this storage throws -- inside a DESTROY, during someone else's stack unwind. Its catch block then rewrites the exception that was already travelling: a DBIO::Exception is mutated in place to "Transaction aborted: <original Rollback failed: <our refusal>">, once per guard on the stack. A delete_all inside a caller's own guard nests three deep, so the real error arrives behind three "Transaction aborted:" prefixes and a repeated paragraph about a rollback that was never possible in the first place.
That decoration is noise on a driver where it is never news. Every rollback on this storage fails, always, for the same reason -- it is a property of ClickHouse, not of this call -- and it is already stated by the one-time txn_begin warning, by $guard->rollback, and in the documentation. Paying for it with a damaged exception on every failed bulk operation is a bad trade, so this guard drops the refusal and keeps the error.
The carp is deliberately not carp_once: each unwind is a separate incident with its own partial write, the same reason "txn_do" in DBIO::ClickHouse::Storage warns on every failure rather than once.
SEE ALSO
"TRANSACTIONS" in DBIO::ClickHouse::Storage - the whole policy
DBIO::Storage::TxnScopeGuard - the base class
AUTHOR
DBIO Authors
COPYRIGHT AND LICENSE
Copyright (C) 2026 DBIO Authors
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.