mirror of https://gitee.com/bigwinds/arangodb
661 lines
23 KiB
C++
661 lines
23 KiB
C++
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief ditches for documents, datafiles etc.
|
|
///
|
|
/// @file
|
|
///
|
|
/// DISCLAIMER
|
|
///
|
|
/// Copyright 2014 ArangoDB GmbH, Cologne, Germany
|
|
/// Copyright 2004-2014 triAGENS 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 Dr. Frank Celler
|
|
/// @author Copyright 2014, ArangoDB GmbH, Cologne, Germany
|
|
/// @author Copyright 2011-2013, triAGENS GmbH, Cologne, Germany
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "Ditch.h"
|
|
#include "Basics/logging.h"
|
|
#include "Basics/MutexLocker.h"
|
|
#include "VocBase/document-collection.h"
|
|
|
|
using namespace triagens::arango;
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- class Ditch
|
|
// -----------------------------------------------------------------------------
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- constructors / destructors
|
|
// -----------------------------------------------------------------------------
|
|
|
|
Ditch::Ditch (Ditches* ditches,
|
|
char const* filename,
|
|
int line)
|
|
: _ditches(ditches),
|
|
_prev(nullptr),
|
|
_next(nullptr),
|
|
_filename(filename),
|
|
_line(line) {
|
|
}
|
|
|
|
Ditch::~Ditch () {
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- public methods
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief return the associated collection
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
TRI_document_collection_t* Ditch::collection () const {
|
|
return _ditches->collection();
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- class DocumentDitch
|
|
// -----------------------------------------------------------------------------
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- constructors / destructors
|
|
// -----------------------------------------------------------------------------
|
|
|
|
DocumentDitch::DocumentDitch (Ditches* ditches,
|
|
bool usedByTransaction,
|
|
char const* filename,
|
|
int line)
|
|
: Ditch(ditches, filename, line),
|
|
_usedByExternal(0),
|
|
_usedByTransaction(usedByTransaction) {
|
|
}
|
|
|
|
DocumentDitch::~DocumentDitch () {
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief sets the _usedByTransaction flag, using the required lock
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void DocumentDitch::setUsedByTransaction () {
|
|
auto callback = [this] () -> void {
|
|
_usedByTransaction = true;
|
|
};
|
|
_ditches->executeProtected(callback);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief increases the _usedByExternal value, using the required lock
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void DocumentDitch::setUsedByExternal () {
|
|
auto callback = [this] () -> void {
|
|
++_usedByExternal;
|
|
};
|
|
_ditches->executeProtected(callback);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- class ReplicationDitch
|
|
// -----------------------------------------------------------------------------
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- constructors / destructors
|
|
// -----------------------------------------------------------------------------
|
|
|
|
ReplicationDitch::ReplicationDitch (Ditches* ditches,
|
|
char const* filename,
|
|
int line)
|
|
: Ditch(ditches, filename, line) {
|
|
}
|
|
|
|
ReplicationDitch::~ReplicationDitch () {
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- class CompactionDitch
|
|
// -----------------------------------------------------------------------------
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- constructors / destructors
|
|
// -----------------------------------------------------------------------------
|
|
|
|
CompactionDitch::CompactionDitch (Ditches* ditches,
|
|
char const* filename,
|
|
int line)
|
|
: Ditch(ditches, filename, line) {
|
|
}
|
|
|
|
CompactionDitch::~CompactionDitch () {
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- class DropDatafileDitch
|
|
// -----------------------------------------------------------------------------
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- constructors / destructors
|
|
// -----------------------------------------------------------------------------
|
|
|
|
DropDatafileDitch::DropDatafileDitch (Ditches* ditches,
|
|
TRI_datafile_t* datafile,
|
|
void* data,
|
|
std::function<void(TRI_datafile_t*, void*)> callback,
|
|
char const* filename,
|
|
int line)
|
|
: Ditch(ditches, filename, line),
|
|
_datafile(datafile),
|
|
_data(data),
|
|
_callback(callback) {
|
|
}
|
|
|
|
DropDatafileDitch::~DropDatafileDitch () {
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- class RenameDatafileDitch
|
|
// -----------------------------------------------------------------------------
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- constructors / destructors
|
|
// -----------------------------------------------------------------------------
|
|
|
|
RenameDatafileDitch::RenameDatafileDitch (Ditches* ditches,
|
|
TRI_datafile_t* datafile,
|
|
void* data,
|
|
std::function<void(TRI_datafile_t*, void*)> callback,
|
|
char const* filename,
|
|
int line)
|
|
: Ditch(ditches, filename, line),
|
|
_datafile(datafile),
|
|
_data(data),
|
|
_callback(callback) {
|
|
}
|
|
|
|
RenameDatafileDitch::~RenameDatafileDitch () {
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- class UnloadCollectionDitch
|
|
// -----------------------------------------------------------------------------
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- constructors / destructors
|
|
// -----------------------------------------------------------------------------
|
|
|
|
UnloadCollectionDitch::UnloadCollectionDitch (Ditches* ditches,
|
|
TRI_collection_t* collection,
|
|
void* data,
|
|
std::function<bool(TRI_collection_t*, void*)> callback,
|
|
char const* filename,
|
|
int line)
|
|
: Ditch(ditches, filename, line),
|
|
_collection(collection),
|
|
_data(data),
|
|
_callback(callback) {
|
|
}
|
|
|
|
UnloadCollectionDitch::~UnloadCollectionDitch () {
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- class DropCollectionDitch
|
|
// -----------------------------------------------------------------------------
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- constructors / destructors
|
|
// -----------------------------------------------------------------------------
|
|
|
|
DropCollectionDitch::DropCollectionDitch (Ditches* ditches,
|
|
TRI_collection_t* collection,
|
|
void* data,
|
|
std::function<bool(TRI_collection_t*, void*)> callback,
|
|
char const* filename,
|
|
int line)
|
|
: Ditch(ditches, filename, line),
|
|
_collection(collection),
|
|
_data(data),
|
|
_callback(callback) {
|
|
}
|
|
|
|
DropCollectionDitch::~DropCollectionDitch () {
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- class Ditches
|
|
// -----------------------------------------------------------------------------
|
|
|
|
Ditches::Ditches (TRI_document_collection_t* collection)
|
|
: _collection(collection),
|
|
_lock(),
|
|
_begin(nullptr),
|
|
_end(nullptr),
|
|
_numDocumentDitches(0) {
|
|
|
|
TRI_ASSERT(_collection != nullptr);
|
|
|
|
}
|
|
|
|
Ditches::~Ditches () {
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- public methods
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief destroy the ditches - to be called on shutdown only
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void Ditches::destroy () {
|
|
auto* ptr = _begin;
|
|
|
|
while (ptr != nullptr) {
|
|
auto* next = ptr->next();
|
|
auto const type = ptr->type();
|
|
|
|
if (type == Ditch::TRI_DITCH_COLLECTION_UNLOAD ||
|
|
type == Ditch::TRI_DITCH_COLLECTION_DROP ||
|
|
type == Ditch::TRI_DITCH_DATAFILE_DROP ||
|
|
type == Ditch::TRI_DITCH_DATAFILE_RENAME ||
|
|
type == Ditch::TRI_DITCH_REPLICATION ||
|
|
type == Ditch::TRI_DITCH_COMPACTION) {
|
|
|
|
delete ptr;
|
|
}
|
|
else if (type == Ditch::TRI_DITCH_DOCUMENT) {
|
|
LOG_ERROR("logic error. shouldn't have document ditches on unload");
|
|
}
|
|
else {
|
|
LOG_ERROR("unknown ditch type");
|
|
}
|
|
|
|
ptr = next;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief return the associated collection
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
TRI_document_collection_t* Ditches::collection () const {
|
|
return _collection;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief run a user-defined function under the lock
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void Ditches::executeProtected (std::function<void()> callback) {
|
|
MUTEX_LOCKER(_lock);
|
|
callback();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief process the first element from the list
|
|
/// the list will remain unchanged if the first element is either a
|
|
/// DocumentDitch, a ReplicationDitch or a CompactionDitch, or if the list
|
|
/// contains any DocumentDitches.
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
Ditch* Ditches::process (bool& popped,
|
|
std::function<bool(Ditch const*)> callback) {
|
|
popped = false;
|
|
|
|
MUTEX_LOCKER(_lock);
|
|
|
|
auto ditch = _begin;
|
|
|
|
if (ditch == nullptr) {
|
|
// nothing to do
|
|
return nullptr;
|
|
}
|
|
|
|
TRI_ASSERT(ditch != nullptr);
|
|
|
|
auto const type = ditch->type();
|
|
|
|
// if it is a DocumentDitch, it means that there is still a reference held
|
|
// to document data in a datafile. We must then not unload or remove a file
|
|
if (type == Ditch::TRI_DITCH_DOCUMENT ||
|
|
type == Ditch::TRI_DITCH_REPLICATION ||
|
|
type == Ditch::TRI_DITCH_COMPACTION ||
|
|
_numDocumentDitches > 0) {
|
|
// did not find anything at the head of the barrier list or found an element marker
|
|
// this means we must exit and cannot throw away datafiles and can unload collections
|
|
return nullptr;
|
|
}
|
|
|
|
// no DocumentDitch at the head of the ditches list. This means that there is
|
|
// some other action we can perform (i.e. unloading a datafile or a collection)
|
|
|
|
// note that there is no need to check the entire list for a DocumentDitch as
|
|
// the list is filled up in chronological order. New ditches are always added to the
|
|
// tail of the list, and if we have the following list
|
|
// HEAD -> TRI_DITCH_DATAFILE_CALLBACK -> TRI_DITCH_DOCUMENT
|
|
// then it is still safe to execute the datafile callback operation, even if there
|
|
// is a TRI_DITCH_DOCUMENT after it.
|
|
// This is the case because the TRI_DITCH_DATAFILE_CALLBACK is only put into the
|
|
// ditches list after changing the pointers in all headers. After the pointers are
|
|
// changed, it is safe to unload/remove an old datafile (that noone points to). And
|
|
// any newer TRI_DITCH_DOCUMENTs will always reference data inside other datafiles.
|
|
|
|
if (! callback(ditch)) {
|
|
return ditch;
|
|
}
|
|
|
|
// found an element to go on with - now unlink the element from the list
|
|
unlink(ditch);
|
|
popped = true;
|
|
|
|
return ditch;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief check whether the ditches contain a ditch of a certain type
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
bool Ditches::contains (Ditch::DitchType type) {
|
|
MUTEX_LOCKER(_lock);
|
|
|
|
if (type == Ditch::TRI_DITCH_DOCUMENT) {
|
|
// shortcut
|
|
return (_numDocumentDitches > 0);
|
|
}
|
|
|
|
auto const* ptr = _begin;
|
|
|
|
while (ptr != nullptr) {
|
|
if (ptr->type() == type) {
|
|
return true;
|
|
}
|
|
|
|
ptr = ptr->_next;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief removes and frees a ditch
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void Ditches::freeDitch (Ditch* ditch) {
|
|
TRI_ASSERT(ditch != nullptr);
|
|
|
|
{
|
|
MUTEX_LOCKER(_lock);
|
|
|
|
unlink(ditch);
|
|
|
|
if (ditch->type() == Ditch::TRI_DITCH_DOCUMENT) {
|
|
// decrease counter
|
|
--_numDocumentDitches;
|
|
}
|
|
}
|
|
|
|
delete ditch;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief removes and frees a ditch
|
|
/// this is used for ditches used by transactions or by externals to protect
|
|
/// the flags by the lock
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void Ditches::freeDocumentDitch (DocumentDitch* ditch,
|
|
bool fromTransaction) {
|
|
TRI_ASSERT(ditch != nullptr);
|
|
|
|
bool shouldFree = false;
|
|
{
|
|
MUTEX_LOCKER(_lock); // FIX_MUTEX
|
|
|
|
// First see who might still be using the ditch:
|
|
if (fromTransaction) {
|
|
TRI_ASSERT(ditch->_usedByTransaction == true);
|
|
ditch->_usedByTransaction = false;
|
|
}
|
|
else {
|
|
// note: _usedByExternal may or may not be set when we get here
|
|
// the reason is that there are ditches not linked at all
|
|
// (when a ditch is created ahead of operations but the operations are
|
|
// not executed etc.)
|
|
if (ditch->_usedByExternal > 0) {
|
|
--ditch->_usedByExternal;
|
|
}
|
|
}
|
|
|
|
if (ditch->_usedByTransaction == false &&
|
|
ditch->_usedByExternal == 0) {
|
|
// Really free it:
|
|
|
|
unlink(ditch);
|
|
|
|
// decrease counter
|
|
--_numDocumentDitches;
|
|
|
|
// free the ditch
|
|
shouldFree = true;
|
|
}
|
|
}
|
|
|
|
if (shouldFree) {
|
|
delete ditch;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief creates a new document ditch and links it
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DocumentDitch* Ditches::createDocumentDitch (bool usedByTransaction,
|
|
char const* filename,
|
|
int line) {
|
|
try {
|
|
auto ditch = new DocumentDitch(this, usedByTransaction, filename, line);
|
|
link(ditch);
|
|
|
|
return ditch;
|
|
}
|
|
catch (...) {
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief creates a new replication ditch and links it
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
ReplicationDitch* Ditches::createReplicationDitch (char const* filename,
|
|
int line) {
|
|
try {
|
|
auto ditch = new ReplicationDitch(this, filename, line);
|
|
link(ditch);
|
|
|
|
return ditch;
|
|
}
|
|
catch (...) {
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief creates a new compaction ditch and links it
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
CompactionDitch* Ditches::createCompactionDitch (char const* filename,
|
|
int line) {
|
|
try {
|
|
auto ditch = new CompactionDitch(this, filename, line);
|
|
link(ditch);
|
|
|
|
return ditch;
|
|
}
|
|
catch (...) {
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief creates a new datafile deletion ditch
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DropDatafileDitch* Ditches::createDropDatafileDitch (TRI_datafile_t* datafile,
|
|
void* data,
|
|
std::function<void(struct TRI_datafile_s*, void*)> callback,
|
|
char const* filename,
|
|
int line) {
|
|
try {
|
|
auto ditch = new DropDatafileDitch(this, datafile, data, callback, filename, line);
|
|
link(ditch);
|
|
|
|
return ditch;
|
|
}
|
|
catch (...) {
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief creates a new datafile rename ditch
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
RenameDatafileDitch* Ditches::createRenameDatafileDitch (TRI_datafile_t* datafile,
|
|
void* data,
|
|
std::function<void(struct TRI_datafile_s*, void*)> callback,
|
|
char const* filename,
|
|
int line) {
|
|
try {
|
|
auto ditch = new RenameDatafileDitch(this, datafile, data, callback, filename, line);
|
|
link(ditch);
|
|
|
|
return ditch;
|
|
}
|
|
catch (...) {
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief creates a new collection unload ditch
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
UnloadCollectionDitch* Ditches::createUnloadCollectionDitch (TRI_collection_t* collection,
|
|
void* data,
|
|
std::function<bool(TRI_collection_t*, void*)> callback,
|
|
char const* filename,
|
|
int line) {
|
|
try {
|
|
auto ditch = new UnloadCollectionDitch(this, collection, data, callback, filename, line);
|
|
link(ditch);
|
|
|
|
return ditch;
|
|
}
|
|
catch (...) {
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief creates a new datafile drop ditch
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
DropCollectionDitch* Ditches::createDropCollectionDitch (TRI_collection_t* collection,
|
|
void* data,
|
|
std::function<bool(TRI_collection_t*, void*)> callback,
|
|
char const* filename,
|
|
int line) {
|
|
try {
|
|
auto ditch = new DropCollectionDitch(this, collection, data, callback, filename, line);
|
|
link(ditch);
|
|
|
|
return ditch;
|
|
}
|
|
catch (...) {
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- private methods
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief inserts the ditch into the linked list of ditches
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void Ditches::link (Ditch* ditch) {
|
|
TRI_ASSERT(ditch != nullptr);
|
|
|
|
MUTEX_LOCKER(_lock); // FIX_MUTEX
|
|
|
|
// empty list
|
|
if (_end == nullptr) {
|
|
ditch->_next = nullptr;
|
|
ditch->_prev = nullptr;
|
|
|
|
_begin = ditch;
|
|
_end = ditch;
|
|
}
|
|
|
|
// add to the end
|
|
else {
|
|
ditch->_next = nullptr;
|
|
ditch->_prev = _end;
|
|
|
|
_end->_next = ditch;
|
|
_end = ditch;
|
|
}
|
|
|
|
if (ditch->type() == Ditch::TRI_DITCH_DOCUMENT) {
|
|
// increase counter
|
|
++_numDocumentDitches;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief unlinks the ditch from the linked list of ditches
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void Ditches::unlink (Ditch* ditch) {
|
|
// ditch is at the beginning of the chain
|
|
if (ditch->_prev == nullptr) {
|
|
_begin = ditch->_next;
|
|
}
|
|
else {
|
|
ditch->_prev->_next = ditch->_next;
|
|
}
|
|
|
|
// ditch is at the end of the chain
|
|
if (ditch->_next == nullptr) {
|
|
_end = ditch->_prev;
|
|
}
|
|
else {
|
|
ditch->_next->_prev = ditch->_prev;
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- END-OF-FILE
|
|
// -----------------------------------------------------------------------------
|
|
|
|
// Local Variables:
|
|
// mode: outline-minor
|
|
// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}"
|
|
// End:
|