1
0
Fork 0
arangodb/arangod/VocBase/RevisionCacheChunk.cpp

227 lines
6.4 KiB
C++

////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2014-2016 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 Jan Steemann
////////////////////////////////////////////////////////////////////////////////
#include "RevisionCacheChunk.h"
#include "Basics/MutexLocker.h"
#include "Utils/Transaction.h"
#include "VocBase/CollectionRevisionsCache.h"
using namespace arangodb;
/// @brief create an empty usage object, pointing to nothing
ChunkProtector::ChunkProtector()
: _chunk(nullptr), _offset(UINT32_MAX), _version(UINT32_MAX), _isResponsible(false) {}
/// @brief create a valid usage object, pointing to vpack in the read cache
ChunkProtector::ChunkProtector(RevisionCacheChunk* chunk, uint32_t offset, uint32_t expectedVersion)
: _chunk(chunk), _offset(offset), _version(expectedVersion), _isResponsible(true) {
TRI_ASSERT(_chunk != nullptr);
if (offset == UINT32_MAX) {
// chunk was full
_chunk = nullptr;
_isResponsible = false;
} else if (!_chunk->use(expectedVersion)) {
// invalid
_chunk = nullptr;
_isResponsible = false;
}
}
/// @brief create a valid usage object, pointing to vpack in the read cache
ChunkProtector::ChunkProtector(RevisionCacheChunk* chunk, uint32_t offset, uint32_t expectedVersion, bool)
: _chunk(chunk), _offset(offset), _version(expectedVersion), _isResponsible(false) {
TRI_ASSERT(_chunk != nullptr);
TRI_ASSERT(_offset != UINT32_MAX);
}
ChunkProtector::~ChunkProtector() {
if (_isResponsible) {
_chunk->release();
}
}
ChunkProtector::ChunkProtector(ChunkProtector&& other) : _chunk(other._chunk), _offset(other._offset), _version(other._version), _isResponsible(other._isResponsible) {
other._chunk = nullptr;
other._isResponsible = false;
}
ChunkProtector& ChunkProtector::operator=(ChunkProtector&& other) {
if (_chunk != nullptr && _chunk != other._chunk && _isResponsible) {
_chunk->release();
}
_chunk = other._chunk;
_offset = other._offset;
_version = other._version;
_isResponsible = other._isResponsible;
other._chunk = nullptr;
other._isResponsible = false;
return *this;
}
void ChunkProtector::steal() {
_isResponsible = false;
}
uint8_t* ChunkProtector::vpack() {
if (_chunk == nullptr) {
return nullptr;
}
return _chunk->data() + _offset;
}
uint8_t const* ChunkProtector::vpack() const {
if (_chunk == nullptr) {
return nullptr;
}
return _chunk->data() + _offset;
}
// note: version must be 1 or higher as version 0 means "WAL"
RevisionCacheChunk::RevisionCacheChunk(CollectionRevisionsCache* collectionCache, uint32_t size)
: _collectionCache(collectionCache), _data(nullptr), _nextWriteOffset(0),
_size(size), _numWritersQueued(0), _versionAndRefCount(buildVersion(1)) {
TRI_ASSERT(versionPart(_versionAndRefCount) == 1);
_data = new uint8_t[size];
}
RevisionCacheChunk::~RevisionCacheChunk() {
delete[] _data;
}
void RevisionCacheChunk::unqueueWriter() {
MUTEX_LOCKER(locker, _writeMutex);
TRI_ASSERT(_numWritersQueued > 0);
--_numWritersQueued;
}
uint32_t RevisionCacheChunk::advanceWritePosition(uint32_t size) {
uint32_t offset;
{
MUTEX_LOCKER(locker, _writeMutex);
if (_nextWriteOffset + size > _size) {
// chunk would be full
return UINT32_MAX; // means: chunk is full
}
offset = _nextWriteOffset;
_nextWriteOffset += size;
++_numWritersQueued;
}
return offset;
}
bool RevisionCacheChunk::invalidate(std::vector<TRI_voc_rid_t>& revisions) {
if (!_collectionCache->allowInvalidation()) {
return false;
}
// wait until all writers have finished
while (true) {
{
MUTEX_LOCKER(locker, _writeMutex);
if (_numWritersQueued == 0) {
break;
}
}
usleep(10000);
}
revisions.clear();
revisions.reserve(8192);
findRevisions(revisions);
invalidate();
if (!revisions.empty()) {
_collectionCache->removeRevisions(revisions);
}
// increase version number once again
invalidate();
return true;
}
void RevisionCacheChunk::findRevisions(std::vector<TRI_voc_rid_t>& revisions) {
// no need for write mutex here as the chunk is read-only once fully
// written to
uint8_t const* data = _data;
uint8_t const* end = _data + _nextWriteOffset;
while (data < end) {
// peek into document at the current position
VPackSlice slice(data);
TRI_ASSERT(slice.isObject());
data += slice.byteSize();
try {
TRI_voc_rid_t rid = Transaction::extractRevFromDocument(slice);
revisions.emplace_back(rid);
} catch (...) {
// LOG(ERR) << "SLICE: " << slice.toJson();
}
}
}
void RevisionCacheChunk::invalidate() {
// increasing the chunk's version number
// this will make all future reads ignore data in the chunk
while (true) {
uint64_t old = _versionAndRefCount.load();
uint64_t desired = increaseVersion(old);
// LOG(ERR) << "OLD: " << old << ", VERSIONPART(OLD): " << versionPart(old) << ", REFPART(OLD): " << refCountPart(old) << ", DESIRED: " << desired;
if (_versionAndRefCount.compare_exchange_strong(old, desired)) {
break;
}
}
}
bool RevisionCacheChunk::use(uint32_t expectedVersion) noexcept {
uint64_t old = _versionAndRefCount.fetch_add(1);
if (versionPart(old) != expectedVersion) {
// version mismatch. now decrease refcount again
--_versionAndRefCount;
return false;
}
// we have increased the reference counter here
return true;
}
bool RevisionCacheChunk::use() noexcept {
++_versionAndRefCount;
return true;
}
void RevisionCacheChunk::release() noexcept {
uint64_t old = _versionAndRefCount.fetch_sub(1);
TRI_ASSERT(refCountPart(old) > 0);
// TODO
}
bool RevisionCacheChunk::isUsed() noexcept {
uint64_t old = _versionAndRefCount.load();
return refCountPart(old) > 0;
}