diff --git a/arangod/Cluster/AgencyComm.cpp b/arangod/Cluster/AgencyComm.cpp index a0e0ace67d..8ff012010a 100644 --- a/arangod/Cluster/AgencyComm.cpp +++ b/arangod/Cluster/AgencyComm.cpp @@ -812,15 +812,19 @@ AgencyCommResult AgencyComm::casValue (std::string const& key, AgencyCommResult AgencyComm::watchValue (std::string const& key, uint64_t waitIndex, - double timeout) { + double timeout, + bool recursive) { - std::string url(buildUrl(key)); - url += "?wait=true"; + std::string url(buildUrl(key) + "?wait=true"); if (waitIndex > 0) { url += "&waitIndex=" + triagens::basics::StringUtils::itoa(waitIndex); } + if (recursive) { + url += "&recursive=true"; + } + AgencyCommResult result; sendWithFailover(triagens::rest::HttpRequest::HTTP_REQUEST_GET, diff --git a/arangod/Cluster/AgencyComm.h b/arangod/Cluster/AgencyComm.h index a2345ce432..6975687fbc 100644 --- a/arangod/Cluster/AgencyComm.h +++ b/arangod/Cluster/AgencyComm.h @@ -360,7 +360,8 @@ namespace triagens { AgencyCommResult watchValue (std::string const&, uint64_t, - double); + double, + bool); // ----------------------------------------------------------------------------- // --SECTION-- private methods diff --git a/arangod/Cluster/HeartbeatThread.cpp b/arangod/Cluster/HeartbeatThread.cpp index d7d5cb0cf9..465d58b9d6 100644 --- a/arangod/Cluster/HeartbeatThread.cpp +++ b/arangod/Cluster/HeartbeatThread.cpp @@ -107,7 +107,8 @@ void HeartbeatThread::run () { // TODO: check if this is CPU-intensive and whether we need to sleep AgencyCommResult result = _agency.watchValue("Commands/" + _myId, lastCommandIndex + 1, - interval); + interval, + false); if (_stop) { break; diff --git a/arangod/V8Server/v8-vocbase.cpp b/arangod/V8Server/v8-vocbase.cpp index 1baf69aa4d..8cc8658288 100644 --- a/arangod/V8Server/v8-vocbase.cpp +++ b/arangod/V8Server/v8-vocbase.cpp @@ -2290,15 +2290,19 @@ static v8::Handle JS_GetAgency (v8::Arguments const& argv) { v8::HandleScope scope; if (argv.Length() < 1) { - TRI_V8_EXCEPTION_USAGE(scope, "get(, )"); + TRI_V8_EXCEPTION_USAGE(scope, "get(, , )"); } const std::string key = TRI_ObjectToString(argv[0]); bool recursive = false; + bool withIndexes = false; if (argv.Length() > 1) { recursive = TRI_ObjectToBoolean(argv[1]); } + if (argv.Length() > 2) { + withIndexes = TRI_ObjectToBoolean(argv[2]); + } AgencyComm comm; AgencyCommResult result = comm.getValues(key, recursive); @@ -2308,20 +2312,52 @@ static v8::Handle JS_GetAgency (v8::Arguments const& argv) { v8::Handle err = v8::String::New(errorDetails.c_str(), errorDetails.size()); return scope.Close(v8::ThrowException(err)); } - - std::map out; - result.flattenJson(out, "", false); - std::map::const_iterator it = out.begin(); - v8::Handle l = v8::Object::New(); - while (it != out.end()) { - const std::string key = (*it).first; - const std::string value = (*it).second; + if (withIndexes) { + // return an object for each key + std::map outValues; + std::map outIndexes; + + result.flattenJson(outValues, "", false); + result.flattenJson(outIndexes, "", true); + + assert(outValues.size() == outIndexes.size()); - l->Set(v8::String::New(key.c_str(), key.size()), v8::String::New(value.c_str(), value.size())); - ++it; + std::map::const_iterator it = outValues.begin(); + std::map::const_iterator it2 = outIndexes.begin(); + + while (it != outValues.end()) { + const std::string key = (*it).first; + const std::string value = (*it).second; + const std::string idx = (*it2).second; + + v8::Handle sub = v8::Object::New(); + + sub->Set(v8::String::New("value"), v8::String::New(value.c_str(), value.size())); + sub->Set(v8::String::New("index"), v8::String::New(idx.c_str(), idx.size())); + + l->Set(v8::String::New(key.c_str(), key.size()), sub); + + ++it; + ++it2; + } + } + else { + // return just the value for each key + std::map out; + + result.flattenJson(out, "", false); + std::map::const_iterator it = out.begin(); + + while (it != out.end()) { + const std::string key = (*it).first; + const std::string value = (*it).second; + + l->Set(v8::String::New(key.c_str(), key.size()), v8::String::New(value.c_str(), value.size())); + ++it; + } } return scope.Close(l); @@ -2400,12 +2436,13 @@ static v8::Handle JS_WatchAgency (v8::Arguments const& argv) { v8::HandleScope scope; if (argv.Length() < 1) { - TRI_V8_EXCEPTION_USAGE(scope, "watch(, , , , , )"); } const std::string key = TRI_ObjectToString(argv[0]); double timeout = 1.0; uint64_t waitIndex = 0; + bool recursive = false; if (argv.Length() > 1) { waitIndex = TRI_ObjectToUInt64(argv[1], true); @@ -2413,9 +2450,12 @@ static v8::Handle JS_WatchAgency (v8::Arguments const& argv) { if (argv.Length() > 2) { timeout = TRI_ObjectToDouble(argv[2]); } + if (argv.Length() > 3) { + recursive = TRI_ObjectToBoolean(argv[3]); + } AgencyComm comm; - AgencyCommResult result = comm.watchValue(key, waitIndex, timeout); + AgencyCommResult result = comm.watchValue(key, waitIndex, timeout, recursive); if (result._statusCode == 0) { // watch timed out diff --git a/js/server/tests/agency.js b/js/server/tests/agency.js index 346b163232..cac582ba08 100644 --- a/js/server/tests/agency.js +++ b/js/server/tests/agency.js @@ -92,10 +92,37 @@ function AgencySuite () { assertTrue(agency.set("UnitTestsAgency/foo", "baz")); assertTrue(agency.set("UnitTestsAgency/foo", "bart")); start = require("internal").time(); + var result = agency.watch("UnitTestsAgency/foo", "1", wait); + end = require("internal").time(); + + assertEqual(0, Math.round(end - start)); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test recursive watch +//////////////////////////////////////////////////////////////////////////////// + + testWatchRecursive : function () { + assertTrue(agency.set("UnitTestsAgency/foo/1", "bar")); + assertTrue(agency.set("UnitTestsAgency/foo/2", "baz")); + + var wait = 1; + var start = require("internal").time(); + assertFalse(agency.watch("UnitTestsAgency/foo", 0, wait)); + var end = require("internal").time(); + assertEqual(wait, Math.round(end - start)); + + assertTrue(agency.set("UnitTestsAgency/foo/3", "bart")); + start = require("internal").time(); var result = agency.watch("UnitTestsAgency/foo", 1, wait); end = require("internal").time(); assertEqual(0, Math.round(end - start)); + + start = require("internal").time(); + result = agency.watch("UnitTestsAgency/foo", 1, wait, true); + end = require("internal").time(); + assertEqual(0, Math.round(end - start)); }, //////////////////////////////////////////////////////////////////////////////// @@ -301,6 +328,61 @@ function AgencySuite () { assertEqual(values["UnitTestsAgency/someDir/foo"], "bar"); }, +//////////////////////////////////////////////////////////////////////////////// +/// @brief test get recursive +//////////////////////////////////////////////////////////////////////////////// + + testGetRecursive : function () { + assertTrue(agency.createDirectory("UnitTestsAgency/someDir")); + + agency.set("UnitTestsAgency/someDir/foo/1/1/1", "bar1"); + agency.set("UnitTestsAgency/someDir/foo/1/1/2", "bar2"); + agency.set("UnitTestsAgency/someDir/foo/1/2/1", "bar3"); + agency.set("UnitTestsAgency/someDir/foo/1/2/2", "bar4"); + agency.set("UnitTestsAgency/someDir/foo/2/1/1", "bar5"); + agency.set("UnitTestsAgency/someDir/foo/2/1/2", "bar6"); + + var values = agency.get("UnitTestsAgency/someDir"); + assertEqual({ }, values); + values = agency.get("UnitTestsAgency/someDir/foo"); + assertEqual({ }, values); + + values = agency.get("UnitTestsAgency/someDir", true); + assertTrue(values.hasOwnProperty("UnitTestsAgency/someDir/foo/1/1/1")); + assertEqual("bar1", values["UnitTestsAgency/someDir/foo/1/1/1"]); + assertTrue(values.hasOwnProperty("UnitTestsAgency/someDir/foo/1/1/2")); + assertEqual("bar2", values["UnitTestsAgency/someDir/foo/1/1/2"]); + assertTrue(values.hasOwnProperty("UnitTestsAgency/someDir/foo/1/2/1")); + assertEqual("bar3", values["UnitTestsAgency/someDir/foo/1/2/1"]); + assertTrue(values.hasOwnProperty("UnitTestsAgency/someDir/foo/1/2/2")); + assertEqual("bar4", values["UnitTestsAgency/someDir/foo/1/2/2"]); + assertTrue(values.hasOwnProperty("UnitTestsAgency/someDir/foo/2/1/1")); + assertEqual("bar5", values["UnitTestsAgency/someDir/foo/2/1/1"]); + assertTrue(values.hasOwnProperty("UnitTestsAgency/someDir/foo/2/1/2")); + assertEqual("bar6", values["UnitTestsAgency/someDir/foo/2/1/2"]); + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test get w/ indexes +//////////////////////////////////////////////////////////////////////////////// + + testGetIndexes : function () { + assertTrue(agency.createDirectory("UnitTestsAgency/someDir")); + + agency.set("UnitTestsAgency/someDir/foo", "bar"); + agency.set("UnitTestsAgency/someDir/bar", "baz"); + + var values = agency.get("UnitTestsAgency/someDir", true, true); + assertTrue(values.hasOwnProperty("UnitTestsAgency/someDir/foo")); + assertTrue(values.hasOwnProperty("UnitTestsAgency/someDir/bar")); + assertEqual(values["UnitTestsAgency/someDir/foo"].value, "bar"); + assertEqual(values["UnitTestsAgency/someDir/bar"].value, "baz"); + assertTrue(values["UnitTestsAgency/someDir/foo"].hasOwnProperty("index")); + assertTrue(values["UnitTestsAgency/someDir/bar"].hasOwnProperty("index")); + + assertNotEqual(values["UnitTestsAgency/someDir/foo"].index, values["UnitTestsAgency/someDir/bar"].index); + }, + //////////////////////////////////////////////////////////////////////////////// /// @brief test set / get directory ////////////////////////////////////////////////////////////////////////////////