1
0
Fork 0

fixed compactor locks

This commit is contained in:
jsteemann 2016-07-26 10:22:44 +02:00
parent b54f0a15d8
commit 9af8a30a36
9 changed files with 172 additions and 321 deletions

View File

@ -179,7 +179,6 @@ void BootstrapFeature::unprepare() {
}
}
} else {
std::vector<std::string> names = databaseFeature->getDatabaseNames();
for (auto& name : databaseFeature->getDatabaseNames()) {
TRI_vocbase_t* vocbase = databaseFeature->useDatabase(name);

View File

@ -1073,7 +1073,6 @@ int DatabaseFeature::createApplicationDirectory(std::string const& name, std::st
int DatabaseFeature::iterateDatabases(VPackSlice const& databases) {
V8DealerFeature* dealer = ApplicationServer::getFeature<V8DealerFeature>("V8Dealer");
std::string const appPath = dealer->appPath();
std::string const databasePath = ApplicationServer::getFeature<DatabasePathFeature>("DatabasePath")->subdirectoryName("databases");
StorageEngine* engine = ApplicationServer::getFeature<EngineSelectorFeature>("EngineSelector")->ENGINE;

View File

@ -22,6 +22,7 @@
////////////////////////////////////////////////////////////////////////////////
#include "CollectionExport.h"
#include "Basics/WriteLocker.h"
#include "Indexes/PrimaryIndex.h"
#include "Utils/CollectionGuard.h"
#include "Utils/SingleCollectionTransaction.h"
@ -63,18 +64,14 @@ CollectionExport::~CollectionExport() {
}
void CollectionExport::run(uint64_t maxWaitTime, size_t limit) {
// try to acquire the exclusive lock on the compaction
while (!TRI_CheckAndLockCompactorVocBase(_document->_vocbase)) {
// didn't get it. try again...
usleep(5000);
{
// try to acquire the exclusive lock on the compaction
WRITE_LOCKER_EVENTUAL(locker, _document->_vocbase->_compactionBlockers._lock, 5000);
// create a ditch under the compaction lock
_ditch = _document->ditches()->createDocumentDitch(false, __FILE__, __LINE__);
}
// create a ditch under the compaction lock
_ditch = _document->ditches()->createDocumentDitch(false, __FILE__, __LINE__);
// release the lock
TRI_UnlockCompactorVocBase(_document->_vocbase);
// now we either have a ditch or not
if (_ditch == nullptr) {
THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY);

View File

@ -23,6 +23,7 @@
#include "CollectionKeys.h"
#include "Basics/StaticStrings.h"
#include "Basics/WriteLocker.h"
#include "Utils/CollectionGuard.h"
#include "Utils/SingleCollectionTransaction.h"
#include "Utils/StandaloneTransactionContext.h"
@ -88,18 +89,14 @@ void CollectionKeys::create(TRI_voc_tick_t maxTick) {
arangodb::wal::LogfileManager::instance()->waitForCollectorQueue(
_document->_info.id(), 30.0);
// try to acquire the exclusive lock on the compaction
while (!TRI_CheckAndLockCompactorVocBase(_document->_vocbase)) {
// didn't get it. try again...
usleep(5000);
{
// try to acquire the exclusive lock on the compaction
WRITE_LOCKER_EVENTUAL(locker, _document->_vocbase->_compactionBlockers._lock, 5000);
// create a ditch under the compaction lock
_ditch = _document->ditches()->createDocumentDitch(false, __FILE__, __LINE__);
}
// create a ditch under the compaction lock
_ditch = _document->ditches()->createDocumentDitch(false, __FILE__, __LINE__);
// release the lock
TRI_UnlockCompactorVocBase(_document->_vocbase);
// now we either have a ditch or not
if (_ditch == nullptr) {
THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY);

View File

@ -25,8 +25,9 @@
#include "ApplicationFeatures/ApplicationServer.h"
#include "Basics/ReadLocker.h"
#include "Basics/WriteLocker.h"
#include "Basics/files.h"
#include "Basics/tri-strings.h"
//#include "Basics/tri-strings.h"
#include "Logger/Logger.h"
#include "Utils/CursorRepository.h"
#include "VocBase/Ditch.h"
@ -259,44 +260,47 @@ void TRI_CleanupVocBase(void* data) {
}
// check if we can get the compactor lock exclusively
if (TRI_CheckAndLockCompactorVocBase(vocbase)) {
try {
READ_LOCKER(readLocker, vocbase->_collectionsLock);
// copy all collections
collections = vocbase->_collections;
} catch (...) {
collections.clear();
// check if compaction is currently disallowed
{
TRY_WRITE_LOCKER(locker, vocbase->_compactionBlockers._lock);
if (locker.isLocked()) {
try {
READ_LOCKER(readLocker, vocbase->_collectionsLock);
// copy all collections
collections = vocbase->_collections;
} catch (...) {
collections.clear();
}
for (auto& collection : collections) {
TRI_ASSERT(collection != nullptr);
TRI_document_collection_t* document;
{
READ_LOCKER(readLocker, collection->_lock);
document = collection->_collection;
}
if (document == nullptr) {
// collection currently not loaded
continue;
}
TRI_ASSERT(document != nullptr);
// we're the only ones that can unload the collection, so using
// the collection pointer outside the lock is ok
// maybe cleanup indexes, unload the collection or some datafiles
// clean indexes?
if (iterations % (uint64_t)CLEANUP_INDEX_ITERATIONS == 0) {
document->cleanupIndexes(document);
}
CleanupDocumentCollection(collection, document);
}
}
for (auto& collection : collections) {
TRI_ASSERT(collection != nullptr);
TRI_document_collection_t* document;
{
READ_LOCKER(readLocker, collection->_lock);
document = collection->_collection;
}
if (document == nullptr) {
// collection currently not loaded
continue;
}
TRI_ASSERT(document != nullptr);
// we're the only ones that can unload the collection, so using
// the collection pointer outside the lock is ok
// maybe cleanup indexes, unload the collection or some datafiles
// clean indexes?
if (iterations % (uint64_t)CLEANUP_INDEX_ITERATIONS == 0) {
document->cleanupIndexes(document);
}
CleanupDocumentCollection(collection, document);
}
TRI_UnlockCompactorVocBase(vocbase);
}
if (vocbase->_state >= 1) {

View File

@ -165,10 +165,7 @@ struct compaction_info_t {
bool _keepDeletions;
};
////////////////////////////////////////////////////////////////////////////////
/// @brief determine the number of documents in the collection
///////////////////////////////////////////////////////////////////////////////
static uint64_t GetNumberOfDocuments(TRI_document_collection_t* document) {
TRI_vocbase_t* vocbase = document->_vocbase;
@ -962,88 +959,15 @@ static bool CompactifyDocumentCollection(TRI_document_collection_t* document) {
return true;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief try to write-lock the compaction
/// returns true if lock acquisition was successful. the caller is responsible
/// to free the write lock eventually
////////////////////////////////////////////////////////////////////////////////
static bool TryLockCompaction(TRI_vocbase_t* vocbase) {
return TRI_TryWriteLockReadWriteLock(&vocbase->_compactionBlockers._lock);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief write-lock the compaction
////////////////////////////////////////////////////////////////////////////////
static void LockCompaction(TRI_vocbase_t* vocbase) {
while (!TryLockCompaction(vocbase)) {
// cycle until we have acquired the write-lock
usleep(1000);
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief write-unlock the compaction
////////////////////////////////////////////////////////////////////////////////
static void UnlockCompaction(TRI_vocbase_t* vocbase) {
TRI_WriteUnlockReadWriteLock(&vocbase->_compactionBlockers._lock);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief atomic check and lock for running the compaction
/// if this function returns true, it has acquired a write-lock on the
/// compactionBlockers structure, which the caller must free eventually
////////////////////////////////////////////////////////////////////////////////
static bool CheckAndLockCompaction(TRI_vocbase_t* vocbase) {
// check if we can acquire the write lock instantly
if (!TryLockCompaction(vocbase)) {
// couldn't acquire the write lock
return false;
}
// we are now holding the write lock
double now = TRI_microtime();
// check if we have a still-valid compaction blocker
for (auto const& blocker : vocbase->_compactionBlockers._data) {
if (blocker._expires > now) {
// found a compaction blocker. unlock and return
UnlockCompaction(vocbase);
return false;
}
}
return true;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief initialize the compaction blockers structure
////////////////////////////////////////////////////////////////////////////////
int TRI_InitCompactorVocBase(TRI_vocbase_t* vocbase) {
TRI_InitReadWriteLock(&vocbase->_compactionBlockers._lock);
return TRI_ERROR_NO_ERROR;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief destroy the compaction blockers structure
////////////////////////////////////////////////////////////////////////////////
void TRI_DestroyCompactorVocBase(TRI_vocbase_t* vocbase) {
TRI_DestroyReadWriteLock(&vocbase->_compactionBlockers._lock);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief remove data of expired compaction blockers
////////////////////////////////////////////////////////////////////////////////
bool TRI_CleanupCompactorVocBase(TRI_vocbase_t* vocbase) {
// check if we can instantly acquire the lock
if (!TryLockCompaction(vocbase)) {
TRY_WRITE_LOCKER(locker, vocbase->_compactionBlockers._lock);
if (!locker.isLocked()) {
// couldn't acquire lock
return false;
}
@ -1064,8 +988,6 @@ bool TRI_CleanupCompactorVocBase(TRI_vocbase_t* vocbase) {
}
}
UnlockCompaction(vocbase);
return true;
}
@ -1084,16 +1006,17 @@ int TRI_InsertBlockerCompactorVocBase(TRI_vocbase_t* vocbase, double lifetime,
blocker._expires = TRI_microtime() + lifetime;
int res = TRI_ERROR_NO_ERROR;
LockCompaction(vocbase);
try {
vocbase->_compactionBlockers._data.push_back(blocker);
} catch (...) {
res = TRI_ERROR_OUT_OF_MEMORY;
{
WRITE_LOCKER_EVENTUAL(locker, vocbase->_compactionBlockers._lock, 1000);
try {
vocbase->_compactionBlockers._data.push_back(blocker);
} catch (...) {
res = TRI_ERROR_OUT_OF_MEMORY;
}
}
UnlockCompaction(vocbase);
if (res != TRI_ERROR_NO_ERROR) {
return res;
}
@ -1109,47 +1032,20 @@ int TRI_InsertBlockerCompactorVocBase(TRI_vocbase_t* vocbase, double lifetime,
int TRI_TouchBlockerCompactorVocBase(TRI_vocbase_t* vocbase, TRI_voc_tick_t id,
double lifetime) {
bool found = false;
if (lifetime <= 0.0) {
return TRI_ERROR_BAD_PARAMETER;
}
LockCompaction(vocbase);
WRITE_LOCKER_EVENTUAL(locker, vocbase->_compactionBlockers._lock, 1000);
for (auto& blocker : vocbase->_compactionBlockers._data) {
if (blocker._id == id) {
blocker._expires = TRI_microtime() + lifetime;
found = true;
break;
return TRI_ERROR_NO_ERROR;
}
}
UnlockCompaction(vocbase);
if (!found) {
return TRI_ERROR_ARANGO_DOCUMENT_NOT_FOUND;
}
return TRI_ERROR_NO_ERROR;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief atomically check-and-lock the compactor
/// if the function returns true, then a write-lock on the compactor was
/// acquired, which must eventually be freed by the caller
////////////////////////////////////////////////////////////////////////////////
bool TRI_CheckAndLockCompactorVocBase(TRI_vocbase_t* vocbase) {
return TryLockCompaction(vocbase);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief unlock the compactor
////////////////////////////////////////////////////////////////////////////////
void TRI_UnlockCompactorVocBase(TRI_vocbase_t* vocbase) {
UnlockCompaction(vocbase);
return TRI_ERROR_ARANGO_DOCUMENT_NOT_FOUND;
}
////////////////////////////////////////////////////////////////////////////////
@ -1158,9 +1054,7 @@ void TRI_UnlockCompactorVocBase(TRI_vocbase_t* vocbase) {
int TRI_RemoveBlockerCompactorVocBase(TRI_vocbase_t* vocbase,
TRI_voc_tick_t id) {
bool found = false;
LockCompaction(vocbase);
WRITE_LOCKER_EVENTUAL(locker, vocbase->_compactionBlockers._lock, 1000);
size_t const n = vocbase->_compactionBlockers._data.size();
@ -1168,18 +1062,26 @@ int TRI_RemoveBlockerCompactorVocBase(TRI_vocbase_t* vocbase,
auto& blocker = vocbase->_compactionBlockers._data[i];
if (blocker._id == id) {
vocbase->_compactionBlockers._data.erase(vocbase->_compactionBlockers._data.begin() + i);
found = true;
break;
return TRI_ERROR_NO_ERROR;
}
}
UnlockCompaction(vocbase);
return TRI_ERROR_ARANGO_DOCUMENT_NOT_FOUND;
}
if (!found) {
return TRI_ERROR_ARANGO_DOCUMENT_NOT_FOUND;
/// @brief check whether there is an active compaction blocker
/// note that this must be called while holding the compactionBlockers lock
static bool HasActiveBlockers(TRI_vocbase_t* vocbase) {
double const now = TRI_microtime();
// check if we have a still-valid compaction blocker
for (auto const& blocker : vocbase->_compactionBlockers._data) {
if (blocker._expires > now) {
// found a compaction blocker
return true;
}
}
return TRI_ERROR_NO_ERROR;
return false;
}
////////////////////////////////////////////////////////////////////////////////
@ -1199,101 +1101,103 @@ void TRI_CompactorVocBase(void* data) {
// compaction loop
int state = vocbase->_state;
// check if compaction is currently disallowed
if (CheckAndLockCompaction(vocbase)) {
// compaction is currently allowed
double now = TRI_microtime();
numCompacted = 0;
{
// check if compaction is currently disallowed
TRY_WRITE_LOCKER(compactionLocker, vocbase->_compactionBlockers._lock);
try {
READ_LOCKER(readLocker, vocbase->_collectionsLock);
// copy all collections
collections = vocbase->_collections;
} catch (...) {
collections.clear();
}
if (compactionLocker.isLocked() && !HasActiveBlockers(vocbase)) {
// compaction is currently allowed
double now = TRI_microtime();
numCompacted = 0;
bool worked;
try {
READ_LOCKER(readLocker, vocbase->_collectionsLock);
// copy all collections
collections = vocbase->_collections;
} catch (...) {
collections.clear();
}
for (auto& collection : collections) {
{
TRY_READ_LOCKER(readLocker, collection->_lock);
bool worked;
if (!readLocker.isLocked()) {
// if we can't acquire the read lock instantly, we continue directly
// we don't want to stall here for too long
continue;
}
for (auto& collection : collections) {
{
TRY_READ_LOCKER(readLocker, collection->_lock);
TRI_document_collection_t* document = collection->_collection;
if (document == nullptr) {
continue;
}
worked = false;
bool doCompact = document->_info.doCompact();
// for document collection, compactify datafiles
if (collection->_status == TRI_VOC_COL_STATUS_LOADED && doCompact) {
// check whether someone else holds a read-lock on the compaction
// lock
TRY_WRITE_LOCKER(locker, document->_compactionLock);
if (!locker.isLocked()) {
// someone else is holding the compactor lock, we'll not compact
if (!readLocker.isLocked()) {
// if we can't acquire the read lock instantly, we continue directly
// we don't want to stall here for too long
continue;
}
try {
if (document->_lastCompaction + COMPACTOR_COLLECTION_INTERVAL <=
now) {
auto ce = document->ditches()->createCompactionDitch(__FILE__,
__LINE__);
TRI_document_collection_t* document = collection->_collection;
if (ce == nullptr) {
// out of memory
LOG_TOPIC(WARN, Logger::COMPACTOR) << "out of memory when trying to create compaction ditch";
} else {
try {
worked = CompactifyDocumentCollection(document);
if (!worked) {
// set compaction stamp
document->_lastCompaction = now;
}
// if we worked, then we don't set the compaction stamp to
// force
// another round of compaction
} catch (...) {
LOG_TOPIC(ERR, Logger::COMPACTOR) << "an unknown exception occurred during compaction";
// in case an error occurs, we must still free this ditch
}
document->ditches()->freeDitch(ce);
}
}
} catch (...) {
// in case an error occurs, we must still relase the lock
LOG_TOPIC(ERR, Logger::COMPACTOR) << "an unknown exception occurred during compaction";
if (document == nullptr) {
continue;
}
worked = false;
bool doCompact = document->_info.doCompact();
// for document collection, compactify datafiles
if (collection->_status == TRI_VOC_COL_STATUS_LOADED && doCompact) {
// check whether someone else holds a read-lock on the compaction
// lock
TRY_WRITE_LOCKER(locker, document->_compactionLock);
if (!locker.isLocked()) {
// someone else is holding the compactor lock, we'll not compact
continue;
}
try {
if (document->_lastCompaction + COMPACTOR_COLLECTION_INTERVAL <=
now) {
auto ce = document->ditches()->createCompactionDitch(__FILE__,
__LINE__);
if (ce == nullptr) {
// out of memory
LOG_TOPIC(WARN, Logger::COMPACTOR) << "out of memory when trying to create compaction ditch";
} else {
try {
worked = CompactifyDocumentCollection(document);
if (!worked) {
// set compaction stamp
document->_lastCompaction = now;
}
// if we worked, then we don't set the compaction stamp to
// force
// another round of compaction
} catch (...) {
LOG_TOPIC(ERR, Logger::COMPACTOR) << "an unknown exception occurred during compaction";
// in case an error occurs, we must still free this ditch
}
document->ditches()->freeDitch(ce);
}
}
} catch (...) {
// in case an error occurs, we must still relase the lock
LOG_TOPIC(ERR, Logger::COMPACTOR) << "an unknown exception occurred during compaction";
}
}
} // end of lock
if (worked) {
++numCompacted;
// signal the cleanup thread that we worked and that it can now wake
// up
TRI_LockCondition(&vocbase->_cleanupCondition);
TRI_SignalCondition(&vocbase->_cleanupCondition);
TRI_UnlockCondition(&vocbase->_cleanupCondition);
}
} // end of lock
if (worked) {
++numCompacted;
// signal the cleanup thread that we worked and that it can now wake
// up
TRI_LockCondition(&vocbase->_cleanupCondition);
TRI_SignalCondition(&vocbase->_cleanupCondition);
TRI_UnlockCondition(&vocbase->_cleanupCondition);
}
}
UnlockCompaction(vocbase);
}
if (numCompacted > 0) {

View File

@ -25,65 +25,23 @@
#define ARANGOD_VOC_BASE_COMPACTOR_H 1
#include "Basics/Common.h"
#include "VocBase/voc-types.h"
struct TRI_vocbase_t;
////////////////////////////////////////////////////////////////////////////////
/// @brief initialize the compaction blockers structure
////////////////////////////////////////////////////////////////////////////////
int TRI_InitCompactorVocBase(TRI_vocbase_t*);
////////////////////////////////////////////////////////////////////////////////
/// @brief destroy the compaction blockers structure
////////////////////////////////////////////////////////////////////////////////
void TRI_DestroyCompactorVocBase(TRI_vocbase_t*);
////////////////////////////////////////////////////////////////////////////////
/// @brief remove data of expired compaction blockers
////////////////////////////////////////////////////////////////////////////////
bool TRI_CleanupCompactorVocBase(TRI_vocbase_t*);
////////////////////////////////////////////////////////////////////////////////
/// @brief insert a compaction blocker
////////////////////////////////////////////////////////////////////////////////
int TRI_InsertBlockerCompactorVocBase(TRI_vocbase_t*, double, TRI_voc_tick_t*);
////////////////////////////////////////////////////////////////////////////////
/// @brief touch an existing compaction blocker
////////////////////////////////////////////////////////////////////////////////
int TRI_TouchBlockerCompactorVocBase(TRI_vocbase_t*, TRI_voc_tick_t, double);
////////////////////////////////////////////////////////////////////////////////
/// @brief remove an existing compaction blocker
////////////////////////////////////////////////////////////////////////////////
int TRI_RemoveBlockerCompactorVocBase(TRI_vocbase_t*, TRI_voc_tick_t);
////////////////////////////////////////////////////////////////////////////////
/// @brief atomically check-and-lock the compactor
/// if the function returns true, then a write-lock on the compactor was
/// acquired, which must eventually be freed by the caller
////////////////////////////////////////////////////////////////////////////////
bool TRI_CheckAndLockCompactorVocBase(TRI_vocbase_t*);
////////////////////////////////////////////////////////////////////////////////
/// @brief unlock the compactor
////////////////////////////////////////////////////////////////////////////////
void TRI_UnlockCompactorVocBase(TRI_vocbase_t*);
////////////////////////////////////////////////////////////////////////////////
/// @brief compactor event loop
////////////////////////////////////////////////////////////////////////////////
void TRI_CompactorVocBase(void*);
#endif

View File

@ -1264,8 +1264,6 @@ TRI_vocbase_t* TRI_OpenVocBase(char const* path,
return nullptr;
}
TRI_InitCompactorVocBase(vocbase);
// .............................................................................
// scan directory for collections
// .............................................................................
@ -1277,7 +1275,6 @@ TRI_vocbase_t* TRI_OpenVocBase(char const* path,
int res = ScanPath(vocbase, vocbase->_path, isUpgrade, iterateMarkers);
if (res != TRI_ERROR_NO_ERROR) {
TRI_DestroyCompactorVocBase(vocbase);
delete vocbase;
TRI_set_errno(res);
@ -1384,8 +1381,6 @@ void TRI_DestroyVocBase(TRI_vocbase_t* vocbase) {
for (auto& collection : vocbase->_collections) {
delete collection;
}
TRI_DestroyCompactorVocBase(vocbase);
}
////////////////////////////////////////////////////////////////////////////////

View File

@ -64,12 +64,10 @@ struct compaction_blocker_t {
};
struct compaction_blockers_t {
TRI_read_write_lock_t _lock;
arangodb::basics::ReadWriteLock _lock;
std::vector<compaction_blocker_t> _data;
};
extern bool IGNORE_DATAFILE_ERRORS;
////////////////////////////////////////////////////////////////////////////////
/// @brief tries to read lock the vocbase collection status
////////////////////////////////////////////////////////////////////////////////