1
0
Fork 0

Merge branch 'devel' of github.com:arangodb/arangodb into 3.3

This commit is contained in:
Frank Celler 2017-11-24 12:29:19 +01:00
commit a54da92fc7
13 changed files with 158 additions and 250 deletions

View File

@ -1,3 +1,9 @@
v3.3.rc3 (2017-11-24)
---------------------
* bug-fixes
v3.3.rc2 (2017-11-22)
---------------------
@ -16,7 +22,10 @@ v3.3.rc1 (2017-11-17)
* add readonly mode REST API
* allow compilation of ArangoDB source code with g++7
* allow compilation of ArangoDB source code with g++ 7
* upgrade minimum required g++ compiler version to g++ 5.4
That means ArangoDB source code will not compile with g++ 4.x or g++ < 5.4 anymore.
* AQL: during a traversal if a vertex is not found. It will not print an ERROR to the log and continue
with a NULL value, but will register a warning at the query and continue with a NULL value.

View File

@ -717,6 +717,10 @@ if test -n "${DOWNLOAD_SYNCER_USER}"; then
rm -f "${FN}"
curl -LJO# -H 'Accept: application/octet-stream' "${SYNCER_URL}?access_token=${OAUTH_TOKEN}" || \
${SRC}/Installation/Jenkins/curl_time_machine.sh "${SYNCER_URL}?access_token=${OAUTH_TOKEN}" "${FN}"
if ! test -s "${FN}" ; then
echo "failed to download syncer binary - aborting!"
exit 1
fi
mv "${FN}" "${BUILD_DIR}/${TN}"
${MD5} < "${BUILD_DIR}/${TN}" | ${SED} "s; .*;;" > "${BUILD_DIR}/${FN}-${SYNCER_REV}"
OLD_MD5=$(cat "${BUILD_DIR}/${FN}-${SYNCER_REV}")
@ -725,6 +729,9 @@ if test -n "${DOWNLOAD_SYNCER_USER}"; then
else
echo "using already downloaded ${BUILD_DIR}/${FN}-${SYNCER_REV} MD5: ${OLD_MD5}"
fi
else
echo "failed to find download URL for arangosync - aborting!"
exit 1
fi
# Log out again:
@ -771,6 +778,10 @@ if test "${DOWNLOAD_STARTER}" == 1; then
if ! test -f "${BUILD_DIR}/${FN}-${STARTER_REV}"; then
rm -f "${FN}"
curl -LO "${STARTER_URL}"
if ! test -s "${FN}" ; then
echo "failed to download starter binary - aborting!"
exit 1
fi
mv "${FN}" "${BUILD_DIR}/${TN}"
${MD5} < "${BUILD_DIR}/${TN}" | ${SED} "s; .*;;" > "${BUILD_DIR}/${FN}-${STARTER_REV}"
chmod a+x "${BUILD_DIR}/${TN}"
@ -779,6 +790,9 @@ if test "${DOWNLOAD_STARTER}" == 1; then
else
echo "using already downloaded ${BUILD_DIR}/${FN}-${STARTER_REV} MD5: ${OLD_MD5}"
fi
else
echo "failed to find download URL for arangodb starter - aborting!"
exit 1
fi
THIRDPARTY_BIN=("${THIRDPARTY_BIN}${BUILD_DIR}/${TN}")
fi

View File

