//////////////////////////////////////////////////////////////////////////////// /// @brief primary collection /// /// @file /// /// DISCLAIMER /// /// Copyright 2004-2013 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 triAGENS GmbH, Cologne, Germany /// /// @author Dr. Frank Celler /// @author Copyright 2011-2013, triAGENS GmbH, Cologne, Germany //////////////////////////////////////////////////////////////////////////////// #include "primary-collection.h" #include "BasicsC/conversions.h" #include "BasicsC/files.h" #include "BasicsC/hashes.h" #include "BasicsC/logging.h" #include "BasicsC/tri-strings.h" #include "VocBase/key-generator.h" #include "VocBase/server.h" #include "VocBase/voc-shaper.h" // ----------------------------------------------------------------------------- // --SECTION-- private functions // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @addtogroup VocBase /// @{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @brief hashes the document id //////////////////////////////////////////////////////////////////////////////// static uint64_t HashKeyHeader (TRI_associative_pointer_t* array, void const* key) { return TRI_FnvHashString((char const*) key); } //////////////////////////////////////////////////////////////////////////////// /// @brief hashes the document header //////////////////////////////////////////////////////////////////////////////// static uint64_t HashElementDocument (TRI_associative_pointer_t* array, void const* element) { TRI_doc_mptr_t const* e = static_cast(element); return TRI_FnvHashString((char const*) e->_key); } //////////////////////////////////////////////////////////////////////////////// /// @brief compares a document id and a document //////////////////////////////////////////////////////////////////////////////// static bool IsEqualKeyDocument (TRI_associative_pointer_t* array, void const* key, void const* element) { TRI_doc_mptr_t const* e = static_cast(element); char const * k = static_cast(key); return (strcmp(k, e->_key) == 0); } //////////////////////////////////////////////////////////////////////////////// /// @brief hashes a datafile identifier //////////////////////////////////////////////////////////////////////////////// static uint64_t HashKeyDatafile (TRI_associative_pointer_t* array, void const* key) { TRI_voc_fid_t const* k = static_cast(key); return *k; } //////////////////////////////////////////////////////////////////////////////// /// @brief hashes a datafile identifier //////////////////////////////////////////////////////////////////////////////// static uint64_t HashElementDatafile (TRI_associative_pointer_t* array, void const* element) { TRI_doc_datafile_info_t const* e = static_cast(element); return e->_fid; } //////////////////////////////////////////////////////////////////////////////// /// @brief compares a datafile identifier and a datafile info //////////////////////////////////////////////////////////////////////////////// static bool IsEqualKeyElementDatafile (TRI_associative_pointer_t* array, void const* key, void const* element) { TRI_voc_fid_t const* k = static_cast(key); TRI_doc_datafile_info_t const* e = static_cast(element); return *k == e->_fid; } //////////////////////////////////////////////////////////////////////////////// /// @brief debug output for datafile information //////////////////////////////////////////////////////////////////////////////// static void DebugDatafileInfoDatafile (TRI_primary_collection_t* primary, TRI_datafile_t* datafile) { TRI_doc_datafile_info_t* dfi; printf("FILE '%s'\n", datafile->getName(datafile)); dfi = TRI_FindDatafileInfoPrimaryCollection(primary, datafile->_fid, false); if (dfi == NULL) { printf(" no info\n\n"); } else { printf(" number alive: %llu\n", (unsigned long long) dfi->_numberAlive); printf(" size alive: %llu\n", (unsigned long long) dfi->_sizeAlive); printf(" number dead: %llu\n", (unsigned long long) dfi->_numberDead); printf(" size dead: %llu\n", (unsigned long long) dfi->_sizeDead); printf(" number shapes: %llu\n", (unsigned long long) dfi->_numberShapes); printf(" size shapes: %llu\n", (unsigned long long) dfi->_sizeShapes); printf(" number attributes: %llu\n", (unsigned long long) dfi->_numberAttributes); printf(" size attributes: %llu\n", (unsigned long long) dfi->_sizeAttributes); printf(" numberdeletion: %llu\n", (unsigned long long) dfi->_numberDeletion); printf("\n"); } } //////////////////////////////////////////////////////////////////////////////// /// @brief debug output for datafile information //////////////////////////////////////////////////////////////////////////////// static void DebugDatafileInfoPrimaryCollection (TRI_primary_collection_t* primary) { size_t n; // journals n = primary->base._journals._length; if (n > 0) { printf("JOURNALS (%d)\n-----------------------------\n", (int) n); for (size_t i = 0; i < n; ++i) { TRI_datafile_t* datafile = static_cast(primary->base._journals._buffer[i]); DebugDatafileInfoDatafile(primary, datafile); } } // compactors n = primary->base._compactors._length; if (n > 0) { printf("COMPACTORS (%d)\n-----------------------------\n", (int) n); for (size_t i = 0; i < n; ++i) { TRI_datafile_t* datafile = static_cast(primary->base._compactors._buffer[i]); DebugDatafileInfoDatafile(primary, datafile); } } // datafiles n = primary->base._datafiles._length; if (n > 0) { printf("DATAFILES (%d)\n-----------------------------\n", (int) n); for (size_t i = 0; i < n; ++i) { TRI_datafile_t* datafile = static_cast(primary->base._datafiles._buffer[i]); DebugDatafileInfoDatafile(primary, datafile); } } } //////////////////////////////////////////////////////////////////////////////// /// @brief creates a compactor file //////////////////////////////////////////////////////////////////////////////// static TRI_datafile_t* CreateCompactor (TRI_primary_collection_t* primary, TRI_voc_fid_t fid, TRI_voc_size_t maximalSize) { TRI_col_header_marker_t cm; TRI_collection_t* collection; TRI_datafile_t* journal; TRI_df_marker_t* position; int res; collection = &primary->base; if (collection->_info._isVolatile) { // in-memory collection journal = TRI_CreateDatafile(NULL, fid, maximalSize, true); } else { char* jname; char* number; char* filename; number = TRI_StringUInt64(fid); jname = TRI_Concatenate3String("compaction-", number, ".db"); filename = TRI_Concatenate2File(collection->_directory, jname); TRI_FreeString(TRI_CORE_MEM_ZONE, number); TRI_FreeString(TRI_CORE_MEM_ZONE, jname); if (TRI_ExistsFile(filename)) { // remove any existing temporary file first TRI_UnlinkFile(filename); } journal = TRI_CreateDatafile(filename, fid, maximalSize, true); TRI_FreeString(TRI_CORE_MEM_ZONE, filename); } if (journal == NULL) { if (TRI_errno() == TRI_ERROR_OUT_OF_MEMORY_MMAP) { collection->_lastError = TRI_set_errno(TRI_ERROR_OUT_OF_MEMORY_MMAP); collection->_state = TRI_COL_STATE_READ; } else { collection->_lastError = TRI_set_errno(TRI_ERROR_ARANGO_NO_JOURNAL); collection->_state = TRI_COL_STATE_WRITE_ERROR; } return NULL; } LOG_TRACE("created new compactor '%s'", journal->getName(journal)); // create a collection header, still in the temporary file res = TRI_ReserveElementDatafile(journal, sizeof(TRI_col_header_marker_t), &position, maximalSize); if (res != TRI_ERROR_NO_ERROR) { collection->_lastError = journal->_lastError; LOG_ERROR("cannot create document header in compactor '%s': %s", journal->getName(journal), TRI_last_error()); TRI_FreeDatafile(journal); return NULL; } TRI_InitMarker((char*) &cm, TRI_COL_MARKER_HEADER, sizeof(TRI_col_header_marker_t)); cm.base._tick = (TRI_voc_tick_t) fid; cm._type = (TRI_col_type_t) collection->_info._type; cm._cid = collection->_info._cid; res = TRI_WriteCrcElementDatafile(journal, position, &cm.base, sizeof(cm), false); if (res != TRI_ERROR_NO_ERROR) { collection->_lastError = journal->_lastError; LOG_ERROR("cannot create document header in compactor '%s': %s", journal->getName(journal), TRI_last_error()); TRI_FreeDatafile(journal); return NULL; } assert(fid == journal->_fid); return journal; } //////////////////////////////////////////////////////////////////////////////// /// @brief creates a journal //////////////////////////////////////////////////////////////////////////////// static TRI_datafile_t* CreateJournal (TRI_primary_collection_t* primary, TRI_voc_size_t maximalSize) { TRI_col_header_marker_t cm; TRI_collection_t* collection; TRI_datafile_t* journal; TRI_df_marker_t* position; TRI_voc_fid_t fid; int res; collection = &primary->base; fid = (TRI_voc_fid_t) TRI_NewTickServer(); if (collection->_info._isVolatile) { // in-memory collection journal = TRI_CreateDatafile(NULL, fid, maximalSize, true); } else { char* jname; char* number; char* filename; // construct a suitable filename (which is temporary at the beginning) number = TRI_StringUInt64(fid); jname = TRI_Concatenate3String("temp-", number, ".db"); filename = TRI_Concatenate2File(collection->_directory, jname); TRI_FreeString(TRI_CORE_MEM_ZONE, number); TRI_FreeString(TRI_CORE_MEM_ZONE, jname); journal = TRI_CreateDatafile(filename, fid, maximalSize, true); TRI_FreeString(TRI_CORE_MEM_ZONE, filename); } if (journal == NULL) { if (TRI_errno() == TRI_ERROR_OUT_OF_MEMORY_MMAP) { collection->_lastError = TRI_set_errno(TRI_ERROR_OUT_OF_MEMORY_MMAP); collection->_state = TRI_COL_STATE_READ; } else { collection->_lastError = TRI_set_errno(TRI_ERROR_ARANGO_NO_JOURNAL); collection->_state = TRI_COL_STATE_WRITE_ERROR; } return NULL; } LOG_TRACE("created new journal '%s'", journal->getName(journal)); // create a collection header, still in the temporary file res = TRI_ReserveElementDatafile(journal, sizeof(TRI_col_header_marker_t), &position, maximalSize); if (res != TRI_ERROR_NO_ERROR) { collection->_lastError = journal->_lastError; LOG_ERROR("cannot create document header in journal '%s': %s", journal->getName(journal), TRI_last_error()); TRI_FreeDatafile(journal); return NULL; } TRI_InitMarker((char*) &cm, TRI_COL_MARKER_HEADER, sizeof(TRI_col_header_marker_t)); cm.base._tick = (TRI_voc_tick_t) fid; cm._type = (TRI_col_type_t) collection->_info._type; cm._cid = collection->_info._cid; res = TRI_WriteCrcElementDatafile(journal, position, &cm.base, sizeof(cm), true); if (res != TRI_ERROR_NO_ERROR) { collection->_lastError = journal->_lastError; LOG_ERROR("cannot create document header in journal '%s': %s", journal->getName(journal), TRI_last_error()); TRI_FreeDatafile(journal); return NULL; } assert(fid == journal->_fid); // if a physical file, we can rename it from the temporary name to the correct name if (journal->isPhysical(journal)) { char* jname; char* number; char* filename; bool ok; // and use the correct name number = TRI_StringUInt64(journal->_fid); jname = TRI_Concatenate3String("journal-", number, ".db"); filename = TRI_Concatenate2File(collection->_directory, jname); TRI_FreeString(TRI_CORE_MEM_ZONE, number); TRI_FreeString(TRI_CORE_MEM_ZONE, jname); ok = TRI_RenameDatafile(journal, filename); if (! ok) { LOG_ERROR("failed to rename the journal to '%s': %s", filename, TRI_last_error()); TRI_FreeDatafile(journal); TRI_FreeString(TRI_CORE_MEM_ZONE, filename); return NULL; } else { LOG_TRACE("renamed journal from %s to '%s'", journal->getName(journal), filename); } TRI_FreeString(TRI_CORE_MEM_ZONE, filename); } TRI_PushBackVectorPointer(&collection->_journals, journal); return journal; } //////////////////////////////////////////////////////////////////////////////// /// @brief closes a journal /// /// Note that the caller must hold a lock protecting the _datafiles and /// _journals entry. //////////////////////////////////////////////////////////////////////////////// static bool CloseJournalPrimaryCollection (TRI_primary_collection_t* primary, size_t position, bool compactor) { TRI_collection_t* collection; TRI_vector_pointer_t* vector; int res; collection = &primary->base; // either use a journal or a compactor if (compactor) { vector = &collection->_compactors; } else { vector = &collection->_journals; } // no journal at this position if (vector->_length <= position) { TRI_set_errno(TRI_ERROR_ARANGO_NO_JOURNAL); return false; } // seal and rename datafile TRI_datafile_t* journal = static_cast(vector->_buffer[position]); res = TRI_SealDatafile(journal); if (res != TRI_ERROR_NO_ERROR) { LOG_ERROR("failed to seal datafile '%s': %s", journal->getName(journal), TRI_last_error()); if (! compactor) { TRI_RemoveVectorPointer(vector, position); TRI_PushBackVectorPointer(&collection->_datafiles, journal); } return false; } if (! compactor && journal->isPhysical(journal)) { // rename the file char* dname; char* filename; char* number; bool ok; number = TRI_StringUInt64(journal->_fid); dname = TRI_Concatenate3String("datafile-", number, ".db"); filename = TRI_Concatenate2File(collection->_directory, dname); TRI_FreeString(TRI_CORE_MEM_ZONE, dname); TRI_FreeString(TRI_CORE_MEM_ZONE, number); ok = TRI_RenameDatafile(journal, filename); if (! ok) { LOG_ERROR("failed to rename datafile '%s' to '%s': %s", journal->getName(journal), filename, TRI_last_error()); TRI_RemoveVectorPointer(vector, position); TRI_PushBackVectorPointer(&collection->_datafiles, journal); TRI_FreeString(TRI_CORE_MEM_ZONE, filename); return false; } TRI_FreeString(TRI_CORE_MEM_ZONE, filename); LOG_TRACE("closed file '%s'", journal->getName(journal)); } if (! compactor) { TRI_RemoveVectorPointer(vector, position); TRI_PushBackVectorPointer(&collection->_datafiles, journal); } return true; } //////////////////////////////////////////////////////////////////////////////// /// @brief free an assoc array of datafile infos //////////////////////////////////////////////////////////////////////////////// static void FreeDatafileInfo (TRI_doc_datafile_info_t* dfi) { TRI_Free(TRI_UNKNOWN_MEM_ZONE, dfi); } //////////////////////////////////////////////////////////////////////////////// /// @brief size of a primary collection /// /// the caller must have read-locked the collection! //////////////////////////////////////////////////////////////////////////////// static TRI_voc_size_t Count (TRI_primary_collection_t* primary) { return (TRI_voc_size_t) primary->_numberDocuments; } //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- // --SECTION-- constructors and destructors // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @addtogroup VocBase /// @{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @brief initialises a primary collection //////////////////////////////////////////////////////////////////////////////// int TRI_InitPrimaryCollection (TRI_primary_collection_t* primary, TRI_shaper_t* shaper) { int res; primary->_shaper = shaper; primary->_capConstraint = NULL; primary->_keyGenerator = NULL; primary->_numberDocuments = 0; primary->_lastCompaction = 0.0; primary->size = Count; res = TRI_InitAssociativePointer(&primary->_datafileInfo, TRI_UNKNOWN_MEM_ZONE, HashKeyDatafile, HashElementDatafile, IsEqualKeyElementDatafile, NULL); if (res != TRI_ERROR_NO_ERROR) { return res; } res = TRI_InitAssociativePointer(&primary->_primaryIndex, TRI_UNKNOWN_MEM_ZONE, HashKeyHeader, HashElementDocument, IsEqualKeyDocument, NULL); if (res != TRI_ERROR_NO_ERROR) { TRI_DestroyAssociativePointer(&primary->_datafileInfo); return res; } TRI_InitBarrierList(&primary->_barrierList, primary); TRI_InitReadWriteLock(&primary->_lock); TRI_InitReadWriteLock(&primary->_compactionLock); return TRI_ERROR_NO_ERROR; } //////////////////////////////////////////////////////////////////////////////// /// @brief destroys a primary collection //////////////////////////////////////////////////////////////////////////////// void TRI_DestroyPrimaryCollection (TRI_primary_collection_t* primary) { size_t i, n; if (primary->_keyGenerator != NULL) { TRI_FreeKeyGenerator(primary->_keyGenerator); } TRI_DestroyReadWriteLock(&primary->_compactionLock); TRI_DestroyReadWriteLock(&primary->_lock); TRI_DestroyAssociativePointer(&primary->_primaryIndex); if (primary->_shaper != NULL) { TRI_FreeVocShaper(primary->_shaper); } n = primary->_datafileInfo._nrAlloc; for (i = 0; i < n; ++i) { TRI_doc_datafile_info_t* dfi = static_cast(primary->_datafileInfo._table[i]); if (dfi != NULL) { FreeDatafileInfo(dfi); } } TRI_DestroyAssociativePointer(&primary->_datafileInfo); TRI_DestroyBarrierList(&primary->_barrierList); TRI_DestroyCollection(&primary->base); } //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- // --SECTION-- protected functions // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @addtogroup VocBase /// @{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @brief removes a datafile description //////////////////////////////////////////////////////////////////////////////// void TRI_RemoveDatafileInfoPrimaryCollection (TRI_primary_collection_t* primary, TRI_voc_fid_t fid) { TRI_RemoveKeyAssociativePointer(&primary->_datafileInfo, &fid); } //////////////////////////////////////////////////////////////////////////////// /// @brief finds a datafile description //////////////////////////////////////////////////////////////////////////////// TRI_doc_datafile_info_t* TRI_FindDatafileInfoPrimaryCollection (TRI_primary_collection_t* primary, TRI_voc_fid_t fid, bool create) { TRI_doc_datafile_info_t const* found = static_cast(TRI_LookupByKeyAssociativePointer(&primary->_datafileInfo, &fid)); if (found != NULL) { return const_cast(found); } if (! create) { return NULL; } // allocate and set to 0 TRI_doc_datafile_info_t* dfi = static_cast(TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_doc_datafile_info_t), true)); if (dfi == NULL) { return NULL; } dfi->_fid = fid; TRI_InsertKeyAssociativePointer(&primary->_datafileInfo, &fid, dfi, true); return dfi; } //////////////////////////////////////////////////////////////////////////////// /// @brief creates a journal /// /// Note that the caller must hold a lock protecting the _journals entry. //////////////////////////////////////////////////////////////////////////////// TRI_datafile_t* TRI_CreateJournalPrimaryCollection (TRI_primary_collection_t* primary, TRI_voc_size_t size) { return CreateJournal(primary, size); } //////////////////////////////////////////////////////////////////////////////// /// @brief closes a journal /// /// Note that the caller must hold a lock protecting the _datafiles and /// _journals entry. //////////////////////////////////////////////////////////////////////////////// bool TRI_CloseJournalPrimaryCollection (TRI_primary_collection_t* primary, size_t position) { return CloseJournalPrimaryCollection(primary, position, false); } //////////////////////////////////////////////////////////////////////////////// /// @brief creates a new compactor file /// /// Note that the caller must hold a lock protecting the _journals entry. //////////////////////////////////////////////////////////////////////////////// TRI_datafile_t* TRI_CreateCompactorPrimaryCollection (TRI_primary_collection_t* primary, TRI_voc_fid_t fid, TRI_voc_size_t maximalSize) { return CreateCompactor(primary, fid, maximalSize); } //////////////////////////////////////////////////////////////////////////////// /// @brief closes an existing compactor file /// /// Note that the caller must hold a lock protecting the _datafiles and /// _journals entry. //////////////////////////////////////////////////////////////////////////////// bool TRI_CloseCompactorPrimaryCollection (TRI_primary_collection_t* primary, size_t position) { return CloseJournalPrimaryCollection(primary, position, true); } //////////////////////////////////////////////////////////////////////////////// /// @brief dump information about all datafiles of a collection //////////////////////////////////////////////////////////////////////////////// void TRI_DebugDatafileInfoPrimaryCollection (TRI_primary_collection_t* primary) { DebugDatafileInfoPrimaryCollection(primary); } //////////////////////////////////////////////////////////////////////////////// /// @brief iterate over all documents in the collection, using a user-defined /// callback function. Returns the total number of documents in the collection /// /// The user can abort the iteration by return "false" from the callback /// function. /// /// Note: the function will not acquire any locks. It is the task of the caller /// to ensure the collection is properly locked //////////////////////////////////////////////////////////////////////////////// size_t TRI_DocumentIteratorPrimaryCollection (TRI_primary_collection_t* primary, void* data, bool (*callback)(TRI_doc_mptr_t const*, TRI_primary_collection_t*, void*)) { if (primary->_primaryIndex._nrUsed > 0) { void** ptr = primary->_primaryIndex._table; void** end = ptr + primary->_primaryIndex._nrAlloc; for (; ptr < end; ++ptr) { if (*ptr) { TRI_doc_mptr_t const* d = (TRI_doc_mptr_t const*) *ptr; if (! callback(d, primary, data)) { break; } } } } return (size_t) primary->_primaryIndex._nrUsed; } //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // Local Variables: // mode: outline-minor // outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|/// @page\\|// --SECTION--\\|/// @\\}" // End: