diff --git a/arangod/V8Server/v8-util.cpp b/arangod/V8Server/v8-util.cpp index a94bf362c5..35e4f7986c 100644 --- a/arangod/V8Server/v8-util.cpp +++ b/arangod/V8Server/v8-util.cpp @@ -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 const arg, return false; } -//////////////////////////////////////////////////////////////////////////////// -/// @brief parse document or document handle from a v8 value (string | object) -//////////////////////////////////////////////////////////////////////////////// - -bool ExtractDocumentHandle(v8::Isolate* isolate, - v8::Handle const val, - std::string& collectionName, - std::unique_ptr& 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 obj = val->ToObject(); - TRI_GET_GLOBAL_STRING(_IdKey); - TRI_GET_GLOBAL_STRING(_KeyKey); - if (obj->HasRealNamedProperty(_IdKey)) { - v8::Handle didVal = obj->Get(_IdKey); - - if (!ParseDocumentHandle(didVal, collectionName, key)) { - return false; - } - } else if (obj->HasRealNamedProperty(_KeyKey)) { - v8::Handle 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 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; } diff --git a/arangod/V8Server/v8-vocbaseprivate.h b/arangod/V8Server/v8-vocbaseprivate.h index fdcc57f09f..6e3d6586fe 100644 --- a/arangod/V8Server/v8-vocbaseprivate.h +++ b/arangod/V8Server/v8-vocbaseprivate.h @@ -76,15 +76,6 @@ TRI_vocbase_t* GetContextVocBase(v8::Isolate* isolate); v8::Handle 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 const val, - std::string& collectionName, - std::unique_ptr& 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 diff --git a/arangod/VocBase/document-collection.cpp b/arangod/VocBase/document-collection.cpp index 66cde165ae..2e4905acf6 100644 --- a/arangod/VocBase/document-collection.cpp +++ b/arangod/VocBase/document-collection.cpp @@ -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(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(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)); diff --git a/arangod/VocBase/server.cpp b/arangod/VocBase/server.cpp index 588b29b457..9c0719583b 100644 --- a/arangod/VocBase/server.cpp +++ b/arangod/VocBase/server.cpp @@ -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); } diff --git a/arangod/VocBase/server.h b/arangod/VocBase/server.h index eb5c04a8d3..16c65aa37c 100644 --- a/arangod/VocBase/server.h +++ b/arangod/VocBase/server.h @@ -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 diff --git a/arangod/VocBase/vocbase.cpp b/arangod/VocBase/vocbase.cpp index 47f8f2b436..6a0ea5f26e 100644 --- a/arangod/VocBase/vocbase.cpp +++ b/arangod/VocBase/vocbase.cpp @@ -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(length)); + return HybridLogicalClock::decodeTimeStamp(r.copyString()); } if (r.isInteger()) { return r.getNumber(); diff --git a/lib/Basics/HybridLogicalClock.cpp b/lib/Basics/HybridLogicalClock.cpp new file mode 100644 index 0000000000..a3acd6de89 --- /dev/null +++ b/lib/Basics/HybridLogicalClock.cpp @@ -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 diff --git a/lib/Basics/HybridLogicalClock.h b/lib/Basics/HybridLogicalClock.h index 6a4655d105..b3bbdc1b4d 100644 --- a/lib/Basics/HybridLogicalClock.h +++ b/lib/Basics/HybridLogicalClock.h @@ -24,8 +24,13 @@ #ifndef ARANGODB_BASICS_HYBRID_LOGICAL_CLOCK_H #define ARANGODB_BASICS_HYBRID_LOGICAL_CLOCK_H 1 +#include "Basics/Common.h" + #include +namespace arangodb { +namespace basics { + class HybridLogicalClock { std::chrono::high_resolution_clock clock; std::atomic 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(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(decodeTable[static_cast(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 diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 08e96aa77a..26c2ff5f3f 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -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