1
0
Fork 0

Use hybrid logical clock for _rev entries.

This commit is contained in:
Max Neunhoeffer 2016-07-05 21:15:32 +02:00
parent dacb54d56f
commit 7fe9fcc00e
9 changed files with 122 additions and 95 deletions

View File

@ -23,6 +23,7 @@
#include "v8-vocbaseprivate.h"
#include "Basics/conversions.h"
#include "Basics/HybridLogicalClock.h"
#include "VocBase/KeyGenerator.h"
#include "V8/v8-conv.h"
@ -109,64 +110,6 @@ static bool ParseDocumentHandle(v8::Handle<v8::Value> const arg,
return false;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief parse document or document handle from a v8 value (string | object)
////////////////////////////////////////////////////////////////////////////////
bool ExtractDocumentHandle(v8::Isolate* isolate,
v8::Handle<v8::Value> const val,
std::string& collectionName,
std::unique_ptr<char[]>& key, TRI_voc_rid_t& rid) {
// reset the collection identifier and the revision
TRI_ASSERT(collectionName.empty());
rid = 0;
// extract the document identifier and revision from a string
if (val->IsString()) {
return ParseDocumentHandle(val, collectionName, key);
}
// extract the document identifier and revision from a document object
if (val->IsObject()) {
TRI_GET_GLOBALS();
v8::Handle<v8::Object> obj = val->ToObject();
TRI_GET_GLOBAL_STRING(_IdKey);
TRI_GET_GLOBAL_STRING(_KeyKey);
if (obj->HasRealNamedProperty(_IdKey)) {
v8::Handle<v8::Value> didVal = obj->Get(_IdKey);
if (!ParseDocumentHandle(didVal, collectionName, key)) {
return false;
}
} else if (obj->HasRealNamedProperty(_KeyKey)) {
v8::Handle<v8::Value> didVal = obj->Get(_KeyKey);
if (!ParseDocumentHandle(didVal, collectionName, key)) {
return false;
}
} else {
return false;
}
TRI_GET_GLOBAL_STRING(_RevKey);
if (!obj->HasRealNamedProperty(_RevKey)) {
return true;
}
rid = TRI_ObjectToUInt64(obj->Get(_RevKey), true);
if (rid == 0) {
return false;
}
return true;
}
// unknown value type. give up
return false;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief parse document or document handle from a v8 value (string | object)
/// Note that the builder must already be open with an object and that it
@ -234,14 +177,19 @@ bool ExtractDocumentHandle(v8::Isolate* isolate,
if (!obj->HasRealNamedProperty(_RevKey)) {
return true;
}
uint64_t rid = 0;
rid = TRI_ObjectToUInt64(obj->Get(_RevKey), true);
v8::Handle<v8::Value> revObj = obj->Get(_RevKey);
if (!revObj->IsString()) {
return true;
}
v8::String::Utf8Value str(revObj);
uint64_t rid
= HybridLogicalClock::decodeTimeStamp(std::string(*str, str.length()));
if (rid == 0) {
return false;
}
builder.add(StaticStrings::RevString, VPackValue(std::to_string(rid)));
builder.add(StaticStrings::RevString, VPackValue(
HybridLogicalClock::encodeTimeStamp(rid)));
return true;
}

View File

@ -76,15 +76,6 @@ TRI_vocbase_t* GetContextVocBase(v8::Isolate* isolate);
v8::Handle<v8::Value> V8TickId(v8::Isolate* isolate, TRI_voc_tick_t tick);
////////////////////////////////////////////////////////////////////////////////
/// @brief parse document or document handle from a v8 value (string | object)
////////////////////////////////////////////////////////////////////////////////
bool ExtractDocumentHandle(v8::Isolate* isolate,
v8::Handle<v8::Value> const val,
std::string& collectionName,
std::unique_ptr<char[]>& key, TRI_voc_rid_t& rid);
////////////////////////////////////////////////////////////////////////////////
/// @brief parse document or document handle from a v8 value (string | object)
/// Note that the builder must already be open with an object and will remain

View File

@ -28,6 +28,7 @@
#include "Basics/conversions.h"
#include "Basics/Exceptions.h"
#include "Basics/FileUtils.h"
#include "Basics/HybridLogicalClock.h"
#include "Basics/Timers.h"
#include "Basics/files.h"
#include "Basics/tri-strings.h"
@ -3383,7 +3384,7 @@ int TRI_document_collection_t::update(Transaction* trx,
// make sure our local tick is at least as high as the remote tick
TRI_UpdateTickServer(revisionId);
} else {
revisionId = TRI_NewTickServer();
revisionId = TRI_HybridLogicalClock();
}
VPackSlice key = newSlice.get(StaticStrings::KeyString);
@ -3439,8 +3440,8 @@ int TRI_document_collection_t::update(Transaction* trx,
if (options.recoveryMarker == nullptr) {
mergeObjectsForUpdate(
trx, VPackSlice(oldHeader->vpack()), newSlice, isEdgeCollection,
std::to_string(revisionId), options.mergeObjects, options.keepNull,
*builder.get());
HybridLogicalClock::encodeTimeStamp(revisionId),
options.mergeObjects, options.keepNull, *builder.get());
if (ServerState::isDBServer(trx->serverRole())) {
// Need to check that no sharding keys have changed:
@ -3534,13 +3535,11 @@ int TRI_document_collection_t::replace(Transaction* trx,
if (!oldRev.isString()) {
return TRI_ERROR_ARANGO_DOCUMENT_REV_BAD;
}
VPackValueLength length;
char const* p = oldRev.getString(length);
revisionId = arangodb::basics::StringUtils::uint64(p, static_cast<size_t>(length));
revisionId = HybridLogicalClock::decodeTimeStamp(oldRev.copyString());
// make sure our local tick is at least as high as the remote tick
TRI_UpdateTickServer(revisionId);
} else {
revisionId = TRI_NewTickServer();
revisionId = TRI_HybridLogicalClock();
}
int res;
@ -3586,7 +3585,9 @@ int TRI_document_collection_t::replace(Transaction* trx,
TransactionBuilderLeaser builder(trx);
newObjectForReplace(
trx, VPackSlice(oldHeader->vpack()),
newSlice, fromSlice, toSlice, isEdgeCollection, std::to_string(revisionId), *builder.get());
newSlice, fromSlice, toSlice, isEdgeCollection,
HybridLogicalClock::encodeTimeStamp(revisionId),
*builder.get());
if (ServerState::isDBServer(trx->serverRole())) {
// Need to check that no sharding keys have changed:
@ -3651,16 +3652,14 @@ int TRI_document_collection_t::remove(arangodb::Transaction* trx,
if (options.isRestore) {
VPackSlice oldRev = TRI_ExtractRevisionIdAsSlice(slice);
if (!oldRev.isString()) {
revisionId = TRI_NewTickServer();
revisionId = TRI_HybridLogicalClock();
} else {
VPackValueLength length;
char const* p = oldRev.getString(length);
revisionId = arangodb::basics::StringUtils::uint64(p, static_cast<size_t>(length));
revisionId = HybridLogicalClock::decodeTimeStamp(oldRev.copyString());
// make sure our local tick is at least as high as the remote tick
TRI_UpdateTickServer(revisionId);
}
} else {
revisionId = TRI_NewTickServer();
revisionId = TRI_HybridLogicalClock();
}
TransactionBuilderLeaser builder(trx);
@ -4124,8 +4123,8 @@ int TRI_document_collection_t::newObjectForInsert(
VPackSlice s = value.get(StaticStrings::KeyString);
if (s.isNone()) {
TRI_ASSERT(!isRestore); // need key in case of restore
newRev = TRI_NewTickServer();
std::string keyString = _keyGenerator->generate(newRev);
newRev = TRI_HybridLogicalClock();
std::string keyString = _keyGenerator->generate(TRI_NewTickServer());
if (keyString.empty()) {
return TRI_ERROR_ARANGO_OUT_OF_KEYS;
}
@ -4180,9 +4179,9 @@ int TRI_document_collection_t::newObjectForInsert(
newRevSt = oldRev.copyString();
} else {
if (newRev == 0) {
newRev = TRI_NewTickServer();
newRev = TRI_HybridLogicalClock();
}
newRevSt = std::to_string(newRev);
newRevSt = HybridLogicalClock::encodeTimeStamp(newRev);
}
builder.add(StaticStrings::RevString, VPackValue(newRevSt));

View File

@ -2071,11 +2071,17 @@ TRI_voc_tick_t TRI_NewTickServer() { return ++CurrentTick; }
/// @brief create a new tick, using a hybrid logical clock
////////////////////////////////////////////////////////////////////////////////
TRI_voc_tick_t TRI_NewTickServerHLC(void) {
TRI_voc_tick_t TRI_HybridLogicalClock(void) {
return hybridLogicalClock.getTimeStamp();
}
TRI_voc_tick_t TRI_NewTickServerHLC(TRI_voc_tick_t received) {
////////////////////////////////////////////////////////////////////////////////
/// @brief create a new tick, using a hybrid logical clock, this variant
/// is supposed to be called when a time stamp is received in network
/// communications.
////////////////////////////////////////////////////////////////////////////////
TRI_voc_tick_t TRI_HybridLogicalClock(TRI_voc_tick_t received) {
return hybridLogicalClock.getTimeStamp(received);
}

View File

@ -254,8 +254,15 @@ TRI_voc_tick_t TRI_NewTickServer(void);
/// @brief create a new tick, using a hybrid logical clock
////////////////////////////////////////////////////////////////////////////////
TRI_voc_tick_t TRI_NewTickServerHLC(void);
TRI_voc_tick_t TRI_NewTickServerHLC(TRI_voc_tick_t received);
TRI_voc_tick_t TRI_HybridLogicalClock(void);
////////////////////////////////////////////////////////////////////////////////
/// @brief create a new tick, using a hybrid logical clock, this variant
/// is supposed to be called when a time stamp is received in network
/// communications.
////////////////////////////////////////////////////////////////////////////////
TRI_voc_tick_t TRI_HybridLogicalClock(TRI_voc_tick_t received);
////////////////////////////////////////////////////////////////////////////////
/// @brief updates the tick counter, with lock

View File

@ -32,6 +32,7 @@
#include "Aql/QueryList.h"
#include "Basics/Exceptions.h"
#include "Basics/FileUtils.h"
#include "Basics/HybridLogicalClock.h"
#include "Basics/StaticStrings.h"
#include "Basics/StringRef.h"
#include "Basics/VelocyPackHelper.h"
@ -2291,9 +2292,7 @@ TRI_voc_rid_t TRI_ExtractRevisionId(VPackSlice slice) {
VPackSlice r(slice.get(StaticStrings::RevString));
if (r.isString()) {
VPackValueLength length;
char const* p = r.getString(length);
return arangodb::basics::StringUtils::uint64(p, static_cast<size_t>(length));
return HybridLogicalClock::decodeTimeStamp(r.copyString());
}
if (r.isInteger()) {
return r.getNumber<TRI_voc_rid_t>();

View File

@ -0,0 +1,45 @@
////////////////////////////////////////////////////////////////////////////////
/// 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 Max Neunhoeffer
////////////////////////////////////////////////////////////////////////////////
#include "Basics/HybridLogicalClock.h"
char arangodb::basics::HybridLogicalClock::encodeTable[65]
= "-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz";
char arangodb::basics::HybridLogicalClock::decodeTable[256]
= {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 0 - 15
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 16 - 31
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 0,-1,-1, // 32 - 47
1, 2, 3, 4, 5, 6, 7, 8, 9,10,-1,-1,-1,-1,-1,-1, // 48 - 63
-1,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25, // 64 - 79
26,27,28,29,30,31,32,33,34,35,36,-1,-1,-1,-1,37, // 80 - 95
-1,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52, // 96 - 111
53,54,55,56,57,58,59,60,61,62,63,-1,-1,-1,-1,-1, // 112 - 127
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 128 - 143
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 144 - 159
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 160 - 175
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 176 - 191
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 192 - 207
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 208 - 223
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 224 - 239
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}; // 240 - 255

View File

@ -24,8 +24,13 @@
#ifndef ARANGODB_BASICS_HYBRID_LOGICAL_CLOCK_H
#define ARANGODB_BASICS_HYBRID_LOGICAL_CLOCK_H 1
#include "Basics/Common.h"
#include <chrono>
namespace arangodb {
namespace basics {
class HybridLogicalClock {
std::chrono::high_resolution_clock clock;
std::atomic<uint64_t> lastTimeStamp;
@ -87,6 +92,25 @@ class HybridLogicalClock {
return newTimeStamp;
}
static std::string encodeTimeStamp(uint64_t t) {
std::string r(11, '\x00');
size_t pos = 11;
while (t > 0) {
r[--pos] = encodeTable[static_cast<uint8_t>(t & 0x3ful)];
t >>= 6;
}
return r.substr(pos, 11-pos);
}
static uint64_t decodeTimeStamp(std::string const& s) {
uint64_t r = 0;
for (size_t i = 0; i < s.size(); i++) {
r = (r << 6) |
static_cast<uint8_t>(decodeTable[static_cast<uint8_t>(s[i])]);
}
return r;
}
private:
// Helper to get the physical time in milliseconds since the epoch:
uint64_t getPhysicalTime() {
@ -108,6 +132,13 @@ class HybridLogicalClock {
return (time << 20) | count;
}
static char encodeTable[65];
static char decodeTable[256];
};
}; // namespace basics
}; // namespace arangodb
#endif

View File

@ -140,6 +140,7 @@ add_library(${LIB_ARANGO} STATIC
Basics/DataProtector.cpp
Basics/Exceptions.cpp
Basics/FileUtils.cpp
Basics/HybridLogicalClock.cpp
Basics/JsonHelper.cpp
Basics/Mutex.cpp
Basics/MutexLocker.cpp