| 313 | } |
| 314 | |
| 315 | bool MergeTreeTransaction::rollback() noexcept |
| 316 | { |
| 317 | auto blocker = CannotAllocateThreadFaultInjector::blockFaultInjections(); |
| 318 | LockMemoryExceptionInThread memory_tracker_lock(VariableContext::Global); |
| 319 | CSN expected = Tx::UnknownCSN; |
| 320 | bool need_rollback = csn.compare_exchange_strong(expected, Tx::RolledBackCSN); |
| 321 | |
| 322 | /// Check that it was not rolled back concurrently |
| 323 | if (!need_rollback) |
| 324 | return false; |
| 325 | |
| 326 | /// It's not a problem if server crash at this point |
| 327 | /// because on startup we will see that TID is not committed and will simply discard these changes. |
| 328 | |
| 329 | RunningMutationsList mutations_to_kill; |
| 330 | DataPartsVector parts_to_remove; |
| 331 | DataPartsVector parts_to_activate; |
| 332 | |
| 333 | { |
| 334 | std::lock_guard lock{mutex}; |
| 335 | mutations_to_kill = mutations; |
| 336 | parts_to_remove = creating_parts; |
| 337 | parts_to_activate = removing_parts; |
| 338 | } |
| 339 | |
| 340 | /// Forcefully stop related mutations if any |
| 341 | for (const auto & table_and_mutation : mutations_to_kill) |
| 342 | table_and_mutation.first->killMutation(table_and_mutation.second); |
| 343 | |
| 344 | /// Discard changes in active parts set |
| 345 | /// Remove parts that were created, restore parts that were removed (except parts that were created by this transaction too) |
| 346 | |
| 347 | /// Kind of optimization: cleanup thread can remove these parts immediately |
| 348 | for (const auto & part : parts_to_remove) |
| 349 | { |
| 350 | /// Write special RolledBackCSN, so we will be able to cleanup transaction log |
| 351 | part->version->setAndStoreCreationCSN(Tx::RolledBackCSN); |
| 352 | } |
| 353 | |
| 354 | for (const auto & part : parts_to_remove) |
| 355 | { |
| 356 | /// NOTE It's possible that part is already removed from working set in the same transaction |
| 357 | /// (or, even worse, in a separate non-transactional query with NonTransactionalTID), |
| 358 | /// but it's not a problem: removePartsFromWorkingSet(...) will do nothing in this case. |
| 359 | const_cast<MergeTreeData &>(part->storage).removePartsFromWorkingSet(NO_TRANSACTION_RAW, {part}, true); |
| 360 | } |
| 361 | |
| 362 | for (const auto & part : parts_to_activate) |
| 363 | if (part->version->getInfo().creation_tid != tid) |
| 364 | const_cast<MergeTreeData &>(part->storage).restoreAndActivatePart(part); |
| 365 | |
| 366 | for (const auto & part : parts_to_activate) |
| 367 | { |
| 368 | /// Clear removal_tid from version metadata file, so we will not need to distinguish TIDs that were not committed |
| 369 | /// and TIDs that were committed long time ago and were removed from the log on log cleanup. |
| 370 | part->version->setAndStoreRemovalTID(Tx::EmptyTID); |
| 371 | part->version->unlockRemovalTID(tid, TransactionInfoContext{part->storage.getStorageID(), part->name}); |
| 372 | } |
no test coverage detected