@ -1481,43 +1481,27 @@ AgencyCommResult AgencyComm::sendWithFailover(
result = send(
connection.get(), method, conTimeout, url, b.toJson(), "");
if (result.successful()) {
std::shared_ptr<VPackBuilder> bodyBuilder
= VPackParser::fromJson(result._body);
VPackSlice outer = bodyBuilder->slice();
// Inquire returns a body like write or if the write is still ongoing
// We check, if the operation is still ongoing then body is {"ongoing:true"}
// _statusCode can be 200 or 412
if (result.successful() || result._statusCode == 412) {
std::shared_ptr<VPackBuilder> resultBody
= VPackParser::fromJson(result._body);
VPackSlice outer = resultBody->slice();
// If the operation is still ongoing, simply ask again later:
if (outer.isString() && outer.copyString() == "ongoing") {
if (outer.isObject() && outer.hasKey("ongoing")) {
continue;
}
// If we get an answer, we have to look at it, the condition is
// that we get that our transaction has been logged with a certain
// positive log index:
if (outer.isArray() && outer.length() > 0) {
uint64_t index = 0;
for (auto const& inner : VPackArrayIterator(outer)) {
if (inner.isArray() && inner.length() > 0) {
for (auto const& i : VPackArrayIterator(inner)) {
if (i.isObject()) {
VPackSlice indexSlice = i.get("index");
if (indexSlice.isInteger()) {
index = indexSlice.getUInt();
break;
}
}
}
}
}
if (index > 0) {
// If we get an answer, and it contains a "results" key,
// we release the connection and break out of the loop letting the
// inquiry result go to the client. Otherwise try again.
if (outer.isObject() && outer.hasKey("results")) {
VPackSlice results = outer.get("results");
if (results.length() > 0) {
LOG_TOPIC(DEBUG, Logger::AGENCYCOMM)
<< body << " succeeded (" << outer.toJson() << ")";
bodyBuilder->clear();
{
VPackArrayBuilder guard(bodyBuilder.get());
bodyBuilder->add(VPackValue(index));
}
result.set(200, "200 OK", clientId);
result._body = bodyBuilder->toJson();
<< "Inquired " << resultBody->toJson();
AgencyCommManager::MANAGER->release(std::move(connection), endpoint);
break;
} else {
// Nothing known, so do a retry of the original operation:

View File

@ -258,14 +258,17 @@ void Agent::reportIn(std::string const& peerId, index_t index, size_t toLog) {
// Update last acknowledged answer
auto t = system_clock::now();
std::chrono::duration<double> d = t - _lastAcked[peerId];
if (peerId != id() && d.count() > _config.minPing() * _config.timeoutMult()) {
LOG_TOPIC(WARN, Logger::AGENCY) << "Last confirmation from peer "
<< peerId << " was received more than minPing ago: " << d.count();
auto secsSince = d.count();
if (secsSince < 1.5e9 && peerId != id()
&& secsSince > _config.minPing() * _config.timeoutMult()) {
LOG_TOPIC(WARN, Logger::AGENCY)
<< "Last confirmation from peer " << peerId
<< " was received more than minPing ago: " << secsSince;
}
LOG_TOPIC(DEBUG, Logger::AGENCY) << "Setting _lastAcked["
<< peerId << "] to time "
LOG_TOPIC(DEBUG, Logger::AGENCY)
<< "Setting _lastAcked[" << peerId << "] to time "
<< std::chrono::duration_cast<std::chrono::microseconds>(
t.time_since_epoch()).count();
t.time_since_epoch()).count();
_lastAcked[peerId] = t;
if (index > _confirmed[peerId]) { // progress this follower?
@ -278,7 +281,6 @@ void Agent::reportIn(std::string const& peerId, index_t index, size_t toLog) {
}
}
duration<double> reportInTime = system_clock::now() - startTime;
if (reportInTime.count() > 0.1) {
LOG_TOPIC(DEBUG, Logger::AGENCY)
@ -945,8 +947,7 @@ trans_ret_t Agent::transient(query_t const& queries) {
}
inquire_ret_t Agent::inquire(query_t const& query) {
inquire_ret_t ret;
write_ret_t Agent::inquire(query_t const& query) {
// Note that we are leading (_constituent.leading()) if and only
// if _constituent.leaderId == our own ID. Therefore, we do not have
@ -954,45 +955,27 @@ inquire_ret_t Agent::inquire(query_t const& query) {
// look at the leaderID.
auto leader = _constituent.leaderID();
if (leader != id()) {
return inquire_ret_t(false, leader);
return write_ret_t(false, leader);
}
write_ret_t ret;
_tiLock.assertNotLockedByCurrentThread();
MUTEX_LOCKER(ioLocker, _ioLock);
auto si = _state.inquire(query);
bool found = false;
auto builder = std::make_shared<VPackBuilder>();
{
VPackArrayBuilder b(builder.get());
for (auto const& i : si) {
VPackArrayBuilder bb(builder.get());
for (auto const& j : i) {
found = true;
VPackObjectBuilder bbb(builder.get());
builder->add("index", VPackValue(j.index));
builder->add("term", VPackValue(j.term));
builder->add("query", VPackSlice(j.entry->data()));
}
}
}
ret = inquire_ret_t(true, id(), builder);
if (!found) {
return ret;
}
ret.indices = _state.inquire(query);
// Check ongoing ones:
for (auto const& s : VPackArrayIterator(query->slice())) {
std::string ss = s.copyString();
if (isTrxOngoing(ss)) {
ret.result->clear();
ret.result->add(VPackValue("ongoing"));
ret.indices.clear();
break;
}
}
ret.accepted = true;
return ret;
}

View File

@ -115,7 +115,7 @@ class Agent : public arangodb::Thread,
read_ret_t read(query_t const&);
/// @brief Inquire success of logs given clientIds
inquire_ret_t inquire(query_t const&);
write_ret_t inquire(query_t const&);
/// @brief Attempt read/write transaction
trans_ret_t transact(query_t const&) override;

View File

@ -508,7 +508,7 @@ RestStatus RestAgencyHandler::handleInquire() {
return RestStatus::DONE;
}
inquire_ret_t ret;
write_ret_t ret;
try {
ret = _agent->inquire(query);
} catch (std::exception const& e) {
@ -519,7 +519,22 @@ RestStatus RestAgencyHandler::handleInquire() {
if (ret.accepted) { // I am leading
generateResult(rest::ResponseCode::OK, ret.result->slice());
Builder body;
bool failed = false;
{ VPackObjectBuilder b(&body);
if (ret.indices.empty()) {
body.add("ongoing", VPackValue(true));
} else {
body.add(VPackValue("results"));
{ VPackArrayBuilder bb(&body);
for (auto const& index : ret.indices) {
body.add(VPackValue(index));
failed = (failed || index == 0);
}}}
}
generateResult(failed ? rest::ResponseCode::PRECONDITION_FAILED :
rest::ResponseCode::OK, body.slice());
} else { // Redirect to leader

View File

@ -1250,7 +1250,7 @@ query_t State::allLogs() const {
}
std::vector<std::vector<log_t>> State::inquire(query_t const& query) const {
std::vector<index_t> State::inquire(query_t const& query) const {
if (!query->slice().isArray()) {
THROW_ARANGO_EXCEPTION_MESSAGE(
20001,
@ -1258,31 +1258,31 @@ std::vector<std::vector<log_t>> State::inquire(query_t const& query) const {
+ ". We got " + query->toJson());
}
std::vector<std::vector<log_t>> result;
std::vector<index_t> result;
size_t pos = 0;
MUTEX_LOCKER(mutexLocker, _logLock); // Cannot be read lock (Compaction)
for (auto const& i : VPackArrayIterator(query->slice())) {
if (!i.isString()) {
THROW_ARANGO_EXCEPTION_MESSAGE(
210002, std::string("ClientIds must be strings. On position ")
+ std::to_string(pos) + " we got " + i.toJson());
+ std::to_string(pos++) + " we got " + i.toJson());
}
std::vector<log_t> transactions;
auto ret = _clientIdLookupTable.equal_range(i.copyString());
index_t index = 0;
for (auto it = ret.first; it != ret.second; ++it) {
if (it->second < _log[0].index) {
continue;
}
transactions.push_back(_log.at(it->second-_cur));
if (index < _log.at(it->second-_cur).index) {
index = _log.at(it->second-_cur).index;
}
}
result.push_back(transactions);
pos++;
result.push_back(index);
}
return result;
}

View File

@ -101,7 +101,7 @@ class State {
bool has(index_t, term_t) const;
/// @brief Get log entries by client Id
std::vector<std::vector<log_t>> inquire(query_t const&) const;
std::vector<index_t> inquire(query_t const&) const;
/// @brief Get complete logged commands by lower and upper bounds.
/// Default: [first, last]

View File

@ -527,7 +527,9 @@ void MMFilesLogfileManager::unprepare() {
LOG_TOPIC(TRACE, arangodb::Logger::FIXME) << "stopping collector thread";
_collectorThread->forceStop();
while (_collectorThread->isRunning()) {
locker.unlock();
usleep(10000);
locker.lock();
}
delete _collectorThread;
_collectorThread = nullptr;

View File

@ -398,34 +398,16 @@ void AuthInfo::reloadAllUsers() {
// tell other coordinators to reload as well
AgencyComm agency;
AgencyWriteTransaction incrementVersion({
AgencyOperation("Sync/UserVersion", AgencySimpleOperationType::INCREMENT_OP)
});
int maxTries = 10;
while (maxTries-- > 0) {
AgencyCommResult commRes = agency.getValues("Sync/UserVersion");
if (!commRes.successful()) {
// Error in communication, note that value not found is not an error
LOG_TOPIC(TRACE, Logger::AUTHENTICATION)
<< "AuthInfo: no agency communication";
break;
}
VPackSlice oldVal = commRes.slice()[0].get(
{AgencyCommManager::path(), "Sync", "UserVersion"});
if (!oldVal.isInteger()) {
LOG_TOPIC(ERR, Logger::AUTHENTICATION)
<< "Sync/UserVersion is not a number";
THROW_ARANGO_EXCEPTION(TRI_ERROR_BAD_PARAMETER);
}
VPackBuilder newVal;
newVal.add(VPackValue(oldVal.getUInt() + 1));
commRes =
agency.casValue("Sync/UserVersion", oldVal, newVal.slice(), 0.0,
AgencyCommManager::CONNECTION_OPTIONS._requestTimeout);
if (commRes.successful()) {
AgencyCommResult result = agency.sendTransactionWithFailover(incrementVersion);
if (result.successful()) {
return;
}
}

View File

@ -2912,4 +2912,4 @@ var cutByResolution = function (str) {
</div>
<div id="workMonitorContent" class="innerContent">
</div></script></head><body><nav class="navbar" style="display: none"><div class="primary"><div class="navlogo"><a class="logo big" href="#"><img id="ArangoDBLogo" class="arangodbLogo" src="img/arangodb-edition-optimized.svg"></a><a class="logo small" href="#"><img class="arangodbLogo" src="img/arangodb_logo_small.png"></a><a class="version"><span id="currentVersion"></span></a></div><div class="statmenu" id="statisticBar"></div><div class="navmenu" id="navigationBar"></div></div></nav><div id="modalPlaceholder"></div><div class="bodyWrapper" style="display: none"><div class="centralRow"><div id="navbar2" class="navbarWrapper secondary"><div class="subnavmenu" id="subNavigationBar"></div></div><div class="resizecontainer contentWrapper"><div id="loadingScreen" class="loadingScreen" style="display: none"><i class="fa fa-circle-o-notch fa-spin fa-3x fa-fw margin-bottom"></i> <span class="sr-only">Loading...</span></div><div id="content" class="centralContent"></div><footer class="footer"><div id="footerBar"></div></footer></div></div></div><div id="progressPlaceholder" style="display:none"></div><div id="spotlightPlaceholder" style="display:none"></div><div id="graphSettingsContent" style="display: none"></div><div id="filterSelectDiv" style="display:none"></div><div id="offlinePlaceholder" style="display:none"><div class="offline-div"><div class="pure-u"><div class="pure-u-1-4"></div><div class="pure-u-1-2 offline-window"><div class="offline-header"><h3>You have been disconnected from the server</h3></div><div class="offline-body"><p>The connection to the server has been lost. The server may be under heavy load.</p><p>Trying to reconnect in <span id="offlineSeconds">10</span> seconds.</p><p class="animation_state"><span><button class="button-success">Reconnect now</button></span></p></div></div><div class="pure-u-1-4"></div></div></div></div><div class="arangoFrame" style=""><div class="outerDiv"><div class="innerDiv"></div></div></div><script src="libs.js?version=1511375069738"></script><script src="app.js?version=1511375069738"></script></body></html>
</div></script></head><body><nav class="navbar" style="display: none"><div class="primary"><div class="navlogo"><a class="logo big" href="#"><img id="ArangoDBLogo" class="arangodbLogo" src="img/arangodb-edition-optimized.svg"></a><a class="logo small" href="#"><img class="arangodbLogo" src="img/arangodb_logo_small.png"></a><a class="version"><span id="currentVersion"></span></a></div><div class="statmenu" id="statisticBar"></div><div class="navmenu" id="navigationBar"></div></div></nav><div id="modalPlaceholder"></div><div class="bodyWrapper" style="display: none"><div class="centralRow"><div id="navbar2" class="navbarWrapper secondary"><div class="subnavmenu" id="subNavigationBar"></div></div><div class="resizecontainer contentWrapper"><div id="loadingScreen" class="loadingScreen" style="display: none"><i class="fa fa-circle-o-notch fa-spin fa-3x fa-fw margin-bottom"></i> <span class="sr-only">Loading...</span></div><div id="content" class="centralContent"></div><footer class="footer"><div id="footerBar"></div></footer></div></div></div><div id="progressPlaceholder" style="display:none"></div><div id="spotlightPlaceholder" style="display:none"></div><div id="graphSettingsContent" style="display: none"></div><div id="filterSelectDiv" style="display:none"></div><div id="offlinePlaceholder" style="display:none"><div class="offline-div"><div class="pure-u"><div class="pure-u-1-4"></div><div class="pure-u-1-2 offline-window"><div class="offline-header"><h3>You have been disconnected from the server</h3></div><div class="offline-body"><p>The connection to the server has been lost. The server may be under heavy load.</p><p>Trying to reconnect in <span id="offlineSeconds">10</span> seconds.</p><p class="animation_state"><span><button class="button-success">Reconnect now</button></span></p></div></div><div class="pure-u-1-4"></div></div></div></div><div class="arangoFrame" style=""><div class="outerDiv"><div class="innerDiv"></div></div></div><script src="libs.js?version=1511485026250"></script><script src="app.js?version=1511485026250"></script></body></html>

View File

@ -259,7 +259,7 @@ function agencyTestSuite () {
assertEqual(readAndCheck([["/a"]]), [{a:13}]);
var res = accessAgency("write", [[{"/a":14},{"/a":12}]]); // fail precond {a:12}
assertEqual(res.statusCode, 412);
assertEqual(res.bodyParsed, {"results":[0]});
assertEqual(res.bodyParsed, {"results":[0]});
writeAndCheck([[{a:{op:"delete"}}]]);
// fail precond oldEmpty
res = accessAgency("write",[[{"a":14},{"a":{"oldEmpty":false}}]]);
@ -415,163 +415,83 @@ function agencyTestSuite () {
testClientIds : function () {
var res;
var cur;
res = accessAgency("write", [[{"a":12}]]).bodyParsed;
cur = res.results[0];
writeAndCheck([[{"/a":12}]]);
var id = [guid(),guid(),guid(),guid(),guid(),guid(),
guid(),guid(),guid(),guid(),guid(),guid(),
guid(),guid(),guid()];
var query = [{"a":12},{"a":13},{"a":13}];
var pre = [{},{"a":12},{"a":12}];
cur += 2;
writeAndCheck([[query[0], pre[0], id[0]]]);
var wres = writeAndCheck([[query[0], pre[0], id[0]]]);
res = accessAgency("inquire",[id[0]]).bodyParsed;
assertEqual(res.length, 1);
assertEqual(res[0].length, 1);
assertEqual(res[0][0].query, query[0]);
assertEqual(res, {"results":[cur]});
assertEqual(res, wres);
writeAndCheck([[query[1], pre[1], id[0]]]);
wres = writeAndCheck([[query[1], pre[1], id[0]]]);
res = accessAgency("inquire",[id[0]]).bodyParsed;
assertEqual(res.length, 1);
assertEqual(res[0].length, 2);
assertEqual(res[0][0].query, query[0]);
assertEqual(res[0][1].query, query[1]);
assertEqual(res, {"results":[++cur]});
assertEqual(res, wres);
res = accessAgency("write",[[query[1], pre[1], id[2]]]);
assertEqual(res.statusCode,412);
res = accessAgency("inquire",[id[2]]).bodyParsed;
assertEqual(res[0].length, 0);
wres = accessAgency("write",[[query[1], pre[1], id[2]]]);
assertEqual(wres.statusCode,412);
res = accessAgency("inquire",[id[2]]);
assertEqual(res.bodyParsed, {"results":[0]});
assertEqual(res, wres);
res = accessAgency("write",[[query[0], pre[0], id[3]],
[query[1], pre[1], id[3]]]);
wres = accessAgency("write",[[query[0], pre[0], id[3]],
[query[1], pre[1], id[3]]]);
assertEqual(wres.statusCode,200);
cur += 2;
res = accessAgency("inquire",[id[3]]);
assertEqual(res.bodyParsed, {"results":[cur]});
assertEqual(res.bodyParsed.results[0], wres.bodyParsed.results[1]);
assertEqual(res.statusCode,200);
res = accessAgency("inquire",[id[3]]).bodyParsed;
assertEqual(res.length, 1);
assertEqual(res[0][0].query, query[0]);
assertEqual(res[0][1].query, query[1]);
res = accessAgency("write",[[query[0], pre[0], id[4]],
[query[1], pre[1], id[4]],
[query[2], pre[2], id[4]]]);
assertEqual(res.statusCode,412);
res = accessAgency("inquire",[id[4]]).bodyParsed;
assertEqual(res.length, 1);
assertEqual(res[0].length, 2);
assertEqual(res[0][0].query, query[0]);
assertEqual(res[0][1].query, query[1]);
res = accessAgency("write",[[query[0], pre[0], id[5]],
[query[2], pre[2], id[5]],
[query[1], pre[1], id[5]]]);
assertEqual(res.statusCode,412);
res = accessAgency("inquire",[id[5]]).bodyParsed;
assertEqual(res.length, 1);
assertEqual(res[0].length, 2);
assertEqual(res[0][0].query, query[0]);
assertEqual(res[0][1].query, query[1]);
wres = accessAgency("write",[[query[0], pre[0], id[4]],
[query[1], pre[1], id[4]],
[query[2], pre[2], id[4]]]);
assertEqual(wres.statusCode,412);
cur += 2;
res = accessAgency("inquire",[id[4]]);
assertEqual(res.bodyParsed, {"results":[cur]});
assertEqual(res.bodyParsed.results[0], wres.bodyParsed.results[1]);
assertEqual(res.statusCode,200);
res = accessAgency("write",[[query[2], pre[2], id[6]],
[query[0], pre[0], id[6]],
[query[1], pre[1], id[6]]]);
assertEqual(res.statusCode,412);
res = accessAgency("inquire",[id[6]]).bodyParsed;
assertEqual(res.length, 1);
assertEqual(res[0].length, 2);
assertEqual(res[0][0].query, query[0]);
assertEqual(res[0][1].query, query[1]);
wres = accessAgency("write",[[query[0], pre[0], id[5]],
[query[2], pre[2], id[5]],
[query[1], pre[1], id[5]]]);
assertEqual(wres.statusCode,412);
cur += 2;
res = accessAgency("inquire",[id[5]]);
assertEqual(res.bodyParsed, {"results":[cur]});
assertEqual(res.bodyParsed.results[0], wres.bodyParsed.results[1]);
assertEqual(res.statusCode,200);
res = accessAgency("write",[[query[2], pre[2], id[7]],
wres = accessAgency("write",[[query[2], pre[2], id[6]],
[query[0], pre[0], id[6]],
[query[1], pre[1], id[6]]]);
assertEqual(wres.statusCode,412);
cur += 2;
res = accessAgency("inquire",[id[6]]);
assertEqual(res.bodyParsed, {"results":[cur]});
assertEqual(res.bodyParsed.results[0], wres.bodyParsed.results[2]);
assertEqual(res.statusCode,200);
wres = accessAgency("write",[[query[2], pre[2], id[7]],
[query[0], pre[0], id[8]],
[query[1], pre[1], id[9]]]);
assertEqual(res.statusCode,412);
res = accessAgency("inquire",[id[7],id[8],id[9]]).bodyParsed;
assertEqual(res.length, 3);
assertEqual(res[0].length, 0);
assertEqual(res[1].length, 1);
assertEqual(res[1][0].query, query[0]);
assertEqual(res[2].length, 1);
assertEqual(res[2][0].query, query[1]);
assertEqual(res.statusCode,200);
cur += 2;
res = accessAgency("inquire",[id[7],id[8],id[9]]);
assertEqual(res, wres);
res = accessAgency("inquire",[id[9],id[7],id[8]]).bodyParsed;
assertEqual(res.length, 3);
assertEqual(res[0].length, 1);
assertEqual(res[0][0].query, query[1]);
assertEqual(res[1].length, 0);
assertEqual(res[2].length, 1);
assertEqual(res[2][0].query, query[0]);
res = accessAgency("inquire",[id[8],id[9],id[7]]).bodyParsed;
assertEqual(res.length, 3);
assertEqual(res[0].length, 1);
assertEqual(res[0][0].query, query[0]);
assertEqual(res[1].length, 1);
assertEqual(res[1][0].query, query[1]);
assertEqual(res[2].length, 0);
res = accessAgency("inquire",[id[7],id[9],id[8]]).bodyParsed;
assertEqual(res.length, 3);
assertEqual(res[0].length, 0);
assertEqual(res[1].length, 1);
assertEqual(res[1][0].query, query[1]);
assertEqual(res[2].length, 1);
assertEqual(res[2][0].query, query[0]);
res = accessAgency("inquire",[id[8],id[7],id[9]]).bodyParsed;
assertEqual(res.length, 3);
assertEqual(res[0].length, 1);
assertEqual(res[0][0].query, query[0]);
assertEqual(res[1].length, 0);
assertEqual(res[2].length, 1);
assertEqual(res[2][0].query, query[1]);
res = accessAgency("inquire",[id[7],id[8],id[9]]).bodyParsed;
assertEqual(res.length, 3);
assertEqual(res[0].length, 0);
assertEqual(res[1].length, 1);
assertEqual(res[1][0].query, query[0]);
assertEqual(res[2].length, 1);
assertEqual(res[2][0].query, query[1]);
res = accessAgency("inquire",[id[7],id[8],id[9]]).bodyParsed;
assertEqual(res.length, 3);
assertEqual(res[0].length, 0);
assertEqual(res[1].length, 1);
assertEqual(res[1][0].query, query[0]);
assertEqual(res[2].length, 1);
assertEqual(res[2][0].query, query[1]);
res = accessAgency("write",[[query[2], pre[2], id[10]],
[query[0], pre[0], id[11]],
[query[1], pre[1], id[12]],
[query[0], pre[0], id[12]]]);
res = accessAgency("inquire",[id[10],id[11],id[12]]).bodyParsed;
assertEqual(res.length, 3);
assertEqual(res[0].length, 0);
assertEqual(res[1].length, 1);
assertEqual(res[1][0].query, query[0]);
assertEqual(res[2].length, 2);
assertEqual(res[2][0].query, query[1]);
assertEqual(res[2][1].query, query[0]);
res = accessAgency("transact",[[query[0], pre[0], id[13]],
[query[2], pre[2], id[13]],
[query[1], pre[1], id[13]],
["a"]]);
assertEqual(res.statusCode,412);
assertEqual(res.bodyParsed.length, 4);
assertEqual(res.bodyParsed[0] > 0, true);
assertEqual(res.bodyParsed[1] > 0, true);
assertEqual(res.bodyParsed[2], {a : 13});
assertEqual(res.bodyParsed[3], query[1]);
res = accessAgency("inquire",[id[13]]).bodyParsed;
assertEqual(res.length, 1);
assertEqual(res[0].length, 2);
assertEqual(res[0][0].query, query[0]);
assertEqual(res[0][1].query, query[2]);
},
@ -1045,7 +965,6 @@ function agencyTestSuite () {
writeAndCheck(huge);
assertEqual(readAndCheck([["a"]]), [{"a":20000}]);
}
};
}