//////////////////////////////////////////////////////////////////////////////// /// DISCLAIMER /// /// Copyright 2018 ArangoDB GmbH, Cologne, Germany /// /// Licensed under the Apache License, Version 2.0 (the "License"); /// you may not use this file except in compliance with the License. /// You may obtain a copy of the License at /// /// http://www.apache.org/licenses/LICENSE-2.0 /// /// Unless required by applicable law or agreed to in writing, software /// distributed under the License is distributed on an "AS IS" BASIS, /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. /// See the License for the specific language governing permissions and /// limitations under the License. /// /// Copyright holder is ArangoDB GmbH, Cologne, Germany /// /// @author Simon Grätzer //////////////////////////////////////////////////////////////////////////////// #include "ClusterTransactionState.h" #include "Basics/Exceptions.h" #include "Logger/Logger.h" #include "Cluster/ClusterMethods.h" #include "Cluster/ClusterTrxMethods.h" #include "ClusterEngine/ClusterEngine.h" #include "Statistics/ServerStatistics.h" #include "StorageEngine/EngineSelectorFeature.h" #include "StorageEngine/TransactionCollection.h" #include "Transaction/Manager.h" #include "Transaction/ManagerFeature.h" #include "Transaction/Methods.h" #include "VocBase/LogicalCollection.h" using namespace arangodb; /// @brief transaction type ClusterTransactionState::ClusterTransactionState(TRI_vocbase_t& vocbase, TRI_voc_tid_t tid, transaction::Options const& options) : TransactionState(vocbase, tid, options) {} /// @brief start a transaction Result ClusterTransactionState::beginTransaction(transaction::Hints hints) { LOG_TRX("03dec", TRACE, this, nestingLevel()) << "beginning " << AccessMode::typeString(_type) << " transaction"; TRI_ASSERT(!hasHint(transaction::Hints::Hint::NO_USAGE_LOCK) || !AccessMode::isWriteOrExclusive(_type)); if (nestingLevel() == 0) { // set hints _hints = hints; } auto cleanup = scopeGuard([&] { if (nestingLevel() == 0) { updateStatus(transaction::Status::ABORTED); ServerStatistics::statistics()._transactionsStatistics._transactionsAborted++; } // free what we have got so far unuseCollections(nestingLevel()); }); Result res = useCollections(nestingLevel()); if (res.fail()) { // something is wrong return res; } // all valid if (nestingLevel() == 0) { updateStatus(transaction::Status::RUNNING); transaction::ManagerFeature::manager()->registerTransaction(id(), nullptr, isReadOnlyTransaction()); ServerStatistics::statistics()._transactionsStatistics._transactionsStarted++; setRegistered(); ClusterEngine* ce = static_cast(EngineSelectorFeature::ENGINE); if (ce->isMMFiles() && hasHint(transaction::Hints::Hint::GLOBAL_MANAGED)) { TRI_ASSERT(isCoordinator()); std::vector leaders; allCollections([&leaders](TransactionCollection& c) { auto shardIds = c.collection()->shardIds(); for (auto const& pair : *shardIds) { std::vector const& servers = pair.second; if (!servers.empty()) { leaders.push_back(servers[0]); } } return true; // continue }); res = ClusterTrxMethods::beginTransactionOnLeaders(*this, leaders); if (res.fail()) { // something is wrong return res; } } } else { TRI_ASSERT(_status == transaction::Status::RUNNING); } cleanup.cancel(); return res; } /// @brief commit a transaction Result ClusterTransactionState::commitTransaction(transaction::Methods* activeTrx) { LOG_TRX("927c0", TRACE, this, nestingLevel()) << "committing " << AccessMode::typeString(_type) << " transaction"; TRI_ASSERT(_status == transaction::Status::RUNNING); TRI_IF_FAILURE("TransactionWriteCommitMarker") { return Result(TRI_ERROR_DEBUG); } arangodb::Result res; if (nestingLevel() == 0) { updateStatus(transaction::Status::COMMITTED); ServerStatistics::statistics()._transactionsStatistics._transactionsCommitted++; } unuseCollections(nestingLevel()); return res; } /// @brief abort and rollback a transaction Result ClusterTransactionState::abortTransaction(transaction::Methods* activeTrx) { LOG_TRX("fc653", TRACE, this, nestingLevel()) << "aborting " << AccessMode::typeString(_type) << " transaction"; TRI_ASSERT(_status == transaction::Status::RUNNING); Result res; if (nestingLevel() == 0) { updateStatus(transaction::Status::ABORTED); ServerStatistics::statistics()._transactionsStatistics._transactionsAborted++; } unuseCollections(nestingLevel()); return res; }