1
0
Fork 0
arangodb/arangod/ClusterEngine/ClusterTransactionCollectio...

189 lines
6.2 KiB
C++

////////////////////////////////////////////////////////////////////////////////
/// 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 "Basics/Exceptions.h"
#include "Cluster/ClusterInfo.h"
#include "ClusterTransactionCollection.h"
#include "Logger/Logger.h"
#include "StorageEngine/TransactionState.h"
#include "Transaction/Hints.h"
#include "Transaction/Methods.h"
#include "VocBase/LogicalCollection.h"
using namespace arangodb;
ClusterTransactionCollection::ClusterTransactionCollection(TransactionState* trx,
TRI_voc_cid_t cid,
AccessMode::Type accessType,
int nestingLevel)
: TransactionCollection(trx, cid, accessType, nestingLevel) {}
ClusterTransactionCollection::~ClusterTransactionCollection() {}
/// @brief whether or not any write operations for the collection happened
bool ClusterTransactionCollection::hasOperations() const {
return false; //(_numInserts > 0 || _numRemoves > 0 || _numUpdates > 0);
}
void ClusterTransactionCollection::freeOperations(transaction::Methods* /*activeTrx*/,
bool /*mustRollback*/) {}
bool ClusterTransactionCollection::canAccess(AccessMode::Type accessType) const {
// check if access type matches
if (AccessMode::isWriteOrExclusive(accessType) &&
!AccessMode::isWriteOrExclusive(_accessType)) {
// type doesn't match. probably also a mistake by the caller
return false;
}
return true;
}
int ClusterTransactionCollection::use(int nestingLevel) {
if (_nestingLevel != nestingLevel) {
// only process our own collections
return TRI_ERROR_NO_ERROR;
}
if (_collection == nullptr) {
// open the collection
ClusterInfo* ci = ClusterInfo::instance();
if (ci == nullptr) {
return TRI_ERROR_SHUTTING_DOWN;
}
_collection =
ci->getCollectionNT(_transaction->vocbase().name(), std::to_string(_cid));
if (_collection == nullptr) {
return TRI_ERROR_ARANGO_DATA_SOURCE_NOT_FOUND;
}
if (!_transaction->hasHint(transaction::Hints::Hint::LOCK_NEVER) &&
!_transaction->hasHint(transaction::Hints::Hint::NO_USAGE_LOCK)) {
// use and usage-lock
LOG_TRX("8154f", TRACE, _transaction, nestingLevel) << "using collection " << _cid;
}
}
if (AccessMode::isWriteOrExclusive(_accessType) && !isLocked()) {
// r/w lock the collection
int res = doLock(_accessType, nestingLevel);
if (res == TRI_ERROR_LOCKED) {
// TRI_ERROR_LOCKED is not an error, but it indicates that the lock
// operation has actually acquired the lock (and that the lock has not
// been held before)
res = TRI_ERROR_NO_ERROR;
} else if (res != TRI_ERROR_NO_ERROR) {
return res;
}
}
return TRI_ERROR_NO_ERROR;
}
void ClusterTransactionCollection::unuse(int nestingLevel) {
// nothing to do here. we're postponing the unlocking until release()
}
void ClusterTransactionCollection::release() {
if (isLocked()) {
// unlock our own r/w locks
doUnlock(_accessType, 0);
_lockType = AccessMode::Type::NONE;
}
// the top level transaction releases all collections
if (_collection != nullptr) {
// unuse collection, remove usage-lock
LOG_TRX("1cb8d", TRACE, _transaction, 0) << "unusing collection " << _cid;
_collection = nullptr;
}
}
/// @brief lock a collection
/// returns TRI_ERROR_LOCKED in case the lock was successfully acquired
/// returns TRI_ERROR_NO_ERROR in case the lock does not need to be acquired and
/// no other error occurred returns any other error code otherwise
int ClusterTransactionCollection::doLock(AccessMode::Type type, int nestingLevel) {
if (!AccessMode::isWriteOrExclusive(type)) {
_lockType = type;
return TRI_ERROR_NO_ERROR;
}
if (_transaction->hasHint(transaction::Hints::Hint::LOCK_NEVER)) {
// never lock
return TRI_ERROR_NO_ERROR;
}
TRI_ASSERT(_collection != nullptr);
TRI_ASSERT(!isLocked());
TRI_ASSERT(_collection);
LOG_TRX("b4a05", TRACE, _transaction, nestingLevel) << "write-locking collection " << _cid;
_lockType = type;
// not an error, but we use TRI_ERROR_LOCKED to indicate that we actually
// acquired the lock ourselves
return TRI_ERROR_LOCKED;
}
/// @brief unlock a collection
int ClusterTransactionCollection::doUnlock(AccessMode::Type type, int nestingLevel) {
if (!AccessMode::isWriteOrExclusive(type) || !AccessMode::isWriteOrExclusive(_lockType)) {
_lockType = AccessMode::Type::NONE;
return TRI_ERROR_NO_ERROR;
}
if (_transaction->hasHint(transaction::Hints::Hint::LOCK_NEVER)) {
// never unlock
return TRI_ERROR_NO_ERROR;
}
TRI_ASSERT(_collection != nullptr);
TRI_ASSERT(isLocked());
if (_nestingLevel < nestingLevel) {
// only process our own collections
return TRI_ERROR_NO_ERROR;
}
if (!AccessMode::isWriteOrExclusive(type) && AccessMode::isWriteOrExclusive(_lockType)) {
// do not remove a write-lock if a read-unlock was requested!
return TRI_ERROR_NO_ERROR;
}
if (AccessMode::isWriteOrExclusive(type) && !AccessMode::isWriteOrExclusive(_lockType)) {
// we should never try to write-unlock a collection that we have only
// read-locked
LOG_TOPIC("e8aab", ERR, arangodb::Logger::FIXME) << "logic error in doUnlock";
TRI_ASSERT(false);
return TRI_ERROR_INTERNAL;
}
TRI_ASSERT(_collection);
_lockType = AccessMode::Type::NONE;
return TRI_ERROR_NO_ERROR;
}