mirror of https://gitee.com/bigwinds/arangodb
Merge branch 'devel' of github.com:arangodb/ArangoDB into devel
This commit is contained in:
commit
3408fb2445
79
CHANGELOG
79
CHANGELOG
|
@ -1,6 +1,32 @@
|
|||
devel
|
||||
-----
|
||||
|
||||
* removed IndexThreadFeature, made --database.index-threads option obsolete
|
||||
|
||||
* changed index filling to make it more parallel, dispatch tasks to boost::asio
|
||||
|
||||
* more detailed stacktraces in Foxx apps
|
||||
|
||||
|
||||
v3.1.10 (2017-XX-XX)
|
||||
--------------------
|
||||
|
||||
* updated versions of bundled node modules:
|
||||
- joi: from 8.4.2 to 9.2.0
|
||||
- joi-to-json-schema: from 2.2.0 to 2.3.0
|
||||
- sinon: from 1.17.4 to 1.17.6
|
||||
- lodash: from 4.13.1 to 4.16.6
|
||||
|
||||
* added shortcut for AQL ternary operator
|
||||
instead of `condition ? true-part : false-part` it is now possible to also use a
|
||||
shortcut variant `condition ? : false-part`, e.g.
|
||||
|
||||
FOR doc IN docs RETURN doc.value ?: 'not present'
|
||||
|
||||
instead of
|
||||
|
||||
FOR doc IN docs RETURN doc.value ? doc.value : 'not present'
|
||||
|
||||
* fixed wrong sorting order in cluster, if an index was used to sort with many
|
||||
shards.
|
||||
|
||||
|
@ -16,38 +42,38 @@ shards.
|
|||
setting the flag to `true` will make the count operation returned the per-shard
|
||||
counts for the collection:
|
||||
|
||||
db._create("test", { numberOfShards: 10 });
|
||||
for (i = 0; i < 1000; ++i) {
|
||||
db.test.insert({value: i});
|
||||
db._create("test", { numberOfShards: 10 });
|
||||
for (i = 0; i < 1000; ++i) {
|
||||
db.test.insert({value: i});
|
||||
}
|
||||
db.test.count(true);
|
||||
|
||||
{
|
||||
"s100058" : 99,
|
||||
"s100057" : 103,
|
||||
"s100056" : 100,
|
||||
"s100050" : 94,
|
||||
"s100055" : 90,
|
||||
"s100054" : 122,
|
||||
"s100051" : 109,
|
||||
"s100059" : 99,
|
||||
"s100053" : 95,
|
||||
"s100052" : 89
|
||||
{
|
||||
"s100058" : 99,
|
||||
"s100057" : 103,
|
||||
"s100056" : 100,
|
||||
"s100050" : 94,
|
||||
"s100055" : 90,
|
||||
"s100054" : 122,
|
||||
"s100051" : 109,
|
||||
"s100059" : 99,
|
||||
"s100053" : 95,
|
||||
"s100052" : 89
|
||||
}
|
||||
|
||||
* added optional memory limit for AQL queries:
|
||||
|
||||
db._query("FOR i IN 1..100000 SORT i RETURN i", {}, { options: { memoryLimit: 100000 } });
|
||||
|
||||
This option limits the default maximum amount of memory (in bytes) that a single
|
||||
This option limits the default maximum amount of memory (in bytes) that a single
|
||||
AQL query can use.
|
||||
When a single AQL query reaches the specified limit value, the query will be
|
||||
aborted with a *resource limit exceeded* exception. In a cluster, the memory
|
||||
accounting is done per shard, so the limit value is effectively a memory limit per
|
||||
query per shard.
|
||||
|
||||
The global limit value can be overriden per query by setting the *memoryLimit*
|
||||
option value for individual queries when running an AQL query.
|
||||
The global limit value can be overriden per query by setting the *memoryLimit*
|
||||
option value for individual queries when running an AQL query.
|
||||
|
||||
* added server startup option `--query.memory-limit`
|
||||
|
||||
|
@ -60,6 +86,13 @@ edge attribute `label`.
|
|||
|
||||
* added option -D to define a configuration file environment key=value
|
||||
|
||||
* changed encoding behavior for URLs encoded in the C++ code of ArangoDB:
|
||||
previously the special characters `-`, `_`, `~` and `.` were returned as-is
|
||||
after URL-encoding, now `.` will be encoded to be `%2e`.
|
||||
This also changes the behavior of how incoming URIs are processed: previously
|
||||
occurrences of `..` in incoming request URIs were collapsed (e.g. `a/../b/` was
|
||||
collapsed to a plain `b/`). Now `..` in incoming request URIs are not collapsed.
|
||||
|
||||
* Foxx request URL suffix is no longer unescaped
|
||||
|
||||
* @arangodb/request option json now defaults to `true` if the response body is not empty and encoding is not explicitly set to `null` (binary).
|
||||
|
@ -170,7 +203,7 @@ v3.1.4 (2016-12-08)
|
|||
|
||||
* agency RAFT timing estimates more conservative in waitForSync
|
||||
scenario
|
||||
|
||||
|
||||
* agency RAFT timing estimates capped at maximum 2.0/10.0 for low/high
|
||||
|
||||
|
||||
|
@ -281,7 +314,7 @@ v3.1.1 (2016-11-15)
|
|||
produced an empty result
|
||||
|
||||
* disallow updating `_from` and `_to` values of edges in Smart Graphs. Updating these
|
||||
attributes would lead to potential redistribution of edges to other shards, which must be
|
||||
attributes would lead to potential redistribution of edges to other shards, which must be
|
||||
avoided.
|
||||
|
||||
* fixed issue #2148
|
||||
|
@ -1169,7 +1202,7 @@ v2.8.12 (XXXX-XX-XX)
|
|||
v2.8.11 (2016-07-13)
|
||||
--------------------
|
||||
|
||||
* fixed array index batch insertion issues for hash indexes that caused problems when
|
||||
* fixed array index batch insertion issues for hash indexes that caused problems when
|
||||
no elements remained for insertion
|
||||
|
||||
* fixed issue #1937
|
||||
|
@ -1611,9 +1644,9 @@ v2.8.0-beta7 (2016-01-06)
|
|||
* fixes for AQL optimizer and traversal
|
||||
|
||||
* added `--create-collection-type` option to arangoimp
|
||||
|
||||
This allows specifying the type of the collection to be created when
|
||||
`--create-collection` is set to `true`.
|
||||
|
||||
This allows specifying the type of the collection to be created when
|
||||
`--create-collection` is set to `true`.
|
||||
|
||||
* Foxx export cache should no longer break if a broken app is loaded in the
|
||||
web admin interface.
|
||||
|
|
|
@ -270,6 +270,17 @@ evaluates to true, and the third operand otherwise.
|
|||
u.age > 15 || u.active == true ? u.userId : null
|
||||
```
|
||||
|
||||
There is also a shortcut variant of the ternary operator with just two
|
||||
operands. This variant can be used when the expression for the boolean
|
||||
condition and the return value should be the same:
|
||||
|
||||
*Examples*
|
||||
|
||||
```js
|
||||
u.value ? : 'value is null, 0 or not present'
|
||||
```
|
||||
|
||||
|
||||
#### Range operator
|
||||
|
||||
AQL supports expressing simple numeric ranges with the *..* operator.
|
||||
|
|
|
@ -132,16 +132,16 @@ daemon. This parameter must be specified if either the flag *daemon* or
|
|||
|
||||
`--console`
|
||||
|
||||
Runs the server in an exclusive emergency console mode. When
|
||||
Runs the server in an exclusive emergency console mode. When
|
||||
starting the server with this option, the server is started with
|
||||
an interactive JavaScript emergency console, with all networking
|
||||
and HTTP interfaces of the server disabled.
|
||||
|
||||
No requests can be made to the server in this mode, and the only
|
||||
way to work with the server in this mode is by using the emergency
|
||||
console.
|
||||
Note that the server cannot be started in this mode if it is
|
||||
already running in this or another mode.
|
||||
console.
|
||||
Note that the server cannot be started in this mode if it is
|
||||
already running in this or another mode.
|
||||
|
||||
|
||||
### Random Generator
|
||||
|
@ -419,21 +419,6 @@ This option only has an effect if the query cache mode is set to either *on* or
|
|||
*demand*.
|
||||
|
||||
|
||||
### Index threads
|
||||
|
||||
`--database.index-threads`
|
||||
|
||||
Specifies the *number* of background threads for index creation. When a
|
||||
collection contains extra indexes other than the primary index, these other
|
||||
indexes can be built by multiple threads in parallel. The index threads are
|
||||
shared among multiple collections and databases. Specifying a value of *0* will
|
||||
turn off parallel building, meaning that indexes for each collection are built
|
||||
sequentially by the thread that opened the collection. If the number of index
|
||||
threads is greater than 1, it will also be used to built the edge index of a
|
||||
collection in parallel (this also requires the edge index in the collection to
|
||||
be split into multiple buckets).
|
||||
|
||||
|
||||
### V8 contexts
|
||||
|
||||
`--javascript.v8-contexts number`
|
||||
|
@ -491,4 +476,3 @@ might change in the future if a different version of V8 is being used in
|
|||
ArangoDB. Not all options offered by V8 might be sensible to use in the context
|
||||
of ArangoDB. Use the specific options only if you are sure that they are not
|
||||
harmful for the regular database operation.
|
||||
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
|
||||
|
||||
@brief number of background threads for parallel index creation
|
||||
`--database.index-threads`
|
||||
|
||||
Specifies the *number* of background threads for index creation. When a
|
||||
collection contains extra indexes other than the primary index, these
|
||||
other
|
||||
indexes can be built by multiple threads in parallel. The index threads
|
||||
are shared among multiple collections and databases. Specifying a value of
|
||||
*0* will turn off parallel building, meaning that indexes for each
|
||||
collection
|
||||
are built sequentially by the thread that opened the collection.
|
||||
If the number of index threads is greater than 1, it will also be used to
|
||||
built the edge index of a collection in parallel (this also requires the
|
||||
edge index in the collection to be split into multiple buckets).
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
|
||||
|
||||
@brief scheduler backend
|
||||
`--scheduler.backend arg`
|
||||
|
||||
The I/O method used by the event handler. The default (if this option is
|
||||
not specified) is to try all recommended backends. This is platform
|
||||
specific. See libev for further details and the meaning of select, poll
|
||||
and epoll.
|
||||
|
|
@ -51,11 +51,6 @@
|
|||
* Project Home: http://site.icu-project.org/
|
||||
* License: [ICU License](http://source.icu-project.org/repos/icu/icu/trunk/license.html)
|
||||
|
||||
### libev 4.11
|
||||
|
||||
* Project Home: http://software.schmorp.de/pkg/libev.html
|
||||
* License: Dual-License [BSD-style 2-Clause License](http://cvs.schmorp.de/libev/LICENSE?revision=1.11&view=markup)
|
||||
|
||||
### linenoise-ng
|
||||
|
||||
* GitHub: https://github.com/arangodb/linenoise-ng
|
||||
|
|
|
@ -612,7 +612,7 @@ std::string AgencyCommManager::redirect(
|
|||
<< specification << ", url = " << rest;
|
||||
|
||||
if (endpoint == specification) {
|
||||
LOG_TOPIC(WARN, Logger::AGENCYCOMM)
|
||||
LOG_TOPIC(DEBUG, Logger::AGENCYCOMM)
|
||||
<< "got an agency redirect back to the old agency '" << endpoint << "'";
|
||||
failedNonLocking(std::move(connection), endpoint);
|
||||
return "";
|
||||
|
@ -632,7 +632,7 @@ std::string AgencyCommManager::redirect(
|
|||
std::remove(_endpoints.begin(), _endpoints.end(), specification),
|
||||
_endpoints.end());
|
||||
|
||||
LOG_TOPIC(WARN, Logger::AGENCYCOMM)
|
||||
LOG_TOPIC(DEBUG, Logger::AGENCYCOMM)
|
||||
<< "Got an agency redirect from '" << endpoint
|
||||
<< "' to '" << specification << "'";
|
||||
|
||||
|
@ -1359,17 +1359,11 @@ AgencyCommResult AgencyComm::sendWithFailover(
|
|||
LOG_TOPIC(ERR, Logger::AGENCYCOMM) << result._message;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (1 < tries) {
|
||||
LOG_TOPIC(WARN, Logger::AGENCYCOMM)
|
||||
<< "Retrying agency communication at '" << endpoint
|
||||
<< "', tries: " << tries << " ("
|
||||
<< 1.e-2 * (
|
||||
std::round(
|
||||
1.e+2 * std::chrono::duration<double>(
|
||||
std::chrono::steady_clock::now() - started).count())) << "s)";
|
||||
}
|
||||
|
||||
|
||||
double elapsed = 1.e-2 * (
|
||||
std::round(1.e+2 * std::chrono::duration<double>(
|
||||
std::chrono::steady_clock::now() - started).count()));
|
||||
|
||||
// try to send; if we fail completely, do not retry
|
||||
try {
|
||||
result = send(connection.get(), method, conTimeout, url, body, clientId);
|
||||
|
@ -1379,7 +1373,7 @@ AgencyCommResult AgencyComm::sendWithFailover(
|
|||
connection = AgencyCommManager::MANAGER->acquire(endpoint);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// got a result, we are done
|
||||
if (result.successful()) {
|
||||
AgencyCommManager::MANAGER->release(std::move(connection), endpoint);
|
||||
|
@ -1389,7 +1383,7 @@ AgencyCommResult AgencyComm::sendWithFailover(
|
|||
// break on a watch timeout (drop connection)
|
||||
if (!clientId.empty() && result._sent &&
|
||||
(result._statusCode == 0 || result._statusCode == 503)) {
|
||||
|
||||
|
||||
VPackBuilder b;
|
||||
{
|
||||
VPackArrayBuilder ab(&b);
|
||||
|
@ -1470,6 +1464,26 @@ AgencyCommResult AgencyComm::sendWithFailover(
|
|||
break;
|
||||
}
|
||||
|
||||
if (tries%50 == 0) {
|
||||
LOG_TOPIC(WARN, Logger::AGENCYCOMM)
|
||||
<< "Bad agency communiction! Unsuccessful consecutive tries:"
|
||||
<< tries << " (" << elapsed << "s). Network checks needed!";
|
||||
} else if (tries%15 == 0) {
|
||||
LOG_TOPIC(INFO, Logger::AGENCYCOMM)
|
||||
<< "Flaky agency communication. Unsuccessful consecutive tries: "
|
||||
<< tries << " (" << elapsed << "s). Network checks advised.";
|
||||
}
|
||||
|
||||
if (1 < tries) {
|
||||
LOG_TOPIC(DEBUG, Logger::AGENCYCOMM)
|
||||
<< "Retrying agency communication at '" << endpoint
|
||||
<< "', tries: " << tries << " ("
|
||||
<< 1.e-2 * (
|
||||
std::round(
|
||||
1.e+2 * std::chrono::duration<double>(
|
||||
std::chrono::steady_clock::now() - started).count())) << "s)";
|
||||
}
|
||||
|
||||
// here we have failed and want to try next endpoint
|
||||
AgencyCommManager::MANAGER->failed(std::move(connection), endpoint);
|
||||
endpoint.clear();
|
||||
|
|
|
@ -1042,7 +1042,7 @@ struct CoordinatorInstanciator : public WalkerWorker<ExecutionNode> {
|
|||
id = TRI_NewTickServer();
|
||||
|
||||
try {
|
||||
queryRegistry->insert(id, engine->getQuery(), 3600.0);
|
||||
queryRegistry->insert(id, engine->getQuery(), 600.0);
|
||||
} catch (...) {
|
||||
delete engine->getQuery();
|
||||
// This deletes the new query as well as the engine
|
||||
|
|
|
@ -45,7 +45,7 @@ class QueryRegistry {
|
|||
/// a query for this <vocbase> and <id> combination and an exception will
|
||||
/// be thrown in that case. The time to live <ttl> is in seconds and the
|
||||
/// query will be deleted if it is not opened for that amount of time.
|
||||
void insert(QueryId id, Query* query, double ttl = 3600.0);
|
||||
void insert(QueryId id, Query* query, double ttl = 600.0);
|
||||
|
||||
/// @brief open, find a query in the registry, if none is found, a nullptr
|
||||
/// is returned, otherwise, ownership of the query is transferred to the
|
||||
|
|
|
@ -105,7 +105,7 @@ void RestAqlHandler::createQueryFromVelocyPack() {
|
|||
}
|
||||
|
||||
// Now the query is ready to go, store it in the registry and return:
|
||||
double ttl = 3600.0;
|
||||
double ttl = 600.0;
|
||||
bool found;
|
||||
std::string const& ttlstring = _request->header("ttl", found);
|
||||
|
||||
|
@ -317,7 +317,7 @@ void RestAqlHandler::createQueryFromString() {
|
|||
}
|
||||
|
||||
// Now the query is ready to go, store it in the registry and return:
|
||||
double ttl = 3600.0;
|
||||
double ttl = 600.0;
|
||||
bool found;
|
||||
std::string const& ttlstring = _request->header("ttl", found);
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ if (MSVC)
|
|||
VERSION_REVISION ${BUILD_ID}
|
||||
)
|
||||
endif ()
|
||||
|
||||
|
||||
set(ROCKSDB_FILES
|
||||
Indexes/RocksDBFeature.cpp
|
||||
Indexes/RocksDBIndex.cpp
|
||||
|
@ -342,7 +342,6 @@ SET(ARANGOD_SOURCES
|
|||
VocBase/Ditch.cpp
|
||||
VocBase/EdgeCollectionInfo.cpp
|
||||
VocBase/Graphs.cpp
|
||||
VocBase/IndexThreadFeature.cpp
|
||||
VocBase/KeyGenerator.cpp
|
||||
VocBase/LogicalCollection.cpp
|
||||
VocBase/ManagedDocumentResult.cpp
|
||||
|
|
|
@ -79,9 +79,11 @@ void ClusterFeature::collectOptions(std::shared_ptr<ProgramOptions> options) {
|
|||
options->addSection("cluster", "Configure the cluster");
|
||||
|
||||
options->addObsoleteOption("--cluster.username",
|
||||
"username used for cluster-internal communication");
|
||||
"username used for cluster-internal communication",
|
||||
true);
|
||||
options->addObsoleteOption("--cluster.password",
|
||||
"password used for cluster-internal communication");
|
||||
"password used for cluster-internal communication",
|
||||
true);
|
||||
|
||||
options->addOption("--cluster.agency-endpoint",
|
||||
"agency endpoint to connect to",
|
||||
|
|
|
@ -85,6 +85,60 @@ HeartbeatThread::HeartbeatThread(AgencyCallbackRegistry* agencyCallbackRegistry,
|
|||
|
||||
HeartbeatThread::~HeartbeatThread() { shutdown(); }
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief running of heartbeat background jobs (in JavaScript), we run
|
||||
/// these by instantiating an object in class HeartbeatBackgroundJob,
|
||||
/// which is a std::function<void()> and holds a shared_ptr to the
|
||||
/// HeartbeatThread singleton itself. This instance is then posted to
|
||||
/// the io_service for execution in the thread pool. Should the heartbeat
|
||||
/// thread itself terminate during shutdown, then the HeartbeatThread
|
||||
/// singleton itself is still kept alive by the shared_ptr in the instance
|
||||
/// of HeartbeatBackgroundJob. The operator() method simply calls the
|
||||
/// runBackgroundJob() method of the heartbeat thread. Should this have
|
||||
/// to schedule another background job, then it can simply create a new
|
||||
/// HeartbeatBackgroundJob instance, again using shared_from_this() to
|
||||
/// create a new shared_ptr keeping the HeartbeatThread object alive.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class HeartbeatBackgroundJob {
|
||||
std::shared_ptr<HeartbeatThread> _heartbeatThread;
|
||||
public:
|
||||
explicit HeartbeatBackgroundJob(std::shared_ptr<HeartbeatThread> hbt)
|
||||
: _heartbeatThread(hbt) {}
|
||||
|
||||
void operator()() {
|
||||
_heartbeatThread->runBackgroundJob();
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief method runBackgroundJob()
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void HeartbeatThread::runBackgroundJob() {
|
||||
uint64_t jobNr = ++_backgroundJobsLaunched;
|
||||
LOG_TOPIC(INFO, Logger::HEARTBEAT) << "sync callback started " << jobNr;
|
||||
{
|
||||
DBServerAgencySync job(this);
|
||||
job.work();
|
||||
}
|
||||
LOG_TOPIC(INFO, Logger::HEARTBEAT) << "sync callback ended " << jobNr;
|
||||
|
||||
{
|
||||
MUTEX_LOCKER(mutexLocker, *_statusLock);
|
||||
TRI_ASSERT(_backgroundJobScheduledOrRunning);
|
||||
if (_launchAnotherBackgroundJob) {
|
||||
jobNr = ++_backgroundJobsPosted;
|
||||
LOG_TOPIC(INFO, Logger::HEARTBEAT) << "dispatching sync tail " << jobNr;
|
||||
_launchAnotherBackgroundJob = false;
|
||||
_ioService->post(HeartbeatBackgroundJob(shared_from_this()));
|
||||
} else {
|
||||
_backgroundJobScheduledOrRunning = false;
|
||||
_launchAnotherBackgroundJob = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief heartbeat main loop
|
||||
/// the heartbeat thread constantly reports the current server status to the
|
||||
|
@ -102,31 +156,6 @@ void HeartbeatThread::run() {
|
|||
if (ServerState::instance()->isCoordinator()) {
|
||||
runCoordinator();
|
||||
} else {
|
||||
// Set the member variable that holds a closure to run background
|
||||
// jobs in JS:
|
||||
auto self = shared_from_this();
|
||||
_backgroundJob = [self, this]() {
|
||||
uint64_t jobNr = ++_backgroundJobsLaunched;
|
||||
LOG_TOPIC(DEBUG, Logger::HEARTBEAT) << "sync callback started " << jobNr;
|
||||
{
|
||||
DBServerAgencySync job(this);
|
||||
job.work();
|
||||
}
|
||||
LOG_TOPIC(DEBUG, Logger::HEARTBEAT) << "sync callback ended " << jobNr;
|
||||
|
||||
{
|
||||
MUTEX_LOCKER(mutexLocker, *_statusLock);
|
||||
if (_launchAnotherBackgroundJob) {
|
||||
LOG_TOPIC(DEBUG, Logger::HEARTBEAT) << "dispatching sync tail "
|
||||
<< ++_backgroundJobsPosted;
|
||||
_launchAnotherBackgroundJob = false;
|
||||
_ioService->post(_backgroundJob);
|
||||
} else {
|
||||
_backgroundJobScheduledOrRunning = false;
|
||||
_launchAnotherBackgroundJob = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
runDBServer();
|
||||
}
|
||||
}
|
||||
|
@ -338,16 +367,6 @@ void HeartbeatThread::runDBServer() {
|
|||
}
|
||||
|
||||
_agencyCallbackRegistry->unregisterCallback(planAgencyCallback);
|
||||
int count = 0;
|
||||
while (++count < 3000) {
|
||||
{
|
||||
MUTEX_LOCKER(mutexLocker, *_statusLock);
|
||||
if (!_backgroundJobScheduledOrRunning) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
usleep(100000);
|
||||
}
|
||||
LOG_TOPIC(TRACE, Logger::HEARTBEAT)
|
||||
<< "stopped heartbeat thread (DBServer version)";
|
||||
}
|
||||
|
@ -577,7 +596,7 @@ bool HeartbeatThread::init() {
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void HeartbeatThread::dispatchedJobResult(DBServerAgencySyncResult result) {
|
||||
LOG_TOPIC(DEBUG, Logger::HEARTBEAT) << "Dispatched job returned!";
|
||||
LOG_TOPIC(INFO, Logger::HEARTBEAT) << "Dispatched job returned!";
|
||||
bool doSleep = false;
|
||||
{
|
||||
MUTEX_LOCKER(mutexLocker, *_statusLock);
|
||||
|
@ -761,10 +780,10 @@ void HeartbeatThread::syncDBServerStatusQuo() {
|
|||
}
|
||||
|
||||
// schedule a job for the change:
|
||||
LOG_TOPIC(DEBUG, Logger::HEARTBEAT) << "dispatching sync "
|
||||
<< ++_backgroundJobsPosted;
|
||||
uint64_t jobNr = ++_backgroundJobsPosted;
|
||||
LOG_TOPIC(INFO, Logger::HEARTBEAT) << "dispatching sync " << jobNr;
|
||||
_backgroundJobScheduledOrRunning = true;
|
||||
_ioService->post(_backgroundJob);
|
||||
_ioService->post(HeartbeatBackgroundJob(shared_from_this()));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -74,6 +74,8 @@ class HeartbeatThread : public Thread,
|
|||
|
||||
void setReady() { _ready.store(true); }
|
||||
|
||||
void runBackgroundJob();
|
||||
|
||||
void dispatchedJobResult(DBServerAgencySyncResult);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -253,12 +255,6 @@ class HeartbeatThread : public Thread,
|
|||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool _launchAnotherBackgroundJob;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief _backgroundJob, the closure that does the work
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
std::function<void()> _backgroundJob;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "Aql/AstNode.h"
|
||||
#include "Aql/SortCondition.h"
|
||||
#include "Basics/Exceptions.h"
|
||||
#include "Basics/LocalTaskQueue.h"
|
||||
#include "Basics/StaticStrings.h"
|
||||
#include "Basics/StringRef.h"
|
||||
#include "Basics/fasthash.h"
|
||||
|
@ -45,9 +46,9 @@ using namespace arangodb;
|
|||
/// @brief hard-coded vector of the index attributes
|
||||
/// note that the attribute names must be hard-coded here to avoid an init-order
|
||||
/// fiasco with StaticStrings::FromString etc.
|
||||
static std::vector<std::vector<arangodb::basics::AttributeName>> const IndexAttributes
|
||||
{{arangodb::basics::AttributeName("_from", false)},
|
||||
{arangodb::basics::AttributeName("_to", false)}};
|
||||
static std::vector<std::vector<arangodb::basics::AttributeName>> const
|
||||
IndexAttributes{{arangodb::basics::AttributeName("_from", false)},
|
||||
{arangodb::basics::AttributeName("_to", false)}};
|
||||
|
||||
/// @brief hashes an edge key
|
||||
static uint64_t HashElementKey(void*, VPackSlice const* key) {
|
||||
|
@ -58,7 +59,8 @@ static uint64_t HashElementKey(void*, VPackSlice const* key) {
|
|||
}
|
||||
|
||||
/// @brief hashes an edge
|
||||
static uint64_t HashElementEdge(void*, SimpleIndexElement const& element, bool byKey) {
|
||||
static uint64_t HashElementEdge(void*, SimpleIndexElement const& element,
|
||||
bool byKey) {
|
||||
if (byKey) {
|
||||
return element.hash();
|
||||
}
|
||||
|
@ -68,11 +70,12 @@ static uint64_t HashElementEdge(void*, SimpleIndexElement const& element, bool b
|
|||
}
|
||||
|
||||
/// @brief checks if key and element match
|
||||
static bool IsEqualKeyEdge(void* userData, VPackSlice const* left, SimpleIndexElement const& right) {
|
||||
static bool IsEqualKeyEdge(void* userData, VPackSlice const* left,
|
||||
SimpleIndexElement const& right) {
|
||||
TRI_ASSERT(left != nullptr);
|
||||
IndexLookupContext* context = static_cast<IndexLookupContext*>(userData);
|
||||
TRI_ASSERT(context != nullptr);
|
||||
|
||||
|
||||
try {
|
||||
VPackSlice tmp = right.slice(context);
|
||||
TRI_ASSERT(tmp.isString());
|
||||
|
@ -83,17 +86,20 @@ static bool IsEqualKeyEdge(void* userData, VPackSlice const* left, SimpleIndexEl
|
|||
}
|
||||
|
||||
/// @brief checks for elements are equal
|
||||
static bool IsEqualElementEdge(void*, SimpleIndexElement const& left, SimpleIndexElement const& right) {
|
||||
static bool IsEqualElementEdge(void*, SimpleIndexElement const& left,
|
||||
SimpleIndexElement const& right) {
|
||||
return left.revisionId() == right.revisionId();
|
||||
}
|
||||
|
||||
/// @brief checks for elements are equal
|
||||
static bool IsEqualElementEdgeByKey(void* userData, SimpleIndexElement const& left, SimpleIndexElement const& right) {
|
||||
static bool IsEqualElementEdgeByKey(void* userData,
|
||||
SimpleIndexElement const& left,
|
||||
SimpleIndexElement const& right) {
|
||||
IndexLookupContext* context = static_cast<IndexLookupContext*>(userData);
|
||||
try {
|
||||
VPackSlice lSlice = left.slice(context);
|
||||
VPackSlice rSlice = right.slice(context);
|
||||
|
||||
|
||||
TRI_ASSERT(lSlice.isString());
|
||||
TRI_ASSERT(rSlice.isString());
|
||||
|
||||
|
@ -102,8 +108,9 @@ static bool IsEqualElementEdgeByKey(void* userData, SimpleIndexElement const& le
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
EdgeIndexIterator::EdgeIndexIterator(LogicalCollection* collection, arangodb::Transaction* trx,
|
||||
|
||||
EdgeIndexIterator::EdgeIndexIterator(LogicalCollection* collection,
|
||||
arangodb::Transaction* trx,
|
||||
ManagedDocumentResult* mmdr,
|
||||
arangodb::EdgeIndex const* index,
|
||||
TRI_EdgeIndexHash_t const* indexImpl,
|
||||
|
@ -115,13 +122,12 @@ EdgeIndexIterator::EdgeIndexIterator(LogicalCollection* collection, arangodb::Tr
|
|||
_posInBuffer(0),
|
||||
_batchSize(1000),
|
||||
_lastElement() {
|
||||
|
||||
keys.release(); // now we have ownership for _keys
|
||||
keys.release(); // now we have ownership for _keys
|
||||
}
|
||||
|
||||
EdgeIndexIterator::~EdgeIndexIterator() {
|
||||
if (_keys != nullptr) {
|
||||
// return the VPackBuilder to the transaction context
|
||||
// return the VPackBuilder to the transaction context
|
||||
_trx->transactionContextPtr()->returnBuilder(_keys.release());
|
||||
}
|
||||
}
|
||||
|
@ -144,7 +150,7 @@ IndexLookupResult EdgeIndexIterator::next() {
|
|||
_posInBuffer = 0;
|
||||
_index->lookupByKeyContinue(&_context, _lastElement, _buffer, _batchSize);
|
||||
}
|
||||
|
||||
|
||||
if (_buffer.empty()) {
|
||||
_lastElement = SimpleIndexElement();
|
||||
} else {
|
||||
|
@ -160,7 +166,8 @@ IndexLookupResult EdgeIndexIterator::next() {
|
|||
return IndexLookupResult();
|
||||
}
|
||||
|
||||
void EdgeIndexIterator::nextBabies(std::vector<IndexLookupResult>& buffer, size_t limit) {
|
||||
void EdgeIndexIterator::nextBabies(std::vector<IndexLookupResult>& buffer,
|
||||
size_t limit) {
|
||||
size_t atMost = _batchSize > limit ? limit : _batchSize;
|
||||
|
||||
if (atMost == 0) {
|
||||
|
@ -189,7 +196,7 @@ void EdgeIndexIterator::nextBabies(std::vector<IndexLookupResult>& buffer, size_
|
|||
for (auto& it : _buffer) {
|
||||
buffer.emplace_back(it.revisionId());
|
||||
}
|
||||
|
||||
|
||||
if (_buffer.empty()) {
|
||||
_lastElement = SimpleIndexElement();
|
||||
} else {
|
||||
|
@ -197,7 +204,7 @@ void EdgeIndexIterator::nextBabies(std::vector<IndexLookupResult>& buffer, size_
|
|||
// found something
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// found no result. now go to next lookup value in _keys
|
||||
_iterator.next();
|
||||
}
|
||||
|
@ -211,13 +218,11 @@ void EdgeIndexIterator::reset() {
|
|||
_iterator.reset();
|
||||
_lastElement = SimpleIndexElement();
|
||||
}
|
||||
|
||||
AnyDirectionEdgeIndexIterator::AnyDirectionEdgeIndexIterator(LogicalCollection* collection,
|
||||
arangodb::Transaction* trx,
|
||||
ManagedDocumentResult* mmdr,
|
||||
arangodb::EdgeIndex const* index,
|
||||
EdgeIndexIterator* outboundIterator,
|
||||
EdgeIndexIterator* inboundIterator)
|
||||
|
||||
AnyDirectionEdgeIndexIterator::AnyDirectionEdgeIndexIterator(
|
||||
LogicalCollection* collection, arangodb::Transaction* trx,
|
||||
ManagedDocumentResult* mmdr, arangodb::EdgeIndex const* index,
|
||||
EdgeIndexIterator* outboundIterator, EdgeIndexIterator* inboundIterator)
|
||||
: IndexIterator(collection, trx, mmdr, index),
|
||||
_outbound(outboundIterator),
|
||||
_inbound(inboundIterator),
|
||||
|
@ -240,7 +245,8 @@ IndexLookupResult AnyDirectionEdgeIndexIterator::next() {
|
|||
return res;
|
||||
}
|
||||
|
||||
void AnyDirectionEdgeIndexIterator::nextBabies(std::vector<IndexLookupResult>& result, size_t limit) {
|
||||
void AnyDirectionEdgeIndexIterator::nextBabies(
|
||||
std::vector<IndexLookupResult>& result, size_t limit) {
|
||||
result.clear();
|
||||
for (size_t i = 0; i < limit; ++i) {
|
||||
IndexLookupResult res = next();
|
||||
|
@ -261,8 +267,10 @@ void AnyDirectionEdgeIndexIterator::reset() {
|
|||
EdgeIndex::EdgeIndex(TRI_idx_iid_t iid, arangodb::LogicalCollection* collection)
|
||||
: Index(iid, collection,
|
||||
std::vector<std::vector<arangodb::basics::AttributeName>>(
|
||||
{{arangodb::basics::AttributeName(StaticStrings::FromString, false)},
|
||||
{arangodb::basics::AttributeName(StaticStrings::ToString, false)}}),
|
||||
{{arangodb::basics::AttributeName(StaticStrings::FromString,
|
||||
false)},
|
||||
{arangodb::basics::AttributeName(StaticStrings::ToString,
|
||||
false)}}),
|
||||
false, false),
|
||||
_edgesFrom(nullptr),
|
||||
_edgesTo(nullptr),
|
||||
|
@ -276,10 +284,9 @@ EdgeIndex::EdgeIndex(TRI_idx_iid_t iid, arangodb::LogicalCollection* collection)
|
|||
|
||||
auto context = [this]() -> std::string { return this->context(); };
|
||||
|
||||
_edgesFrom = new TRI_EdgeIndexHash_t(HashElementKey, HashElementEdge,
|
||||
IsEqualKeyEdge, IsEqualElementEdge,
|
||||
IsEqualElementEdgeByKey, _numBuckets,
|
||||
64, context);
|
||||
_edgesFrom = new TRI_EdgeIndexHash_t(
|
||||
HashElementKey, HashElementEdge, IsEqualKeyEdge, IsEqualElementEdge,
|
||||
IsEqualElementEdgeByKey, _numBuckets, 64, context);
|
||||
|
||||
_edgesTo = new TRI_EdgeIndexHash_t(
|
||||
HashElementKey, HashElementEdge, IsEqualKeyEdge, IsEqualElementEdge,
|
||||
|
@ -326,8 +333,8 @@ void EdgeIndex::buildSearchValue(TRI_edge_direction_e dir,
|
|||
builder.close();
|
||||
}
|
||||
|
||||
void EdgeIndex::buildSearchValue(TRI_edge_direction_e dir,
|
||||
VPackSlice const& id, VPackBuilder& builder) {
|
||||
void EdgeIndex::buildSearchValue(TRI_edge_direction_e dir, VPackSlice const& id,
|
||||
VPackBuilder& builder) {
|
||||
TRI_ASSERT(id.isString());
|
||||
builder.openArray();
|
||||
switch (dir) {
|
||||
|
@ -416,14 +423,14 @@ void EdgeIndex::buildSearchValueFromArray(TRI_edge_direction_e dir,
|
|||
}
|
||||
|
||||
/// @brief return a selectivity estimate for the index
|
||||
double EdgeIndex::selectivityEstimate(arangodb::StringRef const* attribute) const {
|
||||
if (_edgesFrom == nullptr ||
|
||||
_edgesTo == nullptr ||
|
||||
double EdgeIndex::selectivityEstimate(
|
||||
arangodb::StringRef const* attribute) const {
|
||||
if (_edgesFrom == nullptr || _edgesTo == nullptr ||
|
||||
ServerState::instance()->isCoordinator()) {
|
||||
// use hard-coded selectivity estimate in case of cluster coordinator
|
||||
return 0.1;
|
||||
}
|
||||
|
||||
|
||||
if (attribute != nullptr) {
|
||||
// the index attribute is given here
|
||||
// now check if we can restrict the selectivity estimation to the correct
|
||||
|
@ -470,16 +477,16 @@ void EdgeIndex::toVelocyPackFigures(VPackBuilder& builder) const {
|
|||
builder.add("to", VPackValue(VPackValueType::Object));
|
||||
_edgesTo->appendToVelocyPack(builder);
|
||||
builder.close();
|
||||
//builder.add("buckets", VPackValue(_numBuckets));
|
||||
// builder.add("buckets", VPackValue(_numBuckets));
|
||||
}
|
||||
|
||||
int EdgeIndex::insert(arangodb::Transaction* trx, TRI_voc_rid_t revisionId,
|
||||
VPackSlice const& doc, bool isRollback) {
|
||||
SimpleIndexElement fromElement(buildFromElement(revisionId, doc));
|
||||
SimpleIndexElement toElement(buildToElement(revisionId, doc));
|
||||
|
||||
ManagedDocumentResult result(trx);
|
||||
IndexLookupContext context(trx, _collection, &result, 1);
|
||||
|
||||
ManagedDocumentResult result(trx);
|
||||
IndexLookupContext context(trx, _collection, &result, 1);
|
||||
_edgesFrom->insert(&context, fromElement, true, isRollback);
|
||||
|
||||
try {
|
||||
|
@ -497,11 +504,11 @@ int EdgeIndex::remove(arangodb::Transaction* trx, TRI_voc_rid_t revisionId,
|
|||
VPackSlice const& doc, bool isRollback) {
|
||||
SimpleIndexElement fromElement(buildFromElement(revisionId, doc));
|
||||
SimpleIndexElement toElement(buildToElement(revisionId, doc));
|
||||
|
||||
ManagedDocumentResult result(trx);
|
||||
IndexLookupContext context(trx, _collection, &result, 1);
|
||||
|
||||
try {
|
||||
|
||||
ManagedDocumentResult result(trx);
|
||||
IndexLookupContext context(trx, _collection, &result, 1);
|
||||
|
||||
try {
|
||||
_edgesFrom->remove(&context, fromElement);
|
||||
_edgesTo->remove(&context, toElement);
|
||||
return TRI_ERROR_NO_ERROR;
|
||||
|
@ -513,15 +520,21 @@ int EdgeIndex::remove(arangodb::Transaction* trx, TRI_voc_rid_t revisionId,
|
|||
}
|
||||
}
|
||||
|
||||
int EdgeIndex::batchInsert(arangodb::Transaction* trx,
|
||||
std::vector<std::pair<TRI_voc_rid_t, VPackSlice>> const& documents,
|
||||
size_t numThreads) {
|
||||
void EdgeIndex::batchInsert(
|
||||
arangodb::Transaction* trx,
|
||||
std::vector<std::pair<TRI_voc_rid_t, VPackSlice>> const& documents,
|
||||
arangodb::basics::LocalTaskQueue* queue) {
|
||||
if (documents.empty()) {
|
||||
return TRI_ERROR_NO_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<SimpleIndexElement> elements;
|
||||
elements.reserve(documents.size());
|
||||
|
||||
std::shared_ptr<std::vector<SimpleIndexElement>> fromElements;
|
||||
fromElements.reset(new std::vector<SimpleIndexElement>());
|
||||
fromElements->reserve(documents.size());
|
||||
|
||||
std::shared_ptr<std::vector<SimpleIndexElement>> toElements;
|
||||
toElements.reset(new std::vector<SimpleIndexElement>());
|
||||
toElements->reserve(documents.size());
|
||||
|
||||
// functions that will be called for each thread
|
||||
auto creator = [&trx, this]() -> void* {
|
||||
|
@ -534,32 +547,26 @@ int EdgeIndex::batchInsert(arangodb::Transaction* trx,
|
|||
delete context;
|
||||
};
|
||||
|
||||
// TODO: create parallel tasks for this
|
||||
|
||||
// _from
|
||||
for (auto const& it : documents) {
|
||||
VPackSlice value(Transaction::extractFromFromDocument(it.second));
|
||||
elements.emplace_back(SimpleIndexElement(it.first, value, static_cast<uint32_t>(value.begin() - it.second.begin())));
|
||||
}
|
||||
|
||||
int res = _edgesFrom->batchInsert(creator, destroyer, &elements, numThreads);
|
||||
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
return res;
|
||||
fromElements->emplace_back(SimpleIndexElement(
|
||||
it.first, value,
|
||||
static_cast<uint32_t>(value.begin() - it.second.begin())));
|
||||
}
|
||||
|
||||
// _to
|
||||
elements.clear();
|
||||
for (auto const& it : documents) {
|
||||
VPackSlice value(Transaction::extractToFromDocument(it.second));
|
||||
elements.emplace_back(SimpleIndexElement(it.first, value, static_cast<uint32_t>(value.begin() - it.second.begin())));
|
||||
toElements->emplace_back(SimpleIndexElement(
|
||||
it.first, value,
|
||||
static_cast<uint32_t>(value.begin() - it.second.begin())));
|
||||
}
|
||||
|
||||
res = _edgesTo->batchInsert(creator, destroyer, &elements, numThreads);
|
||||
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
return res;
|
||||
}
|
||||
|
||||
return TRI_ERROR_NO_ERROR;
|
||||
_edgesFrom->batchInsert(creator, destroyer, fromElements, queue);
|
||||
_edgesTo->batchInsert(creator, destroyer, toElements, queue);
|
||||
}
|
||||
|
||||
/// @brief unload the index data from memory
|
||||
|
@ -578,8 +585,8 @@ int EdgeIndex::sizeHint(arangodb::Transaction* trx, size_t size) {
|
|||
|
||||
// set an initial size for the index for some new nodes to be created
|
||||
// without resizing
|
||||
ManagedDocumentResult result(trx);
|
||||
IndexLookupContext context(trx, _collection, &result, 1);
|
||||
ManagedDocumentResult result(trx);
|
||||
IndexLookupContext context(trx, _collection, &result, 1);
|
||||
int err = _edgesFrom->resize(&context, size + 2049);
|
||||
|
||||
if (err != TRI_ERROR_NO_ERROR) {
|
||||
|
@ -600,7 +607,6 @@ bool EdgeIndex::supportsFilterCondition(
|
|||
arangodb::aql::AstNode const* node,
|
||||
arangodb::aql::Variable const* reference, size_t itemsInIndex,
|
||||
size_t& estimatedItems, double& estimatedCost) const {
|
||||
|
||||
SimpleAttributeEqualityMatcher matcher(IndexAttributes);
|
||||
return matcher.matchOne(this, node, reference, itemsInIndex, estimatedItems,
|
||||
estimatedCost);
|
||||
|
@ -608,8 +614,7 @@ bool EdgeIndex::supportsFilterCondition(
|
|||
|
||||
/// @brief creates an IndexIterator for the given Condition
|
||||
IndexIterator* EdgeIndex::iteratorForCondition(
|
||||
arangodb::Transaction* trx,
|
||||
ManagedDocumentResult* mmdr,
|
||||
arangodb::Transaction* trx, ManagedDocumentResult* mmdr,
|
||||
arangodb::aql::AstNode const* node,
|
||||
arangodb::aql::Variable const* reference, bool reverse) const {
|
||||
TRI_ASSERT(node->type == aql::NODE_TYPE_OPERATOR_NARY_AND);
|
||||
|
@ -651,7 +656,6 @@ IndexIterator* EdgeIndex::iteratorForCondition(
|
|||
arangodb::aql::AstNode* EdgeIndex::specializeCondition(
|
||||
arangodb::aql::AstNode* node,
|
||||
arangodb::aql::Variable const* reference) const {
|
||||
|
||||
SimpleAttributeEqualityMatcher matcher(IndexAttributes);
|
||||
return matcher.specializeOne(this, node, reference);
|
||||
}
|
||||
|
@ -700,13 +704,12 @@ void EdgeIndex::expandInSearchValues(VPackSlice const slice,
|
|||
/// Each key needs to have the following formats:
|
||||
///
|
||||
/// 1) {"eq": <compareValue>} // The value in index is exactly this
|
||||
///
|
||||
///
|
||||
/// Reverse is not supported, hence ignored
|
||||
/// NOTE: The iterator is only valid as long as the slice points to
|
||||
/// a valid memory region.
|
||||
IndexIterator* EdgeIndex::iteratorForSlice(
|
||||
arangodb::Transaction* trx,
|
||||
ManagedDocumentResult* mmdr,
|
||||
arangodb::Transaction* trx, ManagedDocumentResult* mmdr,
|
||||
arangodb::velocypack::Slice const searchValues, bool) const {
|
||||
if (!searchValues.isArray() || searchValues.length() != 2) {
|
||||
// Invalid searchValue
|
||||
|
@ -717,7 +720,7 @@ IndexIterator* EdgeIndex::iteratorForSlice(
|
|||
TRI_ASSERT(it.valid());
|
||||
|
||||
VPackSlice const from = it.value();
|
||||
|
||||
|
||||
it.next();
|
||||
TRI_ASSERT(it.valid());
|
||||
VPackSlice const to = it.value();
|
||||
|
@ -730,20 +733,24 @@ IndexIterator* EdgeIndex::iteratorForSlice(
|
|||
TransactionBuilderLeaser fromBuilder(trx);
|
||||
std::unique_ptr<VPackBuilder> fromKeys(fromBuilder.steal());
|
||||
fromKeys->add(from);
|
||||
auto left = std::make_unique<EdgeIndexIterator>(_collection, trx, mmdr, this, _edgesFrom, fromKeys);
|
||||
auto left = std::make_unique<EdgeIndexIterator>(
|
||||
_collection, trx, mmdr, this, _edgesFrom, fromKeys);
|
||||
|
||||
TransactionBuilderLeaser toBuilder(trx);
|
||||
std::unique_ptr<VPackBuilder> toKeys(toBuilder.steal());
|
||||
toKeys->add(to);
|
||||
auto right = std::make_unique<EdgeIndexIterator>(_collection, trx, mmdr, this, _edgesTo, toKeys);
|
||||
return new AnyDirectionEdgeIndexIterator(_collection, trx, mmdr, this, left.release(), right.release());
|
||||
auto right = std::make_unique<EdgeIndexIterator>(_collection, trx, mmdr,
|
||||
this, _edgesTo, toKeys);
|
||||
return new AnyDirectionEdgeIndexIterator(_collection, trx, mmdr, this,
|
||||
left.release(), right.release());
|
||||
}
|
||||
// OUTBOUND search
|
||||
TRI_ASSERT(to.isNull());
|
||||
TransactionBuilderLeaser builder(trx);
|
||||
std::unique_ptr<VPackBuilder> keys(builder.steal());
|
||||
keys->add(from);
|
||||
return new EdgeIndexIterator(_collection, trx, mmdr, this, _edgesFrom, keys);
|
||||
return new EdgeIndexIterator(_collection, trx, mmdr, this, _edgesFrom,
|
||||
keys);
|
||||
} else {
|
||||
// INBOUND search
|
||||
TRI_ASSERT(to.isArray());
|
||||
|
@ -756,12 +763,10 @@ IndexIterator* EdgeIndex::iteratorForSlice(
|
|||
|
||||
/// @brief create the iterator
|
||||
IndexIterator* EdgeIndex::createEqIterator(
|
||||
arangodb::Transaction* trx,
|
||||
ManagedDocumentResult* mmdr,
|
||||
arangodb::Transaction* trx, ManagedDocumentResult* mmdr,
|
||||
arangodb::aql::AstNode const* attrNode,
|
||||
arangodb::aql::AstNode const* valNode) const {
|
||||
|
||||
// lease builder, but immediately pass it to the unique_ptr so we don't leak
|
||||
// lease builder, but immediately pass it to the unique_ptr so we don't leak
|
||||
TransactionBuilderLeaser builder(trx);
|
||||
std::unique_ptr<VPackBuilder> keys(builder.steal());
|
||||
keys->openArray();
|
||||
|
@ -775,21 +780,20 @@ IndexIterator* EdgeIndex::createEqIterator(
|
|||
// _from or _to?
|
||||
bool const isFrom = (attrNode->stringEquals(StaticStrings::FromString));
|
||||
|
||||
return new EdgeIndexIterator(_collection, trx, mmdr, this, isFrom ? _edgesFrom : _edgesTo, keys);
|
||||
return new EdgeIndexIterator(_collection, trx, mmdr, this,
|
||||
isFrom ? _edgesFrom : _edgesTo, keys);
|
||||
}
|
||||
|
||||
/// @brief create the iterator
|
||||
IndexIterator* EdgeIndex::createInIterator(
|
||||
arangodb::Transaction* trx,
|
||||
ManagedDocumentResult* mmdr,
|
||||
arangodb::Transaction* trx, ManagedDocumentResult* mmdr,
|
||||
arangodb::aql::AstNode const* attrNode,
|
||||
arangodb::aql::AstNode const* valNode) const {
|
||||
|
||||
// lease builder, but immediately pass it to the unique_ptr so we don't leak
|
||||
// lease builder, but immediately pass it to the unique_ptr so we don't leak
|
||||
TransactionBuilderLeaser builder(trx);
|
||||
std::unique_ptr<VPackBuilder> keys(builder.steal());
|
||||
keys->openArray();
|
||||
|
||||
|
||||
size_t const n = valNode->numMembers();
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
handleValNode(keys.get(), valNode->getMemberUnchecked(i));
|
||||
|
@ -806,7 +810,8 @@ IndexIterator* EdgeIndex::createInIterator(
|
|||
// _from or _to?
|
||||
bool const isFrom = (attrNode->stringEquals(StaticStrings::FromString));
|
||||
|
||||
return new EdgeIndexIterator(_collection, trx, mmdr, this, isFrom ? _edgesFrom : _edgesTo, keys);
|
||||
return new EdgeIndexIterator(_collection, trx, mmdr, this,
|
||||
isFrom ? _edgesFrom : _edgesTo, keys);
|
||||
}
|
||||
|
||||
/// @brief add a single value node to the iterator's keys
|
||||
|
@ -817,24 +822,30 @@ void EdgeIndex::handleValNode(VPackBuilder* keys,
|
|||
}
|
||||
|
||||
keys->openObject();
|
||||
keys->add(StaticStrings::IndexEq, VPackValuePair(valNode->getStringValue(), valNode->getStringLength(), VPackValueType::String));
|
||||
keys->add(StaticStrings::IndexEq,
|
||||
VPackValuePair(valNode->getStringValue(),
|
||||
valNode->getStringLength(), VPackValueType::String));
|
||||
keys->close();
|
||||
|
||||
|
||||
TRI_IF_FAILURE("EdgeIndex::collectKeys") {
|
||||
THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG);
|
||||
}
|
||||
}
|
||||
|
||||
SimpleIndexElement EdgeIndex::buildFromElement(TRI_voc_rid_t revisionId, VPackSlice const& doc) const {
|
||||
SimpleIndexElement EdgeIndex::buildFromElement(TRI_voc_rid_t revisionId,
|
||||
VPackSlice const& doc) const {
|
||||
TRI_ASSERT(doc.isObject());
|
||||
VPackSlice value(Transaction::extractFromFromDocument(doc));
|
||||
TRI_ASSERT(value.isString());
|
||||
return SimpleIndexElement(revisionId, value, static_cast<uint32_t>(value.begin() - doc.begin()));
|
||||
return SimpleIndexElement(revisionId, value,
|
||||
static_cast<uint32_t>(value.begin() - doc.begin()));
|
||||
}
|
||||
|
||||
SimpleIndexElement EdgeIndex::buildToElement(TRI_voc_rid_t revisionId, VPackSlice const& doc) const {
|
||||
SimpleIndexElement EdgeIndex::buildToElement(TRI_voc_rid_t revisionId,
|
||||
VPackSlice const& doc) const {
|
||||
TRI_ASSERT(doc.isObject());
|
||||
VPackSlice value(Transaction::extractToFromDocument(doc));
|
||||
TRI_ASSERT(value.isString());
|
||||
return SimpleIndexElement(revisionId, value, static_cast<uint32_t>(value.begin() - doc.begin()));
|
||||
return SimpleIndexElement(revisionId, value,
|
||||
static_cast<uint32_t>(value.begin() - doc.begin()));
|
||||
}
|
||||
|
|
|
@ -24,22 +24,26 @@
|
|||
#ifndef ARANGOD_INDEXES_EDGE_INDEX_H
|
||||
#define ARANGOD_INDEXES_EDGE_INDEX_H 1
|
||||
|
||||
#include "Basics/Common.h"
|
||||
#include "Basics/AssocMulti.h"
|
||||
#include "Basics/Common.h"
|
||||
#include "Indexes/Index.h"
|
||||
#include "Indexes/IndexIterator.h"
|
||||
#include "VocBase/vocbase.h"
|
||||
#include "VocBase/voc-types.h"
|
||||
#include "VocBase/vocbase.h"
|
||||
|
||||
#include <velocypack/Iterator.h>
|
||||
#include <velocypack/Slice.h>
|
||||
|
||||
namespace arangodb {
|
||||
class EdgeIndex;
|
||||
|
||||
typedef arangodb::basics::AssocMulti<arangodb::velocypack::Slice, SimpleIndexElement,
|
||||
uint32_t, false> TRI_EdgeIndexHash_t;
|
||||
namespace basics {
|
||||
class LocalTaskQueue;
|
||||
}
|
||||
|
||||
class EdgeIndex;
|
||||
|
||||
typedef arangodb::basics::AssocMulti<arangodb::velocypack::Slice,
|
||||
SimpleIndexElement, uint32_t, false>
|
||||
TRI_EdgeIndexHash_t;
|
||||
|
||||
class EdgeIndexIterator final : public IndexIterator {
|
||||
public:
|
||||
|
@ -50,7 +54,7 @@ class EdgeIndexIterator final : public IndexIterator {
|
|||
std::unique_ptr<VPackBuilder>& keys);
|
||||
|
||||
~EdgeIndexIterator();
|
||||
|
||||
|
||||
char const* typeName() const override { return "edge-index-iterator"; }
|
||||
|
||||
IndexLookupResult next() override;
|
||||
|
@ -82,7 +86,7 @@ class AnyDirectionEdgeIndexIterator final : public IndexIterator {
|
|||
delete _outbound;
|
||||
delete _inbound;
|
||||
}
|
||||
|
||||
|
||||
char const* typeName() const override { return "any-edge-index-iterator"; }
|
||||
|
||||
IndexLookupResult next() override;
|
||||
|
@ -120,19 +124,18 @@ class EdgeIndex final : public Index {
|
|||
public:
|
||||
/// @brief typedef for hash tables
|
||||
public:
|
||||
IndexType type() const override {
|
||||
return Index::TRI_IDX_TYPE_EDGE_INDEX;
|
||||
}
|
||||
IndexType type() const override { return Index::TRI_IDX_TYPE_EDGE_INDEX; }
|
||||
|
||||
bool allowExpansion() const override { return false; }
|
||||
|
||||
|
||||
bool canBeDropped() const override { return false; }
|
||||
|
||||
bool isSorted() const override { return false; }
|
||||
|
||||
bool hasSelectivityEstimate() const override { return true; }
|
||||
|
||||
double selectivityEstimate(arangodb::StringRef const* = nullptr) const override;
|
||||
double selectivityEstimate(
|
||||
arangodb::StringRef const* = nullptr) const override;
|
||||
|
||||
size_t memory() const override;
|
||||
|
||||
|
@ -140,12 +143,16 @@ class EdgeIndex final : public Index {
|
|||
|
||||
void toVelocyPackFigures(VPackBuilder&) const override;
|
||||
|
||||
int insert(arangodb::Transaction*, TRI_voc_rid_t, arangodb::velocypack::Slice const&, bool isRollback) override;
|
||||
int insert(arangodb::Transaction*, TRI_voc_rid_t,
|
||||
arangodb::velocypack::Slice const&, bool isRollback) override;
|
||||
|
||||
int remove(arangodb::Transaction*, TRI_voc_rid_t, arangodb::velocypack::Slice const&, bool isRollback) override;
|
||||
int remove(arangodb::Transaction*, TRI_voc_rid_t,
|
||||
arangodb::velocypack::Slice const&, bool isRollback) override;
|
||||
|
||||
void batchInsert(arangodb::Transaction*,
|
||||
std::vector<std::pair<TRI_voc_rid_t, VPackSlice>> const&,
|
||||
arangodb::basics::LocalTaskQueue*) override;
|
||||
|
||||
int batchInsert(arangodb::Transaction*, std::vector<std::pair<TRI_voc_rid_t, VPackSlice>> const&, size_t) override;
|
||||
|
||||
int unload() override;
|
||||
|
||||
int sizeHint(arangodb::Transaction*, size_t) override;
|
||||
|
@ -188,30 +195,31 @@ class EdgeIndex final : public Index {
|
|||
/// Reverse is not supported, hence ignored
|
||||
/// NOTE: The iterator is only valid as long as the slice points to
|
||||
/// a valid memory region.
|
||||
IndexIterator* iteratorForSlice(arangodb::Transaction*,
|
||||
IndexIterator* iteratorForSlice(arangodb::Transaction*,
|
||||
ManagedDocumentResult*,
|
||||
arangodb::velocypack::Slice const,
|
||||
bool) const override;
|
||||
|
||||
private:
|
||||
/// @brief create the iterator
|
||||
IndexIterator* createEqIterator(
|
||||
arangodb::Transaction*,
|
||||
ManagedDocumentResult*,
|
||||
arangodb::aql::AstNode const*,
|
||||
arangodb::aql::AstNode const*) const;
|
||||
|
||||
IndexIterator* createInIterator(
|
||||
arangodb::Transaction*,
|
||||
ManagedDocumentResult*,
|
||||
arangodb::aql::AstNode const*,
|
||||
arangodb::aql::AstNode const*) const;
|
||||
IndexIterator* createEqIterator(arangodb::Transaction*,
|
||||
ManagedDocumentResult*,
|
||||
arangodb::aql::AstNode const*,
|
||||
arangodb::aql::AstNode const*) const;
|
||||
|
||||
IndexIterator* createInIterator(arangodb::Transaction*,
|
||||
ManagedDocumentResult*,
|
||||
arangodb::aql::AstNode const*,
|
||||
arangodb::aql::AstNode const*) const;
|
||||
|
||||
/// @brief add a single value node to the iterator's keys
|
||||
void handleValNode(VPackBuilder* keys, arangodb::aql::AstNode const* valNode) const;
|
||||
void handleValNode(VPackBuilder* keys,
|
||||
arangodb::aql::AstNode const* valNode) const;
|
||||
|
||||
SimpleIndexElement buildFromElement(TRI_voc_rid_t, arangodb::velocypack::Slice const& doc) const;
|
||||
SimpleIndexElement buildToElement(TRI_voc_rid_t, arangodb::velocypack::Slice const& doc) const;
|
||||
SimpleIndexElement buildFromElement(
|
||||
TRI_voc_rid_t, arangodb::velocypack::Slice const& doc) const;
|
||||
SimpleIndexElement buildToElement(
|
||||
TRI_voc_rid_t, arangodb::velocypack::Slice const& doc) const;
|
||||
|
||||
private:
|
||||
/// @brief the hash table for _from
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "Aql/SortCondition.h"
|
||||
#include "Basics/Exceptions.h"
|
||||
#include "Basics/FixedSizeAllocator.h"
|
||||
#include "Basics/LocalTaskQueue.h"
|
||||
#include "Basics/VelocyPackHelper.h"
|
||||
#include "Indexes/IndexLookupContext.h"
|
||||
#include "Indexes/SimpleAttributeEqualityMatcher.h"
|
||||
|
@ -44,13 +45,13 @@ LookupBuilder::LookupBuilder(
|
|||
arangodb::aql::Variable const* reference,
|
||||
std::vector<std::vector<arangodb::basics::AttributeName>> const& fields)
|
||||
: _builder(trx), _usesIn(false), _isEmpty(false), _inStorage(trx) {
|
||||
|
||||
TRI_ASSERT(node->type == aql::NODE_TYPE_OPERATOR_NARY_AND);
|
||||
_coveredFields = fields.size();
|
||||
TRI_ASSERT(node->numMembers() == _coveredFields);
|
||||
|
||||
std::pair<arangodb::aql::Variable const*,
|
||||
std::vector<arangodb::basics::AttributeName>> paramPair;
|
||||
std::vector<arangodb::basics::AttributeName>>
|
||||
paramPair;
|
||||
std::vector<size_t> storageOrder;
|
||||
|
||||
for (size_t i = 0; i < _coveredFields; ++i) {
|
||||
|
@ -88,7 +89,9 @@ LookupBuilder::LookupBuilder(
|
|||
_inStorage->openArray();
|
||||
}
|
||||
valNode->toVelocyPackValue(*(_inStorage.get()));
|
||||
_inPosition.emplace(j, std::make_pair(0, std::vector<arangodb::velocypack::Slice>()));
|
||||
_inPosition.emplace(
|
||||
j,
|
||||
std::make_pair(0, std::vector<arangodb::velocypack::Slice>()));
|
||||
_usesIn = true;
|
||||
storageOrder.emplace_back(j);
|
||||
} else {
|
||||
|
@ -111,7 +114,7 @@ LookupBuilder::LookupBuilder(
|
|||
auto f = storageOrder.begin();
|
||||
for (auto const& values : VPackArrayIterator(storageSlice)) {
|
||||
tmp.clear();
|
||||
TRI_IF_FAILURE("Index::permutationIN") {
|
||||
TRI_IF_FAILURE("Index::permutationIN") {
|
||||
THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG);
|
||||
}
|
||||
for (auto const& value : VPackArrayIterator(values)) {
|
||||
|
@ -132,9 +135,7 @@ LookupBuilder::LookupBuilder(
|
|||
buildNextSearchValue();
|
||||
}
|
||||
|
||||
VPackSlice LookupBuilder::lookup() {
|
||||
return _builder->slice();
|
||||
}
|
||||
VPackSlice LookupBuilder::lookup() { return _builder->slice(); }
|
||||
|
||||
bool LookupBuilder::hasAndGetNext() {
|
||||
_builder->clear();
|
||||
|
@ -201,12 +202,11 @@ void LookupBuilder::buildNextSearchValue() {
|
|||
}
|
||||
}
|
||||
}
|
||||
_builder->close(); // End of search Array
|
||||
_builder->close(); // End of search Array
|
||||
}
|
||||
|
||||
/// @brief determines if two elements are equal
|
||||
static bool IsEqualElementElementUnique(void*,
|
||||
HashIndexElement const* left,
|
||||
static bool IsEqualElementElementUnique(void*, HashIndexElement const* left,
|
||||
HashIndexElement const* right) {
|
||||
// this is quite simple
|
||||
return left->revisionId() == right->revisionId();
|
||||
|
@ -218,7 +218,7 @@ static bool IsEqualElementElementMulti(void* userData,
|
|||
HashIndexElement const* right) {
|
||||
TRI_ASSERT(left != nullptr);
|
||||
TRI_ASSERT(right != nullptr);
|
||||
|
||||
|
||||
if (left->revisionId() != right->revisionId()) {
|
||||
return false;
|
||||
}
|
||||
|
@ -228,12 +228,13 @@ static bool IsEqualElementElementMulti(void* userData,
|
|||
|
||||
IndexLookupContext* context = static_cast<IndexLookupContext*>(userData);
|
||||
TRI_ASSERT(context != nullptr);
|
||||
|
||||
|
||||
for (size_t i = 0; i < context->numFields(); ++i) {
|
||||
VPackSlice leftData = left->slice(context, i);
|
||||
VPackSlice rightData = right->slice(context, i);
|
||||
|
||||
int res = arangodb::basics::VelocyPackHelper::compare(leftData, rightData, false);
|
||||
int res =
|
||||
arangodb::basics::VelocyPackHelper::compare(leftData, rightData, false);
|
||||
|
||||
if (res != 0) {
|
||||
return false;
|
||||
|
@ -249,8 +250,7 @@ static uint64_t HashKey(void*, VPackSlice const* key) {
|
|||
}
|
||||
|
||||
/// @brief determines if a key corresponds to an element
|
||||
static bool IsEqualKeyElementMulti(void* userData,
|
||||
VPackSlice const* left,
|
||||
static bool IsEqualKeyElementMulti(void* userData, VPackSlice const* left,
|
||||
HashIndexElement const* right) {
|
||||
TRI_ASSERT(left->isArray());
|
||||
TRI_ASSERT(right->revisionId() != 0);
|
||||
|
@ -259,12 +259,13 @@ static bool IsEqualKeyElementMulti(void* userData,
|
|||
|
||||
// TODO: is it a performance improvement to compare the hash values first?
|
||||
size_t const n = left->length();
|
||||
|
||||
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
VPackSlice const leftVPack = left->at(i);
|
||||
VPackSlice const rightVPack = right->slice(context, i);
|
||||
|
||||
int res = arangodb::basics::VelocyPackHelper::compare(leftVPack, rightVPack, false);
|
||||
|
||||
int res = arangodb::basics::VelocyPackHelper::compare(leftVPack, rightVPack,
|
||||
false);
|
||||
|
||||
if (res != 0) {
|
||||
return false;
|
||||
|
@ -291,7 +292,7 @@ HashIndexIterator::HashIndexIterator(LogicalCollection* collection,
|
|||
_lookups(trx, node, reference, index->fields()),
|
||||
_buffer(),
|
||||
_posInBuffer(0) {
|
||||
_index->lookup(_trx, _lookups.lookup(), _buffer);
|
||||
_index->lookup(_trx, _lookups.lookup(), _buffer);
|
||||
}
|
||||
|
||||
IndexLookupResult HashIndexIterator::next() {
|
||||
|
@ -316,9 +317,10 @@ IndexLookupResult HashIndexIterator::next() {
|
|||
}
|
||||
}
|
||||
|
||||
void HashIndexIterator::nextBabies(std::vector<IndexLookupResult>& result, size_t atMost) {
|
||||
void HashIndexIterator::nextBabies(std::vector<IndexLookupResult>& result,
|
||||
size_t atMost) {
|
||||
result.clear();
|
||||
|
||||
|
||||
if (atMost == 0) {
|
||||
return;
|
||||
}
|
||||
|
@ -361,24 +363,23 @@ void HashIndexIterator::reset() {
|
|||
_lookups.reset();
|
||||
_index->lookup(_trx, _lookups.lookup(), _buffer);
|
||||
}
|
||||
|
||||
HashIndexIteratorVPack::HashIndexIteratorVPack(LogicalCollection* collection,
|
||||
arangodb::Transaction* trx,
|
||||
ManagedDocumentResult* mmdr,
|
||||
HashIndex const* index,
|
||||
std::unique_ptr<arangodb::velocypack::Builder>& searchValues)
|
||||
|
||||
HashIndexIteratorVPack::HashIndexIteratorVPack(
|
||||
LogicalCollection* collection, arangodb::Transaction* trx,
|
||||
ManagedDocumentResult* mmdr, HashIndex const* index,
|
||||
std::unique_ptr<arangodb::velocypack::Builder>& searchValues)
|
||||
: IndexIterator(collection, trx, mmdr, index),
|
||||
_index(index),
|
||||
_searchValues(searchValues.get()),
|
||||
_iterator(_searchValues->slice()),
|
||||
_buffer(),
|
||||
_posInBuffer(0) {
|
||||
searchValues.release(); // now we have ownership for searchValues
|
||||
searchValues.release(); // now we have ownership for searchValues
|
||||
}
|
||||
|
||||
HashIndexIteratorVPack::~HashIndexIteratorVPack() {
|
||||
if (_searchValues != nullptr) {
|
||||
// return the VPackBuilder to the transaction context
|
||||
// return the VPackBuilder to the transaction context
|
||||
_trx->transactionContextPtr()->returnBuilder(_searchValues.release());
|
||||
}
|
||||
}
|
||||
|
@ -419,8 +420,7 @@ void HashIndexIteratorVPack::reset() {
|
|||
|
||||
/// @brief create the unique array
|
||||
HashIndex::UniqueArray::UniqueArray(
|
||||
size_t numPaths,
|
||||
TRI_HashArray_t* hashArray, HashElementFunc* hashElement,
|
||||
size_t numPaths, TRI_HashArray_t* hashArray, HashElementFunc* hashElement,
|
||||
IsEqualElementElementByKey* isEqualElElByKey)
|
||||
: _hashArray(hashArray),
|
||||
_hashElement(hashElement),
|
||||
|
@ -461,7 +461,9 @@ HashIndex::MultiArray::~MultiArray() {
|
|||
|
||||
HashIndex::HashIndex(TRI_idx_iid_t iid, LogicalCollection* collection,
|
||||
VPackSlice const& info)
|
||||
: PathBasedIndex(iid, collection, info, sizeof(TRI_voc_rid_t) + sizeof(uint32_t), false), _uniqueArray(nullptr) {
|
||||
: PathBasedIndex(iid, collection, info,
|
||||
sizeof(TRI_voc_rid_t) + sizeof(uint32_t), false),
|
||||
_uniqueArray(nullptr) {
|
||||
uint32_t indexBuckets = 1;
|
||||
|
||||
if (collection != nullptr) {
|
||||
|
@ -469,27 +471,28 @@ HashIndex::HashIndex(TRI_idx_iid_t iid, LogicalCollection* collection,
|
|||
}
|
||||
|
||||
auto func = std::make_unique<HashElementFunc>();
|
||||
auto compare = std::make_unique<IsEqualElementElementByKey>(_paths.size(), _useExpansion);
|
||||
auto compare = std::make_unique<IsEqualElementElementByKey>(_paths.size(),
|
||||
_useExpansion);
|
||||
|
||||
if (_unique) {
|
||||
auto array = std::make_unique<TRI_HashArray_t>(
|
||||
HashKey, *(func.get()), IsEqualKeyElementUnique, IsEqualElementElementUnique,
|
||||
*(compare.get()), indexBuckets,
|
||||
HashKey, *(func.get()), IsEqualKeyElementUnique,
|
||||
IsEqualElementElementUnique, *(compare.get()), indexBuckets,
|
||||
[this]() -> std::string { return this->context(); });
|
||||
|
||||
_uniqueArray =
|
||||
new HashIndex::UniqueArray(numPaths(), array.get(), func.get(), compare.get());
|
||||
_uniqueArray = new HashIndex::UniqueArray(numPaths(), array.get(),
|
||||
func.get(), compare.get());
|
||||
array.release();
|
||||
} else {
|
||||
_multiArray = nullptr;
|
||||
|
||||
auto array = std::make_unique<TRI_HashArrayMulti_t>(
|
||||
HashKey, *(func.get()), IsEqualKeyElementMulti, IsEqualElementElementMulti,
|
||||
*(compare.get()), indexBuckets, 64,
|
||||
HashKey, *(func.get()), IsEqualKeyElementMulti,
|
||||
IsEqualElementElementMulti, *(compare.get()), indexBuckets, 64,
|
||||
[this]() -> std::string { return this->context(); });
|
||||
|
||||
_multiArray =
|
||||
new HashIndex::MultiArray(numPaths(), array.get(), func.get(), compare.get());
|
||||
_multiArray = new HashIndex::MultiArray(numPaths(), array.get(), func.get(),
|
||||
compare.get());
|
||||
|
||||
array.release();
|
||||
}
|
||||
|
@ -529,8 +532,7 @@ size_t HashIndex::memory() const {
|
|||
size_t elementSize = HashIndexElement::baseMemoryUsage(_paths.size());
|
||||
|
||||
if (_unique) {
|
||||
return static_cast<size_t>(elementSize *
|
||||
_uniqueArray->_hashArray->size() +
|
||||
return static_cast<size_t>(elementSize * _uniqueArray->_hashArray->size() +
|
||||
_uniqueArray->_hashArray->memoryUsage());
|
||||
}
|
||||
return static_cast<size_t>(elementSize * _multiArray->_hashArray->size() +
|
||||
|
@ -567,7 +569,7 @@ bool HashIndex::matchesDefinition(VPackSlice const& info) const {
|
|||
auto value = info.get("id");
|
||||
if (!value.isNone()) {
|
||||
// We already have an id.
|
||||
if(!value.isString()) {
|
||||
if (!value.isString()) {
|
||||
// Invalid ID
|
||||
return false;
|
||||
}
|
||||
|
@ -608,7 +610,7 @@ bool HashIndex::matchesDefinition(VPackSlice const& info) const {
|
|||
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
if (arangodb::basics::AttributeName::isIdentical(_fields[i], translate,
|
||||
false)) {
|
||||
false)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
@ -661,21 +663,25 @@ int HashIndex::remove(arangodb::Transaction* trx, TRI_voc_rid_t revisionId,
|
|||
return res;
|
||||
}
|
||||
|
||||
int HashIndex::batchInsert(arangodb::Transaction* trx,
|
||||
std::vector<std::pair<TRI_voc_rid_t, VPackSlice>> const& documents,
|
||||
size_t numThreads) {
|
||||
void HashIndex::batchInsert(
|
||||
arangodb::Transaction* trx,
|
||||
std::vector<std::pair<TRI_voc_rid_t, VPackSlice>> const& documents,
|
||||
arangodb::basics::LocalTaskQueue* queue) {
|
||||
TRI_ASSERT(queue != nullptr);
|
||||
if (_unique) {
|
||||
return batchInsertUnique(trx, documents, numThreads);
|
||||
batchInsertUnique(trx, documents, queue);
|
||||
} else {
|
||||
batchInsertMulti(trx, documents, queue);
|
||||
}
|
||||
|
||||
return batchInsertMulti(trx, documents, numThreads);
|
||||
}
|
||||
|
||||
|
||||
int HashIndex::unload() {
|
||||
if (_unique) {
|
||||
_uniqueArray->_hashArray->truncate([](HashIndexElement*) -> bool { return true; });
|
||||
_uniqueArray->_hashArray->truncate(
|
||||
[](HashIndexElement*) -> bool { return true; });
|
||||
} else {
|
||||
_multiArray->_hashArray->truncate([](HashIndexElement*) -> bool { return true; });
|
||||
_multiArray->_hashArray->truncate(
|
||||
[](HashIndexElement*) -> bool { return true; });
|
||||
}
|
||||
_allocator->deallocateAll();
|
||||
return TRI_ERROR_NO_ERROR;
|
||||
|
@ -688,8 +694,8 @@ int HashIndex::sizeHint(arangodb::Transaction* trx, size_t size) {
|
|||
// than if the index would be fully populated
|
||||
size /= 5;
|
||||
}
|
||||
|
||||
ManagedDocumentResult result(trx);
|
||||
|
||||
ManagedDocumentResult result(trx);
|
||||
IndexLookupContext context(trx, _collection, &result, numPaths());
|
||||
|
||||
if (_unique) {
|
||||
|
@ -700,15 +706,14 @@ int HashIndex::sizeHint(arangodb::Transaction* trx, size_t size) {
|
|||
}
|
||||
|
||||
/// @brief locates entries in the hash index given VelocyPack slices
|
||||
int HashIndex::lookup(arangodb::Transaction* trx,
|
||||
VPackSlice key,
|
||||
int HashIndex::lookup(arangodb::Transaction* trx, VPackSlice key,
|
||||
std::vector<HashIndexElement*>& documents) const {
|
||||
if (key.isNone()) {
|
||||
return TRI_ERROR_NO_ERROR;
|
||||
}
|
||||
|
||||
ManagedDocumentResult result(trx);
|
||||
IndexLookupContext context(trx, _collection, &result, numPaths());
|
||||
ManagedDocumentResult result(trx);
|
||||
IndexLookupContext context(trx, _collection, &result, numPaths());
|
||||
|
||||
if (_unique) {
|
||||
HashIndexElement* found =
|
||||
|
@ -731,8 +736,9 @@ int HashIndex::lookup(arangodb::Transaction* trx,
|
|||
return TRI_ERROR_NO_ERROR;
|
||||
}
|
||||
|
||||
int HashIndex::insertUnique(arangodb::Transaction* trx, TRI_voc_rid_t revisionId,
|
||||
VPackSlice const& doc, bool isRollback) {
|
||||
int HashIndex::insertUnique(arangodb::Transaction* trx,
|
||||
TRI_voc_rid_t revisionId, VPackSlice const& doc,
|
||||
bool isRollback) {
|
||||
std::vector<HashIndexElement*> elements;
|
||||
int res = fillElement<HashIndexElement>(elements, revisionId, doc);
|
||||
|
||||
|
@ -744,15 +750,14 @@ int HashIndex::insertUnique(arangodb::Transaction* trx, TRI_voc_rid_t revisionId
|
|||
|
||||
return res;
|
||||
}
|
||||
|
||||
ManagedDocumentResult result(trx);
|
||||
IndexLookupContext context(trx, _collection, &result, numPaths());
|
||||
|
||||
auto work =
|
||||
[this, &context](HashIndexElement* element, bool) -> int {
|
||||
TRI_IF_FAILURE("InsertHashIndex") { return TRI_ERROR_DEBUG; }
|
||||
return _uniqueArray->_hashArray->insert(&context, element);
|
||||
};
|
||||
ManagedDocumentResult result(trx);
|
||||
IndexLookupContext context(trx, _collection, &result, numPaths());
|
||||
|
||||
auto work = [this, &context](HashIndexElement* element, bool) -> int {
|
||||
TRI_IF_FAILURE("InsertHashIndex") { return TRI_ERROR_DEBUG; }
|
||||
return _uniqueArray->_hashArray->insert(&context, element);
|
||||
};
|
||||
|
||||
size_t const n = elements.size();
|
||||
|
||||
|
@ -773,28 +778,35 @@ int HashIndex::insertUnique(arangodb::Transaction* trx, TRI_voc_rid_t revisionId
|
|||
return res;
|
||||
}
|
||||
|
||||
int HashIndex::batchInsertUnique(arangodb::Transaction* trx,
|
||||
std::vector<std::pair<TRI_voc_rid_t, VPackSlice>> const& documents, size_t numThreads) {
|
||||
std::vector<HashIndexElement*> elements;
|
||||
elements.reserve(documents.size());
|
||||
void HashIndex::batchInsertUnique(
|
||||
arangodb::Transaction* trx,
|
||||
std::vector<std::pair<TRI_voc_rid_t, VPackSlice>> const& documents,
|
||||
arangodb::basics::LocalTaskQueue* queue) {
|
||||
TRI_ASSERT(queue != nullptr);
|
||||
std::shared_ptr<std::vector<HashIndexElement*>> elements;
|
||||
elements.reset(new std::vector<HashIndexElement*>());
|
||||
elements->reserve(documents.size());
|
||||
|
||||
// TODO: create parallel tasks for this
|
||||
for (auto& doc : documents) {
|
||||
int res = fillElement<HashIndexElement>(elements, doc.first, doc.second);
|
||||
int res =
|
||||
fillElement<HashIndexElement>(*(elements.get()), doc.first, doc.second);
|
||||
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
for (auto& it : elements) {
|
||||
for (auto& it : *(elements.get())) {
|
||||
// free all elements to prevent leak
|
||||
_allocator->deallocate(it);
|
||||
}
|
||||
return res;
|
||||
queue->setStatus(res);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (elements.empty()) {
|
||||
|
||||
if (elements->empty()) {
|
||||
// no elements left to insert
|
||||
return TRI_ERROR_NO_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// functions that will be called for each thread
|
||||
auto creator = [&trx, this]() -> void* {
|
||||
ManagedDocumentResult* result = new ManagedDocumentResult(trx);
|
||||
|
@ -805,17 +817,23 @@ int HashIndex::batchInsertUnique(arangodb::Transaction* trx,
|
|||
delete context->result();
|
||||
delete context;
|
||||
};
|
||||
|
||||
int res = _uniqueArray->_hashArray->batchInsert(creator, destroyer, &elements, numThreads);
|
||||
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
for (auto& it : elements) {
|
||||
// free all elements to prevent leak
|
||||
_allocator->deallocate(it);
|
||||
// queue the actual insertion tasks
|
||||
_uniqueArray->_hashArray->batchInsert(creator, destroyer, elements, queue);
|
||||
|
||||
// queue cleanup callback
|
||||
auto allocator = _allocator.get();
|
||||
auto callback = [elements, queue, allocator]() -> void {
|
||||
if (queue->status() != TRI_ERROR_NO_ERROR) {
|
||||
for (auto& it : *(elements.get())) {
|
||||
// free all elements to prevent leak
|
||||
allocator->deallocate(it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
};
|
||||
std::shared_ptr<arangodb::basics::LocalCallbackTask> cbTask;
|
||||
cbTask.reset(new arangodb::basics::LocalCallbackTask(queue, callback));
|
||||
queue->enqueueCallback(cbTask);
|
||||
}
|
||||
|
||||
int HashIndex::insertMulti(arangodb::Transaction* trx, TRI_voc_rid_t revisionId,
|
||||
|
@ -829,9 +847,9 @@ int HashIndex::insertMulti(arangodb::Transaction* trx, TRI_voc_rid_t revisionId,
|
|||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
ManagedDocumentResult result(trx);
|
||||
IndexLookupContext context(trx, _collection, &result, numPaths());
|
||||
|
||||
ManagedDocumentResult result(trx);
|
||||
IndexLookupContext context(trx, _collection, &result, numPaths());
|
||||
|
||||
auto work = [this, &context](HashIndexElement*& element, bool) {
|
||||
TRI_IF_FAILURE("InsertHashIndex") {
|
||||
|
@ -879,29 +897,35 @@ int HashIndex::insertMulti(arangodb::Transaction* trx, TRI_voc_rid_t revisionId,
|
|||
return TRI_ERROR_NO_ERROR;
|
||||
}
|
||||
|
||||
int HashIndex::batchInsertMulti(arangodb::Transaction* trx,
|
||||
std::vector<std::pair<TRI_voc_rid_t, VPackSlice>> const& documents, size_t numThreads) {
|
||||
std::vector<HashIndexElement*> elements;
|
||||
elements.reserve(documents.size());
|
||||
void HashIndex::batchInsertMulti(
|
||||
arangodb::Transaction* trx,
|
||||
std::vector<std::pair<TRI_voc_rid_t, VPackSlice>> const& documents,
|
||||
arangodb::basics::LocalTaskQueue* queue) {
|
||||
TRI_ASSERT(queue != nullptr);
|
||||
std::shared_ptr<std::vector<HashIndexElement*>> elements;
|
||||
elements.reset(new std::vector<HashIndexElement*>());
|
||||
elements->reserve(documents.size());
|
||||
|
||||
// TODO: create parallel tasks for this
|
||||
for (auto& doc : documents) {
|
||||
int res = fillElement<HashIndexElement>(elements, doc.first, doc.second);
|
||||
int res =
|
||||
fillElement<HashIndexElement>(*(elements.get()), doc.first, doc.second);
|
||||
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
// Filling the elements failed for some reason. Assume loading as failed
|
||||
for (auto& el : elements) {
|
||||
for (auto& el : *(elements.get())) {
|
||||
// Free all elements that are not yet in the index
|
||||
_allocator->deallocate(el);
|
||||
}
|
||||
return res;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (elements.empty()) {
|
||||
if (elements->empty()) {
|
||||
// no elements left to insert
|
||||
return TRI_ERROR_NO_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// functions that will be called for each thread
|
||||
auto creator = [&trx, this]() -> void* {
|
||||
ManagedDocumentResult* result = new ManagedDocumentResult(trx);
|
||||
|
@ -913,15 +937,29 @@ int HashIndex::batchInsertMulti(arangodb::Transaction* trx,
|
|||
delete context;
|
||||
};
|
||||
|
||||
return _multiArray->_hashArray->batchInsert(creator, destroyer, &elements, numThreads);
|
||||
// queue actual insertion tasks
|
||||
_multiArray->_hashArray->batchInsert(creator, destroyer, elements, queue);
|
||||
|
||||
// queue cleanup callback
|
||||
auto allocator = _allocator.get();
|
||||
auto callback = [elements, queue, allocator]() -> void {
|
||||
if (queue->status() != TRI_ERROR_NO_ERROR) {
|
||||
// free all elements to prevent leak
|
||||
for (auto& it : *(elements.get())) {
|
||||
allocator->deallocate(it);
|
||||
}
|
||||
}
|
||||
};
|
||||
std::shared_ptr<arangodb::basics::LocalCallbackTask> cbTask;
|
||||
cbTask.reset(new arangodb::basics::LocalCallbackTask(queue, callback));
|
||||
queue->enqueueCallback(cbTask);
|
||||
}
|
||||
|
||||
int HashIndex::removeUniqueElement(arangodb::Transaction* trx,
|
||||
HashIndexElement* element,
|
||||
bool isRollback) {
|
||||
HashIndexElement* element, bool isRollback) {
|
||||
TRI_IF_FAILURE("RemoveHashIndex") { return TRI_ERROR_DEBUG; }
|
||||
ManagedDocumentResult result(trx);
|
||||
IndexLookupContext context(trx, _collection, &result, numPaths());
|
||||
ManagedDocumentResult result(trx);
|
||||
IndexLookupContext context(trx, _collection, &result, numPaths());
|
||||
HashIndexElement* old = _uniqueArray->_hashArray->remove(&context, element);
|
||||
|
||||
if (old == nullptr) {
|
||||
|
@ -937,11 +975,10 @@ int HashIndex::removeUniqueElement(arangodb::Transaction* trx,
|
|||
}
|
||||
|
||||
int HashIndex::removeMultiElement(arangodb::Transaction* trx,
|
||||
HashIndexElement* element,
|
||||
bool isRollback) {
|
||||
HashIndexElement* element, bool isRollback) {
|
||||
TRI_IF_FAILURE("RemoveHashIndex") { return TRI_ERROR_DEBUG; }
|
||||
ManagedDocumentResult result(trx);
|
||||
IndexLookupContext context(trx, _collection, &result, numPaths());
|
||||
ManagedDocumentResult result(trx);
|
||||
IndexLookupContext context(trx, _collection, &result, numPaths());
|
||||
HashIndexElement* old = _multiArray->_hashArray->remove(&context, element);
|
||||
|
||||
if (old == nullptr) {
|
||||
|
@ -961,7 +998,6 @@ bool HashIndex::supportsFilterCondition(
|
|||
arangodb::aql::AstNode const* node,
|
||||
arangodb::aql::Variable const* reference, size_t itemsInIndex,
|
||||
size_t& estimatedItems, double& estimatedCost) const {
|
||||
|
||||
SimpleAttributeEqualityMatcher matcher(_fields);
|
||||
return matcher.matchAll(this, node, reference, itemsInIndex, estimatedItems,
|
||||
estimatedCost);
|
||||
|
@ -969,11 +1005,10 @@ bool HashIndex::supportsFilterCondition(
|
|||
|
||||
/// @brief creates an IndexIterator for the given Condition
|
||||
IndexIterator* HashIndex::iteratorForCondition(
|
||||
arangodb::Transaction* trx,
|
||||
ManagedDocumentResult* mmdr,
|
||||
arangodb::Transaction* trx, ManagedDocumentResult* mmdr,
|
||||
arangodb::aql::AstNode const* node,
|
||||
arangodb::aql::Variable const* reference, bool) const {
|
||||
TRI_IF_FAILURE("HashIndex::noIterator") {
|
||||
TRI_IF_FAILURE("HashIndex::noIterator") {
|
||||
THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG);
|
||||
}
|
||||
return new HashIndexIterator(_collection, trx, mmdr, this, node, reference);
|
||||
|
@ -988,7 +1023,7 @@ IndexIterator* HashIndex::iteratorForSlice(arangodb::Transaction* trx,
|
|||
// Invalid searchValue
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
TransactionBuilderLeaser builder(trx);
|
||||
std::unique_ptr<VPackBuilder> keys(builder.steal());
|
||||
keys->add(searchValues);
|
||||
|
@ -999,7 +1034,6 @@ IndexIterator* HashIndex::iteratorForSlice(arangodb::Transaction* trx,
|
|||
arangodb::aql::AstNode* HashIndex::specializeCondition(
|
||||
arangodb::aql::AstNode* node,
|
||||
arangodb::aql::Variable const* reference) const {
|
||||
|
||||
SimpleAttributeEqualityMatcher matcher(_fields);
|
||||
return matcher.specializeAll(this, node, reference);
|
||||
}
|
||||
|
|
|
@ -24,16 +24,16 @@
|
|||
#ifndef ARANGOD_INDEXES_HASH_INDEX_H
|
||||
#define ARANGOD_INDEXES_HASH_INDEX_H 1
|
||||
|
||||
#include "Basics/Common.h"
|
||||
#include "Basics/AssocMulti.h"
|
||||
#include "Basics/AssocUnique.h"
|
||||
#include "Basics/Common.h"
|
||||
#include "Basics/VelocyPackHelper.h"
|
||||
#include "Basics/fasthash.h"
|
||||
#include "Indexes/PathBasedIndex.h"
|
||||
#include "Indexes/IndexIterator.h"
|
||||
#include "Indexes/PathBasedIndex.h"
|
||||
#include "Utils/Transaction.h"
|
||||
#include "VocBase/vocbase.h"
|
||||
#include "VocBase/voc-types.h"
|
||||
#include "VocBase/vocbase.h"
|
||||
|
||||
#include <velocypack/Iterator.h>
|
||||
#include <velocypack/Slice.h>
|
||||
|
@ -41,52 +41,53 @@
|
|||
|
||||
/// @brief hash index query parameter
|
||||
namespace arangodb {
|
||||
namespace basics {
|
||||
class LocalTaskQueue;
|
||||
}
|
||||
|
||||
class HashIndex;
|
||||
|
||||
/// @brief Class to build Slice lookups out of AST Conditions
|
||||
class LookupBuilder {
|
||||
private:
|
||||
TransactionBuilderLeaser _builder;
|
||||
bool _usesIn;
|
||||
bool _isEmpty;
|
||||
size_t _coveredFields;
|
||||
std::unordered_map<size_t, arangodb::aql::AstNode const*> _mappingFieldCondition;
|
||||
std::unordered_map<
|
||||
size_t, std::pair<size_t, std::vector<arangodb::velocypack::Slice>>>
|
||||
_inPosition;
|
||||
TransactionBuilderLeaser _inStorage;
|
||||
private:
|
||||
TransactionBuilderLeaser _builder;
|
||||
bool _usesIn;
|
||||
bool _isEmpty;
|
||||
size_t _coveredFields;
|
||||
std::unordered_map<size_t, arangodb::aql::AstNode const*>
|
||||
_mappingFieldCondition;
|
||||
std::unordered_map<
|
||||
size_t, std::pair<size_t, std::vector<arangodb::velocypack::Slice>>>
|
||||
_inPosition;
|
||||
TransactionBuilderLeaser _inStorage;
|
||||
|
||||
public:
|
||||
LookupBuilder(
|
||||
arangodb::Transaction*, arangodb::aql::AstNode const*,
|
||||
arangodb::aql::Variable const*,
|
||||
std::vector<std::vector<arangodb::basics::AttributeName>> const&);
|
||||
public:
|
||||
LookupBuilder(
|
||||
arangodb::Transaction*, arangodb::aql::AstNode const*,
|
||||
arangodb::aql::Variable const*,
|
||||
std::vector<std::vector<arangodb::basics::AttributeName>> const&);
|
||||
|
||||
arangodb::velocypack::Slice lookup();
|
||||
arangodb::velocypack::Slice lookup();
|
||||
|
||||
bool hasAndGetNext();
|
||||
bool hasAndGetNext();
|
||||
|
||||
void reset();
|
||||
void reset();
|
||||
|
||||
private:
|
||||
|
||||
bool incrementInPosition();
|
||||
void buildNextSearchValue();
|
||||
private:
|
||||
bool incrementInPosition();
|
||||
void buildNextSearchValue();
|
||||
};
|
||||
|
||||
class HashIndexIterator final : public IndexIterator {
|
||||
public:
|
||||
|
||||
/// @brief Construct an HashIndexIterator based on Ast Conditions
|
||||
HashIndexIterator(LogicalCollection* collection, arangodb::Transaction* trx,
|
||||
ManagedDocumentResult* mmdr,
|
||||
HashIndex const* index,
|
||||
/// @brief Construct an HashIndexIterator based on Ast Conditions
|
||||
HashIndexIterator(LogicalCollection* collection, arangodb::Transaction* trx,
|
||||
ManagedDocumentResult* mmdr, HashIndex const* index,
|
||||
arangodb::aql::AstNode const*,
|
||||
arangodb::aql::Variable const*);
|
||||
|
||||
~HashIndexIterator() = default;
|
||||
|
||||
|
||||
char const* typeName() const override { return "hash-index-iterator"; }
|
||||
|
||||
IndexLookupResult next() override;
|
||||
|
@ -104,16 +105,14 @@ class HashIndexIterator final : public IndexIterator {
|
|||
|
||||
class HashIndexIteratorVPack final : public IndexIterator {
|
||||
public:
|
||||
|
||||
/// @brief Construct an HashIndexIterator based on VelocyPack
|
||||
HashIndexIteratorVPack(LogicalCollection* collection,
|
||||
arangodb::Transaction* trx,
|
||||
ManagedDocumentResult* mmdr,
|
||||
HashIndex const* index,
|
||||
/// @brief Construct an HashIndexIterator based on VelocyPack
|
||||
HashIndexIteratorVPack(
|
||||
LogicalCollection* collection, arangodb::Transaction* trx,
|
||||
ManagedDocumentResult* mmdr, HashIndex const* index,
|
||||
std::unique_ptr<arangodb::velocypack::Builder>& searchValues);
|
||||
|
||||
~HashIndexIteratorVPack();
|
||||
|
||||
|
||||
char const* typeName() const override { return "hash-index-iterator-vpack"; }
|
||||
|
||||
IndexLookupResult next() override;
|
||||
|
@ -141,19 +140,18 @@ class HashIndex final : public PathBasedIndex {
|
|||
~HashIndex();
|
||||
|
||||
public:
|
||||
IndexType type() const override {
|
||||
return Index::TRI_IDX_TYPE_HASH_INDEX;
|
||||
}
|
||||
|
||||
IndexType type() const override { return Index::TRI_IDX_TYPE_HASH_INDEX; }
|
||||
|
||||
bool allowExpansion() const override { return true; }
|
||||
|
||||
|
||||
bool canBeDropped() const override { return true; }
|
||||
|
||||
bool isSorted() const override { return false; }
|
||||
|
||||
bool hasSelectivityEstimate() const override { return true; }
|
||||
|
||||
double selectivityEstimate(arangodb::StringRef const* = nullptr) const override;
|
||||
double selectivityEstimate(
|
||||
arangodb::StringRef const* = nullptr) const override;
|
||||
|
||||
size_t memory() const override;
|
||||
|
||||
|
@ -162,12 +160,17 @@ class HashIndex final : public PathBasedIndex {
|
|||
|
||||
bool matchesDefinition(VPackSlice const& info) const override;
|
||||
|
||||
int insert(arangodb::Transaction*, TRI_voc_rid_t, arangodb::velocypack::Slice const&, bool isRollback) override;
|
||||
int insert(arangodb::Transaction*, TRI_voc_rid_t,
|
||||
arangodb::velocypack::Slice const&, bool isRollback) override;
|
||||
|
||||
int remove(arangodb::Transaction*, TRI_voc_rid_t, arangodb::velocypack::Slice const&, bool isRollback) override;
|
||||
int remove(arangodb::Transaction*, TRI_voc_rid_t,
|
||||
arangodb::velocypack::Slice const&, bool isRollback) override;
|
||||
|
||||
void batchInsert(
|
||||
arangodb::Transaction*,
|
||||
std::vector<std::pair<TRI_voc_rid_t, arangodb::velocypack::Slice>> const&,
|
||||
arangodb::basics::LocalTaskQueue* queue = nullptr) override;
|
||||
|
||||
int batchInsert(arangodb::Transaction*, std::vector<std::pair<TRI_voc_rid_t, arangodb::velocypack::Slice>> const&, size_t) override;
|
||||
|
||||
int unload() override;
|
||||
|
||||
int sizeHint(arangodb::Transaction*, size_t) override;
|
||||
|
@ -184,12 +187,13 @@ class HashIndex final : public PathBasedIndex {
|
|||
arangodb::aql::Variable const*,
|
||||
bool) const override;
|
||||
|
||||
/// @brief creates an IndexIterator for the given VelocyPackSlices
|
||||
/// Each slice represents the field at the same position. (order matters)
|
||||
/// And each slice has to be an object of one of the following types:
|
||||
/// 1) {"eq": <compareValue>} // The value in index is exactly this
|
||||
/// 2) {"in": <compareValues>} // The value in index os one of them
|
||||
IndexIterator* iteratorForSlice(arangodb::Transaction*,
|
||||
/// @brief creates an IndexIterator for the given VelocyPackSlices
|
||||
/// Each slice represents the field at the same position. (order
|
||||
/// matters)
|
||||
/// And each slice has to be an object of one of the following types:
|
||||
/// 1) {"eq": <compareValue>} // The value in index is exactly this
|
||||
/// 2) {"in": <compareValues>} // The value in index os one of them
|
||||
IndexIterator* iteratorForSlice(arangodb::Transaction*,
|
||||
ManagedDocumentResult*,
|
||||
arangodb::velocypack::Slice const,
|
||||
bool) const override;
|
||||
|
@ -198,20 +202,25 @@ class HashIndex final : public PathBasedIndex {
|
|||
arangodb::aql::AstNode*, arangodb::aql::Variable const*) const override;
|
||||
|
||||
private:
|
||||
|
||||
/// @brief locates entries in the hash index given a velocypack slice
|
||||
int lookup(arangodb::Transaction*, arangodb::velocypack::Slice,
|
||||
std::vector<HashIndexElement*>&) const;
|
||||
|
||||
int insertUnique(arangodb::Transaction*, TRI_voc_rid_t, arangodb::velocypack::Slice const&, bool isRollback);
|
||||
int insertUnique(arangodb::Transaction*, TRI_voc_rid_t,
|
||||
arangodb::velocypack::Slice const&, bool isRollback);
|
||||
|
||||
int batchInsertUnique(arangodb::Transaction*,
|
||||
std::vector<std::pair<TRI_voc_rid_t, arangodb::velocypack::Slice>> const&, size_t);
|
||||
void batchInsertUnique(
|
||||
arangodb::Transaction*,
|
||||
std::vector<std::pair<TRI_voc_rid_t, arangodb::velocypack::Slice>> const&,
|
||||
arangodb::basics::LocalTaskQueue* queue = nullptr);
|
||||
|
||||
int insertMulti(arangodb::Transaction*, TRI_voc_rid_t, arangodb::velocypack::Slice const&, bool isRollback);
|
||||
int insertMulti(arangodb::Transaction*, TRI_voc_rid_t,
|
||||
arangodb::velocypack::Slice const&, bool isRollback);
|
||||
|
||||
int batchInsertMulti(arangodb::Transaction*,
|
||||
std::vector<std::pair<TRI_voc_rid_t, arangodb::velocypack::Slice>> const&, size_t);
|
||||
void batchInsertMulti(
|
||||
arangodb::Transaction*,
|
||||
std::vector<std::pair<TRI_voc_rid_t, arangodb::velocypack::Slice>> const&,
|
||||
arangodb::basics::LocalTaskQueue* queue = nullptr);
|
||||
|
||||
int removeUniqueElement(arangodb::Transaction*, HashIndexElement*, bool);
|
||||
|
||||
|
@ -231,7 +240,7 @@ class HashIndex final : public PathBasedIndex {
|
|||
uint64_t operator()(void* userData, HashIndexElement const* element,
|
||||
bool byKey = true) {
|
||||
uint64_t hash = element->hash();
|
||||
|
||||
|
||||
if (byKey) {
|
||||
return hash;
|
||||
}
|
||||
|
@ -247,7 +256,8 @@ class HashIndex final : public PathBasedIndex {
|
|||
bool _allowExpansion;
|
||||
|
||||
public:
|
||||
IsEqualElementElementByKey(size_t n, bool allowExpansion) : _numFields(n), _allowExpansion(allowExpansion) {}
|
||||
IsEqualElementElementByKey(size_t n, bool allowExpansion)
|
||||
: _numFields(n), _allowExpansion(allowExpansion) {}
|
||||
|
||||
bool operator()(void* userData, HashIndexElement const* left,
|
||||
HashIndexElement const* right) {
|
||||
|
@ -259,12 +269,13 @@ class HashIndex final : public PathBasedIndex {
|
|||
}
|
||||
|
||||
IndexLookupContext* context = static_cast<IndexLookupContext*>(userData);
|
||||
|
||||
|
||||
for (size_t i = 0; i < _numFields; ++i) {
|
||||
VPackSlice leftData = left->slice(context, i);
|
||||
VPackSlice rightData = right->slice(context, i);
|
||||
|
||||
int res = arangodb::basics::VelocyPackHelper::compare(leftData, rightData, false);
|
||||
int res = arangodb::basics::VelocyPackHelper::compare(leftData,
|
||||
rightData, false);
|
||||
|
||||
if (res != 0) {
|
||||
return false;
|
||||
|
@ -276,10 +287,10 @@ class HashIndex final : public PathBasedIndex {
|
|||
};
|
||||
|
||||
private:
|
||||
|
||||
/// @brief the actual hash index (unique type)
|
||||
typedef arangodb::basics::AssocUnique<arangodb::velocypack::Slice,
|
||||
HashIndexElement*> TRI_HashArray_t;
|
||||
HashIndexElement*>
|
||||
TRI_HashArray_t;
|
||||
|
||||
struct UniqueArray {
|
||||
UniqueArray() = delete;
|
||||
|
@ -296,13 +307,12 @@ class HashIndex final : public PathBasedIndex {
|
|||
|
||||
/// @brief the actual hash index (multi type)
|
||||
typedef arangodb::basics::AssocMulti<arangodb::velocypack::Slice,
|
||||
HashIndexElement*, uint32_t,
|
||||
false> TRI_HashArrayMulti_t;
|
||||
HashIndexElement*, uint32_t, false>
|
||||
TRI_HashArrayMulti_t;
|
||||
|
||||
struct MultiArray {
|
||||
MultiArray() = delete;
|
||||
MultiArray(size_t numPaths,
|
||||
TRI_HashArrayMulti_t*, HashElementFunc*,
|
||||
MultiArray(size_t numPaths, TRI_HashArrayMulti_t*, HashElementFunc*,
|
||||
IsEqualElementElementByKey*);
|
||||
~MultiArray();
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "Aql/AstNode.h"
|
||||
#include "Aql/Variable.h"
|
||||
#include "Basics/Exceptions.h"
|
||||
#include "Basics/LocalTaskQueue.h"
|
||||
#include "Basics/StaticStrings.h"
|
||||
#include "Basics/StringRef.h"
|
||||
#include "Basics/StringUtils.h"
|
||||
|
@ -34,9 +35,10 @@
|
|||
#include "VocBase/LogicalCollection.h"
|
||||
#include "VocBase/ticks.h"
|
||||
|
||||
#include <ostream>
|
||||
#include <velocypack/Iterator.h>
|
||||
#include <velocypack/velocypack-aliases.h>
|
||||
#include <iostream>
|
||||
#include <ostream>
|
||||
|
||||
using namespace arangodb;
|
||||
|
||||
|
@ -53,7 +55,7 @@ Index::Index(
|
|||
}
|
||||
|
||||
Index::Index(TRI_idx_iid_t iid, arangodb::LogicalCollection* collection,
|
||||
VPackSlice const& slice)
|
||||
VPackSlice const& slice)
|
||||
: _iid(iid),
|
||||
_collection(collection),
|
||||
_fields(),
|
||||
|
@ -61,9 +63,9 @@ Index::Index(TRI_idx_iid_t iid, arangodb::LogicalCollection* collection,
|
|||
slice, "unique", false)),
|
||||
_sparse(arangodb::basics::VelocyPackHelper::getBooleanValue(
|
||||
slice, "sparse", false)) {
|
||||
|
||||
VPackSlice const fields = slice.get("fields");
|
||||
setFields(fields, Index::allowExpansion(Index::type(slice.get("type").copyString())));
|
||||
setFields(fields,
|
||||
Index::allowExpansion(Index::type(slice.get("type").copyString())));
|
||||
}
|
||||
|
||||
/// @brief create an index stub with a hard-coded selectivity estimate
|
||||
|
@ -78,9 +80,9 @@ Index::Index(VPackSlice const& slice)
|
|||
slice, "unique", false)),
|
||||
_sparse(arangodb::basics::VelocyPackHelper::getBooleanValue(
|
||||
slice, "sparse", false)) {
|
||||
|
||||
VPackSlice const fields = slice.get("fields");
|
||||
setFields(fields, Index::allowExpansion(Index::type(slice.get("type").copyString())));
|
||||
setFields(fields,
|
||||
Index::allowExpansion(Index::type(slice.get("type").copyString())));
|
||||
}
|
||||
|
||||
Index::~Index() {}
|
||||
|
@ -102,15 +104,17 @@ void Index::setFields(VPackSlice const& fields, bool allowExpansion) {
|
|||
}
|
||||
|
||||
std::vector<arangodb::basics::AttributeName> parsedAttributes;
|
||||
TRI_ParseAttributeString(name.copyString(), parsedAttributes, allowExpansion);
|
||||
TRI_ParseAttributeString(name.copyString(), parsedAttributes,
|
||||
allowExpansion);
|
||||
_fields.emplace_back(std::move(parsedAttributes));
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief validate fields from slice
|
||||
void Index::validateFields(VPackSlice const& slice) {
|
||||
bool const allowExpansion = Index::allowExpansion(Index::type(slice.get("type").copyString()));
|
||||
|
||||
bool const allowExpansion =
|
||||
Index::allowExpansion(Index::type(slice.get("type").copyString()));
|
||||
|
||||
VPackSlice fields = slice.get("fields");
|
||||
|
||||
if (!fields.isArray()) {
|
||||
|
@ -125,7 +129,8 @@ void Index::validateFields(VPackSlice const& slice) {
|
|||
}
|
||||
|
||||
std::vector<arangodb::basics::AttributeName> parsedAttributes;
|
||||
TRI_ParseAttributeString(name.copyString(), parsedAttributes, allowExpansion);
|
||||
TRI_ParseAttributeString(name.copyString(), parsedAttributes,
|
||||
allowExpansion);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -158,7 +163,7 @@ Index::IndexType Index::type(char const* type) {
|
|||
|
||||
return TRI_IDX_TYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
|
||||
Index::IndexType Index::type(std::string const& type) {
|
||||
return Index::type(type.c_str());
|
||||
}
|
||||
|
@ -351,8 +356,8 @@ std::string Index::context() const {
|
|||
|
||||
result << "index { id: " << id() << ", type: " << typeName()
|
||||
<< ", collection: " << _collection->dbName() << "/"
|
||||
<< _collection->name()
|
||||
<< ", unique: " << (_unique ? "true" : "false") << ", fields: ";
|
||||
<< _collection->name() << ", unique: " << (_unique ? "true" : "false")
|
||||
<< ", fields: ";
|
||||
result << "[";
|
||||
for (size_t i = 0; i < _fields.size(); ++i) {
|
||||
if (i > 0) {
|
||||
|
@ -432,7 +437,7 @@ bool Index::matchesDefinition(VPackSlice const& info) const {
|
|||
auto value = info.get("id");
|
||||
if (!value.isNone()) {
|
||||
// We already have an id.
|
||||
if(!value.isString()) {
|
||||
if (!value.isString()) {
|
||||
// Invalid ID
|
||||
return false;
|
||||
}
|
||||
|
@ -485,12 +490,21 @@ double Index::selectivityEstimate(StringRef const*) const {
|
|||
bool Index::implicitlyUnique() const {
|
||||
// simply return whether the index actually is unique
|
||||
// in this base class, we cannot do anything else
|
||||
return _unique;
|
||||
return _unique;
|
||||
}
|
||||
|
||||
/// @brief default implementation for selectivityEstimate
|
||||
int Index::batchInsert(arangodb::Transaction*, std::vector<std::pair<TRI_voc_rid_t, VPackSlice>> const&, size_t) {
|
||||
THROW_ARANGO_EXCEPTION(TRI_ERROR_NOT_IMPLEMENTED);
|
||||
void Index::batchInsert(
|
||||
arangodb::Transaction* trx,
|
||||
std::vector<std::pair<TRI_voc_rid_t, arangodb::velocypack::Slice>> const&
|
||||
documents,
|
||||
arangodb::basics::LocalTaskQueue* queue) {
|
||||
for (auto const& it : documents) {
|
||||
int status = insert(trx, it.first, it.second, false);
|
||||
if (status != TRI_ERROR_NO_ERROR) {
|
||||
queue->setStatus(status);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief default implementation for cleanup
|
||||
|
@ -528,8 +542,7 @@ bool Index::supportsFilterCondition(arangodb::aql::AstNode const*,
|
|||
/// @brief default implementation for supportsSortCondition
|
||||
bool Index::supportsSortCondition(arangodb::aql::SortCondition const*,
|
||||
arangodb::aql::Variable const*,
|
||||
size_t itemsInIndex,
|
||||
double& estimatedCost,
|
||||
size_t itemsInIndex, double& estimatedCost,
|
||||
size_t& coveredAttributes) const {
|
||||
// by default, no sort conditions are supported
|
||||
coveredAttributes = 0;
|
||||
|
@ -559,12 +572,11 @@ arangodb::aql::AstNode* Index::specializeCondition(
|
|||
}
|
||||
|
||||
/// @brief perform some base checks for an index condition part
|
||||
bool Index::canUseConditionPart(arangodb::aql::AstNode const* access,
|
||||
arangodb::aql::AstNode const* other,
|
||||
arangodb::aql::AstNode const* op,
|
||||
arangodb::aql::Variable const* reference,
|
||||
std::unordered_set<std::string>& nonNullAttributes,
|
||||
bool isExecution) const {
|
||||
bool Index::canUseConditionPart(
|
||||
arangodb::aql::AstNode const* access, arangodb::aql::AstNode const* other,
|
||||
arangodb::aql::AstNode const* op, arangodb::aql::Variable const* reference,
|
||||
std::unordered_set<std::string>& nonNullAttributes,
|
||||
bool isExecution) const {
|
||||
if (_sparse) {
|
||||
if (op->type == arangodb::aql::NODE_TYPE_OPERATOR_BINARY_NIN) {
|
||||
return false;
|
||||
|
@ -600,19 +612,27 @@ bool Index::canUseConditionPart(arangodb::aql::AstNode const* access,
|
|||
if (!other->isConstant()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (op->type == arangodb::aql::NODE_TYPE_OPERATOR_BINARY_NE &&
|
||||
other->isNullValue()) {
|
||||
// != null. now note that a certain attribute cannot become null
|
||||
try { nonNullAttributes.emplace(access->toString()); } catch (...) {}
|
||||
try {
|
||||
nonNullAttributes.emplace(access->toString());
|
||||
} catch (...) {
|
||||
}
|
||||
} else if (op->type == arangodb::aql::NODE_TYPE_OPERATOR_BINARY_GT) {
|
||||
// > null. now note that a certain attribute cannot become null
|
||||
try { nonNullAttributes.emplace(access->toString()); } catch (...) {}
|
||||
try {
|
||||
nonNullAttributes.emplace(access->toString());
|
||||
} catch (...) {
|
||||
}
|
||||
} else if (op->type == arangodb::aql::NODE_TYPE_OPERATOR_BINARY_GE &&
|
||||
!other->isNullValue()) {
|
||||
// >= non-null. now note that a certain attribute cannot become null
|
||||
try { nonNullAttributes.emplace(access->toString()); } catch (...) {}
|
||||
try {
|
||||
nonNullAttributes.emplace(access->toString());
|
||||
} catch (...) {
|
||||
}
|
||||
}
|
||||
|
||||
if (op->type == arangodb::aql::NODE_TYPE_OPERATOR_BINARY_LT ||
|
||||
|
@ -621,7 +641,8 @@ bool Index::canUseConditionPart(arangodb::aql::AstNode const* access,
|
|||
// null values
|
||||
try {
|
||||
// check if we've marked this attribute as being non-null already
|
||||
if (nonNullAttributes.find(access->toString()) == nonNullAttributes.end()) {
|
||||
if (nonNullAttributes.find(access->toString()) ==
|
||||
nonNullAttributes.end()) {
|
||||
return false;
|
||||
}
|
||||
} catch (...) {
|
||||
|
@ -636,7 +657,8 @@ bool Index::canUseConditionPart(arangodb::aql::AstNode const* access,
|
|||
// reason
|
||||
try {
|
||||
// check if we've marked this attribute as being non-null already
|
||||
if (nonNullAttributes.find(access->toString()) == nonNullAttributes.end()) {
|
||||
if (nonNullAttributes.find(access->toString()) ==
|
||||
nonNullAttributes.end()) {
|
||||
return false;
|
||||
}
|
||||
} catch (...) {
|
||||
|
@ -694,8 +716,8 @@ void Index::expandInSearchValues(VPackSlice const base,
|
|||
VPackBuilder& result) const {
|
||||
TRI_ASSERT(base.isArray());
|
||||
|
||||
VPackArrayBuilder baseGuard(&result);
|
||||
for (auto const& oneLookup: VPackArrayIterator(base)) {
|
||||
VPackArrayBuilder baseGuard(&result);
|
||||
for (auto const& oneLookup : VPackArrayIterator(base)) {
|
||||
TRI_ASSERT(oneLookup.isArray());
|
||||
|
||||
bool usesIn = false;
|
||||
|
@ -720,11 +742,12 @@ void Index::expandInSearchValues(VPackSlice const base,
|
|||
if (current.hasKey(StaticStrings::IndexIn)) {
|
||||
VPackSlice inList = current.get(StaticStrings::IndexIn);
|
||||
|
||||
std::unordered_set<VPackSlice,
|
||||
arangodb::basics::VelocyPackHelper::VPackHash,
|
||||
arangodb::basics::VelocyPackHelper::VPackEqual>
|
||||
tmp(static_cast<size_t>(inList.length()), arangodb::basics::VelocyPackHelper::VPackHash(),
|
||||
arangodb::basics::VelocyPackHelper::VPackEqual());
|
||||
std::unordered_set<VPackSlice,
|
||||
arangodb::basics::VelocyPackHelper::VPackHash,
|
||||
arangodb::basics::VelocyPackHelper::VPackEqual>
|
||||
tmp(static_cast<size_t>(inList.length()),
|
||||
arangodb::basics::VelocyPackHelper::VPackHash(),
|
||||
arangodb::basics::VelocyPackHelper::VPackEqual());
|
||||
|
||||
TRI_ASSERT(inList.isArray());
|
||||
if (inList.length() == 0) {
|
||||
|
@ -744,16 +767,16 @@ void Index::expandInSearchValues(VPackSlice const base,
|
|||
}
|
||||
// If there is an entry in elements for one depth it was an in,
|
||||
// all of them are now unique so we simply have to multiply
|
||||
|
||||
|
||||
size_t level = n - 1;
|
||||
std::vector<size_t> positions(n, 0);
|
||||
bool done = false;
|
||||
while (!done) {
|
||||
TRI_IF_FAILURE("Index::permutationIN") {
|
||||
TRI_IF_FAILURE("Index::permutationIN") {
|
||||
THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG);
|
||||
}
|
||||
VPackArrayBuilder guard(&result);
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
auto list = elements.find(i);
|
||||
if (list == elements.end()) {
|
||||
// Insert
|
||||
|
@ -765,7 +788,8 @@ void Index::expandInSearchValues(VPackSlice const base,
|
|||
}
|
||||
while (true) {
|
||||
auto list = elements.find(level);
|
||||
if (list != elements.end() && ++positions[level] < list->second.size()) {
|
||||
if (list != elements.end() &&
|
||||
++positions[level] < list->second.size()) {
|
||||
level = n - 1;
|
||||
// abort inner iteration
|
||||
break;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// DISCLAIMER
|
||||
///
|
||||
|
@ -24,16 +25,19 @@
|
|||
#ifndef ARANGOD_INDEXES_INDEX_H
|
||||
#define ARANGOD_INDEXES_INDEX_H 1
|
||||
|
||||
#include "Basics/Common.h"
|
||||
#include "Basics/AttributeNameParser.h"
|
||||
#include "Basics/Common.h"
|
||||
#include "Basics/Exceptions.h"
|
||||
#include "Indexes/IndexElement.h"
|
||||
#include "VocBase/vocbase.h"
|
||||
#include "VocBase/voc-types.h"
|
||||
#include "VocBase/vocbase.h"
|
||||
|
||||
#include <iosfwd>
|
||||
|
||||
namespace arangodb {
|
||||
namespace basics {
|
||||
class LocalTaskQueue;
|
||||
}
|
||||
|
||||
class LogicalCollection;
|
||||
class ManagedDocumentResult;
|
||||
|
@ -154,9 +158,7 @@ class Index {
|
|||
}
|
||||
|
||||
/// @brief return the underlying collection
|
||||
inline LogicalCollection* collection() const {
|
||||
return _collection;
|
||||
}
|
||||
inline LogicalCollection* collection() const { return _collection; }
|
||||
|
||||
/// @brief return a contextual string for logging
|
||||
std::string context() const;
|
||||
|
@ -216,13 +218,15 @@ class Index {
|
|||
|
||||
/// @brief whether or not the index has a selectivity estimate
|
||||
virtual bool hasSelectivityEstimate() const = 0;
|
||||
|
||||
|
||||
/// @brief return the selectivity estimate of the index
|
||||
/// must only be called if hasSelectivityEstimate() returns true
|
||||
virtual double selectivityEstimate(arangodb::StringRef const* = nullptr) const;
|
||||
|
||||
virtual double selectivityEstimate(
|
||||
arangodb::StringRef const* = nullptr) const;
|
||||
|
||||
/// @brief whether or not the index is implicitly unique
|
||||
/// this can be the case if the index is not declared as unique, but contains a
|
||||
/// this can be the case if the index is not declared as unique, but contains
|
||||
/// a
|
||||
/// unique attribute such as _key
|
||||
virtual bool implicitlyUnique() const;
|
||||
|
||||
|
@ -234,11 +238,16 @@ class Index {
|
|||
virtual void toVelocyPackFigures(arangodb::velocypack::Builder&) const;
|
||||
std::shared_ptr<arangodb::velocypack::Builder> toVelocyPackFigures() const;
|
||||
|
||||
virtual int insert(arangodb::Transaction*, TRI_voc_rid_t revisionId, arangodb::velocypack::Slice const&, bool isRollback) = 0;
|
||||
virtual int remove(arangodb::Transaction*, TRI_voc_rid_t revisionId, arangodb::velocypack::Slice const&, bool isRollback) = 0;
|
||||
|
||||
virtual int batchInsert(arangodb::Transaction*, std::vector<std::pair<TRI_voc_rid_t, arangodb::velocypack::Slice>> const&, size_t);
|
||||
|
||||
virtual int insert(arangodb::Transaction*, TRI_voc_rid_t revisionId,
|
||||
arangodb::velocypack::Slice const&, bool isRollback) = 0;
|
||||
virtual int remove(arangodb::Transaction*, TRI_voc_rid_t revisionId,
|
||||
arangodb::velocypack::Slice const&, bool isRollback) = 0;
|
||||
|
||||
virtual void batchInsert(
|
||||
arangodb::Transaction*,
|
||||
std::vector<std::pair<TRI_voc_rid_t, arangodb::velocypack::Slice>> const&,
|
||||
arangodb::basics::LocalTaskQueue* queue = nullptr);
|
||||
|
||||
virtual int unload() = 0;
|
||||
|
||||
// a garbage collection function for the index
|
||||
|
|
|
@ -124,7 +124,7 @@ int PathBasedIndex::fillElement(std::vector<T*>& elements,
|
|||
auto slices = buildIndexValue(doc);
|
||||
|
||||
if (slices.size() == n) {
|
||||
// if shapes.size() != n, then the value is not inserted into the index
|
||||
// if slices.size() != n, then the value is not inserted into the index
|
||||
// because of index sparsity!
|
||||
T* element = static_cast<T*>(_allocator->allocate());
|
||||
TRI_ASSERT(element != nullptr);
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
|
||||
#include "DatabaseFeature.h"
|
||||
|
||||
#include "Agency/v8-agency.h"
|
||||
#include "Agency/v8-agency.h"
|
||||
#include "ApplicationFeatures/ApplicationServer.h"
|
||||
#include "Aql/QueryCache.h"
|
||||
|
@ -83,7 +82,7 @@ void DatabaseManagerThread::run() {
|
|||
StorageEngine* engine = EngineSelectorFeature::ENGINE;
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
try {
|
||||
// check if we have to drop some database
|
||||
TRI_vocbase_t* database = nullptr;
|
||||
|
||||
|
@ -129,7 +128,8 @@ void DatabaseManagerThread::run() {
|
|||
databaseFeature->_databasesProtector.scan();
|
||||
delete oldLists;
|
||||
|
||||
// From now on no other thread can possibly see the old TRI_vocbase_t*,
|
||||
// From now on no other thread can possibly see the old
|
||||
// TRI_vocbase_t*,
|
||||
// note that there is only one DatabaseManager thread, so it is
|
||||
// not possible that another thread has seen this very database
|
||||
// and tries to free it at the same time!
|
||||
|
@ -143,8 +143,8 @@ void DatabaseManagerThread::run() {
|
|||
RocksDBFeature::dropDatabase(database->id());
|
||||
|
||||
LOG(TRACE) << "physically removing database directory '"
|
||||
<< engine->databasePath(database) << "' of database '"
|
||||
<< database->name() << "'";
|
||||
<< engine->databasePath(database) << "' of database '"
|
||||
<< database->name() << "'";
|
||||
|
||||
std::string path;
|
||||
|
||||
|
@ -158,7 +158,7 @@ void DatabaseManagerThread::run() {
|
|||
|
||||
if (TRI_IsDirectory(path.c_str())) {
|
||||
LOG(TRACE) << "removing app directory '" << path
|
||||
<< "' of database '" << database->name() << "'";
|
||||
<< "' of database '" << database->name() << "'";
|
||||
|
||||
TRI_RemoveDirectory(path.c_str());
|
||||
}
|
||||
|
@ -174,8 +174,10 @@ void DatabaseManagerThread::run() {
|
|||
usleep(10000);
|
||||
};
|
||||
while (!arangodb::wal::LogfileManager::instance()
|
||||
->executeWhileNothingQueued(callback)) {
|
||||
LOG(DEBUG) << "Trying to shutdown dropped database, waiting for phase in which the collector thread does not have queued operations.";
|
||||
->executeWhileNothingQueued(callback)) {
|
||||
LOG(DEBUG) << "Trying to shutdown dropped database, waiting for "
|
||||
"phase in which the collector thread does not have "
|
||||
"queued operations.";
|
||||
usleep(500000);
|
||||
}
|
||||
|
||||
|
@ -226,7 +228,7 @@ void DatabaseManagerThread::run() {
|
|||
|
||||
} catch (...) {
|
||||
}
|
||||
|
||||
|
||||
// next iteration
|
||||
}
|
||||
}
|
||||
|
@ -252,7 +254,6 @@ DatabaseFeature::DatabaseFeature(ApplicationServer* server)
|
|||
startsAfter("EngineSelector");
|
||||
startsAfter("LogfileManager");
|
||||
startsAfter("InitDatabase");
|
||||
startsAfter("IndexThread");
|
||||
startsAfter("RevisionCache");
|
||||
}
|
||||
|
||||
|
@ -298,11 +299,17 @@ void DatabaseFeature::collectOptions(std::shared_ptr<ProgramOptions> options) {
|
|||
"--database.replication-applier",
|
||||
"switch to enable or disable the replication applier",
|
||||
new BooleanParameter(&_replicationApplier));
|
||||
|
||||
options->addHiddenOption("--database.check-30-revisions",
|
||||
"check _rev values in collections created before 3.1",
|
||||
new DiscreteValuesParameter<StringParameter>(&_check30Revisions,
|
||||
std::unordered_set<std::string>{ "true", "false", "fail" }));
|
||||
|
||||
options->addHiddenOption(
|
||||
"--database.check-30-revisions",
|
||||
"check _rev values in collections created before 3.1",
|
||||
new DiscreteValuesParameter<StringParameter>(
|
||||
&_check30Revisions,
|
||||
std::unordered_set<std::string>{"true", "false", "fail"}));
|
||||
|
||||
options->addObsoleteOption(
|
||||
"--database.index-threads",
|
||||
"threads to start for parallel background index creation", true);
|
||||
}
|
||||
|
||||
void DatabaseFeature::validateOptions(std::shared_ptr<ProgramOptions> options) {
|
||||
|
@ -362,7 +369,7 @@ void DatabaseFeature::start() {
|
|||
}
|
||||
|
||||
// TODO: handle _upgrade and _checkVersion here
|
||||
|
||||
|
||||
// activate deadlock detection in case we're not running in cluster mode
|
||||
if (!arangodb::ServerState::instance()->isRunningInCluster()) {
|
||||
enableDeadlockDetection();
|
||||
|
@ -865,10 +872,9 @@ std::vector<std::string> DatabaseFeature::getDatabaseNamesForUser(
|
|||
TRI_vocbase_t* vocbase = p.second;
|
||||
TRI_ASSERT(vocbase != nullptr);
|
||||
|
||||
auto authentication = application_features::ApplicationServer::getFeature<AuthenticationFeature>(
|
||||
"Authentication");
|
||||
auto level = authentication->canUseDatabase(
|
||||
username, vocbase->name());
|
||||
auto authentication = application_features::ApplicationServer::getFeature<
|
||||
AuthenticationFeature>("Authentication");
|
||||
auto level = authentication->canUseDatabase(username, vocbase->name());
|
||||
|
||||
if (level == AuthLevel::NONE) {
|
||||
continue;
|
||||
|
@ -1312,8 +1318,8 @@ int DatabaseFeature::writeCreateMarker(TRI_voc_tick_t id,
|
|||
int res = TRI_ERROR_NO_ERROR;
|
||||
|
||||
try {
|
||||
MMFilesDatabaseMarker marker(TRI_DF_MARKER_VPACK_CREATE_DATABASE,
|
||||
id, slice);
|
||||
MMFilesDatabaseMarker marker(TRI_DF_MARKER_VPACK_CREATE_DATABASE, id,
|
||||
slice);
|
||||
MMFilesWalSlotInfoCopy slotInfo =
|
||||
arangodb::wal::LogfileManager::instance()->allocateAndWrite(marker,
|
||||
false);
|
||||
|
@ -1347,7 +1353,7 @@ int DatabaseFeature::writeDropMarker(TRI_voc_tick_t id) {
|
|||
builder.close();
|
||||
|
||||
MMFilesDatabaseMarker marker(TRI_DF_MARKER_VPACK_DROP_DATABASE, id,
|
||||
builder.slice());
|
||||
builder.slice());
|
||||
|
||||
MMFilesWalSlotInfoCopy slotInfo =
|
||||
arangodb::wal::LogfileManager::instance()->allocateAndWrite(marker,
|
||||
|
|
|
@ -124,8 +124,7 @@ void ServerFeature::validateOptions(std::shared_ptr<ProgramOptions>) {
|
|||
|
||||
if (!_restServer) {
|
||||
ApplicationServer::disableFeatures({"Daemon", "Endpoint", "GeneralServer",
|
||||
"Scheduler", "SslServer",
|
||||
"Supervisor"});
|
||||
"SslServer", "Supervisor"});
|
||||
|
||||
DatabaseFeature* database =
|
||||
ApplicationServer::getFeature<DatabaseFeature>("Database");
|
||||
|
@ -158,7 +157,7 @@ void ServerFeature::validateOptions(std::shared_ptr<ProgramOptions>) {
|
|||
}
|
||||
|
||||
void ServerFeature::start() {
|
||||
if (_operationMode != OperationMode::MODE_CONSOLE && _restServer) {
|
||||
if (_operationMode != OperationMode::MODE_CONSOLE) {
|
||||
auto scheduler =
|
||||
ApplicationServer::getFeature<SchedulerFeature>("Scheduler");
|
||||
|
||||
|
|
|
@ -78,7 +78,6 @@
|
|||
#include "StorageEngine/RocksDBEngine.h"
|
||||
#include "V8Server/FoxxQueuesFeature.h"
|
||||
#include "V8Server/V8DealerFeature.h"
|
||||
#include "VocBase/IndexThreadFeature.h"
|
||||
#include "Wal/LogfileManager.h"
|
||||
|
||||
#include "Indexes/RocksDBFeature.h"
|
||||
|
@ -112,9 +111,8 @@ static int runServer(int argc, char** argv) {
|
|||
"Cluster", "Daemon",
|
||||
"Dispatcher", "FoxxQueues",
|
||||
"GeneralServer", "LoggerBufferFeature",
|
||||
"Server", "Scheduler",
|
||||
"SslServer", "Statistics",
|
||||
"Supervisor"};
|
||||
"Server", "SslServer",
|
||||
"Statistics", "Supervisor"};
|
||||
|
||||
int ret = EXIT_FAILURE;
|
||||
|
||||
|
@ -136,7 +134,6 @@ static int runServer(int argc, char** argv) {
|
|||
server.addFeature(new FrontendFeature(&server));
|
||||
server.addFeature(new GeneralServerFeature(&server));
|
||||
server.addFeature(new GreetingsFeature(&server, "arangod"));
|
||||
server.addFeature(new IndexThreadFeature(&server));
|
||||
server.addFeature(new InitDatabaseFeature(&server, nonServerFeatures));
|
||||
server.addFeature(new LanguageFeature(&server));
|
||||
server.addFeature(new LockfileFeature(&server));
|
||||
|
@ -246,5 +243,5 @@ int main(int argc, char* argv[]) {
|
|||
}
|
||||
} else
|
||||
#endif
|
||||
return runServer(argc, argv);
|
||||
return runServer(argc, argv);
|
||||
}
|
||||
|
|
|
@ -239,7 +239,8 @@ void SchedulerFeature::buildScheduler() {
|
|||
|
||||
_scheduler->setMinimal(_nrMinimalThreads);
|
||||
_scheduler->setRealMaximum(_nrMaximalThreads);
|
||||
|
||||
|
||||
TRI_ASSERT(SCHEDULER == nullptr);
|
||||
SCHEDULER = _scheduler.get();
|
||||
}
|
||||
|
||||
|
|
|
@ -23,13 +23,13 @@
|
|||
|
||||
#include "MMFilesWalRecoverState.h"
|
||||
#include "ApplicationFeatures/ApplicationServer.h"
|
||||
#include "Basics/Exceptions.h"
|
||||
#include "Basics/FileUtils.h"
|
||||
#include "Basics/VelocyPackHelper.h"
|
||||
#include "Basics/conversions.h"
|
||||
#include "Basics/files.h"
|
||||
#include "Basics/Exceptions.h"
|
||||
#include "Basics/memory-map.h"
|
||||
#include "Basics/tri-strings.h"
|
||||
#include "Basics/VelocyPackHelper.h"
|
||||
#include "RestServer/DatabaseFeature.h"
|
||||
#include "StorageEngine/MMFilesDatafileHelper.h"
|
||||
#include "StorageEngine/MMFilesWalSlots.h"
|
||||
|
@ -50,8 +50,10 @@ using namespace arangodb;
|
|||
template <typename T>
|
||||
static inline T NumericValue(VPackSlice const& slice, char const* attribute) {
|
||||
if (!slice.isObject()) {
|
||||
LOG(ERR) << "invalid value type when looking for attribute '" << attribute << "': expecting object";
|
||||
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_BAD_PARAMETER, "invalid attribute value: expecting object");
|
||||
LOG(ERR) << "invalid value type when looking for attribute '" << attribute
|
||||
<< "': expecting object";
|
||||
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_BAD_PARAMETER,
|
||||
"invalid attribute value: expecting object");
|
||||
}
|
||||
VPackSlice v = slice.get(attribute);
|
||||
if (v.isString()) {
|
||||
|
@ -60,9 +62,10 @@ static inline T NumericValue(VPackSlice const& slice, char const* attribute) {
|
|||
if (v.isNumber()) {
|
||||
return v.getNumber<T>();
|
||||
}
|
||||
|
||||
|
||||
LOG(ERR) << "invalid value for attribute '" << attribute << "'";
|
||||
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_BAD_PARAMETER, "invalid attribute value");
|
||||
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_BAD_PARAMETER,
|
||||
"invalid attribute value");
|
||||
}
|
||||
|
||||
/// @brief creates the recover state
|
||||
|
@ -79,8 +82,9 @@ MMFilesWalRecoverState::MMFilesWalRecoverState(bool ignoreRecoveryErrors)
|
|||
maxRevisionId(0),
|
||||
lastDatabaseId(0),
|
||||
lastCollectionId(0) {
|
||||
|
||||
databaseFeature = application_features::ApplicationServer::getFeature<DatabaseFeature>("Database");
|
||||
databaseFeature =
|
||||
application_features::ApplicationServer::getFeature<DatabaseFeature>(
|
||||
"Database");
|
||||
}
|
||||
|
||||
/// @brief destroys the recover state
|
||||
|
@ -125,7 +129,8 @@ TRI_vocbase_t* MMFilesWalRecoverState::useDatabase(TRI_voc_tick_t databaseId) {
|
|||
}
|
||||
|
||||
/// @brief release a database (so it can be dropped)
|
||||
TRI_vocbase_t* MMFilesWalRecoverState::releaseDatabase(TRI_voc_tick_t databaseId) {
|
||||
TRI_vocbase_t* MMFilesWalRecoverState::releaseDatabase(
|
||||
TRI_voc_tick_t databaseId) {
|
||||
auto it = openedDatabases.find(databaseId);
|
||||
|
||||
if (it == openedDatabases.end()) {
|
||||
|
@ -163,7 +168,8 @@ TRI_vocbase_t* MMFilesWalRecoverState::releaseDatabase(TRI_voc_tick_t databaseId
|
|||
}
|
||||
|
||||
/// @brief release a collection (so it can be dropped)
|
||||
arangodb::LogicalCollection* MMFilesWalRecoverState::releaseCollection(TRI_voc_cid_t collectionId) {
|
||||
arangodb::LogicalCollection* MMFilesWalRecoverState::releaseCollection(
|
||||
TRI_voc_cid_t collectionId) {
|
||||
auto it = openedCollections.find(collectionId);
|
||||
|
||||
if (it == openedCollections.end()) {
|
||||
|
@ -191,13 +197,15 @@ arangodb::LogicalCollection* MMFilesWalRecoverState::useCollection(
|
|||
|
||||
TRI_set_errno(TRI_ERROR_NO_ERROR);
|
||||
TRI_vocbase_col_status_e status; // ignored here
|
||||
arangodb::LogicalCollection* collection = vocbase->useCollection(collectionId, status);
|
||||
arangodb::LogicalCollection* collection =
|
||||
vocbase->useCollection(collectionId, status);
|
||||
|
||||
if (collection == nullptr) {
|
||||
res = TRI_errno();
|
||||
|
||||
if (res == TRI_ERROR_ARANGO_CORRUPTED_COLLECTION) {
|
||||
LOG(WARN) << "unable to open collection " << collectionId << ". Please check the logs above for errors.";
|
||||
LOG(WARN) << "unable to open collection " << collectionId
|
||||
<< ". Please check the logs above for errors.";
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
|
@ -225,10 +233,12 @@ LogicalCollection* MMFilesWalRecoverState::getCollection(
|
|||
}
|
||||
|
||||
int res;
|
||||
arangodb::LogicalCollection* collection = useCollection(vocbase, collectionId, res);
|
||||
arangodb::LogicalCollection* collection =
|
||||
useCollection(vocbase, collectionId, res);
|
||||
|
||||
if (collection == nullptr) {
|
||||
LOG(TRACE) << "collection " << collectionId << " of database " << databaseId << " not found";
|
||||
LOG(TRACE) << "collection " << collectionId << " of database " << databaseId
|
||||
<< " not found";
|
||||
return nullptr;
|
||||
}
|
||||
return collection;
|
||||
|
@ -238,7 +248,8 @@ LogicalCollection* MMFilesWalRecoverState::getCollection(
|
|||
int MMFilesWalRecoverState::executeSingleOperation(
|
||||
TRI_voc_tick_t databaseId, TRI_voc_cid_t collectionId,
|
||||
TRI_df_marker_t const* marker, TRI_voc_fid_t fid,
|
||||
std::function<int(SingleCollectionTransaction*, MMFilesMarkerEnvelope*)> func) {
|
||||
std::function<int(SingleCollectionTransaction*, MMFilesMarkerEnvelope*)>
|
||||
func) {
|
||||
// first find the correct database
|
||||
TRI_vocbase_t* vocbase = useDatabase(databaseId);
|
||||
|
||||
|
@ -248,7 +259,8 @@ int MMFilesWalRecoverState::executeSingleOperation(
|
|||
}
|
||||
|
||||
int res;
|
||||
arangodb::LogicalCollection* collection = useCollection(vocbase, collectionId, res);
|
||||
arangodb::LogicalCollection* collection =
|
||||
useCollection(vocbase, collectionId, res);
|
||||
|
||||
if (collection == nullptr) {
|
||||
if (res == TRI_ERROR_ARANGO_CORRUPTED_COLLECTION) {
|
||||
|
@ -267,14 +279,17 @@ int MMFilesWalRecoverState::executeSingleOperation(
|
|||
res = TRI_ERROR_INTERNAL;
|
||||
|
||||
try {
|
||||
SingleCollectionTransaction trx(arangodb::StandaloneTransactionContext::Create(vocbase), collectionId, TRI_TRANSACTION_WRITE);
|
||||
SingleCollectionTransaction trx(
|
||||
arangodb::StandaloneTransactionContext::Create(vocbase), collectionId,
|
||||
TRI_TRANSACTION_WRITE);
|
||||
|
||||
trx.addHint(TRI_TRANSACTION_HINT_SINGLE_OPERATION, false);
|
||||
trx.addHint(TRI_TRANSACTION_HINT_NO_BEGIN_MARKER, false);
|
||||
trx.addHint(TRI_TRANSACTION_HINT_NO_ABORT_MARKER, false);
|
||||
trx.addHint(TRI_TRANSACTION_HINT_NO_THROTTLING, false);
|
||||
trx.addHint(TRI_TRANSACTION_HINT_LOCK_NEVER, false);
|
||||
trx.addHint(TRI_TRANSACTION_HINT_RECOVERY, false); // to turn off waitForSync!
|
||||
trx.addHint(TRI_TRANSACTION_HINT_RECOVERY,
|
||||
false); // to turn off waitForSync!
|
||||
|
||||
res = trx.begin();
|
||||
|
||||
|
@ -294,13 +309,16 @@ int MMFilesWalRecoverState::executeSingleOperation(
|
|||
// commit the operation
|
||||
res = trx.commit();
|
||||
} catch (arangodb::basics::Exception const& ex) {
|
||||
LOG(ERR) << "caught exception during recovery of marker type " << TRI_NameMarkerDatafile(marker) << ": " << ex.what();
|
||||
LOG(ERR) << "caught exception during recovery of marker type "
|
||||
<< TRI_NameMarkerDatafile(marker) << ": " << ex.what();
|
||||
res = ex.code();
|
||||
} catch (std::exception const& ex) {
|
||||
LOG(ERR) << "caught exception during recovery of marker type " << TRI_NameMarkerDatafile(marker) << ": " << ex.what();
|
||||
LOG(ERR) << "caught exception during recovery of marker type "
|
||||
<< TRI_NameMarkerDatafile(marker) << ": " << ex.what();
|
||||
res = TRI_ERROR_INTERNAL;
|
||||
} catch (...) {
|
||||
LOG(ERR) << "caught unknown exception during recovery of marker type " << TRI_NameMarkerDatafile(marker);
|
||||
LOG(ERR) << "caught unknown exception during recovery of marker type "
|
||||
<< TRI_NameMarkerDatafile(marker);
|
||||
res = TRI_ERROR_INTERNAL;
|
||||
}
|
||||
|
||||
|
@ -309,9 +327,11 @@ int MMFilesWalRecoverState::executeSingleOperation(
|
|||
|
||||
/// @brief callback to handle one marker during recovery
|
||||
/// this function only builds up state and does not change any data
|
||||
bool MMFilesWalRecoverState::InitialScanMarker(TRI_df_marker_t const* marker, void* data,
|
||||
MMFilesDatafile* datafile) {
|
||||
MMFilesWalRecoverState* state = reinterpret_cast<MMFilesWalRecoverState*>(data);
|
||||
bool MMFilesWalRecoverState::InitialScanMarker(TRI_df_marker_t const* marker,
|
||||
void* data,
|
||||
MMFilesDatafile* datafile) {
|
||||
MMFilesWalRecoverState* state =
|
||||
reinterpret_cast<MMFilesWalRecoverState*>(data);
|
||||
|
||||
TRI_ASSERT(marker != nullptr);
|
||||
|
||||
|
@ -328,9 +348,11 @@ bool MMFilesWalRecoverState::InitialScanMarker(TRI_df_marker_t const* marker, vo
|
|||
|
||||
switch (type) {
|
||||
case TRI_DF_MARKER_VPACK_DOCUMENT: {
|
||||
VPackSlice const payloadSlice(reinterpret_cast<char const*>(marker) + MMFilesDatafileHelper::VPackOffset(type));
|
||||
VPackSlice const payloadSlice(reinterpret_cast<char const*>(marker) +
|
||||
MMFilesDatafileHelper::VPackOffset(type));
|
||||
if (payloadSlice.isObject()) {
|
||||
TRI_voc_rid_t revisionId = Transaction::extractRevFromDocument(payloadSlice);
|
||||
TRI_voc_rid_t revisionId =
|
||||
Transaction::extractRevFromDocument(payloadSlice);
|
||||
if (revisionId > state->maxRevisionId) {
|
||||
state->maxRevisionId = revisionId;
|
||||
}
|
||||
|
@ -344,7 +366,8 @@ bool MMFilesWalRecoverState::InitialScanMarker(TRI_df_marker_t const* marker, vo
|
|||
// transaction,
|
||||
// we'll have it in the failed list at the end of the scan and can ignore
|
||||
// it
|
||||
TRI_voc_tick_t const databaseId = MMFilesDatafileHelper::DatabaseId(marker);
|
||||
TRI_voc_tick_t const databaseId =
|
||||
MMFilesDatafileHelper::DatabaseId(marker);
|
||||
TRI_voc_tid_t const tid = MMFilesDatafileHelper::TransactionId(marker);
|
||||
state->failedTransactions.emplace(tid, std::make_pair(databaseId, false));
|
||||
break;
|
||||
|
@ -359,22 +382,25 @@ bool MMFilesWalRecoverState::InitialScanMarker(TRI_df_marker_t const* marker, vo
|
|||
|
||||
case TRI_DF_MARKER_VPACK_ABORT_TRANSACTION: {
|
||||
// insert this transaction into the list of failed transactions
|
||||
TRI_voc_tick_t const databaseId = MMFilesDatafileHelper::DatabaseId(marker);
|
||||
TRI_voc_tick_t const databaseId =
|
||||
MMFilesDatafileHelper::DatabaseId(marker);
|
||||
TRI_voc_tid_t const tid = MMFilesDatafileHelper::TransactionId(marker);
|
||||
state->failedTransactions[tid] = std::make_pair(databaseId, true);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case TRI_DF_MARKER_VPACK_DROP_DATABASE: {
|
||||
// note that the database was dropped and doesn't need to be recovered
|
||||
TRI_voc_tick_t const databaseId = MMFilesDatafileHelper::DatabaseId(marker);
|
||||
TRI_voc_tick_t const databaseId =
|
||||
MMFilesDatafileHelper::DatabaseId(marker);
|
||||
state->totalDroppedDatabases.emplace(databaseId);
|
||||
break;
|
||||
}
|
||||
|
||||
case TRI_DF_MARKER_VPACK_DROP_COLLECTION: {
|
||||
// note that the collection was dropped and doesn't need to be recovered
|
||||
TRI_voc_cid_t const collectionId = MMFilesDatafileHelper::CollectionId(marker);
|
||||
TRI_voc_cid_t const collectionId =
|
||||
MMFilesDatafileHelper::CollectionId(marker);
|
||||
state->totalDroppedCollections.emplace(collectionId);
|
||||
break;
|
||||
}
|
||||
|
@ -389,24 +415,29 @@ bool MMFilesWalRecoverState::InitialScanMarker(TRI_df_marker_t const* marker, vo
|
|||
|
||||
/// @brief callback to replay one marker during recovery
|
||||
/// this function modifies indexes etc.
|
||||
bool MMFilesWalRecoverState::ReplayMarker(TRI_df_marker_t const* marker, void* data,
|
||||
MMFilesDatafile* datafile) {
|
||||
MMFilesWalRecoverState* state = reinterpret_cast<MMFilesWalRecoverState*>(data);
|
||||
bool MMFilesWalRecoverState::ReplayMarker(TRI_df_marker_t const* marker,
|
||||
void* data,
|
||||
MMFilesDatafile* datafile) {
|
||||
MMFilesWalRecoverState* state =
|
||||
reinterpret_cast<MMFilesWalRecoverState*>(data);
|
||||
|
||||
#ifdef ARANGODB_ENABLE_FAILURE_TESTS
|
||||
LOG(TRACE) << "replaying marker of type " << TRI_NameMarkerDatafile(marker);
|
||||
#endif
|
||||
|
||||
|
||||
TRI_df_marker_type_t const type = marker->getType();
|
||||
|
||||
try {
|
||||
switch (type) {
|
||||
case TRI_DF_MARKER_PROLOGUE: {
|
||||
// simply note the last state
|
||||
TRI_voc_tick_t const databaseId = MMFilesDatafileHelper::DatabaseId(marker);
|
||||
TRI_voc_cid_t const collectionId = MMFilesDatafileHelper::CollectionId(marker);
|
||||
TRI_voc_tick_t const databaseId =
|
||||
MMFilesDatafileHelper::DatabaseId(marker);
|
||||
TRI_voc_cid_t const collectionId =
|
||||
MMFilesDatafileHelper::CollectionId(marker);
|
||||
|
||||
LOG(TRACE) << "found prologue marker. databaseId: " << databaseId << ", collectionId: " << collectionId;
|
||||
LOG(TRACE) << "found prologue marker. databaseId: " << databaseId
|
||||
<< ", collectionId: " << collectionId;
|
||||
state->resetCollection(databaseId, collectionId);
|
||||
return true;
|
||||
}
|
||||
|
@ -417,33 +448,41 @@ bool MMFilesWalRecoverState::ReplayMarker(TRI_df_marker_t const* marker, void* d
|
|||
|
||||
case TRI_DF_MARKER_VPACK_DOCUMENT: {
|
||||
// re-insert the document/edge into the collection
|
||||
TRI_voc_tick_t const databaseId = state->lastDatabaseId; // from prologue
|
||||
TRI_voc_cid_t const collectionId = state->lastCollectionId; // from prologue
|
||||
|
||||
TRI_voc_tick_t const databaseId =
|
||||
state->lastDatabaseId; // from prologue
|
||||
TRI_voc_cid_t const collectionId =
|
||||
state->lastCollectionId; // from prologue
|
||||
|
||||
if (state->isDropped(databaseId, collectionId)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
TRI_voc_tid_t const tid = MMFilesDatafileHelper::TransactionId(marker);
|
||||
|
||||
|
||||
if (state->ignoreTransaction(tid)) {
|
||||
// transaction was aborted
|
||||
return true;
|
||||
}
|
||||
|
||||
LOG(TRACE) << "found document marker. databaseId: " << databaseId << ", collectionId: " << collectionId << ", transactionId: " << tid;
|
||||
|
||||
LOG(TRACE) << "found document marker. databaseId: " << databaseId
|
||||
<< ", collectionId: " << collectionId
|
||||
<< ", transactionId: " << tid;
|
||||
|
||||
int res = state->executeSingleOperation(
|
||||
databaseId, collectionId, marker, datafile->fid(),
|
||||
[&](SingleCollectionTransaction* trx, MMFilesMarkerEnvelope* envelope) -> int {
|
||||
[&](SingleCollectionTransaction* trx,
|
||||
MMFilesMarkerEnvelope* envelope) -> int {
|
||||
if (trx->documentCollection()->isVolatile()) {
|
||||
return TRI_ERROR_NO_ERROR;
|
||||
}
|
||||
|
||||
TRI_df_marker_t const* marker = static_cast<TRI_df_marker_t const*>(envelope->mem());
|
||||
TRI_df_marker_t const* marker =
|
||||
static_cast<TRI_df_marker_t const*>(envelope->mem());
|
||||
|
||||
std::string const collectionName = trx->documentCollection()->name();
|
||||
uint8_t const* ptr = reinterpret_cast<uint8_t const*>(marker) + MMFilesDatafileHelper::VPackOffset(type);
|
||||
std::string const collectionName =
|
||||
trx->documentCollection()->name();
|
||||
uint8_t const* ptr = reinterpret_cast<uint8_t const*>(marker) +
|
||||
MMFilesDatafileHelper::VPackOffset(type);
|
||||
|
||||
OperationOptions options;
|
||||
options.silent = true;
|
||||
|
@ -454,7 +493,8 @@ bool MMFilesWalRecoverState::ReplayMarker(TRI_df_marker_t const* marker, void* d
|
|||
|
||||
// try an insert first
|
||||
TRI_ASSERT(VPackSlice(ptr).isObject());
|
||||
OperationResult opRes = trx->insert(collectionName, VPackSlice(ptr), options);
|
||||
OperationResult opRes =
|
||||
trx->insert(collectionName, VPackSlice(ptr), options);
|
||||
int res = opRes.code;
|
||||
|
||||
if (res == TRI_ERROR_ARANGO_UNIQUE_CONSTRAINT_VIOLATED) {
|
||||
|
@ -469,7 +509,9 @@ bool MMFilesWalRecoverState::ReplayMarker(TRI_df_marker_t const* marker, void* d
|
|||
if (res != TRI_ERROR_NO_ERROR && res != TRI_ERROR_ARANGO_CONFLICT &&
|
||||
res != TRI_ERROR_ARANGO_DATABASE_NOT_FOUND &&
|
||||
res != TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND) {
|
||||
LOG(WARN) << "unable to insert document in collection " << collectionId << " of database " << databaseId << ": " << TRI_errno_string(res);
|
||||
LOG(WARN) << "unable to insert document in collection "
|
||||
<< collectionId << " of database " << databaseId << ": "
|
||||
<< TRI_errno_string(res);
|
||||
++state->errorCount;
|
||||
return state->canContinue();
|
||||
}
|
||||
|
@ -478,8 +520,10 @@ bool MMFilesWalRecoverState::ReplayMarker(TRI_df_marker_t const* marker, void* d
|
|||
|
||||
case TRI_DF_MARKER_VPACK_REMOVE: {
|
||||
// re-apply the remove operation
|
||||
TRI_voc_tick_t const databaseId = state->lastDatabaseId; // from prologue
|
||||
TRI_voc_cid_t const collectionId = state->lastCollectionId; // from prologue
|
||||
TRI_voc_tick_t const databaseId =
|
||||
state->lastDatabaseId; // from prologue
|
||||
TRI_voc_cid_t const collectionId =
|
||||
state->lastCollectionId; // from prologue
|
||||
|
||||
TRI_ASSERT(databaseId > 0);
|
||||
TRI_ASSERT(collectionId > 0);
|
||||
|
@ -489,24 +533,30 @@ bool MMFilesWalRecoverState::ReplayMarker(TRI_df_marker_t const* marker, void* d
|
|||
}
|
||||
|
||||
TRI_voc_tid_t const tid = MMFilesDatafileHelper::TransactionId(marker);
|
||||
|
||||
|
||||
if (state->ignoreTransaction(tid)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
LOG(TRACE) << "found remove marker. databaseId: " << databaseId << ", collectionId: " << collectionId << ", transactionId: " << tid;
|
||||
|
||||
LOG(TRACE) << "found remove marker. databaseId: " << databaseId
|
||||
<< ", collectionId: " << collectionId
|
||||
<< ", transactionId: " << tid;
|
||||
|
||||
int res = state->executeSingleOperation(
|
||||
databaseId, collectionId, marker, datafile->fid(),
|
||||
[&](SingleCollectionTransaction* trx, MMFilesMarkerEnvelope* envelope) -> int {
|
||||
[&](SingleCollectionTransaction* trx,
|
||||
MMFilesMarkerEnvelope* envelope) -> int {
|
||||
if (trx->documentCollection()->isVolatile()) {
|
||||
return TRI_ERROR_NO_ERROR;
|
||||
}
|
||||
|
||||
TRI_df_marker_t const* marker = static_cast<TRI_df_marker_t const*>(envelope->mem());
|
||||
|
||||
std::string const collectionName = trx->documentCollection()->name();
|
||||
uint8_t const* ptr = reinterpret_cast<uint8_t const*>(marker) + MMFilesDatafileHelper::VPackOffset(type);
|
||||
TRI_df_marker_t const* marker =
|
||||
static_cast<TRI_df_marker_t const*>(envelope->mem());
|
||||
|
||||
std::string const collectionName =
|
||||
trx->documentCollection()->name();
|
||||
uint8_t const* ptr = reinterpret_cast<uint8_t const*>(marker) +
|
||||
MMFilesDatafileHelper::VPackOffset(type);
|
||||
|
||||
OperationOptions options;
|
||||
options.silent = true;
|
||||
|
@ -515,20 +565,23 @@ bool MMFilesWalRecoverState::ReplayMarker(TRI_df_marker_t const* marker, void* d
|
|||
options.ignoreRevs = true;
|
||||
|
||||
try {
|
||||
OperationResult opRes = trx->remove(collectionName, VPackSlice(ptr), options);
|
||||
OperationResult opRes =
|
||||
trx->remove(collectionName, VPackSlice(ptr), options);
|
||||
if (opRes.code == TRI_ERROR_ARANGO_DOCUMENT_NOT_FOUND) {
|
||||
// document to delete is not present. this error can be ignored
|
||||
// document to delete is not present. this error can be
|
||||
// ignored
|
||||
return TRI_ERROR_NO_ERROR;
|
||||
}
|
||||
return opRes.code;
|
||||
} catch (arangodb::basics::Exception const& ex) {
|
||||
if (ex.code() == TRI_ERROR_ARANGO_DOCUMENT_NOT_FOUND) {
|
||||
// document to delete is not present. this error can be ignored
|
||||
// document to delete is not present. this error can be
|
||||
// ignored
|
||||
return TRI_ERROR_NO_ERROR;
|
||||
}
|
||||
return ex.code();
|
||||
}
|
||||
// should not get here...
|
||||
// should not get here...
|
||||
return TRI_ERROR_INTERNAL;
|
||||
});
|
||||
|
||||
|
@ -536,7 +589,9 @@ bool MMFilesWalRecoverState::ReplayMarker(TRI_df_marker_t const* marker, void* d
|
|||
res != TRI_ERROR_ARANGO_DATABASE_NOT_FOUND &&
|
||||
res != TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND &&
|
||||
res != TRI_ERROR_ARANGO_DOCUMENT_NOT_FOUND) {
|
||||
LOG(WARN) << "unable to remove document in collection " << collectionId << " of database " << databaseId << ": " << TRI_errno_string(res);
|
||||
LOG(WARN) << "unable to remove document in collection "
|
||||
<< collectionId << " of database " << databaseId << ": "
|
||||
<< TRI_errno_string(res);
|
||||
++state->errorCount;
|
||||
return state->canContinue();
|
||||
}
|
||||
|
@ -548,10 +603,13 @@ bool MMFilesWalRecoverState::ReplayMarker(TRI_df_marker_t const* marker, void* d
|
|||
// -----------------------------------------------------------------------------
|
||||
|
||||
case TRI_DF_MARKER_VPACK_RENAME_COLLECTION: {
|
||||
TRI_voc_tick_t const databaseId = MMFilesDatafileHelper::DatabaseId(marker);
|
||||
TRI_voc_cid_t const collectionId = MMFilesDatafileHelper::CollectionId(marker);
|
||||
VPackSlice const payloadSlice(reinterpret_cast<char const*>(marker) + MMFilesDatafileHelper::VPackOffset(type));
|
||||
|
||||
TRI_voc_tick_t const databaseId =
|
||||
MMFilesDatafileHelper::DatabaseId(marker);
|
||||
TRI_voc_cid_t const collectionId =
|
||||
MMFilesDatafileHelper::CollectionId(marker);
|
||||
VPackSlice const payloadSlice(reinterpret_cast<char const*>(marker) +
|
||||
MMFilesDatafileHelper::VPackOffset(type));
|
||||
|
||||
if (!payloadSlice.isObject()) {
|
||||
LOG(WARN) << "cannot rename collection: invalid marker";
|
||||
++state->errorCount;
|
||||
|
@ -561,8 +619,9 @@ bool MMFilesWalRecoverState::ReplayMarker(TRI_df_marker_t const* marker, void* d
|
|||
if (state->isDropped(databaseId)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
LOG(TRACE) << "found collection rename marker. databaseId: " << databaseId << ", collectionId: " << collectionId;
|
||||
|
||||
LOG(TRACE) << "found collection rename marker. databaseId: "
|
||||
<< databaseId << ", collectionId: " << collectionId;
|
||||
|
||||
TRI_vocbase_t* vocbase = state->useDatabase(databaseId);
|
||||
|
||||
|
@ -572,7 +631,8 @@ bool MMFilesWalRecoverState::ReplayMarker(TRI_df_marker_t const* marker, void* d
|
|||
return true;
|
||||
}
|
||||
|
||||
arangodb::LogicalCollection* collection = state->releaseCollection(collectionId);
|
||||
arangodb::LogicalCollection* collection =
|
||||
state->releaseCollection(collectionId);
|
||||
|
||||
if (collection == nullptr) {
|
||||
collection = vocbase->lookupCollection(collectionId);
|
||||
|
@ -586,7 +646,9 @@ bool MMFilesWalRecoverState::ReplayMarker(TRI_df_marker_t const* marker, void* d
|
|||
|
||||
VPackSlice nameSlice = payloadSlice.get("name");
|
||||
if (!nameSlice.isString()) {
|
||||
LOG(WARN) << "cannot rename collection " << collectionId << " in database " << databaseId << ": name attribute is no string";
|
||||
LOG(WARN) << "cannot rename collection " << collectionId
|
||||
<< " in database " << databaseId
|
||||
<< ": name attribute is no string";
|
||||
++state->errorCount;
|
||||
return state->canContinue();
|
||||
}
|
||||
|
@ -604,7 +666,9 @@ bool MMFilesWalRecoverState::ReplayMarker(TRI_df_marker_t const* marker, void* d
|
|||
int res = vocbase->renameCollection(collection, name, true, false);
|
||||
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
LOG(WARN) << "cannot rename collection " << collectionId << " in database " << databaseId << " to '" << name << "': " << TRI_errno_string(res);
|
||||
LOG(WARN) << "cannot rename collection " << collectionId
|
||||
<< " in database " << databaseId << " to '" << name
|
||||
<< "': " << TRI_errno_string(res);
|
||||
++state->errorCount;
|
||||
return state->canContinue();
|
||||
}
|
||||
|
@ -612,10 +676,13 @@ bool MMFilesWalRecoverState::ReplayMarker(TRI_df_marker_t const* marker, void* d
|
|||
}
|
||||
|
||||
case TRI_DF_MARKER_VPACK_CHANGE_COLLECTION: {
|
||||
TRI_voc_tick_t const databaseId = MMFilesDatafileHelper::DatabaseId(marker);
|
||||
TRI_voc_cid_t const collectionId = MMFilesDatafileHelper::CollectionId(marker);
|
||||
VPackSlice const payloadSlice(reinterpret_cast<char const*>(marker) + MMFilesDatafileHelper::VPackOffset(type));
|
||||
|
||||
TRI_voc_tick_t const databaseId =
|
||||
MMFilesDatafileHelper::DatabaseId(marker);
|
||||
TRI_voc_cid_t const collectionId =
|
||||
MMFilesDatafileHelper::CollectionId(marker);
|
||||
VPackSlice const payloadSlice(reinterpret_cast<char const*>(marker) +
|
||||
MMFilesDatafileHelper::VPackOffset(type));
|
||||
|
||||
if (!payloadSlice.isObject()) {
|
||||
LOG(WARN) << "cannot change properties of collection: invalid marker";
|
||||
++state->errorCount;
|
||||
|
@ -625,8 +692,9 @@ bool MMFilesWalRecoverState::ReplayMarker(TRI_df_marker_t const* marker, void* d
|
|||
if (state->isDropped(databaseId)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
LOG(TRACE) << "found collection change marker. databaseId: " << databaseId << ", collectionId: " << collectionId;
|
||||
|
||||
LOG(TRACE) << "found collection change marker. databaseId: "
|
||||
<< databaseId << ", collectionId: " << collectionId;
|
||||
|
||||
TRI_vocbase_t* vocbase = state->useDatabase(databaseId);
|
||||
|
||||
|
@ -641,17 +709,22 @@ bool MMFilesWalRecoverState::ReplayMarker(TRI_df_marker_t const* marker, void* d
|
|||
|
||||
if (collection == nullptr) {
|
||||
// if the underlying collection is gone, we can go on
|
||||
LOG(TRACE) << "cannot change properties of collection " << collectionId << " in database " << databaseId << ": " << TRI_errno_string(TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND);
|
||||
LOG(TRACE) << "cannot change properties of collection "
|
||||
<< collectionId << " in database " << databaseId << ": "
|
||||
<< TRI_errno_string(TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND);
|
||||
return true;
|
||||
}
|
||||
|
||||
// turn off sync temporarily if the database or collection are going to be
|
||||
// turn off sync temporarily if the database or collection are going to
|
||||
// be
|
||||
// dropped later
|
||||
bool const forceSync = state->willBeDropped(databaseId, collectionId);
|
||||
int res = collection->update(payloadSlice, forceSync);
|
||||
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
LOG(WARN) << "cannot change collection properties for collection " << collectionId << " in database " << databaseId << ": " << TRI_errno_string(res);
|
||||
LOG(WARN) << "cannot change collection properties for collection "
|
||||
<< collectionId << " in database " << databaseId << ": "
|
||||
<< TRI_errno_string(res);
|
||||
++state->errorCount;
|
||||
return state->canContinue();
|
||||
}
|
||||
|
@ -660,51 +733,63 @@ bool MMFilesWalRecoverState::ReplayMarker(TRI_df_marker_t const* marker, void* d
|
|||
}
|
||||
|
||||
case TRI_DF_MARKER_VPACK_CREATE_INDEX: {
|
||||
TRI_voc_tick_t const databaseId = MMFilesDatafileHelper::DatabaseId(marker);
|
||||
TRI_voc_cid_t const collectionId = MMFilesDatafileHelper::CollectionId(marker);
|
||||
VPackSlice const payloadSlice(reinterpret_cast<char const*>(marker) + MMFilesDatafileHelper::VPackOffset(type));
|
||||
|
||||
TRI_voc_tick_t const databaseId =
|
||||
MMFilesDatafileHelper::DatabaseId(marker);
|
||||
TRI_voc_cid_t const collectionId =
|
||||
MMFilesDatafileHelper::CollectionId(marker);
|
||||
VPackSlice const payloadSlice(reinterpret_cast<char const*>(marker) +
|
||||
MMFilesDatafileHelper::VPackOffset(type));
|
||||
|
||||
if (!payloadSlice.isObject()) {
|
||||
LOG(WARN) << "cannot create index for collection: invalid marker";
|
||||
++state->errorCount;
|
||||
return state->canContinue();
|
||||
}
|
||||
|
||||
|
||||
TRI_idx_iid_t indexId = NumericValue<TRI_idx_iid_t>(payloadSlice, "id");
|
||||
|
||||
if (state->isDropped(databaseId, collectionId)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
LOG(TRACE) << "found create index marker. databaseId: " << databaseId << ", collectionId: " << collectionId;
|
||||
|
||||
LOG(TRACE) << "found create index marker. databaseId: " << databaseId
|
||||
<< ", collectionId: " << collectionId;
|
||||
|
||||
TRI_vocbase_t* vocbase = state->useDatabase(databaseId);
|
||||
|
||||
if (vocbase == nullptr) {
|
||||
// if the underlying database is gone, we can go on
|
||||
LOG(TRACE) << "cannot create index for collection " << collectionId << " in database " << databaseId << ": " << TRI_errno_string(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND);
|
||||
LOG(TRACE) << "cannot create index for collection " << collectionId
|
||||
<< " in database " << databaseId << ": "
|
||||
<< TRI_errno_string(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND);
|
||||
return true;
|
||||
}
|
||||
|
||||
arangodb::LogicalCollection* col = vocbase->lookupCollection(collectionId);
|
||||
arangodb::LogicalCollection* col =
|
||||
vocbase->lookupCollection(collectionId);
|
||||
|
||||
if (col == nullptr) {
|
||||
// if the underlying collection gone, we can go on
|
||||
LOG(TRACE) << "cannot create index for collection " << collectionId << " in database " << databaseId << ": " << TRI_errno_string(TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND);
|
||||
LOG(TRACE) << "cannot create index for collection " << collectionId
|
||||
<< " in database " << databaseId << ": "
|
||||
<< TRI_errno_string(TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND);
|
||||
return true;
|
||||
}
|
||||
|
||||
RocksDBFeature::dropIndex(databaseId, collectionId, indexId);
|
||||
|
||||
std::string const indexName("index-" + std::to_string(indexId) + ".json");
|
||||
std::string const filename(arangodb::basics::FileUtils::buildFilename(col->path(), indexName));
|
||||
std::string const indexName("index-" + std::to_string(indexId) +
|
||||
".json");
|
||||
std::string const filename(
|
||||
arangodb::basics::FileUtils::buildFilename(col->path(), indexName));
|
||||
|
||||
bool const forceSync = state->willBeDropped(databaseId, collectionId);
|
||||
bool ok = arangodb::basics::VelocyPackHelper::velocyPackToFile(
|
||||
filename, payloadSlice, forceSync);
|
||||
|
||||
if (!ok) {
|
||||
LOG(WARN) << "cannot create index " << indexId << ", collection " << collectionId << " in database " << databaseId;
|
||||
LOG(WARN) << "cannot create index " << indexId << ", collection "
|
||||
<< collectionId << " in database " << databaseId;
|
||||
++state->errorCount;
|
||||
return state->canContinue();
|
||||
} else {
|
||||
|
@ -715,7 +800,8 @@ bool MMFilesWalRecoverState::ReplayMarker(TRI_df_marker_t const* marker, void* d
|
|||
int res = col->restoreIndex(&trx, payloadSlice, unused);
|
||||
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
LOG(WARN) << "cannot create index " << indexId << ", collection " << collectionId << " in database " << databaseId;
|
||||
LOG(WARN) << "cannot create index " << indexId << ", collection "
|
||||
<< collectionId << " in database " << databaseId;
|
||||
++state->errorCount;
|
||||
return state->canContinue();
|
||||
}
|
||||
|
@ -725,18 +811,22 @@ bool MMFilesWalRecoverState::ReplayMarker(TRI_df_marker_t const* marker, void* d
|
|||
}
|
||||
|
||||
case TRI_DF_MARKER_VPACK_CREATE_COLLECTION: {
|
||||
TRI_voc_tick_t const databaseId = MMFilesDatafileHelper::DatabaseId(marker);
|
||||
TRI_voc_cid_t const collectionId = MMFilesDatafileHelper::CollectionId(marker);
|
||||
VPackSlice const payloadSlice(reinterpret_cast<char const*>(marker) + MMFilesDatafileHelper::VPackOffset(type));
|
||||
|
||||
TRI_voc_tick_t const databaseId =
|
||||
MMFilesDatafileHelper::DatabaseId(marker);
|
||||
TRI_voc_cid_t const collectionId =
|
||||
MMFilesDatafileHelper::CollectionId(marker);
|
||||
VPackSlice const payloadSlice(reinterpret_cast<char const*>(marker) +
|
||||
MMFilesDatafileHelper::VPackOffset(type));
|
||||
|
||||
if (!payloadSlice.isObject()) {
|
||||
LOG(WARN) << "cannot create collection: invalid marker";
|
||||
++state->errorCount;
|
||||
return state->canContinue();
|
||||
}
|
||||
|
||||
LOG(TRACE) << "found create collection marker. databaseId: " << databaseId << ", collectionId: " << collectionId;
|
||||
|
||||
|
||||
LOG(TRACE) << "found create collection marker. databaseId: "
|
||||
<< databaseId << ", collectionId: " << collectionId;
|
||||
|
||||
// remove the drop marker
|
||||
state->droppedCollections.erase(collectionId);
|
||||
|
||||
|
@ -752,12 +842,13 @@ bool MMFilesWalRecoverState::ReplayMarker(TRI_df_marker_t const* marker, void* d
|
|||
return true;
|
||||
}
|
||||
|
||||
arangodb::LogicalCollection* collection = state->releaseCollection(collectionId);
|
||||
arangodb::LogicalCollection* collection =
|
||||
state->releaseCollection(collectionId);
|
||||
|
||||
if (collection == nullptr) {
|
||||
collection = vocbase->lookupCollection(collectionId);
|
||||
}
|
||||
|
||||
|
||||
if (collection != nullptr) {
|
||||
// drop an existing collection
|
||||
vocbase->dropCollection(collection, true, false);
|
||||
|
@ -765,7 +856,8 @@ bool MMFilesWalRecoverState::ReplayMarker(TRI_df_marker_t const* marker, void* d
|
|||
|
||||
RocksDBFeature::dropCollection(databaseId, collectionId);
|
||||
|
||||
// check if there is another collection with the same name as the one that
|
||||
// check if there is another collection with the same name as the one
|
||||
// that
|
||||
// we attempt to create
|
||||
VPackSlice const nameSlice = payloadSlice.get("name");
|
||||
std::string name = "";
|
||||
|
@ -774,14 +866,16 @@ bool MMFilesWalRecoverState::ReplayMarker(TRI_df_marker_t const* marker, void* d
|
|||
name = nameSlice.copyString();
|
||||
collection = vocbase->lookupCollection(name);
|
||||
|
||||
if (collection != nullptr) {
|
||||
if (collection != nullptr) {
|
||||
TRI_voc_cid_t otherCid = collection->cid();
|
||||
|
||||
state->releaseCollection(otherCid);
|
||||
vocbase->dropCollection(collection, true, false);
|
||||
}
|
||||
} else {
|
||||
LOG(WARN) << "empty name attribute in create collection marker for collection " << collectionId << " and database " << databaseId;
|
||||
LOG(WARN) << "empty name attribute in create collection marker for "
|
||||
"collection "
|
||||
<< collectionId << " and database " << databaseId;
|
||||
++state->errorCount;
|
||||
return state->canContinue();
|
||||
}
|
||||
|
@ -802,15 +896,18 @@ bool MMFilesWalRecoverState::ReplayMarker(TRI_df_marker_t const* marker, void* d
|
|||
int res = TRI_ERROR_NO_ERROR;
|
||||
try {
|
||||
if (state->willBeDropped(collectionId)) {
|
||||
// in case we detect that this collection is going to be deleted anyway,
|
||||
// in case we detect that this collection is going to be deleted
|
||||
// anyway,
|
||||
// set the sync properties to false temporarily
|
||||
bool oldSync = state->databaseFeature->forceSyncProperties();
|
||||
state->databaseFeature->forceSyncProperties(false);
|
||||
collection = vocbase->createCollection(b2.slice(), collectionId, false);
|
||||
collection =
|
||||
vocbase->createCollection(b2.slice(), collectionId, false);
|
||||
state->databaseFeature->forceSyncProperties(oldSync);
|
||||
} else {
|
||||
// collection will be kept
|
||||
collection = vocbase->createCollection(b2.slice(), collectionId, false);
|
||||
collection =
|
||||
vocbase->createCollection(b2.slice(), collectionId, false);
|
||||
}
|
||||
TRI_ASSERT(collection != nullptr);
|
||||
} catch (basics::Exception const& ex) {
|
||||
|
@ -820,7 +917,9 @@ bool MMFilesWalRecoverState::ReplayMarker(TRI_df_marker_t const* marker, void* d
|
|||
}
|
||||
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
LOG(WARN) << "cannot create collection " << collectionId << " in database " << databaseId << ": " << TRI_errno_string(res);
|
||||
LOG(WARN) << "cannot create collection " << collectionId
|
||||
<< " in database " << databaseId << ": "
|
||||
<< TRI_errno_string(res);
|
||||
++state->errorCount;
|
||||
return state->canContinue();
|
||||
}
|
||||
|
@ -828,17 +927,20 @@ bool MMFilesWalRecoverState::ReplayMarker(TRI_df_marker_t const* marker, void* d
|
|||
}
|
||||
|
||||
case TRI_DF_MARKER_VPACK_CREATE_DATABASE: {
|
||||
TRI_voc_tick_t const databaseId = MMFilesDatafileHelper::DatabaseId(marker);
|
||||
VPackSlice const payloadSlice(reinterpret_cast<char const*>(marker) + MMFilesDatafileHelper::VPackOffset(type));
|
||||
|
||||
TRI_voc_tick_t const databaseId =
|
||||
MMFilesDatafileHelper::DatabaseId(marker);
|
||||
VPackSlice const payloadSlice(reinterpret_cast<char const*>(marker) +
|
||||
MMFilesDatafileHelper::VPackOffset(type));
|
||||
|
||||
if (!payloadSlice.isObject()) {
|
||||
LOG(WARN) << "cannot create database: invalid marker";
|
||||
++state->errorCount;
|
||||
return state->canContinue();
|
||||
}
|
||||
|
||||
LOG(TRACE) << "found create database marker. databaseId: " << databaseId;
|
||||
|
||||
|
||||
LOG(TRACE) << "found create database marker. databaseId: "
|
||||
<< databaseId;
|
||||
|
||||
// remove the drop marker
|
||||
state->droppedDatabases.erase(databaseId);
|
||||
TRI_vocbase_t* vocbase = state->releaseDatabase(databaseId);
|
||||
|
@ -852,7 +954,8 @@ bool MMFilesWalRecoverState::ReplayMarker(TRI_df_marker_t const* marker, void* d
|
|||
VPackSlice const nameSlice = payloadSlice.get("name");
|
||||
|
||||
if (!nameSlice.isString()) {
|
||||
LOG(WARN) << "cannot unpack database properties for database " << databaseId;
|
||||
LOG(WARN) << "cannot unpack database properties for database "
|
||||
<< databaseId;
|
||||
++state->errorCount;
|
||||
return state->canContinue();
|
||||
}
|
||||
|
@ -869,18 +972,20 @@ bool MMFilesWalRecoverState::ReplayMarker(TRI_df_marker_t const* marker, void* d
|
|||
// TODO: how to signal a dropDatabase failure here?
|
||||
state->databaseFeature->dropDatabase(nameString, false, true, false);
|
||||
}
|
||||
|
||||
|
||||
RocksDBFeature::dropDatabase(databaseId);
|
||||
|
||||
vocbase = nullptr;
|
||||
/* TODO: check what TRI_ERROR_ARANGO_DATABASE_NOT_FOUND means here
|
||||
/* TODO: check what TRI_ERROR_ARANGO_DATABASE_NOT_FOUND means here
|
||||
WaitForDeletion(state->server, databaseId,
|
||||
TRI_ERROR_ARANGO_DATABASE_NOT_FOUND);
|
||||
*/
|
||||
int res = state->databaseFeature->createDatabase(databaseId, nameString, false, vocbase);
|
||||
int res = state->databaseFeature->createDatabase(databaseId, nameString,
|
||||
false, vocbase);
|
||||
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
LOG(WARN) << "cannot create database " << databaseId << ": " << TRI_errno_string(res);
|
||||
LOG(WARN) << "cannot create database " << databaseId << ": "
|
||||
<< TRI_errno_string(res);
|
||||
++state->errorCount;
|
||||
return state->canContinue();
|
||||
}
|
||||
|
@ -889,19 +994,24 @@ bool MMFilesWalRecoverState::ReplayMarker(TRI_df_marker_t const* marker, void* d
|
|||
}
|
||||
|
||||
case TRI_DF_MARKER_VPACK_DROP_INDEX: {
|
||||
TRI_voc_tick_t const databaseId = MMFilesDatafileHelper::DatabaseId(marker);
|
||||
TRI_voc_cid_t const collectionId = MMFilesDatafileHelper::CollectionId(marker);
|
||||
VPackSlice const payloadSlice(reinterpret_cast<char const*>(marker) + MMFilesDatafileHelper::VPackOffset(type));
|
||||
|
||||
TRI_voc_tick_t const databaseId =
|
||||
MMFilesDatafileHelper::DatabaseId(marker);
|
||||
TRI_voc_cid_t const collectionId =
|
||||
MMFilesDatafileHelper::CollectionId(marker);
|
||||
VPackSlice const payloadSlice(reinterpret_cast<char const*>(marker) +
|
||||
MMFilesDatafileHelper::VPackOffset(type));
|
||||
|
||||
if (!payloadSlice.isObject()) {
|
||||
LOG(WARN) << "cannot drop index for collection: invalid marker";
|
||||
++state->errorCount;
|
||||
return state->canContinue();
|
||||
}
|
||||
|
||||
|
||||
TRI_idx_iid_t indexId = NumericValue<TRI_idx_iid_t>(payloadSlice, "id");
|
||||
|
||||
LOG(TRACE) << "found drop index marker. databaseId: " << databaseId << ", collectionId: " << collectionId << ", indexId: " << indexId;
|
||||
|
||||
LOG(TRACE) << "found drop index marker. databaseId: " << databaseId
|
||||
<< ", collectionId: " << collectionId
|
||||
<< ", indexId: " << indexId;
|
||||
|
||||
if (state->isDropped(databaseId, collectionId)) {
|
||||
return true;
|
||||
|
@ -915,7 +1025,8 @@ bool MMFilesWalRecoverState::ReplayMarker(TRI_df_marker_t const* marker, void* d
|
|||
return true;
|
||||
}
|
||||
|
||||
arangodb::LogicalCollection* col = vocbase->lookupCollection(collectionId);
|
||||
arangodb::LogicalCollection* col =
|
||||
vocbase->lookupCollection(collectionId);
|
||||
|
||||
if (col == nullptr) {
|
||||
// if the underlying collection gone, we can go on
|
||||
|
@ -928,21 +1039,26 @@ bool MMFilesWalRecoverState::ReplayMarker(TRI_df_marker_t const* marker, void* d
|
|||
RocksDBFeature::dropIndex(databaseId, collectionId, indexId);
|
||||
|
||||
// additionally remove the index file
|
||||
std::string const indexName("index-" + std::to_string(indexId) + ".json");
|
||||
std::string const filename(arangodb::basics::FileUtils::buildFilename(col->path(), indexName));
|
||||
std::string const indexName("index-" + std::to_string(indexId) +
|
||||
".json");
|
||||
std::string const filename(
|
||||
arangodb::basics::FileUtils::buildFilename(col->path(), indexName));
|
||||
|
||||
TRI_UnlinkFile(filename.c_str());
|
||||
break;
|
||||
}
|
||||
|
||||
case TRI_DF_MARKER_VPACK_DROP_COLLECTION: {
|
||||
TRI_voc_tick_t const databaseId = MMFilesDatafileHelper::DatabaseId(marker);
|
||||
TRI_voc_cid_t const collectionId = MMFilesDatafileHelper::CollectionId(marker);
|
||||
|
||||
TRI_voc_tick_t const databaseId =
|
||||
MMFilesDatafileHelper::DatabaseId(marker);
|
||||
TRI_voc_cid_t const collectionId =
|
||||
MMFilesDatafileHelper::CollectionId(marker);
|
||||
|
||||
// insert the drop marker
|
||||
state->droppedCollections.emplace(collectionId);
|
||||
|
||||
LOG(TRACE) << "found drop collection marker. databaseId: " << databaseId << ", collectionId: " << collectionId;
|
||||
|
||||
LOG(TRACE) << "found drop collection marker. databaseId: " << databaseId
|
||||
<< ", collectionId: " << collectionId;
|
||||
|
||||
TRI_vocbase_t* vocbase = state->useDatabase(databaseId);
|
||||
|
||||
|
@ -952,7 +1068,8 @@ bool MMFilesWalRecoverState::ReplayMarker(TRI_df_marker_t const* marker, void* d
|
|||
}
|
||||
|
||||
// ignore any potential error returned by this call
|
||||
arangodb::LogicalCollection* collection = state->releaseCollection(collectionId);
|
||||
arangodb::LogicalCollection* collection =
|
||||
state->releaseCollection(collectionId);
|
||||
|
||||
if (collection == nullptr) {
|
||||
collection = vocbase->lookupCollection(collectionId);
|
||||
|
@ -966,11 +1083,12 @@ bool MMFilesWalRecoverState::ReplayMarker(TRI_df_marker_t const* marker, void* d
|
|||
}
|
||||
|
||||
case TRI_DF_MARKER_VPACK_DROP_DATABASE: {
|
||||
TRI_voc_tick_t const databaseId = MMFilesDatafileHelper::DatabaseId(marker);
|
||||
TRI_voc_tick_t const databaseId =
|
||||
MMFilesDatafileHelper::DatabaseId(marker);
|
||||
|
||||
// insert the drop marker
|
||||
state->droppedDatabases.emplace(databaseId);
|
||||
|
||||
|
||||
LOG(TRACE) << "found drop database marker. databaseId: " << databaseId;
|
||||
|
||||
TRI_vocbase_t* vocbase = state->releaseDatabase(databaseId);
|
||||
|
@ -983,9 +1101,9 @@ bool MMFilesWalRecoverState::ReplayMarker(TRI_df_marker_t const* marker, void* d
|
|||
RocksDBFeature::dropDatabase(databaseId);
|
||||
break;
|
||||
}
|
||||
|
||||
case TRI_DF_MARKER_HEADER:
|
||||
case TRI_DF_MARKER_COL_HEADER:
|
||||
|
||||
case TRI_DF_MARKER_HEADER:
|
||||
case TRI_DF_MARKER_COL_HEADER:
|
||||
case TRI_DF_MARKER_FOOTER: {
|
||||
// new datafile or end of datafile. forget state!
|
||||
state->resetCollection();
|
||||
|
@ -1015,7 +1133,8 @@ int MMFilesWalRecoverState::replayLogfile(wal::Logfile* logfile, int number) {
|
|||
|
||||
int const n = static_cast<int>(logfilesToProcess.size());
|
||||
|
||||
LOG(INFO) << "replaying WAL logfile '" << logfileName << "' (" << (number + 1) << " of " << n << ")";
|
||||
LOG(INFO) << "replaying WAL logfile '" << logfileName << "' (" << (number + 1)
|
||||
<< " of " << n << ")";
|
||||
|
||||
MMFilesDatafile* df = logfile->df();
|
||||
|
||||
|
@ -1023,8 +1142,10 @@ int MMFilesWalRecoverState::replayLogfile(wal::Logfile* logfile, int number) {
|
|||
df->sequentialAccess();
|
||||
df->willNeed();
|
||||
|
||||
if (!TRI_IterateDatafile(df, &MMFilesWalRecoverState::ReplayMarker, static_cast<void*>(this))) {
|
||||
LOG(WARN) << "WAL inspection failed when scanning logfile '" << logfileName << "'";
|
||||
if (!TRI_IterateDatafile(df, &MMFilesWalRecoverState::ReplayMarker,
|
||||
static_cast<void*>(this))) {
|
||||
LOG(WARN) << "WAL inspection failed when scanning logfile '" << logfileName
|
||||
<< "'";
|
||||
return TRI_ERROR_ARANGO_RECOVERY;
|
||||
}
|
||||
|
||||
|
@ -1061,7 +1182,7 @@ int MMFilesWalRecoverState::abortOpenTransactions() {
|
|||
|
||||
LOG(TRACE) << "writing abort markers for still open transactions";
|
||||
int res = TRI_ERROR_NO_ERROR;
|
||||
|
||||
|
||||
VPackBuilder builder;
|
||||
|
||||
try {
|
||||
|
@ -1076,10 +1197,12 @@ int MMFilesWalRecoverState::abortOpenTransactions() {
|
|||
}
|
||||
|
||||
TRI_voc_tick_t databaseId = (*it).second.first;
|
||||
|
||||
MMFilesTransactionMarker marker(TRI_DF_MARKER_VPACK_ABORT_TRANSACTION, databaseId, transactionId);
|
||||
|
||||
MMFilesTransactionMarker marker(TRI_DF_MARKER_VPACK_ABORT_TRANSACTION,
|
||||
databaseId, transactionId);
|
||||
MMFilesWalSlotInfoCopy slotInfo =
|
||||
arangodb::wal::LogfileManager::instance()->allocateAndWrite(marker, false);
|
||||
arangodb::wal::LogfileManager::instance()->allocateAndWrite(marker,
|
||||
false);
|
||||
|
||||
if (slotInfo.errorCode != TRI_ERROR_NO_ERROR) {
|
||||
THROW_ARANGO_EXCEPTION(slotInfo.errorCode);
|
||||
|
@ -1132,7 +1255,7 @@ int MMFilesWalRecoverState::fillIndexes() {
|
|||
arangodb::StandaloneTransactionContext::Create(collection->vocbase()),
|
||||
collection->cid(), TRI_TRANSACTION_WRITE);
|
||||
|
||||
int res = collection->fillIndexes(&trx);
|
||||
int res = collection->fillIndexes(&trx, *(collection->indexList()));
|
||||
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
return res;
|
||||
|
|
|
@ -1,72 +0,0 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// DISCLAIMER
|
||||
///
|
||||
/// Copyright 2016 ArangoDB 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 "IndexThreadFeature.h"
|
||||
|
||||
#include "ApplicationFeatures/ApplicationServer.h"
|
||||
#include "Basics/ThreadPool.h"
|
||||
#include "ProgramOptions/ProgramOptions.h"
|
||||
|
||||
using namespace arangodb;
|
||||
using namespace arangodb::application_features;
|
||||
using namespace arangodb::basics;
|
||||
using namespace arangodb::options;
|
||||
|
||||
IndexThreadFeature::IndexThreadFeature(ApplicationServer* server)
|
||||
: ApplicationFeature(server, "IndexThread"),
|
||||
_indexThreads(2) {
|
||||
setOptional(false);
|
||||
requiresElevatedPrivileges(false);
|
||||
startsAfter("DatabasePath");
|
||||
startsAfter("EngineSelector");
|
||||
}
|
||||
|
||||
void IndexThreadFeature::collectOptions(std::shared_ptr<ProgramOptions> options) {
|
||||
options->addSection("database", "Configure the database");
|
||||
|
||||
options->addHiddenOption(
|
||||
"--database.index-threads",
|
||||
"threads to start for parallel background index creation",
|
||||
new UInt64Parameter(&_indexThreads));
|
||||
}
|
||||
|
||||
void IndexThreadFeature::validateOptions(std::shared_ptr<ProgramOptions> options) {
|
||||
// some arbitrary limit
|
||||
if (_indexThreads > 128) {
|
||||
_indexThreads = 128;
|
||||
}
|
||||
}
|
||||
|
||||
void IndexThreadFeature::start() {
|
||||
// create the index thread pool
|
||||
if (_indexThreads > 0) {
|
||||
_indexPool.reset(new ThreadPool(static_cast<size_t>(_indexThreads), "IndexBuilder"));
|
||||
}
|
||||
LOG(TRACE) << "starting " << _indexThreads << " index thread(s)";
|
||||
}
|
||||
|
||||
void IndexThreadFeature::unprepare() {
|
||||
LOG(TRACE) << "stopping index thread(s)";
|
||||
// turn off index threads
|
||||
_indexPool.reset();
|
||||
}
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// DISCLAIMER
|
||||
///
|
||||
/// Copyright 2016 ArangoDB 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
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef APPLICATION_FEATURES_INDEX_THREAD_FEATURE_H
|
||||
#define APPLICATION_FEATURES_INDEX_THREAD_FEATURE_H 1
|
||||
|
||||
#include "ApplicationFeatures/ApplicationFeature.h"
|
||||
|
||||
namespace arangodb {
|
||||
namespace basics {
|
||||
class ThreadPool;
|
||||
}
|
||||
|
||||
class IndexThreadFeature final : public application_features::ApplicationFeature {
|
||||
public:
|
||||
explicit IndexThreadFeature(application_features::ApplicationServer* server);
|
||||
|
||||
public:
|
||||
void collectOptions(std::shared_ptr<options::ProgramOptions>) override final;
|
||||
void validateOptions(std::shared_ptr<options::ProgramOptions>) override final;
|
||||
void start() override final;
|
||||
void unprepare() override final;
|
||||
|
||||
basics::ThreadPool* getThreadPool() const { return _indexPool.get(); }
|
||||
|
||||
private:
|
||||
uint64_t _indexThreads;
|
||||
std::unique_ptr<basics::ThreadPool> _indexPool;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -19,16 +19,17 @@
|
|||
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
||||
///
|
||||
/// @author Michael Hackstein
|
||||
/// @author Daniel H. Larkin
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "LogicalCollection.h"
|
||||
|
||||
#include "Aql/QueryCache.h"
|
||||
#include "Basics/Barrier.h"
|
||||
#include "Basics/LocalTaskQueue.h"
|
||||
#include "Basics/ReadLocker.h"
|
||||
#include "Basics/StaticStrings.h"
|
||||
#include "Basics/StringUtils.h"
|
||||
#include "Basics/ThreadPool.h"
|
||||
#include "Basics/Timers.h"
|
||||
#include "Basics/VelocyPackHelper.h"
|
||||
#include "Basics/WriteLocker.h"
|
||||
|
@ -46,6 +47,8 @@
|
|||
#include "Indexes/SkiplistIndex.h"
|
||||
#include "RestServer/DatabaseFeature.h"
|
||||
#include "RestServer/RevisionCacheFeature.h"
|
||||
#include "Scheduler/Scheduler.h"
|
||||
#include "Scheduler/SchedulerFeature.h"
|
||||
#include "StorageEngine/EngineSelectorFeature.h"
|
||||
#include "StorageEngine/MMFilesDocumentOperation.h"
|
||||
#include "StorageEngine/MMFilesWalMarker.h"
|
||||
|
@ -59,7 +62,6 @@
|
|||
#include "Utils/StandaloneTransactionContext.h"
|
||||
#include "VocBase/CollectionRevisionsCache.h"
|
||||
#include "VocBase/DatafileStatisticsContainer.h"
|
||||
#include "VocBase/IndexThreadFeature.h"
|
||||
#include "VocBase/KeyGenerator.h"
|
||||
#include "VocBase/ManagedDocumentResult.h"
|
||||
#include "VocBase/PhysicalCollection.h"
|
||||
|
@ -79,31 +81,31 @@ int TRI_AddOperationTransaction(TRI_transaction_t*, TRI_voc_rid_t,
|
|||
MMFilesDocumentOperation&,
|
||||
MMFilesWalMarker const* marker, bool&);
|
||||
|
||||
/// @brief helper struct for filling indexes
|
||||
class IndexFiller {
|
||||
/// @brief helper class for filling indexes
|
||||
class IndexFillerTask : public arangodb::basics::LocalTask {
|
||||
public:
|
||||
IndexFiller(arangodb::Transaction* trx,
|
||||
arangodb::LogicalCollection* collection, arangodb::Index* idx,
|
||||
std::function<void(int)> callback)
|
||||
: _trx(trx), _collection(collection), _idx(idx), _callback(callback) {}
|
||||
IndexFillerTask(
|
||||
arangodb::basics::LocalTaskQueue* queue, arangodb::Transaction* trx,
|
||||
arangodb::Index* idx,
|
||||
std::vector<std::pair<TRI_voc_rid_t, VPackSlice>> const& documents)
|
||||
: LocalTask(queue), _trx(trx), _idx(idx), _documents(documents) {}
|
||||
|
||||
void operator()() {
|
||||
int res = TRI_ERROR_INTERNAL;
|
||||
void run() {
|
||||
TRI_ASSERT(_idx->type() != Index::IndexType::TRI_IDX_TYPE_PRIMARY_INDEX);
|
||||
|
||||
try {
|
||||
res = _collection->fillIndex(_trx, _idx);
|
||||
} catch (...) {
|
||||
_idx->batchInsert(_trx, _documents, _queue);
|
||||
} catch (std::exception& e) {
|
||||
_queue->setStatus(TRI_ERROR_INTERNAL);
|
||||
}
|
||||
|
||||
_callback(res);
|
||||
_queue->join();
|
||||
}
|
||||
|
||||
private:
|
||||
arangodb::Transaction* _trx;
|
||||
arangodb::LogicalCollection* _collection;
|
||||
arangodb::Index* _idx;
|
||||
std::function<void(int)> _callback;
|
||||
std::vector<std::pair<TRI_voc_rid_t, VPackSlice>> const& _documents;
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
@ -1106,7 +1108,7 @@ void LogicalCollection::drop() {
|
|||
if (_revisionsCache != nullptr) {
|
||||
_revisionsCache->clear();
|
||||
}
|
||||
|
||||
|
||||
// make sure collection has been closed
|
||||
this->close();
|
||||
|
||||
|
@ -1386,8 +1388,9 @@ void LogicalCollection::open(bool ignoreErrors) {
|
|||
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
THROW_ARANGO_EXCEPTION_MESSAGE(
|
||||
res, std::string("cannot open document collection from path '") +
|
||||
path() + "': " + TRI_errno_string(res));
|
||||
res,
|
||||
std::string("cannot open document collection from path '") + path() +
|
||||
"': " + TRI_errno_string(res));
|
||||
}
|
||||
|
||||
arangodb::SingleCollectionTransaction trx(
|
||||
|
@ -1414,8 +1417,9 @@ void LogicalCollection::open(bool ignoreErrors) {
|
|||
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
THROW_ARANGO_EXCEPTION_MESSAGE(
|
||||
res, std::string("cannot iterate data of document collection: ") +
|
||||
TRI_errno_string(res));
|
||||
res,
|
||||
std::string("cannot iterate data of document collection: ") +
|
||||
TRI_errno_string(res));
|
||||
}
|
||||
|
||||
_isInitialIteration = false;
|
||||
|
@ -1452,7 +1456,7 @@ void LogicalCollection::open(bool ignoreErrors) {
|
|||
|
||||
if (!arangodb::wal::LogfileManager::instance()->isInRecovery()) {
|
||||
// build the index structures, and fill the indexes
|
||||
fillIndexes(&trx);
|
||||
fillIndexes(&trx, *(indexList()));
|
||||
}
|
||||
|
||||
_revisionsCache->allowInvalidation(true);
|
||||
|
@ -1584,9 +1588,12 @@ std::shared_ptr<Index> LogicalCollection::createIndex(Transaction* trx,
|
|||
}
|
||||
|
||||
TRI_ASSERT(idx.get()->type() != Index::IndexType::TRI_IDX_TYPE_PRIMARY_INDEX);
|
||||
int res = fillIndex(trx, idx.get(), false);
|
||||
std::vector<std::shared_ptr<arangodb::Index>> indexListLocal;
|
||||
indexListLocal.emplace_back(idx);
|
||||
int res = fillIndexes(trx, indexListLocal, false);
|
||||
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
usleep(1000000);
|
||||
THROW_ARANGO_EXCEPTION(res);
|
||||
}
|
||||
|
||||
|
@ -1605,7 +1612,8 @@ std::shared_ptr<Index> LogicalCollection::createIndex(Transaction* trx,
|
|||
VPackBuilder builder;
|
||||
bool const doSync =
|
||||
application_features::ApplicationServer::getFeature<DatabaseFeature>(
|
||||
"Database")->forceSyncProperties();
|
||||
"Database")
|
||||
->forceSyncProperties();
|
||||
toVelocyPack(builder, false);
|
||||
update(builder.slice(), doSync);
|
||||
}
|
||||
|
@ -1638,7 +1646,9 @@ int LogicalCollection::restoreIndex(Transaction* trx, VPackSlice const& info,
|
|||
|
||||
TRI_ASSERT(newIdx.get()->type() !=
|
||||
Index::IndexType::TRI_IDX_TYPE_PRIMARY_INDEX);
|
||||
int res = fillIndex(trx, newIdx.get());
|
||||
std::vector<std::shared_ptr<arangodb::Index>> indexListLocal;
|
||||
indexListLocal.emplace_back(newIdx);
|
||||
int res = fillIndexes(trx, indexListLocal, false);
|
||||
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
return res;
|
||||
|
@ -1675,8 +1685,7 @@ int LogicalCollection::saveIndex(arangodb::Index* idx, bool writeMarker) {
|
|||
|
||||
try {
|
||||
MMFilesCollectionMarker marker(TRI_DF_MARKER_VPACK_CREATE_INDEX,
|
||||
_vocbase->id(), cid(),
|
||||
builder->slice());
|
||||
_vocbase->id(), cid(), builder->slice());
|
||||
|
||||
MMFilesWalSlotInfoCopy slotInfo =
|
||||
arangodb::wal::LogfileManager::instance()->allocateAndWrite(marker,
|
||||
|
@ -1754,7 +1763,8 @@ bool LogicalCollection::dropIndex(TRI_idx_iid_t iid, bool writeMarker) {
|
|||
VPackBuilder builder;
|
||||
bool const doSync =
|
||||
application_features::ApplicationServer::getFeature<DatabaseFeature>(
|
||||
"Database")->forceSyncProperties();
|
||||
"Database")
|
||||
->forceSyncProperties();
|
||||
toVelocyPack(builder, false);
|
||||
update(builder.slice(), doSync);
|
||||
}
|
||||
|
@ -1769,8 +1779,8 @@ bool LogicalCollection::dropIndex(TRI_idx_iid_t iid, bool writeMarker) {
|
|||
markerBuilder.close();
|
||||
|
||||
MMFilesCollectionMarker marker(TRI_DF_MARKER_VPACK_DROP_INDEX,
|
||||
_vocbase->id(), cid(),
|
||||
markerBuilder.slice());
|
||||
_vocbase->id(), cid(),
|
||||
markerBuilder.slice());
|
||||
|
||||
MMFilesWalSlotInfoCopy slotInfo =
|
||||
arangodb::wal::LogfileManager::instance()->allocateAndWrite(marker,
|
||||
|
@ -1855,17 +1865,47 @@ int LogicalCollection::detectIndexes(arangodb::Transaction* trx) {
|
|||
return TRI_ERROR_NO_ERROR;
|
||||
}
|
||||
|
||||
int LogicalCollection::fillIndexes(arangodb::Transaction* trx) {
|
||||
std::vector<std::shared_ptr<arangodb::Index>> const*
|
||||
LogicalCollection::indexList() const {
|
||||
return &_indexes;
|
||||
}
|
||||
|
||||
int LogicalCollection::fillIndexes(
|
||||
arangodb::Transaction* trx,
|
||||
std::vector<std::shared_ptr<arangodb::Index>> const& indexes,
|
||||
bool skipPersistent) {
|
||||
// distribute the work to index threads plus this thread
|
||||
TRI_ASSERT(!ServerState::instance()->isCoordinator());
|
||||
size_t const n = _indexes.size();
|
||||
size_t const n = indexes.size();
|
||||
|
||||
if (n == 1) {
|
||||
if (n == 0 || (n == 1 &&
|
||||
indexes[0].get()->type() ==
|
||||
Index::IndexType::TRI_IDX_TYPE_PRIMARY_INDEX)) {
|
||||
return TRI_ERROR_NO_ERROR;
|
||||
}
|
||||
|
||||
bool rolledBack = false;
|
||||
auto rollbackAll = [&]() -> void {
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
auto idx = indexes[i].get();
|
||||
if (idx->type() == Index::IndexType::TRI_IDX_TYPE_PRIMARY_INDEX) {
|
||||
continue;
|
||||
}
|
||||
if (idx->isPersistent()) {
|
||||
continue;
|
||||
}
|
||||
idx->unload(); // TODO: check is this safe? truncate not necessarily
|
||||
// feasible
|
||||
}
|
||||
};
|
||||
|
||||
double start = TRI_microtime();
|
||||
|
||||
TRI_ASSERT(SchedulerFeature::SCHEDULER != nullptr);
|
||||
auto ioService = SchedulerFeature::SCHEDULER->ioService();
|
||||
TRI_ASSERT(ioService != nullptr);
|
||||
arangodb::basics::LocalTaskQueue queue(ioService);
|
||||
|
||||
// only log performance infos for indexes with more than this number of
|
||||
// entries
|
||||
static size_t const NotificationSizeThreshold = 131072;
|
||||
|
@ -1877,73 +1917,107 @@ int LogicalCollection::fillIndexes(arangodb::Transaction* trx) {
|
|||
<< "/" << name() << " }, indexes: " << (n - 1);
|
||||
}
|
||||
|
||||
TRI_ASSERT(n > 1);
|
||||
TRI_ASSERT(n > 0);
|
||||
|
||||
std::atomic<int> result(TRI_ERROR_NO_ERROR);
|
||||
try {
|
||||
TRI_ASSERT(!ServerState::instance()->isCoordinator());
|
||||
|
||||
{
|
||||
arangodb::basics::Barrier barrier(n - 1);
|
||||
// give the index a size hint
|
||||
auto nrUsed = primaryIndex->size();
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
auto idx = indexes[i];
|
||||
if (idx->type() == Index::IndexType::TRI_IDX_TYPE_PRIMARY_INDEX) {
|
||||
continue;
|
||||
}
|
||||
idx.get()->sizeHint(trx, nrUsed);
|
||||
}
|
||||
|
||||
auto indexPool =
|
||||
application_features::ApplicationServer::getFeature<IndexThreadFeature>(
|
||||
"IndexThread")
|
||||
->getThreadPool();
|
||||
// process documents a million at a time
|
||||
size_t blockSize = 1024 * 1024 * 1;
|
||||
|
||||
auto callback = [&barrier, &result](int res) -> void {
|
||||
// update the error code
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
int expected = TRI_ERROR_NO_ERROR;
|
||||
result.compare_exchange_strong(expected, res,
|
||||
std::memory_order_acquire);
|
||||
if (nrUsed < blockSize) {
|
||||
blockSize = nrUsed;
|
||||
}
|
||||
if (blockSize == 0) {
|
||||
blockSize = 1;
|
||||
}
|
||||
|
||||
ManagedDocumentResult mmdr(trx);
|
||||
|
||||
std::vector<std::pair<TRI_voc_rid_t, VPackSlice>> documents;
|
||||
documents.reserve(blockSize);
|
||||
|
||||
auto insertInAllIndexes = [&]() -> void {
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
auto idx = indexes[i];
|
||||
if (idx->type() == Index::IndexType::TRI_IDX_TYPE_PRIMARY_INDEX) {
|
||||
continue;
|
||||
}
|
||||
fillIndex(&queue, trx, idx.get(), documents, skipPersistent);
|
||||
}
|
||||
|
||||
barrier.join();
|
||||
queue.dispatchAndWait();
|
||||
|
||||
if (queue.status() != TRI_ERROR_NO_ERROR) {
|
||||
rollbackAll();
|
||||
rolledBack = true;
|
||||
}
|
||||
};
|
||||
|
||||
// now actually fill the secondary indexes
|
||||
for (size_t i = 1; i < n; ++i) {
|
||||
auto idx = _indexes[i];
|
||||
TRI_ASSERT(idx->type() != Index::IndexType::TRI_IDX_TYPE_PRIMARY_INDEX);
|
||||
if (nrUsed > 0) {
|
||||
arangodb::basics::BucketPosition position;
|
||||
uint64_t total = 0;
|
||||
|
||||
// index threads must come first, otherwise this thread will block the
|
||||
// loop and
|
||||
// prevent distribution to threads
|
||||
if (indexPool != nullptr && i != (n - 1)) {
|
||||
try {
|
||||
// move task into thread pool
|
||||
IndexFiller indexTask(trx, this, idx.get(), callback);
|
||||
while (true) {
|
||||
SimpleIndexElement element =
|
||||
primaryIndex->lookupSequential(trx, position, total);
|
||||
|
||||
static_cast<arangodb::basics::ThreadPool*>(indexPool)->enqueue(
|
||||
indexTask);
|
||||
} catch (...) {
|
||||
// set error code
|
||||
int expected = TRI_ERROR_NO_ERROR;
|
||||
result.compare_exchange_strong(expected, TRI_ERROR_INTERNAL,
|
||||
std::memory_order_acquire);
|
||||
|
||||
barrier.join();
|
||||
}
|
||||
} else {
|
||||
// fill index in this thread
|
||||
int res;
|
||||
|
||||
try {
|
||||
res = fillIndex(trx, idx.get());
|
||||
} catch (...) {
|
||||
res = TRI_ERROR_INTERNAL;
|
||||
if (!element) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
int expected = TRI_ERROR_NO_ERROR;
|
||||
result.compare_exchange_strong(expected, res,
|
||||
std::memory_order_acquire);
|
||||
}
|
||||
TRI_voc_rid_t revisionId = element.revisionId();
|
||||
|
||||
barrier.join();
|
||||
if (readRevision(trx, mmdr, revisionId)) {
|
||||
uint8_t const* vpack = mmdr.vpack();
|
||||
TRI_ASSERT(vpack != nullptr);
|
||||
documents.emplace_back(std::make_pair(revisionId, VPackSlice(vpack)));
|
||||
|
||||
if (documents.size() == blockSize) {
|
||||
// now actually fill the secondary indexes
|
||||
insertInAllIndexes();
|
||||
if (queue.status() != TRI_ERROR_NO_ERROR) {
|
||||
break;
|
||||
};
|
||||
documents.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// barrier waits here until all threads have joined
|
||||
// process the remainder of the documents
|
||||
if (queue.status() == TRI_ERROR_NO_ERROR && !documents.empty()) {
|
||||
insertInAllIndexes();
|
||||
}
|
||||
|
||||
// TODO: fix perf logging?
|
||||
} catch (arangodb::basics::Exception const& ex) {
|
||||
queue.setStatus(ex.code());
|
||||
} catch (std::bad_alloc const&) {
|
||||
queue.setStatus(TRI_ERROR_OUT_OF_MEMORY);
|
||||
} catch (std::exception const& ex) {
|
||||
LOG(WARN) << "caught exception while filling indexes: " << ex.what();
|
||||
queue.setStatus(TRI_ERROR_INTERNAL);
|
||||
} catch (...) {
|
||||
LOG(WARN) << "caught unknown exception while filling indexes";
|
||||
queue.setStatus(TRI_ERROR_INTERNAL);
|
||||
}
|
||||
|
||||
if (queue.status() != TRI_ERROR_NO_ERROR && !rolledBack) {
|
||||
try {
|
||||
rollbackAll();
|
||||
} catch (...) {
|
||||
}
|
||||
}
|
||||
|
||||
LOG_TOPIC(TRACE, Logger::PERFORMANCE)
|
||||
|
@ -1951,7 +2025,7 @@ int LogicalCollection::fillIndexes(arangodb::Transaction* trx) {
|
|||
<< " s, fill-indexes-document-collection { collection: "
|
||||
<< _vocbase->name() << "/" << name() << " }, indexes: " << (n - 1);
|
||||
|
||||
return result.load();
|
||||
return queue.status();
|
||||
}
|
||||
|
||||
void LogicalCollection::addIndex(std::shared_ptr<arangodb::Index> idx) {
|
||||
|
@ -1959,7 +2033,7 @@ void LogicalCollection::addIndex(std::shared_ptr<arangodb::Index> idx) {
|
|||
TRI_ASSERT(idx->type() != arangodb::Index::TRI_IDX_TYPE_PRIMARY_INDEX ||
|
||||
_indexes.empty());
|
||||
|
||||
auto const id = idx->id();
|
||||
auto const id = idx->id();
|
||||
for (auto const& it : _indexes) {
|
||||
if (it->id() == id) {
|
||||
// already have this particular index. do not add it again
|
||||
|
@ -1982,8 +2056,7 @@ void LogicalCollection::addIndex(std::shared_ptr<arangodb::Index> idx) {
|
|||
|
||||
void LogicalCollection::addIndexCoordinator(
|
||||
std::shared_ptr<arangodb::Index> idx, bool distribute) {
|
||||
|
||||
auto const id = idx->id();
|
||||
auto const id = idx->id();
|
||||
for (auto const& it : _indexes) {
|
||||
if (it->id() == id) {
|
||||
// already have this particular index. do not add it again
|
||||
|
@ -2126,9 +2199,9 @@ int LogicalCollection::insert(Transaction* trx, VPackSlice const slice,
|
|||
}
|
||||
|
||||
// create marker
|
||||
MMFilesCrudMarker insertMarker(
|
||||
TRI_DF_MARKER_VPACK_DOCUMENT,
|
||||
TRI_MarkerIdTransaction(trx->getInternals()), newSlice);
|
||||
MMFilesCrudMarker insertMarker(TRI_DF_MARKER_VPACK_DOCUMENT,
|
||||
TRI_MarkerIdTransaction(trx->getInternals()),
|
||||
newSlice);
|
||||
|
||||
MMFilesWalMarker const* marker;
|
||||
if (options.recoveryMarker == nullptr) {
|
||||
|
@ -2143,8 +2216,7 @@ int LogicalCollection::insert(Transaction* trx, VPackSlice const slice,
|
|||
return TRI_ERROR_DEBUG;
|
||||
}
|
||||
|
||||
MMFilesDocumentOperation operation(this,
|
||||
TRI_VOC_DOCUMENT_OPERATION_INSERT);
|
||||
MMFilesDocumentOperation operation(this, TRI_VOC_DOCUMENT_OPERATION_INSERT);
|
||||
|
||||
TRI_IF_FAILURE("InsertDocumentNoHeader") {
|
||||
// test what happens if no header can be acquired
|
||||
|
@ -2177,13 +2249,13 @@ int LogicalCollection::insert(Transaction* trx, VPackSlice const slice,
|
|||
bool const useDeadlockDetector =
|
||||
(lock && !trx->isSingleOperationTransaction());
|
||||
try {
|
||||
arangodb::CollectionWriteLocker collectionLocker(this, useDeadlockDetector,
|
||||
lock);
|
||||
arangodb::CollectionWriteLocker collectionLocker(
|
||||
this, useDeadlockDetector, lock);
|
||||
|
||||
try {
|
||||
// insert into indexes
|
||||
res = insertDocument(trx, revisionId, doc, operation, marker,
|
||||
options.waitForSync);
|
||||
options.waitForSync);
|
||||
} catch (basics::Exception const& ex) {
|
||||
res = ex.code();
|
||||
} catch (std::bad_alloc const&) {
|
||||
|
@ -2194,7 +2266,7 @@ int LogicalCollection::insert(Transaction* trx, VPackSlice const slice,
|
|||
} catch (...) {
|
||||
// the collectionLocker may have thrown in its constructor...
|
||||
// if it did, then we need to manually remove the revision id
|
||||
// from the list of revisions
|
||||
// from the list of revisions
|
||||
try {
|
||||
removeRevision(revisionId, false);
|
||||
} catch (...) {
|
||||
|
@ -2212,7 +2284,7 @@ int LogicalCollection::insert(Transaction* trx, VPackSlice const slice,
|
|||
|
||||
// store the tick that was used for writing the document
|
||||
resultMarkerTick = operation.tick();
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
@ -2323,9 +2395,9 @@ int LogicalCollection::update(Transaction* trx, VPackSlice const newSlice,
|
|||
}
|
||||
|
||||
// create marker
|
||||
MMFilesCrudMarker updateMarker(
|
||||
TRI_DF_MARKER_VPACK_DOCUMENT,
|
||||
TRI_MarkerIdTransaction(trx->getInternals()), builder->slice());
|
||||
MMFilesCrudMarker updateMarker(TRI_DF_MARKER_VPACK_DOCUMENT,
|
||||
TRI_MarkerIdTransaction(trx->getInternals()),
|
||||
builder->slice());
|
||||
|
||||
MMFilesWalMarker const* marker;
|
||||
if (options.recoveryMarker == nullptr) {
|
||||
|
@ -2336,8 +2408,7 @@ int LogicalCollection::update(Transaction* trx, VPackSlice const newSlice,
|
|||
|
||||
VPackSlice const newDoc(marker->vpack());
|
||||
|
||||
MMFilesDocumentOperation operation(this,
|
||||
TRI_VOC_DOCUMENT_OPERATION_UPDATE);
|
||||
MMFilesDocumentOperation operation(this, TRI_VOC_DOCUMENT_OPERATION_UPDATE);
|
||||
|
||||
try {
|
||||
insertRevision(revisionId, marker->vpack(), 0, true);
|
||||
|
@ -2486,9 +2557,9 @@ int LogicalCollection::replace(Transaction* trx, VPackSlice const newSlice,
|
|||
}
|
||||
|
||||
// create marker
|
||||
MMFilesCrudMarker replaceMarker(
|
||||
TRI_DF_MARKER_VPACK_DOCUMENT,
|
||||
TRI_MarkerIdTransaction(trx->getInternals()), builder->slice());
|
||||
MMFilesCrudMarker replaceMarker(TRI_DF_MARKER_VPACK_DOCUMENT,
|
||||
TRI_MarkerIdTransaction(trx->getInternals()),
|
||||
builder->slice());
|
||||
|
||||
MMFilesWalMarker const* marker;
|
||||
if (options.recoveryMarker == nullptr) {
|
||||
|
@ -2499,8 +2570,7 @@ int LogicalCollection::replace(Transaction* trx, VPackSlice const newSlice,
|
|||
|
||||
VPackSlice const newDoc(marker->vpack());
|
||||
|
||||
MMFilesDocumentOperation operation(
|
||||
this, TRI_VOC_DOCUMENT_OPERATION_REPLACE);
|
||||
MMFilesDocumentOperation operation(this, TRI_VOC_DOCUMENT_OPERATION_REPLACE);
|
||||
|
||||
try {
|
||||
insertRevision(revisionId, marker->vpack(), 0, true);
|
||||
|
@ -2585,9 +2655,9 @@ int LogicalCollection::remove(arangodb::Transaction* trx,
|
|||
}
|
||||
|
||||
// create marker
|
||||
MMFilesCrudMarker removeMarker(
|
||||
TRI_DF_MARKER_VPACK_REMOVE, TRI_MarkerIdTransaction(trx->getInternals()),
|
||||
builder->slice());
|
||||
MMFilesCrudMarker removeMarker(TRI_DF_MARKER_VPACK_REMOVE,
|
||||
TRI_MarkerIdTransaction(trx->getInternals()),
|
||||
builder->slice());
|
||||
|
||||
MMFilesWalMarker const* marker;
|
||||
if (options.recoveryMarker == nullptr) {
|
||||
|
@ -2609,8 +2679,7 @@ int LogicalCollection::remove(arangodb::Transaction* trx,
|
|||
}
|
||||
TRI_ASSERT(!key.isNone());
|
||||
|
||||
MMFilesDocumentOperation operation(this,
|
||||
TRI_VOC_DOCUMENT_OPERATION_REMOVE);
|
||||
MMFilesDocumentOperation operation(this, TRI_VOC_DOCUMENT_OPERATION_REMOVE);
|
||||
|
||||
bool const useDeadlockDetector =
|
||||
(lock && !trx->isSingleOperationTransaction());
|
||||
|
@ -2720,9 +2789,9 @@ int LogicalCollection::remove(arangodb::Transaction* trx,
|
|||
}
|
||||
|
||||
// create marker
|
||||
MMFilesCrudMarker removeMarker(
|
||||
TRI_DF_MARKER_VPACK_REMOVE, TRI_MarkerIdTransaction(trx->getInternals()),
|
||||
builder->slice());
|
||||
MMFilesCrudMarker removeMarker(TRI_DF_MARKER_VPACK_REMOVE,
|
||||
TRI_MarkerIdTransaction(trx->getInternals()),
|
||||
builder->slice());
|
||||
|
||||
MMFilesWalMarker const* marker = &removeMarker;
|
||||
|
||||
|
@ -2734,8 +2803,7 @@ int LogicalCollection::remove(arangodb::Transaction* trx,
|
|||
VPackSlice key = Transaction::extractKeyFromDocument(oldDoc);
|
||||
TRI_ASSERT(!key.isNone());
|
||||
|
||||
MMFilesDocumentOperation operation(this,
|
||||
TRI_VOC_DOCUMENT_OPERATION_REMOVE);
|
||||
MMFilesDocumentOperation operation(this, TRI_VOC_DOCUMENT_OPERATION_REMOVE);
|
||||
|
||||
bool const useDeadlockDetector =
|
||||
(lock && !trx->isSingleOperationTransaction());
|
||||
|
@ -2873,206 +2941,33 @@ void LogicalCollection::sizeHint(Transaction* trx, int64_t hint) {
|
|||
_revisionsCache->sizeHint(hint);
|
||||
}
|
||||
|
||||
/// @brief initializes an index with all existing documents
|
||||
int LogicalCollection::fillIndex(arangodb::Transaction* trx,
|
||||
arangodb::Index* idx, bool skipPersistent) {
|
||||
/// @brief initializes an index with a set of existing documents
|
||||
void LogicalCollection::fillIndex(
|
||||
arangodb::basics::LocalTaskQueue* queue, arangodb::Transaction* trx,
|
||||
arangodb::Index* idx,
|
||||
std::vector<std::pair<TRI_voc_rid_t, VPackSlice>> const& documents,
|
||||
bool skipPersistent) {
|
||||
TRI_ASSERT(idx->type() != Index::IndexType::TRI_IDX_TYPE_PRIMARY_INDEX);
|
||||
TRI_ASSERT(!ServerState::instance()->isCoordinator());
|
||||
if (!useSecondaryIndexes()) {
|
||||
return TRI_ERROR_NO_ERROR;
|
||||
return; // TRI_ERROR_NO_ERROR;
|
||||
}
|
||||
|
||||
if (idx->isPersistent() && skipPersistent) {
|
||||
return TRI_ERROR_NO_ERROR;
|
||||
return; // TRI_ERROR_NO_ERROR;
|
||||
}
|
||||
|
||||
try {
|
||||
size_t nrUsed = primaryIndex()->size();
|
||||
auto indexPool =
|
||||
application_features::ApplicationServer::getFeature<IndexThreadFeature>(
|
||||
"IndexThread")
|
||||
->getThreadPool();
|
||||
|
||||
int res;
|
||||
|
||||
if (indexPool != nullptr && idx->hasBatchInsert() && nrUsed > 256 * 1024 &&
|
||||
_indexBuckets > 1) {
|
||||
// use batch insert if there is an index pool,
|
||||
// the collection has more than one index bucket
|
||||
// and it contains a significant amount of documents
|
||||
res = fillIndexBatch(trx, idx);
|
||||
} else {
|
||||
res = fillIndexSequential(trx, idx);
|
||||
}
|
||||
|
||||
return res;
|
||||
} catch (arangodb::basics::Exception const& ex) {
|
||||
return ex.code();
|
||||
} catch (std::bad_alloc const&) {
|
||||
return TRI_ERROR_OUT_OF_MEMORY;
|
||||
} catch (std::exception const& ex) {
|
||||
LOG(WARN) << "caught exception while filling indexes: " << ex.what();
|
||||
return TRI_ERROR_INTERNAL;
|
||||
// move task into thread pool
|
||||
std::shared_ptr<IndexFillerTask> worker;
|
||||
worker.reset(new IndexFillerTask(queue, trx, idx, documents));
|
||||
queue->enqueue(worker);
|
||||
} catch (...) {
|
||||
LOG(WARN) << "caught unknown exception while filling indexes";
|
||||
return TRI_ERROR_INTERNAL;
|
||||
// set error code
|
||||
queue->setStatus(TRI_ERROR_INTERNAL);
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief fill an index in batches
|
||||
int LogicalCollection::fillIndexBatch(arangodb::Transaction* trx,
|
||||
arangodb::Index* idx) {
|
||||
TRI_ASSERT(!ServerState::instance()->isCoordinator());
|
||||
auto indexPool =
|
||||
application_features::ApplicationServer::getFeature<IndexThreadFeature>(
|
||||
"IndexThread")
|
||||
->getThreadPool();
|
||||
|
||||
double start = TRI_microtime();
|
||||
|
||||
LOG_TOPIC(TRACE, Logger::PERFORMANCE)
|
||||
<< "fill-index-batch { collection: " << _vocbase->name() << "/" << name()
|
||||
<< " }, " << idx->context() << ", threads: " << indexPool->numThreads()
|
||||
<< ", buckets: " << indexBuckets();
|
||||
|
||||
// give the index a size hint
|
||||
auto primaryIndex = this->primaryIndex();
|
||||
|
||||
auto nrUsed = primaryIndex->size();
|
||||
|
||||
idx->sizeHint(trx, nrUsed);
|
||||
|
||||
// process documents a million at a time
|
||||
size_t blockSize = 1024 * 1024 * 1;
|
||||
|
||||
if (nrUsed < blockSize) {
|
||||
blockSize = nrUsed;
|
||||
}
|
||||
if (blockSize == 0) {
|
||||
blockSize = 1;
|
||||
}
|
||||
|
||||
int res = TRI_ERROR_NO_ERROR;
|
||||
|
||||
ManagedDocumentResult mmdr(trx);
|
||||
|
||||
std::vector<std::pair<TRI_voc_rid_t, VPackSlice>> documents;
|
||||
documents.reserve(blockSize);
|
||||
|
||||
if (nrUsed > 0) {
|
||||
arangodb::basics::BucketPosition position;
|
||||
uint64_t total = 0;
|
||||
|
||||
while (true) {
|
||||
SimpleIndexElement element =
|
||||
primaryIndex->lookupSequential(trx, position, total);
|
||||
|
||||
if (!element) {
|
||||
break;
|
||||
}
|
||||
|
||||
TRI_voc_rid_t revisionId = element.revisionId();
|
||||
|
||||
if (readRevision(trx, mmdr, revisionId)) {
|
||||
uint8_t const* vpack = mmdr.vpack();
|
||||
TRI_ASSERT(vpack != nullptr);
|
||||
documents.emplace_back(std::make_pair(revisionId, VPackSlice(vpack)));
|
||||
|
||||
if (documents.size() == blockSize) {
|
||||
res = idx->batchInsert(trx, documents, indexPool->numThreads());
|
||||
documents.clear();
|
||||
|
||||
// some error occurred
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// process the remainder of the documents
|
||||
if (res == TRI_ERROR_NO_ERROR && !documents.empty()) {
|
||||
res = idx->batchInsert(trx, documents, indexPool->numThreads());
|
||||
}
|
||||
|
||||
LOG_TOPIC(TRACE, Logger::PERFORMANCE)
|
||||
<< "[timer] " << Logger::FIXED(TRI_microtime() - start)
|
||||
<< " s, fill-index-batch { collection: " << _vocbase->name() << "/"
|
||||
<< name() << " }, " << idx->context()
|
||||
<< ", threads: " << indexPool->numThreads()
|
||||
<< ", buckets: " << indexBuckets();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/// @brief fill an index sequentially
|
||||
int LogicalCollection::fillIndexSequential(arangodb::Transaction* trx,
|
||||
arangodb::Index* idx) {
|
||||
TRI_ASSERT(!ServerState::instance()->isCoordinator());
|
||||
double start = TRI_microtime();
|
||||
|
||||
LOG_TOPIC(TRACE, Logger::PERFORMANCE)
|
||||
<< "fill-index-sequential { collection: " << _vocbase->name() << "/"
|
||||
<< name() << " }, " << idx->context() << ", buckets: " << indexBuckets();
|
||||
|
||||
// give the index a size hint
|
||||
auto primaryIndex = this->primaryIndex();
|
||||
size_t nrUsed = primaryIndex->size();
|
||||
|
||||
TRI_ASSERT(idx->type() != Index::IndexType::TRI_IDX_TYPE_PRIMARY_INDEX);
|
||||
idx->sizeHint(trx, nrUsed);
|
||||
|
||||
if (nrUsed > 0) {
|
||||
#ifdef ARANGODB_ENABLE_MAINTAINER_MODE
|
||||
static int const LoopSize = 10000;
|
||||
int counter = 0;
|
||||
int loops = 0;
|
||||
#endif
|
||||
|
||||
arangodb::basics::BucketPosition position;
|
||||
uint64_t total = 0;
|
||||
ManagedDocumentResult result(trx);
|
||||
|
||||
while (true) {
|
||||
SimpleIndexElement element =
|
||||
primaryIndex->lookupSequential(trx, position, total);
|
||||
|
||||
if (!element) {
|
||||
break;
|
||||
}
|
||||
|
||||
TRI_voc_rid_t revisionId = element.revisionId();
|
||||
if (readRevision(trx, result, revisionId)) {
|
||||
uint8_t const* vpack = result.vpack();
|
||||
TRI_ASSERT(vpack != nullptr);
|
||||
int res = idx->insert(trx, revisionId, VPackSlice(vpack), false);
|
||||
|
||||
if (res != TRI_ERROR_NO_ERROR) {
|
||||
return res;
|
||||
}
|
||||
} else {
|
||||
return TRI_ERROR_ARANGO_DOCUMENT_NOT_FOUND; // oops
|
||||
}
|
||||
#ifdef ARANGODB_ENABLE_MAINTAINER_MODE
|
||||
if (++counter == LoopSize) {
|
||||
counter = 0;
|
||||
++loops;
|
||||
LOG(TRACE) << "indexed " << (LoopSize * loops)
|
||||
<< " documents of collection " << cid();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
LOG_TOPIC(TRACE, Logger::PERFORMANCE)
|
||||
<< "[timer] " << Logger::FIXED(TRI_microtime() - start)
|
||||
<< " s, fill-index-sequential { collection: " << _vocbase->name() << "/"
|
||||
<< name() << " }, " << idx->context() << ", buckets: " << indexBuckets();
|
||||
|
||||
return TRI_ERROR_NO_ERROR;
|
||||
}
|
||||
|
||||
/// @brief read unlocks a collection
|
||||
int LogicalCollection::endRead(bool useDeadlockDetector) {
|
||||
if (arangodb::Transaction::_makeNolockHeaders != nullptr) {
|
||||
|
@ -3414,10 +3309,12 @@ int LogicalCollection::updateDocument(
|
|||
|
||||
/// @brief insert a document, low level worker
|
||||
/// the caller must make sure the write lock on the collection is held
|
||||
int LogicalCollection::insertDocument(
|
||||
arangodb::Transaction* trx, TRI_voc_rid_t revisionId, VPackSlice const& doc,
|
||||
MMFilesDocumentOperation& operation,
|
||||
MMFilesWalMarker const* marker, bool& waitForSync) {
|
||||
int LogicalCollection::insertDocument(arangodb::Transaction* trx,
|
||||
TRI_voc_rid_t revisionId,
|
||||
VPackSlice const& doc,
|
||||
MMFilesDocumentOperation& operation,
|
||||
MMFilesWalMarker const* marker,
|
||||
bool& waitForSync) {
|
||||
// insert into primary index first
|
||||
int res = insertPrimaryIndex(trx, revisionId, doc);
|
||||
|
||||
|
@ -3588,7 +3485,8 @@ int LogicalCollection::newObjectForInsert(
|
|||
*p++ = 0xf3; // custom type for _id
|
||||
if (ServerState::isDBServer(trx->serverRole()) && !_isSystem) {
|
||||
// db server in cluster, note: the local collections _statistics,
|
||||
// _statisticsRaw and _statistics15 (which are the only system collections)
|
||||
// _statisticsRaw and _statistics15 (which are the only system
|
||||
// collections)
|
||||
// must not be treated as shards but as local collections
|
||||
MMFilesDatafileHelper::StoreNumber<uint64_t>(p, _planId, sizeof(uint64_t));
|
||||
} else {
|
||||
|
@ -3634,7 +3532,8 @@ int LogicalCollection::newObjectForInsert(
|
|||
return TRI_ERROR_NO_ERROR;
|
||||
}
|
||||
|
||||
/// @brief new object for replace, oldValue must have _key and _id correctly set
|
||||
/// @brief new object for replace, oldValue must have _key and _id correctly
|
||||
/// set
|
||||
void LogicalCollection::newObjectForReplace(
|
||||
Transaction* trx, VPackSlice const& oldValue, VPackSlice const& newValue,
|
||||
VPackSlice const& fromSlice, VPackSlice const& toSlice,
|
||||
|
@ -3840,7 +3739,8 @@ bool LogicalCollection::readRevisionConditional(Transaction* trx,
|
|||
void LogicalCollection::insertRevision(TRI_voc_rid_t revisionId,
|
||||
uint8_t const* dataptr,
|
||||
TRI_voc_fid_t fid, bool isInWal) {
|
||||
// note: there is no need to insert into the cache here as the data points to
|
||||
// note: there is no need to insert into the cache here as the data points
|
||||
// to
|
||||
// temporary storage
|
||||
getPhysical()->insertRevision(revisionId, dataptr, fid, isInWal, true);
|
||||
}
|
||||
|
@ -3848,7 +3748,8 @@ void LogicalCollection::insertRevision(TRI_voc_rid_t revisionId,
|
|||
void LogicalCollection::updateRevision(TRI_voc_rid_t revisionId,
|
||||
uint8_t const* dataptr,
|
||||
TRI_voc_fid_t fid, bool isInWal) {
|
||||
// note: there is no need to modify the cache entry here as insertRevision has
|
||||
// note: there is no need to modify the cache entry here as insertRevision
|
||||
// has
|
||||
// not inserted the document into the cache
|
||||
getPhysical()->updateRevision(revisionId, dataptr, fid, isInWal);
|
||||
}
|
||||
|
|
|
@ -34,6 +34,10 @@
|
|||
struct TRI_df_marker_t;
|
||||
|
||||
namespace arangodb {
|
||||
namespace basics {
|
||||
class LocalTaskQueue;
|
||||
}
|
||||
|
||||
namespace velocypack {
|
||||
class Slice;
|
||||
}
|
||||
|
@ -63,32 +67,31 @@ class LogicalCollection {
|
|||
friend struct ::TRI_vocbase_t;
|
||||
|
||||
public:
|
||||
LogicalCollection(TRI_vocbase_t*, arangodb::velocypack::Slice const&, bool isPhysical);
|
||||
LogicalCollection(TRI_vocbase_t*, arangodb::velocypack::Slice const&,
|
||||
bool isPhysical);
|
||||
|
||||
virtual ~LogicalCollection();
|
||||
|
||||
enum CollectionVersions {
|
||||
VERSION_30 = 5,
|
||||
VERSION_31 = 6
|
||||
};
|
||||
|
||||
enum CollectionVersions { VERSION_30 = 5, VERSION_31 = 6 };
|
||||
|
||||
protected: // If you need a copy outside the class, use clone below.
|
||||
explicit LogicalCollection(LogicalCollection const&);
|
||||
|
||||
private:
|
||||
LogicalCollection& operator=(LogicalCollection const&) = delete;
|
||||
|
||||
public:
|
||||
LogicalCollection() = delete;
|
||||
|
||||
|
||||
virtual std::unique_ptr<LogicalCollection> clone() {
|
||||
auto p = new LogicalCollection(*this);
|
||||
return std::unique_ptr<LogicalCollection>(p);
|
||||
}
|
||||
|
||||
/// @brief hard-coded minimum version number for collections
|
||||
static constexpr uint32_t minimumVersion() { return VERSION_30; }
|
||||
static constexpr uint32_t minimumVersion() { return VERSION_30; }
|
||||
/// @brief current version for collections
|
||||
static constexpr uint32_t currentVersion() { return VERSION_31; }
|
||||
static constexpr uint32_t currentVersion() { return VERSION_31; }
|
||||
|
||||
/// @brief determine whether a collection name is a system collection name
|
||||
static inline bool IsSystemName(std::string const& name) {
|
||||
|
@ -102,13 +105,15 @@ class LogicalCollection {
|
|||
static bool IsAllowedName(bool isSystem, std::string const& name);
|
||||
|
||||
void ensureRevisionsCache();
|
||||
|
||||
|
||||
void isInitialIteration(bool value) { _isInitialIteration = value; }
|
||||
|
||||
// TODO: MOVE TO PHYSICAL?
|
||||
// TODO: MOVE TO PHYSICAL?
|
||||
bool isFullyCollected();
|
||||
int64_t uncollectedLogfileEntries() const { return _uncollectedLogfileEntries.load(); }
|
||||
|
||||
int64_t uncollectedLogfileEntries() const {
|
||||
return _uncollectedLogfileEntries.load();
|
||||
}
|
||||
|
||||
void increaseUncollectedLogfileEntries(int64_t value) {
|
||||
_uncollectedLogfileEntries += value;
|
||||
}
|
||||
|
@ -127,20 +132,15 @@ class LogicalCollection {
|
|||
void lastCompactionStamp(double value) { _lastCompactionStamp = value; }
|
||||
|
||||
void setRevisionError() { _revisionError = true; }
|
||||
|
||||
|
||||
// SECTION: Meta Information
|
||||
uint32_t version() const {
|
||||
return _version;
|
||||
}
|
||||
|
||||
uint32_t version() const { return _version; }
|
||||
|
||||
void setVersion(CollectionVersions version) { _version = version; }
|
||||
|
||||
uint32_t internalVersion() const;
|
||||
|
||||
inline TRI_voc_cid_t cid() const {
|
||||
return _cid;
|
||||
}
|
||||
inline TRI_voc_cid_t cid() const { return _cid; }
|
||||
|
||||
std::string cid_as_string() const;
|
||||
|
||||
|
@ -151,7 +151,7 @@ class LogicalCollection {
|
|||
inline bool useSecondaryIndexes() const { return _useSecondaryIndexes; }
|
||||
|
||||
void useSecondaryIndexes(bool value) { _useSecondaryIndexes = value; }
|
||||
|
||||
|
||||
std::string name() const;
|
||||
std::string dbName() const;
|
||||
std::string const& path() const;
|
||||
|
@ -159,18 +159,18 @@ class LogicalCollection {
|
|||
void distributeShardsLike(std::string const&);
|
||||
|
||||
std::vector<std::string> const& avoidServers() const;
|
||||
void avoidServers(std::vector<std::string> const&) ;
|
||||
void avoidServers(std::vector<std::string> const&);
|
||||
|
||||
// For normal collections the realNames is just a vector of length 1
|
||||
// with its name. For smart edge collections (enterprise only) this is
|
||||
// different.
|
||||
virtual std::vector<std::string> realNames() const {
|
||||
std::vector<std::string> res {name()};
|
||||
std::vector<std::string> res{name()};
|
||||
return res;
|
||||
}
|
||||
// Same here, this is for reading in AQL:
|
||||
virtual std::vector<std::string> realNamesForRead() const {
|
||||
std::vector<std::string> res {name()};
|
||||
std::vector<std::string> res{name()};
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -182,7 +182,8 @@ class LogicalCollection {
|
|||
|
||||
/// @brief try to fetch the collection status under a lock
|
||||
/// the boolean value will be set to true if the lock could be acquired
|
||||
/// if the boolean is false, the return value is always TRI_VOC_COL_STATUS_CORRUPTED
|
||||
/// if the boolean is false, the return value is always
|
||||
/// TRI_VOC_COL_STATUS_CORRUPTED
|
||||
TRI_vocbase_col_status_e tryFetchStatus(bool&);
|
||||
std::string statusString();
|
||||
|
||||
|
@ -203,16 +204,14 @@ class LogicalCollection {
|
|||
bool isVolatile() const;
|
||||
bool waitForSync() const;
|
||||
bool isSmart() const;
|
||||
|
||||
|
||||
void waitForSync(bool value) { _waitForSync = value; }
|
||||
|
||||
std::unique_ptr<arangodb::FollowerInfo> const& followers() const;
|
||||
|
||||
|
||||
void setDeleted(bool);
|
||||
|
||||
Ditches* ditches() const {
|
||||
return getPhysical()->ditches();
|
||||
}
|
||||
Ditches* ditches() const { return getPhysical()->ditches(); }
|
||||
|
||||
void setRevision(TRI_voc_rid_t, bool);
|
||||
|
||||
|
@ -224,7 +223,7 @@ class LogicalCollection {
|
|||
inline arangodb::KeyGenerator* keyGenerator() const {
|
||||
return _keyGenerator.get();
|
||||
}
|
||||
|
||||
|
||||
PhysicalCollection* getPhysical() const { return _physical.get(); }
|
||||
|
||||
// SECTION: Indexes
|
||||
|
@ -247,7 +246,6 @@ class LogicalCollection {
|
|||
int replicationFactor() const;
|
||||
bool isSatellite() const;
|
||||
|
||||
|
||||
// SECTION: Sharding
|
||||
int numberOfShards() const;
|
||||
bool allowUserKeys() const;
|
||||
|
@ -255,7 +253,7 @@ class LogicalCollection {
|
|||
std::vector<std::string> const& shardKeys() const;
|
||||
std::shared_ptr<ShardMap> shardIds() const;
|
||||
void setShardMap(std::shared_ptr<ShardMap>& map);
|
||||
|
||||
|
||||
/// @brief a method to skip certain documents in AQL write operations,
|
||||
/// this is only used in the enterprise edition for smart graphs
|
||||
virtual bool skipForAqlWrite(arangodb::velocypack::Slice document,
|
||||
|
@ -278,16 +276,14 @@ class LogicalCollection {
|
|||
/// The builder has to be an opened Type::Object
|
||||
void toVelocyPack(arangodb::velocypack::Builder&, bool, TRI_voc_tick_t);
|
||||
|
||||
inline TRI_vocbase_t* vocbase() const {
|
||||
return _vocbase;
|
||||
}
|
||||
inline TRI_vocbase_t* vocbase() const { return _vocbase; }
|
||||
|
||||
// Update this collection.
|
||||
virtual int update(arangodb::velocypack::Slice const&, bool);
|
||||
|
||||
/// @brief return the figures for a collection
|
||||
virtual std::shared_ptr<arangodb::velocypack::Builder> figures();
|
||||
|
||||
|
||||
/// @brief opens an existing collection
|
||||
void open(bool ignoreErrors);
|
||||
|
||||
|
@ -297,20 +293,21 @@ class LogicalCollection {
|
|||
/// datafile management
|
||||
|
||||
/// @brief rotate the active journal - will do nothing if there is no journal
|
||||
int rotateActiveJournal() {
|
||||
return getPhysical()->rotateActiveJournal();
|
||||
}
|
||||
|
||||
int rotateActiveJournal() { return getPhysical()->rotateActiveJournal(); }
|
||||
|
||||
/// @brief increase dead stats for a datafile, if it exists
|
||||
void updateStats(TRI_voc_fid_t fid, DatafileStatisticsContainer const& values) {
|
||||
void updateStats(TRI_voc_fid_t fid,
|
||||
DatafileStatisticsContainer const& values) {
|
||||
return getPhysical()->updateStats(fid, values);
|
||||
}
|
||||
|
||||
bool applyForTickRange(TRI_voc_tick_t dataMin, TRI_voc_tick_t dataMax,
|
||||
std::function<bool(TRI_voc_tick_t foundTick, TRI_df_marker_t const* marker)> const& callback) {
|
||||
|
||||
bool applyForTickRange(
|
||||
TRI_voc_tick_t dataMin, TRI_voc_tick_t dataMax,
|
||||
std::function<bool(TRI_voc_tick_t foundTick,
|
||||
TRI_df_marker_t const* marker)> const& callback) {
|
||||
return getPhysical()->applyForTickRange(dataMin, dataMax, callback);
|
||||
}
|
||||
|
||||
|
||||
/// @brief disallow starting the compaction of the collection
|
||||
void preventCompaction() { getPhysical()->preventCompaction(); }
|
||||
bool tryPreventCompaction() { return getPhysical()->tryPreventCompaction(); }
|
||||
|
@ -345,8 +342,13 @@ class LogicalCollection {
|
|||
int restoreIndex(arangodb::Transaction*, arangodb::velocypack::Slice const&,
|
||||
std::shared_ptr<arangodb::Index>&);
|
||||
|
||||
/// @brief Exposes a pointer to index list
|
||||
std::vector<std::shared_ptr<arangodb::Index>> const* indexList() const;
|
||||
|
||||
/// @brief Fill indexes used in recovery
|
||||
int fillIndexes(arangodb::Transaction*);
|
||||
int fillIndexes(arangodb::Transaction*,
|
||||
std::vector<std::shared_ptr<arangodb::Index>> const&,
|
||||
bool skipPersistent = true);
|
||||
|
||||
/// @brief Saves Index to file
|
||||
int saveIndex(arangodb::Index* idx, bool writeMarker);
|
||||
|
@ -356,48 +358,61 @@ class LogicalCollection {
|
|||
int cleanupIndexes();
|
||||
|
||||
// SECTION: Index access (local only)
|
||||
|
||||
int read(arangodb::Transaction*, std::string const&, ManagedDocumentResult& result, bool);
|
||||
int read(arangodb::Transaction*, arangodb::StringRef const&, ManagedDocumentResult& result, bool);
|
||||
|
||||
int read(arangodb::Transaction*, std::string const&,
|
||||
ManagedDocumentResult& result, bool);
|
||||
int read(arangodb::Transaction*, arangodb::StringRef const&,
|
||||
ManagedDocumentResult& result, bool);
|
||||
|
||||
/// @brief processes a truncate operation (note: currently this only clears
|
||||
/// the read-cache
|
||||
int truncate(Transaction* trx);
|
||||
int insert(arangodb::Transaction*, arangodb::velocypack::Slice const,
|
||||
ManagedDocumentResult& result, arangodb::OperationOptions&, TRI_voc_tick_t&, bool);
|
||||
ManagedDocumentResult& result, arangodb::OperationOptions&,
|
||||
TRI_voc_tick_t&, bool);
|
||||
int update(arangodb::Transaction*, arangodb::velocypack::Slice const,
|
||||
ManagedDocumentResult& result, arangodb::OperationOptions&, TRI_voc_tick_t&, bool,
|
||||
TRI_voc_rid_t& prevRev, ManagedDocumentResult& previous);
|
||||
ManagedDocumentResult& result, arangodb::OperationOptions&,
|
||||
TRI_voc_tick_t&, bool, TRI_voc_rid_t& prevRev,
|
||||
ManagedDocumentResult& previous);
|
||||
int replace(arangodb::Transaction*, arangodb::velocypack::Slice const,
|
||||
ManagedDocumentResult& result, arangodb::OperationOptions&, TRI_voc_tick_t&, bool,
|
||||
TRI_voc_rid_t& prevRev, ManagedDocumentResult& previous);
|
||||
ManagedDocumentResult& result, arangodb::OperationOptions&,
|
||||
TRI_voc_tick_t&, bool, TRI_voc_rid_t& prevRev,
|
||||
ManagedDocumentResult& previous);
|
||||
int remove(arangodb::Transaction*, arangodb::velocypack::Slice const,
|
||||
arangodb::OperationOptions&, TRI_voc_tick_t&, bool,
|
||||
arangodb::OperationOptions&, TRI_voc_tick_t&, bool,
|
||||
TRI_voc_rid_t& prevRev, ManagedDocumentResult& previous);
|
||||
/// @brief removes a document or edge, fast path function for database documents
|
||||
int remove(arangodb::Transaction*, TRI_voc_rid_t oldRevisionId, arangodb::velocypack::Slice const,
|
||||
arangodb::OperationOptions&, TRI_voc_tick_t&, bool);
|
||||
/// @brief removes a document or edge, fast path function for database
|
||||
/// documents
|
||||
int remove(arangodb::Transaction*, TRI_voc_rid_t oldRevisionId,
|
||||
arangodb::velocypack::Slice const, arangodb::OperationOptions&,
|
||||
TRI_voc_tick_t&, bool);
|
||||
|
||||
int rollbackOperation(arangodb::Transaction*, TRI_voc_document_operation_e,
|
||||
TRI_voc_rid_t oldRevisionId, arangodb::velocypack::Slice const& oldDoc,
|
||||
TRI_voc_rid_t newRevisionId, arangodb::velocypack::Slice const& newDoc);
|
||||
|
||||
// TODO Make Private and IndexFiller as friend
|
||||
/// @brief initializes an index with all existing documents
|
||||
int fillIndex(arangodb::Transaction*, arangodb::Index*,
|
||||
bool skipPersistent = true);
|
||||
int rollbackOperation(arangodb::Transaction*, TRI_voc_document_operation_e,
|
||||
TRI_voc_rid_t oldRevisionId,
|
||||
arangodb::velocypack::Slice const& oldDoc,
|
||||
TRI_voc_rid_t newRevisionId,
|
||||
arangodb::velocypack::Slice const& newDoc);
|
||||
|
||||
int beginReadTimed(bool useDeadlockDetector, double timeout = 0.0);
|
||||
int beginWriteTimed(bool useDeadlockDetector, double timeout = 0.0);
|
||||
int endRead(bool useDeadlockDetector);
|
||||
int endWrite(bool useDeadlockDetector);
|
||||
|
||||
bool readRevision(arangodb::Transaction*, ManagedDocumentResult& result, TRI_voc_rid_t revisionId);
|
||||
bool readRevisionConditional(arangodb::Transaction*, ManagedDocumentResult& result, TRI_voc_rid_t revisionId, TRI_voc_tick_t maxTick, bool excludeWal);
|
||||
|
||||
void insertRevision(TRI_voc_rid_t revisionId, uint8_t const* dataptr, TRI_voc_fid_t fid, bool isInWal);
|
||||
void updateRevision(TRI_voc_rid_t revisionId, uint8_t const* dataptr, TRI_voc_fid_t fid, bool isInWal);
|
||||
bool updateRevisionConditional(TRI_voc_rid_t revisionId, TRI_df_marker_t const* oldPosition, TRI_df_marker_t const* newPosition, TRI_voc_fid_t newFid, bool isInWal);
|
||||
bool readRevision(arangodb::Transaction*, ManagedDocumentResult& result,
|
||||
TRI_voc_rid_t revisionId);
|
||||
bool readRevisionConditional(arangodb::Transaction*,
|
||||
ManagedDocumentResult& result,
|
||||
TRI_voc_rid_t revisionId, TRI_voc_tick_t maxTick,
|
||||
bool excludeWal);
|
||||
|
||||
void insertRevision(TRI_voc_rid_t revisionId, uint8_t const* dataptr,
|
||||
TRI_voc_fid_t fid, bool isInWal);
|
||||
void updateRevision(TRI_voc_rid_t revisionId, uint8_t const* dataptr,
|
||||
TRI_voc_fid_t fid, bool isInWal);
|
||||
bool updateRevisionConditional(TRI_voc_rid_t revisionId,
|
||||
TRI_df_marker_t const* oldPosition,
|
||||
TRI_df_marker_t const* newPosition,
|
||||
TRI_voc_fid_t newFid, bool isInWal);
|
||||
void removeRevision(TRI_voc_rid_t revisionId, bool updateStats);
|
||||
void removeRevisionCacheEntry(TRI_voc_rid_t revisionId);
|
||||
|
||||
|
@ -416,84 +431,87 @@ class LogicalCollection {
|
|||
|
||||
// SECTION: Indexes (local only)
|
||||
|
||||
// TODO Make Private and IndexFiller as friend
|
||||
/// @brief initializes an index with all existing documents
|
||||
void fillIndex(arangodb::basics::LocalTaskQueue*, arangodb::Transaction*,
|
||||
arangodb::Index*,
|
||||
std::vector<std::pair<TRI_voc_rid_t, VPackSlice>> const&,
|
||||
bool);
|
||||
|
||||
// @brief create index with the given definition.
|
||||
|
||||
bool openIndex(arangodb::velocypack::Slice const&, arangodb::Transaction*);
|
||||
/// @brief fill an index in batches
|
||||
int fillIndexBatch(arangodb::Transaction* trx, arangodb::Index* idx);
|
||||
|
||||
/// @brief fill an index sequentially
|
||||
int fillIndexSequential(arangodb::Transaction* trx, arangodb::Index* idx);
|
||||
|
||||
// SECTION: Index access (local only)
|
||||
int lookupDocument(arangodb::Transaction*, VPackSlice const,
|
||||
ManagedDocumentResult& result);
|
||||
|
||||
int checkRevision(arangodb::Transaction*, TRI_voc_rid_t expected, TRI_voc_rid_t found);
|
||||
int checkRevision(arangodb::Transaction*, TRI_voc_rid_t expected,
|
||||
TRI_voc_rid_t found);
|
||||
|
||||
int updateDocument(arangodb::Transaction*,
|
||||
TRI_voc_rid_t oldRevisionId, arangodb::velocypack::Slice const& oldDoc,
|
||||
TRI_voc_rid_t newRevisionId, arangodb::velocypack::Slice const& newDoc,
|
||||
int updateDocument(arangodb::Transaction*, TRI_voc_rid_t oldRevisionId,
|
||||
arangodb::velocypack::Slice const& oldDoc,
|
||||
TRI_voc_rid_t newRevisionId,
|
||||
arangodb::velocypack::Slice const& newDoc,
|
||||
MMFilesDocumentOperation&, MMFilesWalMarker const*,
|
||||
bool& waitForSync);
|
||||
int insertDocument(arangodb::Transaction*, TRI_voc_rid_t revisionId, arangodb::velocypack::Slice const&,
|
||||
int insertDocument(arangodb::Transaction*, TRI_voc_rid_t revisionId,
|
||||
arangodb::velocypack::Slice const&,
|
||||
MMFilesDocumentOperation&, MMFilesWalMarker const*,
|
||||
bool& waitForSync);
|
||||
|
||||
int insertPrimaryIndex(arangodb::Transaction*, TRI_voc_rid_t revisionId, arangodb::velocypack::Slice const&);
|
||||
|
||||
int deletePrimaryIndex(arangodb::Transaction*, TRI_voc_rid_t revisionId, arangodb::velocypack::Slice const&);
|
||||
int insertPrimaryIndex(arangodb::Transaction*, TRI_voc_rid_t revisionId,
|
||||
arangodb::velocypack::Slice const&);
|
||||
|
||||
int insertSecondaryIndexes(arangodb::Transaction*, TRI_voc_rid_t revisionId, arangodb::velocypack::Slice const&,
|
||||
int deletePrimaryIndex(arangodb::Transaction*, TRI_voc_rid_t revisionId,
|
||||
arangodb::velocypack::Slice const&);
|
||||
|
||||
int insertSecondaryIndexes(arangodb::Transaction*, TRI_voc_rid_t revisionId,
|
||||
arangodb::velocypack::Slice const&,
|
||||
bool isRollback);
|
||||
|
||||
int deleteSecondaryIndexes(arangodb::Transaction*, TRI_voc_rid_t revisionId, arangodb::velocypack::Slice const&,
|
||||
int deleteSecondaryIndexes(arangodb::Transaction*, TRI_voc_rid_t revisionId,
|
||||
arangodb::velocypack::Slice const&,
|
||||
bool isRollback);
|
||||
|
||||
// SECTION: Document pre commit preperation (only local)
|
||||
|
||||
/// @brief new object for insert, value must have _key set correctly.
|
||||
int newObjectForInsert(
|
||||
arangodb::Transaction* trx,
|
||||
arangodb::velocypack::Slice const& value,
|
||||
arangodb::velocypack::Slice const& fromSlice,
|
||||
arangodb::velocypack::Slice const& toSlice,
|
||||
bool isEdgeCollection,
|
||||
arangodb::velocypack::Builder& builder,
|
||||
bool isRestore);
|
||||
int newObjectForInsert(arangodb::Transaction* trx,
|
||||
arangodb::velocypack::Slice const& value,
|
||||
arangodb::velocypack::Slice const& fromSlice,
|
||||
arangodb::velocypack::Slice const& toSlice,
|
||||
bool isEdgeCollection,
|
||||
arangodb::velocypack::Builder& builder,
|
||||
bool isRestore);
|
||||
|
||||
/// @brief new object for replace
|
||||
void newObjectForReplace(
|
||||
arangodb::Transaction* trx,
|
||||
arangodb::velocypack::Slice const& oldValue,
|
||||
arangodb::velocypack::Slice const& newValue,
|
||||
arangodb::velocypack::Slice const& fromSlice,
|
||||
arangodb::velocypack::Slice const& toSlice,
|
||||
bool isEdgeCollection,
|
||||
std::string const& rev,
|
||||
arangodb::velocypack::Builder& builder);
|
||||
void newObjectForReplace(arangodb::Transaction* trx,
|
||||
arangodb::velocypack::Slice const& oldValue,
|
||||
arangodb::velocypack::Slice const& newValue,
|
||||
arangodb::velocypack::Slice const& fromSlice,
|
||||
arangodb::velocypack::Slice const& toSlice,
|
||||
bool isEdgeCollection, std::string const& rev,
|
||||
arangodb::velocypack::Builder& builder);
|
||||
|
||||
/// @brief merge two objects for update
|
||||
void mergeObjectsForUpdate(
|
||||
arangodb::Transaction* trx,
|
||||
arangodb::velocypack::Slice const& oldValue,
|
||||
arangodb::velocypack::Slice const& newValue,
|
||||
bool isEdgeCollection,
|
||||
std::string const& rev,
|
||||
bool mergeObjects, bool keepNull,
|
||||
arangodb::velocypack::Builder& b);
|
||||
void mergeObjectsForUpdate(arangodb::Transaction* trx,
|
||||
arangodb::velocypack::Slice const& oldValue,
|
||||
arangodb::velocypack::Slice const& newValue,
|
||||
bool isEdgeCollection, std::string const& rev,
|
||||
bool mergeObjects, bool keepNull,
|
||||
arangodb::velocypack::Builder& b);
|
||||
|
||||
/// @brief new object for remove, must have _key set
|
||||
void newObjectForRemove(
|
||||
arangodb::Transaction* trx,
|
||||
arangodb::velocypack::Slice const& oldValue,
|
||||
std::string const& rev,
|
||||
arangodb::velocypack::Builder& builder);
|
||||
void newObjectForRemove(arangodb::Transaction* trx,
|
||||
arangodb::velocypack::Slice const& oldValue,
|
||||
std::string const& rev,
|
||||
arangodb::velocypack::Builder& builder);
|
||||
|
||||
void increaseInternalVersion();
|
||||
|
||||
protected:
|
||||
void toVelocyPackInObject(arangodb::velocypack::Builder& result, bool translateCids) const;
|
||||
void toVelocyPackInObject(arangodb::velocypack::Builder& result,
|
||||
bool translateCids) const;
|
||||
|
||||
// SECTION: Meta Information
|
||||
//
|
||||
|
@ -542,7 +560,7 @@ class LogicalCollection {
|
|||
// TODO Really VPack?
|
||||
std::shared_ptr<arangodb::velocypack::Buffer<uint8_t> const>
|
||||
_keyOptions; // options for key creation
|
||||
|
||||
|
||||
uint32_t _version;
|
||||
|
||||
// SECTION: Indexes
|
||||
|
@ -578,21 +596,21 @@ class LogicalCollection {
|
|||
TRI_voc_tick_t _maxTick;
|
||||
|
||||
std::unique_ptr<arangodb::KeyGenerator> _keyGenerator;
|
||||
|
||||
|
||||
mutable arangodb::basics::ReadWriteLock
|
||||
_lock; // lock protecting the status and name
|
||||
|
||||
|
||||
mutable arangodb::basics::ReadWriteLock
|
||||
_idxLock; // lock protecting the indexes
|
||||
|
||||
mutable arangodb::basics::ReadWriteLock
|
||||
_infoLock; // lock protecting the info
|
||||
|
||||
|
||||
arangodb::Mutex _compactionStatusLock;
|
||||
size_t _nextCompactionStartIndex;
|
||||
char const* _lastCompactionStatus;
|
||||
double _lastCompactionStamp;
|
||||
|
||||
|
||||
std::atomic<int64_t> _uncollectedLogfileEntries;
|
||||
|
||||
/// @brief: flag that is set to true when the documents are
|
||||
|
|
|
@ -876,7 +876,7 @@ int LogfileManager::flush(bool waitForSync, bool waitForCollector,
|
|||
res = this->waitForCollector(lastOpenLogfileId, maxWaitTime);
|
||||
|
||||
if (res == TRI_ERROR_LOCK_TIMEOUT) {
|
||||
LOG(ERR) << "got lock timeout when waiting for WAL flush. lastOpenLogfileId: " << lastOpenLogfileId;
|
||||
LOG(DEBUG) << "got lock timeout when waiting for WAL flush. lastOpenLogfileId: " << lastOpenLogfileId;
|
||||
}
|
||||
} else if (res == TRI_ERROR_ARANGO_DATAFILE_EMPTY) {
|
||||
// current logfile is empty and cannot be collected
|
||||
|
@ -887,7 +887,7 @@ int LogfileManager::flush(bool waitForSync, bool waitForCollector,
|
|||
res = this->waitForCollector(lastSealedLogfileId, maxWaitTime);
|
||||
|
||||
if (res == TRI_ERROR_LOCK_TIMEOUT) {
|
||||
LOG(ERR) << "got lock timeout when waiting for WAL flush. lastSealedLogfileId: " << lastSealedLogfileId;
|
||||
LOG(DEBUG) << "got lock timeout when waiting for WAL flush. lastSealedLogfileId: " << lastSealedLogfileId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1731,8 +1731,7 @@ int LogfileManager::waitForCollector(Logfile::IdType logfileId,
|
|||
// try again
|
||||
}
|
||||
|
||||
// TODO: remove debug info here
|
||||
LOG(ERR) << "going into lock timeout. having waited for logfile: " << logfileId << ", maxWaitTime: " << maxWaitTime;
|
||||
LOG(DEBUG) << "going into lock timeout. having waited for logfile: " << logfileId << ", maxWaitTime: " << maxWaitTime;
|
||||
logStatus();
|
||||
|
||||
// waited for too long
|
||||
|
@ -1740,12 +1739,11 @@ int LogfileManager::waitForCollector(Logfile::IdType logfileId,
|
|||
}
|
||||
|
||||
void LogfileManager::logStatus() {
|
||||
// TODO: remove debug info here
|
||||
LOG(ERR) << "logfile manager status report: lastCollectedId: " << _lastCollectedId.load() << ", lastSealedId: " << _lastSealedId.load();
|
||||
LOG(DEBUG) << "logfile manager status report: lastCollectedId: " << _lastCollectedId.load() << ", lastSealedId: " << _lastSealedId.load();
|
||||
READ_LOCKER(locker, _logfilesLock);
|
||||
for (auto logfile : _logfiles) {
|
||||
LOG(ERR) << "- logfile " << logfile.second->id() << ", filename '" << logfile.second->filename()
|
||||
<< "', status " << logfile.second->statusText();
|
||||
LOG(DEBUG) << "- logfile " << logfile.second->id() << ", filename '" << logfile.second->filename()
|
||||
<< "', status " << logfile.second->statusText();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1490,8 +1490,8 @@ function startInstanceAgency (instanceInfo, protocol, options, addArgs, rootDir)
|
|||
usedPorts.push(port);
|
||||
instanceArgs['server.endpoint'] = protocol + '://127.0.0.1:' + port;
|
||||
instanceArgs['agency.my-address'] = protocol + '://127.0.0.1:' + port;
|
||||
instanceArgs['agency.supervision-grace-period'] = '5.0';
|
||||
instanceArgs['agency.supervision-frequency'] = '0.05';
|
||||
instanceArgs['agency.supervision-grace-period'] = '10.0';
|
||||
instanceArgs['agency.supervision-frequency'] = '1.0';
|
||||
|
||||
if (i === N - 1) {
|
||||
let l = [];
|
||||
|
@ -3881,17 +3881,23 @@ testFuncs.upgrade = function (options) {
|
|||
|
||||
result.upgrade.first = executeAndWait(ARANGOD_BIN, argv, options, 'upgrade');
|
||||
|
||||
if (result.upgrade.first !== 0 && !options.force) {
|
||||
if (result.upgrade.first.status !== true) {
|
||||
print('not removing ' + tmpDataDir);
|
||||
return result;
|
||||
return result.upgrade;
|
||||
}
|
||||
|
||||
++result.upgrade.total;
|
||||
|
||||
result.upgrade.second = executeAndWait(ARANGOD_BIN, argv, options, 'upgrade');
|
||||
|
||||
if (result.upgrade.second.status !== true) {
|
||||
print('not removing ' + tmpDataDir);
|
||||
return result.upgrade;
|
||||
}
|
||||
|
||||
cleanupDirectories.push(tmpDataDir);
|
||||
|
||||
|
||||
result.upgrade.status = true;
|
||||
return result;
|
||||
};
|
||||
|
||||
|
|
|
@ -745,7 +745,7 @@ function executePlanForCollections(plannedCollections) {
|
|||
let collection;
|
||||
if (!localCollections.hasOwnProperty(shardName)) {
|
||||
// must create this shard
|
||||
console.info("creating local shard '%s/%s' for central '%s/%s'",
|
||||
console.debug("creating local shard '%s/%s' for central '%s/%s'",
|
||||
database,
|
||||
shardName,
|
||||
database,
|
||||
|
@ -813,7 +813,7 @@ function executePlanForCollections(plannedCollections) {
|
|||
}, {});
|
||||
|
||||
if (Object.keys(properties).length > 0) {
|
||||
console.info("updating properties for local shard '%s/%s'",
|
||||
console.debug("updating properties for local shard '%s/%s'",
|
||||
database,
|
||||
shardName);
|
||||
|
||||
|
@ -831,17 +831,17 @@ function executePlanForCollections(plannedCollections) {
|
|||
|
||||
// Now check whether the status is OK:
|
||||
if (collectionStatus !== collectionInfo.status) {
|
||||
console.info("detected status change for local shard '%s/%s'",
|
||||
console.debug("detected status change for local shard '%s/%s'",
|
||||
database,
|
||||
shardName);
|
||||
|
||||
if (collectionInfo.status === ArangoCollection.STATUS_UNLOADED) {
|
||||
console.info("unloading local shard '%s/%s'",
|
||||
console.debug("unloading local shard '%s/%s'",
|
||||
database,
|
||||
shardName);
|
||||
collection.unload();
|
||||
} else if (collectionInfo.status === ArangoCollection.STATUS_LOADED) {
|
||||
console.info("loading local shard '%s/%s'",
|
||||
console.debug("loading local shard '%s/%s'",
|
||||
database,
|
||||
shardName);
|
||||
collection.load();
|
||||
|
@ -1264,7 +1264,7 @@ function executePlanForDatabases(plannedDatabases) {
|
|||
if (!plannedDatabases.hasOwnProperty(name) && name.substr(0, 1) !== '_') {
|
||||
// must drop database
|
||||
|
||||
console.info("dropping local database '%s'", name);
|
||||
console.debug("dropping local database '%s'", name);
|
||||
|
||||
// Do we have to stop a replication applier first?
|
||||
if (ArangoServerState.role() === 'SECONDARY') {
|
||||
|
@ -1273,7 +1273,7 @@ function executePlanForDatabases(plannedDatabases) {
|
|||
var rep = require('@arangodb/replication');
|
||||
var state = rep.applier.state();
|
||||
if (state.state.running === true) {
|
||||
console.info('stopping replication applier first');
|
||||
console.debug('stopping replication applier first');
|
||||
rep.applier.stop();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,23 +53,8 @@ function optimizerRuleTestSuite() {
|
|||
sorted : true
|
||||
};
|
||||
|
||||
var ruleName = "use-geoindex";
|
||||
var secondRuleName = "use-geoindexes";
|
||||
var removeCalculationNodes = "remove-unnecessary-calculations-2";
|
||||
var ruleName = "geoindex";
|
||||
var colName = "UnitTestsAqlOptimizer" + ruleName.replace(/-/g, "_");
|
||||
var colNameOther = colName + "_XX";
|
||||
|
||||
// various choices to control the optimizer:
|
||||
var paramNone = { optimizer: { rules: [ "-all" ] } };
|
||||
var paramIndexFromSort = { optimizer: { rules: [ "-all", "+" + ruleName ] } };
|
||||
var paramIndexRange = { optimizer: { rules: [ "-all", "+" + secondRuleName ] } };
|
||||
var paramIndexFromSort_IndexRange = { optimizer: { rules: [ "-all", "+" + ruleName, "+" + secondRuleName ] } };
|
||||
var paramIndexFromSort_IndexRange_RemoveCalculations = {
|
||||
optimizer: { rules: [ "-all", "+" + ruleName, "+" + secondRuleName, "+" + removeCalculationNodes ] }
|
||||
};
|
||||
var paramIndexFromSort_RemoveCalculations = {
|
||||
optimizer: { rules: [ "-all", "+" + ruleName, "+" + removeCalculationNodes ] }
|
||||
};
|
||||
|
||||
var geocol;
|
||||
var sortArray = function (l, r) {
|
||||
|
@ -113,19 +98,6 @@ function optimizerRuleTestSuite() {
|
|||
};
|
||||
|
||||
var geodistance = function(latitude1, longitude1, latitude2, longitude2) {
|
||||
//if (TYPEWEIGHT(latitude1) !== TYPEWEIGHT_NUMBER ||
|
||||
// TYPEWEIGHT(longitude1) !== TYPEWEIGHT_NUMBER ||
|
||||
// TYPEWEIGHT(latitude2) !== TYPEWEIGHT_NUMBER ||
|
||||
// TYPEWEIGHT(longitude2) !== TYPEWEIGHT_NUMBER) {
|
||||
// WARN('DISTANCE', INTERNAL.errors.ERROR_QUERY_FUNCTION_ARGUMENT_TYPE_MISMATCH);
|
||||
// return null;
|
||||
//}
|
||||
|
||||
//var p1 = AQL_TO_NUMBER(latitude1) * (Math.PI / 180.0);
|
||||
//var p2 = AQL_TO_NUMBER(latitude2) * (Math.PI / 180.0);
|
||||
//var d1 = AQL_TO_NUMBER(latitude2 - latitude1) * (Math.PI / 180.0);
|
||||
//var d2 = AQL_TO_NUMBER(longitude2 - longitude1) * (Math.PI / 180.0);
|
||||
|
||||
var p1 = (latitude1) * (Math.PI / 180.0);
|
||||
var p2 = (latitude2) * (Math.PI / 180.0);
|
||||
var d1 = (latitude2 - latitude1) * (Math.PI / 180.0);
|
||||
|
@ -165,7 +137,6 @@ function optimizerRuleTestSuite() {
|
|||
|
||||
tearDown : function () {
|
||||
internal.db._drop(colName);
|
||||
internal.db._drop(colNameOther);
|
||||
geocol = null;
|
||||
},
|
||||
|
||||
|
@ -215,14 +186,6 @@ function optimizerRuleTestSuite() {
|
|||
queries.forEach(function(query) {
|
||||
var result = AQL_EXPLAIN(query.string);
|
||||
|
||||
// //optimized on cluster
|
||||
// if (query[1]) {
|
||||
// assertNotEqual(-1, removeAlwaysOnClusterRules(result.plan.rules).indexOf(ruleName), query[0]);
|
||||
// }
|
||||
// else {
|
||||
// assertEqual(-1, removeAlwaysOnClusterRules(result.plan.rules).indexOf(ruleName), query[0]);
|
||||
// }
|
||||
|
||||
//sort nodes
|
||||
if (query.sort) {
|
||||
hasSortNode(result,query);
|
||||
|
@ -268,7 +231,6 @@ function optimizerRuleTestSuite() {
|
|||
var pairs = result.json.map(function(res){
|
||||
return [res.lat,res.lon];
|
||||
});
|
||||
//internal.print(pairs)
|
||||
assertEqual(expected[qindex].sort(),pairs.sort());
|
||||
//expect(expected[qindex].sort()).to.be.equal(result.json.sort())
|
||||
});
|
||||
|
|
|
@ -221,7 +221,27 @@ function ahuacatlTernaryTestSuite () {
|
|||
assertEqual([ 2 ], getQueryResults("RETURN NOOPT([ ] ? 2 : 3)"));
|
||||
assertEqual([ 2 ], getQueryResults("RETURN NOOPT([ 0 ] ? 2 : 3)"));
|
||||
assertEqual([ 2 ], getQueryResults("RETURN NOOPT({ } ? 2 : 3)"));
|
||||
}
|
||||
},
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief test ternary operator shortcut
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
testTernaryShortcut : function () {
|
||||
var expected = [ 'foo', 'foo', 1, 2 ];
|
||||
|
||||
var actual = getQueryResults("FOR i IN [ null, 0, 1, 2 ] RETURN i ? : 'foo'");
|
||||
assertEqual(expected, actual);
|
||||
|
||||
actual = getQueryResults("FOR i IN [ null, 0, 1, 2 ] RETURN i ?: 'foo'");
|
||||
assertEqual(expected, actual);
|
||||
|
||||
actual = getQueryResults("FOR i IN [ null, 0, 1, 2 ] RETURN NOOPT(i ? : 'foo')");
|
||||
assertEqual(expected, actual);
|
||||
|
||||
actual = getQueryResults("FOR i IN [ null, 0, 1, 2 ] RETURN NOOPT(i ?: 'foo')");
|
||||
assertEqual(expected, actual);
|
||||
},
|
||||
|
||||
};
|
||||
}
|
||||
|
|
|
@ -163,7 +163,7 @@ function MovingShardsSuite () {
|
|||
}
|
||||
|
||||
if (!ok) {
|
||||
console.info(
|
||||
console.error(
|
||||
"Failed: Server " + id + " was not cleaned out. List of cleaned servers: ["
|
||||
+ obj.cleanedServers + "]");
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
/// @author Dr. Frank Celler
|
||||
/// @author Martin Schoenert
|
||||
/// @author Max Neunhoeffer
|
||||
/// @author Daniel H. Larkin
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef ARANGODB_BASICS_ASSOC_MULTI_H
|
||||
|
@ -29,19 +30,21 @@
|
|||
// Activate for additional debugging:
|
||||
// #define TRI_CHECK_MULTI_POINTER_HASH 1
|
||||
|
||||
#include "Basics/AssocMultiHelpers.h"
|
||||
#include "Basics/Common.h"
|
||||
#include "Basics/AssocHelpers.h"
|
||||
#include "Basics/IndexBucket.h"
|
||||
#include "Basics/LocalTaskQueue.h"
|
||||
#include "Basics/Mutex.h"
|
||||
#include "Basics/MutexLocker.h"
|
||||
#include "Basics/prime-numbers.h"
|
||||
#include "Logger/Logger.h"
|
||||
|
||||
#include <thread>
|
||||
#include <velocypack/Builder.h>
|
||||
#include <velocypack/velocypack-aliases.h>
|
||||
#include <thread>
|
||||
|
||||
#ifdef TRI_CHECK_MULTI_POINTER_HASH
|
||||
#ifdef TRI_CHECK_MULTI_POINTER_HASH
|
||||
#include <iostream>
|
||||
#endif
|
||||
|
||||
|
@ -90,44 +93,6 @@ namespace basics {
|
|||
///
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <class Element, class IndexType, bool useHashCache>
|
||||
struct Entry {
|
||||
private:
|
||||
uint64_t hashCache; // cache the hash value, this stores the
|
||||
// hashByKey for the first element in the
|
||||
// linked list and the hashByElm for all
|
||||
// others
|
||||
public:
|
||||
Element value; // the data stored in this slot
|
||||
IndexType next; // index of the data following in the linked
|
||||
// list of all items with the same key
|
||||
IndexType prev; // index of the data preceding in the linked
|
||||
// list of all items with the same key
|
||||
uint64_t readHashCache() const { return hashCache; }
|
||||
void writeHashCache(uint64_t v) { hashCache = v; }
|
||||
|
||||
Entry() : hashCache(0), value(), next(INVALID_INDEX), prev(INVALID_INDEX) {}
|
||||
|
||||
private:
|
||||
static IndexType const INVALID_INDEX = ((IndexType)0) - 1;
|
||||
};
|
||||
|
||||
template <class Element, class IndexType>
|
||||
struct Entry<Element, IndexType, false> {
|
||||
Element value; // the data stored in this slot
|
||||
IndexType next; // index of the data following in the linked
|
||||
// list of all items with the same key
|
||||
IndexType prev; // index of the data preceding in the linked
|
||||
// list of all items with the same key
|
||||
uint64_t readHashCache() const { return 0; }
|
||||
void writeHashCache(uint64_t v) { TRI_ASSERT(false); }
|
||||
|
||||
Entry() : value(), next(INVALID_INDEX), prev(INVALID_INDEX) {}
|
||||
|
||||
private:
|
||||
static IndexType const INVALID_INDEX = ((IndexType)0) - 1;
|
||||
};
|
||||
|
||||
template <class Key, class Element, class IndexType = size_t,
|
||||
bool useHashCache = true>
|
||||
class AssocMulti {
|
||||
|
@ -145,12 +110,12 @@ class AssocMulti {
|
|||
typedef std::function<bool(UserData*, Element const&, Element const&)>
|
||||
IsEqualElementElementFuncType;
|
||||
typedef std::function<bool(Element&)> CallbackElementFuncType;
|
||||
|
||||
|
||||
private:
|
||||
typedef Entry<Element, IndexType, useHashCache> EntryType;
|
||||
|
||||
|
||||
typedef arangodb::basics::IndexBucket<EntryType, IndexType, SIZE_MAX> Bucket;
|
||||
|
||||
|
||||
std::vector<Bucket> _buckets;
|
||||
size_t _bucketsMask;
|
||||
|
||||
|
@ -213,7 +178,7 @@ class AssocMulti {
|
|||
}
|
||||
numberBuckets = nr;
|
||||
_bucketsMask = nr - 1;
|
||||
|
||||
|
||||
_buckets.resize(numberBuckets);
|
||||
|
||||
try {
|
||||
|
@ -226,9 +191,7 @@ class AssocMulti {
|
|||
}
|
||||
}
|
||||
|
||||
~AssocMulti() {
|
||||
_buckets.clear();
|
||||
}
|
||||
~AssocMulti() { _buckets.clear(); }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief return the memory used by the hash table
|
||||
|
@ -325,179 +288,102 @@ class AssocMulti {
|
|||
/// @brief adds multiple elements to the array
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int batchInsert(std::function<void*()> const& contextCreator,
|
||||
std::function<void(void*)> const& contextDestroyer,
|
||||
std::vector<Element> const* data,
|
||||
size_t numThreads) {
|
||||
void batchInsert(std::function<void*()> const& contextCreator,
|
||||
std::function<void(void*)> const& contextDestroyer,
|
||||
std::shared_ptr<std::vector<Element> const> data,
|
||||
LocalTaskQueue* queue) {
|
||||
if (data->empty()) {
|
||||
// nothing to do
|
||||
return TRI_ERROR_NO_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
std::atomic<int> res(TRI_ERROR_NO_ERROR);
|
||||
|
||||
std::vector<Element> const& elements = *(data);
|
||||
std::vector<Element> const& elements = *(data.get());
|
||||
|
||||
// set the number of partitioners sensibly
|
||||
size_t numThreads = _buckets.size();
|
||||
if (elements.size() < numThreads) {
|
||||
numThreads = elements.size();
|
||||
}
|
||||
if (numThreads > _buckets.size()) {
|
||||
numThreads = _buckets.size();
|
||||
}
|
||||
|
||||
TRI_ASSERT(numThreads > 0);
|
||||
|
||||
size_t const chunkSize = elements.size() / numThreads;
|
||||
|
||||
typedef std::vector<std::pair<Element, uint64_t>> DocumentsPerBucket;
|
||||
typedef MultiInserterTask<Element, IndexType, useHashCache> Inserter;
|
||||
typedef MultiPartitionerTask<Element, IndexType, useHashCache> Partitioner;
|
||||
|
||||
arangodb::Mutex bucketMapLocker;
|
||||
// allocate working space and coordination tools for tasks
|
||||
|
||||
std::vector<std::vector<DocumentsPerBucket>> allBuckets;
|
||||
allBuckets.resize(_bucketsMask + 1); // initialize to number of buckets
|
||||
std::shared_ptr<std::vector<arangodb::Mutex>> bucketMapLocker;
|
||||
bucketMapLocker.reset(new std::vector<arangodb::Mutex>(_buckets.size()));
|
||||
|
||||
// partition the work into some buckets
|
||||
{
|
||||
std::function<void(size_t, size_t, void*)> partitioner;
|
||||
partitioner = [&](size_t lower, size_t upper, void* userData) -> void {
|
||||
try {
|
||||
std::vector<DocumentsPerBucket> partitions;
|
||||
partitions.resize(_bucketsMask + 1); // initialize to number of buckets
|
||||
|
||||
for (size_t i = lower; i < upper; ++i) {
|
||||
uint64_t hashByKey = _hashElement(userData, elements[i], true);
|
||||
auto bucketId = hashByKey & _bucketsMask;
|
||||
|
||||
partitions[bucketId].emplace_back(elements[i], hashByKey);
|
||||
}
|
||||
|
||||
// transfer ownership to the central map
|
||||
MUTEX_LOCKER(mutexLocker, bucketMapLocker);
|
||||
|
||||
for (size_t i = 0; i < partitions.size(); ++i) {
|
||||
allBuckets[i].emplace_back(std::move(partitions[i]));
|
||||
}
|
||||
} catch (...) {
|
||||
res = TRI_ERROR_INTERNAL;
|
||||
}
|
||||
|
||||
contextDestroyer(userData);
|
||||
};
|
||||
|
||||
std::vector<std::thread> threads;
|
||||
threads.reserve(numThreads);
|
||||
|
||||
try {
|
||||
for (size_t i = 0; i < numThreads; ++i) {
|
||||
size_t lower = i * chunkSize;
|
||||
size_t upper = (i + 1) * chunkSize;
|
||||
|
||||
if (i + 1 == numThreads) {
|
||||
// last chunk. account for potential rounding errors
|
||||
upper = elements.size();
|
||||
} else if (upper > elements.size()) {
|
||||
upper = elements.size();
|
||||
}
|
||||
|
||||
threads.emplace_back(std::thread(partitioner, lower, upper, contextCreator()));
|
||||
}
|
||||
} catch (...) {
|
||||
res = TRI_ERROR_INTERNAL;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < threads.size(); ++i) {
|
||||
// must join threads, otherwise the program will crash
|
||||
threads[i].join();
|
||||
}
|
||||
|
||||
// sort vectors in vectors so that we have a deterministics insertion order
|
||||
for (size_t i = 0; i < allBuckets.size(); ++i) {
|
||||
std::sort(allBuckets[i].begin(), allBuckets[i].end(), [](DocumentsPerBucket const& lhs, DocumentsPerBucket const& rhs) -> bool {
|
||||
if (lhs.empty() && rhs.empty()) {
|
||||
return false;
|
||||
}
|
||||
if (lhs.empty() && !rhs.empty()) {
|
||||
return true;
|
||||
}
|
||||
if (rhs.empty() && !lhs.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return lhs[0].first < rhs[0].first;
|
||||
});
|
||||
}
|
||||
std::shared_ptr<std::vector<std::atomic<size_t>>> bucketFlags;
|
||||
bucketFlags.reset(new std::vector<std::atomic<size_t>>(_buckets.size()));
|
||||
for (size_t i = 0; i < bucketFlags->size(); i++) {
|
||||
(*bucketFlags)[i] = numThreads;
|
||||
}
|
||||
|
||||
if (res.load() != TRI_ERROR_NO_ERROR) {
|
||||
return res.load();
|
||||
}
|
||||
std::shared_ptr<std::vector<std::shared_ptr<Inserter>>> inserters;
|
||||
inserters.reset(new std::vector<std::shared_ptr<Inserter>>);
|
||||
inserters->reserve(_buckets.size());
|
||||
|
||||
// now the data is partitioned...
|
||||
std::shared_ptr<std::vector<std::vector<DocumentsPerBucket>>> allBuckets;
|
||||
allBuckets.reset(
|
||||
new std::vector<std::vector<DocumentsPerBucket>>(_buckets.size()));
|
||||
|
||||
// now insert the bucket data in parallel
|
||||
{
|
||||
auto inserter = [&](size_t chunk, void* userData) -> void {
|
||||
try {
|
||||
for (size_t i = 0; i < allBuckets.size(); ++i) {
|
||||
uint64_t bucketId = i;
|
||||
auto doInsertBinding = [&](
|
||||
UserData* userData, Element const& element, uint64_t hashByKey,
|
||||
Bucket& b, bool const overwrite, bool const checkEquality) -> Element {
|
||||
return doInsert(userData, element, hashByKey, b, overwrite,
|
||||
checkEquality);
|
||||
};
|
||||
|
||||
if (bucketId % numThreads != chunk) {
|
||||
// we're not responsible for this bucket!
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
// create inserter tasks to be dispatched later by partitioners
|
||||
for (size_t i = 0; i < allBuckets->size(); i++) {
|
||||
std::shared_ptr<Inserter> worker;
|
||||
worker.reset(new Inserter(queue, contextDestroyer, &_buckets,
|
||||
doInsertBinding, i, contextCreator(),
|
||||
allBuckets));
|
||||
inserters->emplace_back(worker);
|
||||
}
|
||||
// enqueue partitioner tasks
|
||||
for (size_t i = 0; i < numThreads; ++i) {
|
||||
size_t lower = i * chunkSize;
|
||||
size_t upper = (i + 1) * chunkSize;
|
||||
|
||||
// we're responsible for this bucket!
|
||||
Bucket& b = _buckets[static_cast<size_t>(bucketId)];
|
||||
|
||||
for (auto const& it : allBuckets[i]) {
|
||||
for (auto const& it2 : it) {
|
||||
doInsert(userData, it2.first, it2.second, b, true, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (...) {
|
||||
res = TRI_ERROR_INTERNAL;
|
||||
if (i + 1 == numThreads) {
|
||||
// last chunk. account for potential rounding errors
|
||||
upper = elements.size();
|
||||
} else if (upper > elements.size()) {
|
||||
upper = elements.size();
|
||||
}
|
||||
|
||||
contextDestroyer(userData);
|
||||
};
|
||||
|
||||
std::vector<std::thread> threads;
|
||||
threads.reserve(numThreads);
|
||||
|
||||
try {
|
||||
for (size_t i = 0; i < numThreads; ++i) {
|
||||
threads.emplace_back(std::thread(inserter, i, contextCreator()));
|
||||
}
|
||||
} catch (...) {
|
||||
res = TRI_ERROR_INTERNAL;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < threads.size(); ++i) {
|
||||
// must join threads, otherwise the program will crash
|
||||
threads[i].join();
|
||||
std::shared_ptr<Partitioner> worker;
|
||||
worker.reset(new Partitioner(queue, _hashElement, contextDestroyer,
|
||||
data, lower, upper, contextCreator(),
|
||||
bucketFlags, bucketMapLocker, allBuckets,
|
||||
inserters));
|
||||
queue->enqueue(worker);
|
||||
}
|
||||
} catch (...) {
|
||||
queue->setStatus(TRI_ERROR_INTERNAL);
|
||||
}
|
||||
|
||||
#ifdef TRI_CHECK_MULTI_POINTER_HASH
|
||||
{
|
||||
void* userData = contextCreator();
|
||||
check(userData, true, true);
|
||||
contextDestroyer(userData);
|
||||
auto& checkFn = check;
|
||||
auto callback = [&contextCreator, &contextDestroyer, &checkFn]() -> void {
|
||||
if (queue->status() == TRI_ERROR_NO_ERROR) {
|
||||
void* userData = contextCreator();
|
||||
checkFn(userData, true, true);
|
||||
contextDestroyer(userData);
|
||||
}
|
||||
};
|
||||
std::shared_ptr<arangodb::basics::LocalCallbackTask> cbTask;
|
||||
cbTask.reset(new arangodb::basics::LocalCallbackTask(queue, callback));
|
||||
queue->enqueueCallback(cbTask);
|
||||
}
|
||||
#endif
|
||||
if (res.load() != TRI_ERROR_NO_ERROR) {
|
||||
// Rollback such that the data can be deleted outside
|
||||
void* userData = contextCreator();
|
||||
try {
|
||||
for (auto const& d : *data) {
|
||||
remove(userData, d);
|
||||
}
|
||||
} catch (...) {
|
||||
}
|
||||
contextDestroyer(userData);
|
||||
}
|
||||
return res.load();
|
||||
}
|
||||
|
||||
void truncate(CallbackElementFuncType callback) {
|
||||
|
@ -539,8 +425,9 @@ class AssocMulti {
|
|||
/// @brief adds a key/element to the array
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Element doInsert(UserData* userData, Element const& element, uint64_t hashByKey,
|
||||
Bucket& b, bool const overwrite, bool const checkEquality) {
|
||||
Element doInsert(UserData* userData, Element const& element,
|
||||
uint64_t hashByKey, Bucket& b, bool const overwrite,
|
||||
bool const checkEquality) {
|
||||
// if the checkEquality flag is not set, we do not check for element
|
||||
// equality we use this flag to speed up initial insertion into the
|
||||
// index, i.e. when the index is built for a collection and we know
|
||||
|
@ -574,10 +461,11 @@ class AssocMulti {
|
|||
|
||||
// Now find the first slot with an entry with the same key
|
||||
// that is the start of a linked list, or a free slot:
|
||||
while (b._table[i].value &&
|
||||
(b._table[i].prev != INVALID_INDEX ||
|
||||
(useHashCache && b._table[i].readHashCache() != hashByKey) ||
|
||||
!_isEqualElementElementByKey(userData, element, b._table[i].value))) {
|
||||
while (
|
||||
b._table[i].value &&
|
||||
(b._table[i].prev != INVALID_INDEX ||
|
||||
(useHashCache && b._table[i].readHashCache() != hashByKey) ||
|
||||
!_isEqualElementElementByKey(userData, element, b._table[i].value))) {
|
||||
i = incr(b, i);
|
||||
#ifdef TRI_INTERNAL_STATS
|
||||
// update statistics
|
||||
|
@ -879,10 +767,11 @@ class AssocMulti {
|
|||
#endif
|
||||
|
||||
// search the table
|
||||
while (b._table[i].value &&
|
||||
(b._table[i].prev != INVALID_INDEX ||
|
||||
(useHashCache && b._table[i].readHashCache() != hashByKey) ||
|
||||
!_isEqualElementElementByKey(userData, element, b._table[i].value))) {
|
||||
while (
|
||||
b._table[i].value &&
|
||||
(b._table[i].prev != INVALID_INDEX ||
|
||||
(useHashCache && b._table[i].readHashCache() != hashByKey) ||
|
||||
!_isEqualElementElementByKey(userData, element, b._table[i].value))) {
|
||||
i = incr(b, i);
|
||||
#ifdef TRI_INTERNAL_STATS
|
||||
_nrProbesF++;
|
||||
|
@ -905,8 +794,9 @@ class AssocMulti {
|
|||
/// continuation
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
std::vector<Element>* lookupWithElementByKeyContinue(
|
||||
UserData* userData, Element const& element, size_t limit = 0) const {
|
||||
std::vector<Element>* lookupWithElementByKeyContinue(UserData* userData,
|
||||
Element const& element,
|
||||
size_t limit = 0) const {
|
||||
auto result = std::make_unique<std::vector<Element>>();
|
||||
lookupWithElementByKeyContinue(userData, element, *result.get(), limit);
|
||||
return result.release();
|
||||
|
@ -944,11 +834,11 @@ class AssocMulti {
|
|||
|
||||
// Now find the first slot with an entry with the same key
|
||||
// that is the start of a linked list, or a free slot:
|
||||
while (
|
||||
b._table[i].value &&
|
||||
(b._table[i].prev != INVALID_INDEX ||
|
||||
(useHashCache && b._table[i].readHashCache() != hashByKey) ||
|
||||
!_isEqualElementElementByKey(userData, element, b._table[i].value))) {
|
||||
while (b._table[i].value &&
|
||||
(b._table[i].prev != INVALID_INDEX ||
|
||||
(useHashCache && b._table[i].readHashCache() != hashByKey) ||
|
||||
!_isEqualElementElementByKey(userData, element,
|
||||
b._table[i].value))) {
|
||||
i = incr(b, i);
|
||||
#ifdef TRI_INTERNAL_STATS
|
||||
_nrProbes++;
|
||||
|
@ -1002,7 +892,8 @@ class AssocMulti {
|
|||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief removes an element from the array, caller is responsible to free it
|
||||
/// @brief removes an element from the array, caller is responsible to free
|
||||
/// it
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Element remove(UserData* userData, Element const& element) {
|
||||
|
@ -1149,13 +1040,13 @@ class AssocMulti {
|
|||
|
||||
LOG(TRACE) << "resizing hash " << cb << ", target size: " << targetSize;
|
||||
|
||||
LOG_TOPIC(TRACE, Logger::PERFORMANCE) <<
|
||||
"hash-resize " << cb << ", target size: " << targetSize;
|
||||
LOG_TOPIC(TRACE, Logger::PERFORMANCE) << "hash-resize " << cb
|
||||
<< ", target size: " << targetSize;
|
||||
|
||||
double start = TRI_microtime();
|
||||
|
||||
|
||||
targetSize = TRI_NearPrime(targetSize);
|
||||
|
||||
|
||||
Bucket copy;
|
||||
copy.allocate(targetSize);
|
||||
|
||||
|
@ -1193,19 +1084,21 @@ class AssocMulti {
|
|||
} else {
|
||||
hashByElm = _hashElement(userData, oldTable[k].value, false);
|
||||
}
|
||||
insertFurther(userData, copy, oldTable[k].value, hashByKey, hashByElm,
|
||||
insertPosition);
|
||||
insertFurther(userData, copy, oldTable[k].value, hashByKey,
|
||||
hashByElm, insertPosition);
|
||||
k = oldTable[k].prev;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
b = std::move(copy);
|
||||
|
||||
LOG(TRACE) << "resizing hash " << cb << " done";
|
||||
|
||||
LOG_TOPIC(TRACE, Logger::PERFORMANCE) << "[timer] " << Logger::FIXED(TRI_microtime() - start) << " s, hash-resize, " << cb << ", target size: " << targetSize;
|
||||
LOG_TOPIC(TRACE, Logger::PERFORMANCE)
|
||||
<< "[timer] " << Logger::FIXED(TRI_microtime() - start)
|
||||
<< " s, hash-resize, " << cb << ", target size: " << targetSize;
|
||||
}
|
||||
|
||||
#ifdef TRI_CHECK_MULTI_POINTER_HASH
|
||||
|
@ -1359,10 +1252,11 @@ class AssocMulti {
|
|||
|
||||
// Now find the first slot with an entry with the same key
|
||||
// that is the start of a linked list, or a free slot:
|
||||
while (b._table[i].value &&
|
||||
(b._table[i].prev != INVALID_INDEX ||
|
||||
(useHashCache && b._table[i].readHashCache() != hashByKey) ||
|
||||
!_isEqualElementElementByKey(userData, element, b._table[i].value))) {
|
||||
while (
|
||||
b._table[i].value &&
|
||||
(b._table[i].prev != INVALID_INDEX ||
|
||||
(useHashCache && b._table[i].readHashCache() != hashByKey) ||
|
||||
!_isEqualElementElementByKey(userData, element, b._table[i].value))) {
|
||||
i = incr(b, i);
|
||||
#ifdef TRI_INTERNAL_STATS
|
||||
_nrProbes++;
|
||||
|
|
|
@ -0,0 +1,231 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// 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 Dr. Frank Celler
|
||||
/// @author Martin Schoenert
|
||||
/// @author Max Neunhoeffer
|
||||
/// @author Daniel H. Larkin
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef ARANGODB_BASICS_ASSOC_MULTI_HELPERS_H
|
||||
#define ARANGODB_BASICS_ASSOC_MULTI_HELPERS_H 1
|
||||
|
||||
#include "Basics/Common.h"
|
||||
#include "Basics/IndexBucket.h"
|
||||
#include "Basics/LocalTaskQueue.h"
|
||||
#include "Basics/Mutex.h"
|
||||
#include "Basics/MutexLocker.h"
|
||||
|
||||
namespace arangodb {
|
||||
namespace basics {
|
||||
|
||||
template <class Element, class IndexType, bool useHashCache>
|
||||
struct Entry {
|
||||
private:
|
||||
uint64_t hashCache; // cache the hash value, this stores the
|
||||
// hashByKey for the first element in the
|
||||
// linked list and the hashByElm for all
|
||||
// others
|
||||
public:
|
||||
Element value; // the data stored in this slot
|
||||
IndexType next; // index of the data following in the linked
|
||||
// list of all items with the same key
|
||||
IndexType prev; // index of the data preceding in the linked
|
||||
// list of all items with the same key
|
||||
uint64_t readHashCache() const { return hashCache; }
|
||||
void writeHashCache(uint64_t v) { hashCache = v; }
|
||||
|
||||
Entry() : hashCache(0), value(), next(INVALID_INDEX), prev(INVALID_INDEX) {}
|
||||
|
||||
private:
|
||||
static IndexType const INVALID_INDEX = ((IndexType)0) - 1;
|
||||
};
|
||||
|
||||
template <class Element, class IndexType>
|
||||
struct Entry<Element, IndexType, false> {
|
||||
Element value; // the data stored in this slot
|
||||
IndexType next; // index of the data following in the linked
|
||||
// list of all items with the same key
|
||||
IndexType prev; // index of the data preceding in the linked
|
||||
// list of all items with the same key
|
||||
uint64_t readHashCache() const { return 0; }
|
||||
void writeHashCache(uint64_t v) { TRI_ASSERT(false); }
|
||||
|
||||
Entry() : value(), next(INVALID_INDEX), prev(INVALID_INDEX) {}
|
||||
|
||||
private:
|
||||
static IndexType const INVALID_INDEX = ((IndexType)0) - 1;
|
||||
};
|
||||
|
||||
template <class Element, class IndexType, bool useHashCache>
|
||||
class MultiInserterTask : public LocalTask {
|
||||
private:
|
||||
typedef Entry<Element, IndexType, useHashCache> EntryType;
|
||||
typedef arangodb::basics::IndexBucket<EntryType, IndexType, SIZE_MAX> Bucket;
|
||||
typedef std::vector<std::pair<Element, uint64_t>> DocumentsPerBucket;
|
||||
|
||||
std::function<void(void*)> _contextDestroyer;
|
||||
std::vector<Bucket>* _buckets;
|
||||
std::function<Element(void*, Element const&, uint64_t, Bucket&, bool const,
|
||||
bool const)>
|
||||
_doInsert;
|
||||
|
||||
size_t _i;
|
||||
void* _userData;
|
||||
|
||||
std::shared_ptr<std::vector<std::vector<DocumentsPerBucket>>> _allBuckets;
|
||||
|
||||
public:
|
||||
MultiInserterTask(
|
||||
LocalTaskQueue* queue, std::function<void(void*)> contextDestroyer,
|
||||
std::vector<Bucket>* buckets,
|
||||
std::function<Element(void*, Element const&, uint64_t, Bucket&,
|
||||
bool const, bool const)>
|
||||
doInsert,
|
||||
size_t i, void* userData,
|
||||
std::shared_ptr<std::vector<std::vector<DocumentsPerBucket>>> allBuckets)
|
||||
: LocalTask(queue),
|
||||
_contextDestroyer(contextDestroyer),
|
||||
_buckets(buckets),
|
||||
_doInsert(doInsert),
|
||||
_i(i),
|
||||
_userData(userData),
|
||||
_allBuckets(allBuckets) {}
|
||||
|
||||
void run() {
|
||||
// sort first so we have a deterministic insertion order
|
||||
std::sort((*_allBuckets)[_i].begin(), (*_allBuckets)[_i].end(),
|
||||
[](DocumentsPerBucket const& lhs,
|
||||
DocumentsPerBucket const& rhs) -> bool {
|
||||
if (lhs.empty() && rhs.empty()) {
|
||||
return false;
|
||||
}
|
||||
if (lhs.empty() && !rhs.empty()) {
|
||||
return true;
|
||||
}
|
||||
if (rhs.empty() && !lhs.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return lhs[0].first < rhs[0].first;
|
||||
});
|
||||
// now actually insert them
|
||||
try {
|
||||
Bucket& b = (*_buckets)[static_cast<size_t>(_i)];
|
||||
|
||||
for (auto const& it : (*_allBuckets)[_i]) {
|
||||
for (auto const& it2 : it) {
|
||||
_doInsert(_userData, it2.first, it2.second, b, true, false);
|
||||
}
|
||||
}
|
||||
} catch (...) {
|
||||
_queue->setStatus(TRI_ERROR_INTERNAL);
|
||||
}
|
||||
|
||||
_contextDestroyer(_userData);
|
||||
_queue->join();
|
||||
}
|
||||
};
|
||||
|
||||
template <class Element, class IndexType, bool useHashCache>
|
||||
class MultiPartitionerTask : public LocalTask {
|
||||
private:
|
||||
typedef MultiInserterTask<Element, IndexType, useHashCache> Inserter;
|
||||
typedef std::vector<std::pair<Element, uint64_t>> DocumentsPerBucket;
|
||||
|
||||
std::function<uint64_t(void*, Element const&, bool)> _hashElement;
|
||||
std::function<void(void*)> _contextDestroyer;
|
||||
std::shared_ptr<std::vector<Element> const> _data;
|
||||
std::vector<Element> const* _elements;
|
||||
|
||||
size_t _lower;
|
||||
size_t _upper;
|
||||
void* _userData;
|
||||
|
||||
std::shared_ptr<std::vector<std::atomic<size_t>>> _bucketFlags;
|
||||
std::shared_ptr<std::vector<arangodb::Mutex>> _bucketMapLocker;
|
||||
std::shared_ptr<std::vector<std::vector<DocumentsPerBucket>>> _allBuckets;
|
||||
std::shared_ptr<std::vector<std::shared_ptr<Inserter>>> _inserters;
|
||||
|
||||
uint64_t _bucketsMask;
|
||||
|
||||
public:
|
||||
MultiPartitionerTask(
|
||||
LocalTaskQueue* queue,
|
||||
std::function<uint64_t(void*, Element const&, bool)> hashElement,
|
||||
std::function<void(void*)> const& contextDestroyer,
|
||||
std::shared_ptr<std::vector<Element> const> data, size_t lower,
|
||||
size_t upper, void* userData,
|
||||
std::shared_ptr<std::vector<std::atomic<size_t>>> bucketFlags,
|
||||
std::shared_ptr<std::vector<arangodb::Mutex>> bucketMapLocker,
|
||||
std::shared_ptr<std::vector<std::vector<DocumentsPerBucket>>> allBuckets,
|
||||
std::shared_ptr<std::vector<std::shared_ptr<Inserter>>> inserters)
|
||||
: LocalTask(queue),
|
||||
_hashElement(hashElement),
|
||||
_contextDestroyer(contextDestroyer),
|
||||
_data(data),
|
||||
_elements(_data.get()),
|
||||
_lower(lower),
|
||||
_upper(upper),
|
||||
_userData(userData),
|
||||
_bucketFlags(bucketFlags),
|
||||
_bucketMapLocker(bucketMapLocker),
|
||||
_allBuckets(allBuckets),
|
||||
_inserters(inserters),
|
||||
_bucketsMask(_allBuckets->size() - 1) {}
|
||||
|
||||
void run() {
|
||||
try {
|
||||
std::vector<DocumentsPerBucket> partitions;
|
||||
partitions.resize(
|
||||
_allBuckets->size()); // initialize to number of buckets
|
||||
|
||||
for (size_t i = _lower; i < _upper; ++i) {
|
||||
uint64_t hashByKey = _hashElement(_userData, (*_elements)[i], true);
|
||||
auto bucketId = hashByKey & _bucketsMask;
|
||||
|
||||
partitions[bucketId].emplace_back((*_elements)[i], hashByKey);
|
||||
}
|
||||
|
||||
// transfer ownership to the central map
|
||||
for (size_t i = 0; i < partitions.size(); ++i) {
|
||||
{
|
||||
MUTEX_LOCKER(mutexLocker, (*_bucketMapLocker)[i]);
|
||||
(*_allBuckets)[i].emplace_back(std::move(partitions[i]));
|
||||
(*_bucketFlags)[i]--;
|
||||
if ((*_bucketFlags)[i].load() == 0) {
|
||||
// queue inserter for bucket i
|
||||
_queue->enqueue((*_inserters)[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (...) {
|
||||
_queue->setStatus(TRI_ERROR_INTERNAL);
|
||||
}
|
||||
|
||||
_contextDestroyer(_userData);
|
||||
_queue->join();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace arangodb::basics
|
||||
} // namespace arangodb
|
||||
|
||||
#endif
|
|
@ -21,19 +21,22 @@
|
|||
/// @author Dr. Frank Celler
|
||||
/// @author Martin Schoenert
|
||||
/// @author Michael Hackstein
|
||||
/// @author Daniel H. Larkin
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef ARANGODB_BASICS_ASSOC_UNIQUE_H
|
||||
#define ARANGODB_BASICS_ASSOC_UNIQUE_H 1
|
||||
|
||||
#include "Basics/AssocUniqueHelpers.h"
|
||||
#include "Basics/Common.h"
|
||||
|
||||
#include <thread>
|
||||
#include <velocypack/Builder.h>
|
||||
#include <velocypack/velocypack-aliases.h>
|
||||
#include <thread>
|
||||
|
||||
#include "Basics/AssocHelpers.h"
|
||||
#include "Basics/IndexBucket.h"
|
||||
#include "Basics/LocalTaskQueue.h"
|
||||
#include "Basics/MutexLocker.h"
|
||||
#include "Basics/gcd.h"
|
||||
#include "Basics/prime-numbers.h"
|
||||
|
@ -43,22 +46,6 @@
|
|||
namespace arangodb {
|
||||
namespace basics {
|
||||
|
||||
struct BucketPosition {
|
||||
size_t bucketId;
|
||||
uint64_t position;
|
||||
|
||||
BucketPosition() : bucketId(SIZE_MAX), position(0) {}
|
||||
|
||||
void reset() {
|
||||
bucketId = SIZE_MAX - 1;
|
||||
position = 0;
|
||||
}
|
||||
|
||||
bool operator==(BucketPosition const& other) const {
|
||||
return position == other.position && bucketId == other.bucketId;
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief associative array
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -67,13 +54,15 @@ template <class Key, class Element>
|
|||
class AssocUnique {
|
||||
private:
|
||||
typedef void UserData;
|
||||
typedef arangodb::basics::BucketPosition BucketPosition;
|
||||
|
||||
public:
|
||||
typedef std::function<uint64_t(UserData*, Key const*)> HashKeyFuncType;
|
||||
typedef std::function<uint64_t(UserData*, Element const&)>
|
||||
HashElementFuncType;
|
||||
typedef std::function<bool(UserData*, Key const*, uint64_t hash,
|
||||
Element const&)> IsEqualKeyElementFuncType;
|
||||
Element const&)>
|
||||
IsEqualKeyElementFuncType;
|
||||
typedef std::function<bool(UserData*, Element const&, Element const&)>
|
||||
IsEqualElementElementFuncType;
|
||||
|
||||
|
@ -131,9 +120,7 @@ class AssocUnique {
|
|||
}
|
||||
}
|
||||
|
||||
~AssocUnique() {
|
||||
_buckets.clear();
|
||||
}
|
||||
~AssocUnique() { _buckets.clear(); }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief adhere to the rule of five
|
||||
|
@ -163,8 +150,7 @@ class AssocUnique {
|
|||
if (b._nrAlloc > targetSize && !allowShrink) {
|
||||
return;
|
||||
}
|
||||
if (allowShrink &&
|
||||
b._nrAlloc >= targetSize &&
|
||||
if (allowShrink && b._nrAlloc >= targetSize &&
|
||||
b._nrAlloc < 1.25 * targetSize) {
|
||||
// no need to shrink
|
||||
return;
|
||||
|
@ -180,8 +166,8 @@ class AssocUnique {
|
|||
|
||||
double start = TRI_microtime();
|
||||
if (targetSize > NotificationSizeThreshold) {
|
||||
LOG_TOPIC(TRACE, Logger::PERFORMANCE) <<
|
||||
"hash-resize " << cb << ", target size: " << targetSize;
|
||||
LOG_TOPIC(TRACE, Logger::PERFORMANCE) << "hash-resize " << cb
|
||||
<< ", target size: " << targetSize;
|
||||
}
|
||||
|
||||
TRI_ASSERT(targetSize > 0);
|
||||
|
@ -223,7 +209,9 @@ class AssocUnique {
|
|||
|
||||
LOG(TRACE) << "resizing hash " << cb << " done";
|
||||
|
||||
LOG_TOPIC(TRACE, Logger::PERFORMANCE) << "[timer] " << Logger::FIXED(TRI_microtime() - start) << " s, hash-resize, " << cb << ", target size: " << targetSize;
|
||||
LOG_TOPIC(TRACE, Logger::PERFORMANCE)
|
||||
<< "[timer] " << Logger::FIXED(TRI_microtime() - start)
|
||||
<< " s, hash-resize, " << cb << ", target size: " << targetSize;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -272,19 +260,19 @@ class AssocUnique {
|
|||
/// This does not resize and expects to have enough space
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int doInsert(UserData* userData, Element const& element, Bucket& b, uint64_t hash) {
|
||||
int doInsert(UserData* userData, Element const& element, Bucket& b,
|
||||
uint64_t hash) {
|
||||
uint64_t const n = b._nrAlloc;
|
||||
uint64_t i = hash % n;
|
||||
uint64_t k = i;
|
||||
|
||||
for (; i < n && b._table[i] &&
|
||||
!_isEqualElementElementByKey(userData, element, b._table[i]);
|
||||
!_isEqualElementElementByKey(userData, element, b._table[i]);
|
||||
++i)
|
||||
;
|
||||
if (i == n) {
|
||||
for (i = 0;
|
||||
i < k && b._table[i] &&
|
||||
!_isEqualElementElementByKey(userData, element, b._table[i]);
|
||||
for (i = 0; i < k && b._table[i] &&
|
||||
!_isEqualElementElementByKey(userData, element, b._table[i]);
|
||||
++i)
|
||||
;
|
||||
}
|
||||
|
@ -310,9 +298,7 @@ class AssocUnique {
|
|||
}
|
||||
}
|
||||
|
||||
size_t buckets() const {
|
||||
return _buckets.size();
|
||||
}
|
||||
size_t buckets() const { return _buckets.size(); }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief checks if this index is empty
|
||||
|
@ -350,7 +336,7 @@ class AssocUnique {
|
|||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
|
||||
size_t capacity() const {
|
||||
size_t sum = 0;
|
||||
for (auto& b : _buckets) {
|
||||
|
@ -409,13 +395,12 @@ class AssocUnique {
|
|||
uint64_t k = i;
|
||||
|
||||
for (; i < n && b._table[i] &&
|
||||
!_isEqualElementElementByKey(userData, element, b._table[i]);
|
||||
!_isEqualElementElementByKey(userData, element, b._table[i]);
|
||||
++i)
|
||||
;
|
||||
if (i == n) {
|
||||
for (i = 0;
|
||||
i < k && b._table[i] &&
|
||||
!_isEqualElementElementByKey(userData, element, b._table[i]);
|
||||
for (i = 0; i < k && b._table[i] &&
|
||||
!_isEqualElementElementByKey(userData, element, b._table[i]);
|
||||
++i)
|
||||
;
|
||||
}
|
||||
|
@ -444,12 +429,12 @@ class AssocUnique {
|
|||
uint64_t k = i;
|
||||
|
||||
for (; i < n && b._table[i] &&
|
||||
!_isEqualKeyElement(userData, key, hash, b._table[i]);
|
||||
!_isEqualKeyElement(userData, key, hash, b._table[i]);
|
||||
++i)
|
||||
;
|
||||
if (i == n) {
|
||||
for (i = 0; i < k && b._table[i] &&
|
||||
!_isEqualKeyElement(userData, key, hash, b._table[i]);
|
||||
!_isEqualKeyElement(userData, key, hash, b._table[i]);
|
||||
++i)
|
||||
;
|
||||
}
|
||||
|
@ -461,7 +446,7 @@ class AssocUnique {
|
|||
|
||||
return b._table[i];
|
||||
}
|
||||
|
||||
|
||||
Element* findByKeyRef(UserData* userData, Key const* key) const {
|
||||
uint64_t hash = _hashKey(userData, key);
|
||||
uint64_t i = hash;
|
||||
|
@ -473,12 +458,12 @@ class AssocUnique {
|
|||
uint64_t k = i;
|
||||
|
||||
for (; i < n && b._table[i] &&
|
||||
!_isEqualKeyElement(userData, key, hash, b._table[i]);
|
||||
!_isEqualKeyElement(userData, key, hash, b._table[i]);
|
||||
++i)
|
||||
;
|
||||
if (i == n) {
|
||||
for (i = 0; i < k && b._table[i] &&
|
||||
!_isEqualKeyElement(userData, key, hash, b._table[i]);
|
||||
!_isEqualKeyElement(userData, key, hash, b._table[i]);
|
||||
++i)
|
||||
;
|
||||
}
|
||||
|
@ -510,12 +495,12 @@ class AssocUnique {
|
|||
uint64_t k = i;
|
||||
|
||||
for (; i < n && b._table[i] &&
|
||||
!_isEqualKeyElement(userData, key, hash, b._table[i]);
|
||||
!_isEqualKeyElement(userData, key, hash, b._table[i]);
|
||||
++i)
|
||||
;
|
||||
if (i == n) {
|
||||
for (i = 0; i < k && b._table[i] &&
|
||||
!_isEqualKeyElement(userData, key, hash, b._table[i]);
|
||||
!_isEqualKeyElement(userData, key, hash, b._table[i]);
|
||||
++i)
|
||||
;
|
||||
}
|
||||
|
@ -581,194 +566,109 @@ class AssocUnique {
|
|||
/// @brief adds multiple elements to the array
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int batchInsert(std::function<void*()> const& contextCreator,
|
||||
std::function<void(void*)> const& contextDestroyer,
|
||||
std::vector<Element> const* data,
|
||||
size_t numThreads) {
|
||||
void batchInsert(std::function<void*()> const& contextCreator,
|
||||
std::function<void(void*)> const& contextDestroyer,
|
||||
std::shared_ptr<std::vector<Element> const> data,
|
||||
arangodb::basics::LocalTaskQueue* queue) {
|
||||
TRI_ASSERT(queue != nullptr);
|
||||
if (data->empty()) {
|
||||
// nothing to do
|
||||
return TRI_ERROR_NO_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
std::atomic<int> res(TRI_ERROR_NO_ERROR);
|
||||
std::vector<Element> const& elements = *(data);
|
||||
std::vector<Element> const& elements = *(data.get());
|
||||
|
||||
// set number of partitioners sensibly
|
||||
size_t numThreads = _buckets.size();
|
||||
if (elements.size() < numThreads) {
|
||||
numThreads = elements.size();
|
||||
}
|
||||
if (numThreads > _buckets.size()) {
|
||||
numThreads = _buckets.size();
|
||||
}
|
||||
|
||||
TRI_ASSERT(numThreads > 0);
|
||||
|
||||
size_t const chunkSize = elements.size() / numThreads;
|
||||
|
||||
typedef std::vector<std::pair<Element, uint64_t>> DocumentsPerBucket;
|
||||
arangodb::Mutex bucketMapLocker;
|
||||
typedef UniqueInserterTask<Element> Inserter;
|
||||
typedef UniquePartitionerTask<Element> Partitioner;
|
||||
|
||||
std::unordered_map<uint64_t, std::vector<DocumentsPerBucket>> allBuckets;
|
||||
// allocate working space and coordination tools for tasks
|
||||
|
||||
// partition the work into some buckets
|
||||
{
|
||||
auto partitioner = [&](size_t lower, size_t upper, void* userData) -> void {
|
||||
try {
|
||||
std::unordered_map<uint64_t, DocumentsPerBucket> partitions;
|
||||
std::shared_ptr<std::vector<arangodb::Mutex>> bucketMapLocker;
|
||||
bucketMapLocker.reset(new std::vector<arangodb::Mutex>(_buckets.size()));
|
||||
|
||||
for (size_t i = lower; i < upper; ++i) {
|
||||
uint64_t hash = _hashElement(userData, elements[i]);
|
||||
auto bucketId = hash & _bucketsMask;
|
||||
|
||||
auto it = partitions.find(bucketId);
|
||||
|
||||
if (it == partitions.end()) {
|
||||
it = partitions.emplace(bucketId, DocumentsPerBucket()).first;
|
||||
}
|
||||
|
||||
(*it).second.emplace_back(elements[i], hash);
|
||||
}
|
||||
|
||||
// transfer ownership to the central map
|
||||
MUTEX_LOCKER(mutexLocker, bucketMapLocker);
|
||||
|
||||
for (auto& it : partitions) {
|
||||
auto it2 = allBuckets.find(it.first);
|
||||
|
||||
if (it2 == allBuckets.end()) {
|
||||
it2 = allBuckets.emplace(it.first,
|
||||
std::vector<DocumentsPerBucket>()).first;
|
||||
}
|
||||
|
||||
(*it2).second.emplace_back(std::move(it.second));
|
||||
}
|
||||
} catch (...) {
|
||||
res = TRI_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
contextDestroyer(userData);
|
||||
};
|
||||
|
||||
std::vector<std::thread> threads;
|
||||
threads.reserve(numThreads);
|
||||
|
||||
try {
|
||||
for (size_t i = 0; i < numThreads; ++i) {
|
||||
size_t lower = i * chunkSize;
|
||||
size_t upper = (i + 1) * chunkSize;
|
||||
|
||||
if (i + 1 == numThreads) {
|
||||
// last chunk. account for potential rounding errors
|
||||
upper = elements.size();
|
||||
} else if (upper > elements.size()) {
|
||||
upper = elements.size();
|
||||
}
|
||||
|
||||
threads.emplace_back(std::thread(partitioner, lower, upper, contextCreator()));
|
||||
}
|
||||
} catch (...) {
|
||||
res = TRI_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < threads.size(); ++i) {
|
||||
// must join threads, otherwise the program will crash
|
||||
threads[i].join();
|
||||
}
|
||||
std::shared_ptr<std::vector<std::atomic<size_t>>> bucketFlags;
|
||||
bucketFlags.reset(new std::vector<std::atomic<size_t>>(_buckets.size()));
|
||||
for (size_t i = 0; i < bucketFlags->size(); i++) {
|
||||
(*bucketFlags)[i] = numThreads;
|
||||
}
|
||||
|
||||
if (res.load() != TRI_ERROR_NO_ERROR) {
|
||||
return res.load();
|
||||
}
|
||||
std::shared_ptr<std::vector<std::shared_ptr<Inserter>>> inserters;
|
||||
inserters.reset(new std::vector<std::shared_ptr<Inserter>>);
|
||||
inserters->reserve(_buckets.size());
|
||||
|
||||
// now the data is partitioned...
|
||||
std::shared_ptr<std::vector<std::vector<DocumentsPerBucket>>> allBuckets;
|
||||
allBuckets.reset(
|
||||
new std::vector<std::vector<DocumentsPerBucket>>(_buckets.size()));
|
||||
|
||||
// now insert the bucket data in parallel
|
||||
{
|
||||
auto inserter = [&](size_t chunk, void* userData) -> void {
|
||||
try {
|
||||
for (auto const& it : allBuckets) {
|
||||
uint64_t bucketId = it.first;
|
||||
auto doInsertBinding = [&](UserData* userData, Element const& element,
|
||||
Bucket& b, uint64_t hashByKey) -> int {
|
||||
return doInsert(userData, element, b, hashByKey);
|
||||
};
|
||||
auto checkResizeBinding = [&](UserData* userData, Bucket& b,
|
||||
uint64_t expected) -> bool {
|
||||
return checkResize(userData, b, expected);
|
||||
};
|
||||
|
||||
if (bucketId % numThreads != chunk) {
|
||||
// we're not responsible for this bucket!
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
// generate inserter tasks to be dispatched later by partitioners
|
||||
for (size_t i = 0; i < allBuckets->size(); i++) {
|
||||
std::shared_ptr<Inserter> worker;
|
||||
worker.reset(new Inserter(queue, contextDestroyer, &_buckets,
|
||||
doInsertBinding, checkResizeBinding, i,
|
||||
contextCreator(), allBuckets));
|
||||
inserters->emplace_back(worker);
|
||||
}
|
||||
// queue partitioner tasks
|
||||
for (size_t i = 0; i < numThreads; ++i) {
|
||||
size_t lower = i * chunkSize;
|
||||
size_t upper = (i + 1) * chunkSize;
|
||||
|
||||
// we're responsible for this bucket!
|
||||
Bucket& b = _buckets[static_cast<size_t>(bucketId)];
|
||||
uint64_t expected = 0;
|
||||
|
||||
for (auto const& it2 : it.second) {
|
||||
expected += it2.size();
|
||||
}
|
||||
|
||||
if (!checkResize(userData, b, expected)) {
|
||||
res = TRI_ERROR_OUT_OF_MEMORY;
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto const& it2 : it.second) {
|
||||
for (auto const& it3 : it2) {
|
||||
doInsert(userData, it3.first, b, it3.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (...) {
|
||||
res = TRI_ERROR_OUT_OF_MEMORY;
|
||||
if (i + 1 == numThreads) {
|
||||
// last chunk. account for potential rounding errors
|
||||
upper = elements.size();
|
||||
} else if (upper > elements.size()) {
|
||||
upper = elements.size();
|
||||
}
|
||||
|
||||
contextDestroyer(userData);
|
||||
};
|
||||
|
||||
std::vector<std::thread> threads;
|
||||
threads.reserve(numThreads);
|
||||
|
||||
try {
|
||||
for (size_t i = 0; i < numThreads; ++i) {
|
||||
threads.emplace_back(std::thread(inserter, i, contextCreator()));
|
||||
}
|
||||
} catch (...) {
|
||||
res = TRI_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < threads.size(); ++i) {
|
||||
// must join threads, otherwise the program will crash
|
||||
threads[i].join();
|
||||
std::shared_ptr<Partitioner> worker;
|
||||
worker.reset(new Partitioner(queue, _hashElement, contextDestroyer,
|
||||
data, lower, upper, contextCreator(),
|
||||
bucketFlags, bucketMapLocker, allBuckets,
|
||||
inserters));
|
||||
queue->enqueue(worker);
|
||||
}
|
||||
} catch (...) {
|
||||
queue->setStatus(TRI_ERROR_INTERNAL);
|
||||
}
|
||||
|
||||
if (res.load() != TRI_ERROR_NO_ERROR) {
|
||||
// Rollback such that the data can be deleted outside
|
||||
void* userData = contextCreator();
|
||||
try {
|
||||
for (auto const& d : *data) {
|
||||
remove(userData, d);
|
||||
}
|
||||
} catch (...) {
|
||||
}
|
||||
contextDestroyer(userData);
|
||||
}
|
||||
return res.load();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief helper to heal a hole where we deleted something
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void healHole(UserData* userData, Bucket& b, uint64_t i) {
|
||||
// ...........................................................................
|
||||
//
|
||||
// remove item - destroy any internal memory associated with the
|
||||
// element structure
|
||||
// ...........................................................................
|
||||
//
|
||||
|
||||
b._table[i] = Element();
|
||||
b._nrUsed--;
|
||||
|
||||
uint64_t const n = b._nrAlloc;
|
||||
|
||||
// ...........................................................................
|
||||
//
|
||||
// and now check the following places for items to move closer together
|
||||
// so that there are no gaps in the array
|
||||
// ...........................................................................
|
||||
//
|
||||
|
||||
uint64_t k = TRI_IncModU64(i, n);
|
||||
|
||||
|
@ -805,12 +705,12 @@ class AssocUnique {
|
|||
uint64_t k = i;
|
||||
|
||||
for (; i < n && b._table[i] &&
|
||||
!_isEqualKeyElement(userData, key, hash, b._table[i]);
|
||||
!_isEqualKeyElement(userData, key, hash, b._table[i]);
|
||||
++i)
|
||||
;
|
||||
if (i == n) {
|
||||
for (i = 0; i < k && b._table[i] &&
|
||||
!_isEqualKeyElement(userData, key, hash, b._table[i]);
|
||||
!_isEqualKeyElement(userData, key, hash, b._table[i]);
|
||||
++i)
|
||||
;
|
||||
}
|
||||
|
@ -837,12 +737,12 @@ class AssocUnique {
|
|||
uint64_t k = i;
|
||||
|
||||
for (; i < n && b._table[i] &&
|
||||
!_isEqualElementElement(userData, element, b._table[i]);
|
||||
!_isEqualElementElement(userData, element, b._table[i]);
|
||||
++i)
|
||||
;
|
||||
if (i == n) {
|
||||
for (i = 0; i < k && b._table[i] &&
|
||||
!_isEqualElementElement(userData, element, b._table[i]);
|
||||
!_isEqualElementElement(userData, element, b._table[i]);
|
||||
++i)
|
||||
;
|
||||
}
|
||||
|
@ -868,7 +768,7 @@ class AssocUnique {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// @brief a method to iterate over all elements in a bucket. this method
|
||||
/// can NOT be used for deleting elements
|
||||
bool invokeOnAllElements(CallbackElementFuncType const& callback, Bucket& b) {
|
||||
|
|
|
@ -0,0 +1,201 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// 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 Dr. Frank Celler
|
||||
/// @author Martin Schoenert
|
||||
/// @author Michael Hackstein
|
||||
/// @author Daniel H. Larkin
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef ARANGODB_BASICS_ASSOC_UNIQUE_HELPERS_H
|
||||
#define ARANGODB_BASICS_ASSOC_UNIQUE_HELPERS_H 1
|
||||
|
||||
#include "Basics/Common.h"
|
||||
|
||||
#include "Basics/IndexBucket.h"
|
||||
#include "Basics/LocalTaskQueue.h"
|
||||
#include "Basics/MutexLocker.h"
|
||||
|
||||
namespace arangodb {
|
||||
namespace basics {
|
||||
|
||||
struct BucketPosition {
|
||||
size_t bucketId;
|
||||
uint64_t position;
|
||||
|
||||
BucketPosition() : bucketId(SIZE_MAX), position(0) {}
|
||||
|
||||
void reset() {
|
||||
bucketId = SIZE_MAX - 1;
|
||||
position = 0;
|
||||
}
|
||||
|
||||
bool operator==(BucketPosition const& other) const {
|
||||
return position == other.position && bucketId == other.bucketId;
|
||||
}
|
||||
};
|
||||
|
||||
template <class Element>
|
||||
class UniqueInserterTask : public LocalTask {
|
||||
private:
|
||||
typedef arangodb::basics::IndexBucket<Element, uint64_t, SIZE_MAX> Bucket;
|
||||
typedef std::vector<std::pair<Element, uint64_t>> DocumentsPerBucket;
|
||||
|
||||
std::function<void(void*)> _contextDestroyer;
|
||||
std::vector<Bucket>* _buckets;
|
||||
std::function<int(void*, Element const&, Bucket&, uint64_t)> _doInsert;
|
||||
std::function<bool(void*, Bucket&, uint64_t)> _checkResize;
|
||||
|
||||
size_t _i;
|
||||
void* _userData;
|
||||
|
||||
std::shared_ptr<std::vector<std::vector<DocumentsPerBucket>>> _allBuckets;
|
||||
|
||||
public:
|
||||
UniqueInserterTask(
|
||||
LocalTaskQueue* queue, std::function<void(void*)> contextDestroyer,
|
||||
std::vector<Bucket>* buckets,
|
||||
std::function<int(void*, Element const&, Bucket&, uint64_t)> doInsert,
|
||||
std::function<bool(void*, Bucket&, uint64_t)> checkResize, size_t i,
|
||||
void* userData,
|
||||
std::shared_ptr<std::vector<std::vector<DocumentsPerBucket>>> allBuckets)
|
||||
: LocalTask(queue),
|
||||
_contextDestroyer(contextDestroyer),
|
||||
_buckets(buckets),
|
||||
_doInsert(doInsert),
|
||||
_checkResize(checkResize),
|
||||
_i(i),
|
||||
_userData(userData),
|
||||
_allBuckets(allBuckets) {}
|
||||
|
||||
void run() {
|
||||
// actually insert them
|
||||
try {
|
||||
Bucket& b = (*_buckets)[static_cast<size_t>(_i)];
|
||||
|
||||
for (auto const& it : (*_allBuckets)[_i]) {
|
||||
uint64_t expected = it.size();
|
||||
if (!_checkResize(_userData, b, expected)) {
|
||||
_queue->setStatus(TRI_ERROR_OUT_OF_MEMORY);
|
||||
_queue->join();
|
||||
return;
|
||||
}
|
||||
for (auto const& it2 : it) {
|
||||
int status = _doInsert(_userData, it2.first, b, it2.second);
|
||||
if (status != TRI_ERROR_NO_ERROR) {
|
||||
_queue->setStatus(status);
|
||||
_queue->join();
|
||||
_contextDestroyer(_userData);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (...) {
|
||||
_queue->setStatus(TRI_ERROR_INTERNAL);
|
||||
}
|
||||
|
||||
_contextDestroyer(_userData);
|
||||
_queue->join();
|
||||
}
|
||||
};
|
||||
|
||||
template <class Element>
|
||||
class UniquePartitionerTask : public LocalTask {
|
||||
private:
|
||||
typedef UniqueInserterTask<Element> Inserter;
|
||||
typedef std::vector<std::pair<Element, uint64_t>> DocumentsPerBucket;
|
||||
|
||||
std::function<uint64_t(void*, Element const&)> _hashElement;
|
||||
std::function<void(void*)> _contextDestroyer;
|
||||
std::shared_ptr<std::vector<Element> const> _data;
|
||||
std::vector<Element> const* _elements;
|
||||
|
||||
size_t _lower;
|
||||
size_t _upper;
|
||||
void* _userData;
|
||||
|
||||
std::shared_ptr<std::vector<std::atomic<size_t>>> _bucketFlags;
|
||||
std::shared_ptr<std::vector<arangodb::Mutex>> _bucketMapLocker;
|
||||
std::shared_ptr<std::vector<std::vector<DocumentsPerBucket>>> _allBuckets;
|
||||
std::shared_ptr<std::vector<std::shared_ptr<Inserter>>> _inserters;
|
||||
|
||||
uint64_t _bucketsMask;
|
||||
|
||||
public:
|
||||
UniquePartitionerTask(
|
||||
LocalTaskQueue* queue,
|
||||
std::function<uint64_t(void*, Element const&)> hashElement,
|
||||
std::function<void(void*)> const& contextDestroyer,
|
||||
std::shared_ptr<std::vector<Element> const> data, size_t lower,
|
||||
size_t upper, void* userData,
|
||||
std::shared_ptr<std::vector<std::atomic<size_t>>> bucketFlags,
|
||||
std::shared_ptr<std::vector<arangodb::Mutex>> bucketMapLocker,
|
||||
std::shared_ptr<std::vector<std::vector<DocumentsPerBucket>>> allBuckets,
|
||||
std::shared_ptr<std::vector<std::shared_ptr<Inserter>>> inserters)
|
||||
: LocalTask(queue),
|
||||
_hashElement(hashElement),
|
||||
_contextDestroyer(contextDestroyer),
|
||||
_data(data),
|
||||
_elements(_data.get()),
|
||||
_lower(lower),
|
||||
_upper(upper),
|
||||
_userData(userData),
|
||||
_bucketFlags(bucketFlags),
|
||||
_bucketMapLocker(bucketMapLocker),
|
||||
_allBuckets(allBuckets),
|
||||
_inserters(inserters),
|
||||
_bucketsMask(_allBuckets->size() - 1) {}
|
||||
|
||||
void run() {
|
||||
try {
|
||||
std::vector<DocumentsPerBucket> partitions;
|
||||
partitions.resize(
|
||||
_allBuckets->size()); // initialize to number of buckets
|
||||
|
||||
for (size_t i = _lower; i < _upper; ++i) {
|
||||
uint64_t hashByKey = _hashElement(_userData, (*_elements)[i]);
|
||||
auto bucketId = hashByKey & _bucketsMask;
|
||||
|
||||
partitions[bucketId].emplace_back((*_elements)[i], hashByKey);
|
||||
}
|
||||
|
||||
// transfer ownership to the central map
|
||||
for (size_t i = 0; i < partitions.size(); ++i) {
|
||||
MUTEX_LOCKER(mutexLocker, (*_bucketMapLocker)[i]);
|
||||
(*_allBuckets)[i].emplace_back(std::move(partitions[i]));
|
||||
(*_bucketFlags)[i]--;
|
||||
if ((*_bucketFlags)[i].load() == 0) {
|
||||
// queue inserter for bucket i
|
||||
_queue->enqueue((*_inserters)[i]);
|
||||
}
|
||||
}
|
||||
} catch (...) {
|
||||
_queue->setStatus(TRI_ERROR_INTERNAL);
|
||||
}
|
||||
|
||||
_contextDestroyer(_userData);
|
||||
_queue->join();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace basics
|
||||
} // namespace arangodb
|
||||
|
||||
#endif
|
|
@ -0,0 +1,207 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// 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 Daniel H. Larkin
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "LocalTaskQueue.h"
|
||||
#include "Basics/ConditionLocker.h"
|
||||
#include "Basics/MutexLocker.h"
|
||||
#include "Basics/asio-helper.h"
|
||||
|
||||
using namespace arangodb::basics;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief create a task tied to the specified queue
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
LocalTask::LocalTask(LocalTaskQueue* queue) : _queue(queue) {}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief dispatch this task to the underlying io_service
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void LocalTask::dispatch() {
|
||||
auto self = shared_from_this();
|
||||
_queue->ioService()->post([self, this]() { run(); });
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief create a callback task tied to the specified queue
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
LocalCallbackTask::LocalCallbackTask(LocalTaskQueue* queue,
|
||||
std::function<void()> cb)
|
||||
: _queue(queue), _cb(cb) {}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief run the callback and join
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void LocalCallbackTask::run() {
|
||||
try {
|
||||
_cb();
|
||||
} catch (...) {
|
||||
}
|
||||
_queue->join();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief dispatch this task to the underlying io_service
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void LocalCallbackTask::dispatch() {
|
||||
auto self = shared_from_this();
|
||||
_queue->ioService()->post([self, this]() { run(); });
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief create a queue using the specified io_service
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
LocalTaskQueue::LocalTaskQueue(boost::asio::io_service* ioService)
|
||||
: _ioService(ioService),
|
||||
_queue(),
|
||||
_callbackQueue(),
|
||||
_condition(),
|
||||
_mutex(),
|
||||
_missing(0),
|
||||
_status(TRI_ERROR_NO_ERROR) {
|
||||
TRI_ASSERT(_ioService != nullptr);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief exposes underlying io_service
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
boost::asio::io_service* LocalTaskQueue::ioService() { return _ioService; }
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief destroy the queue.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
LocalTaskQueue::~LocalTaskQueue() {}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief enqueue a task to be run
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void LocalTaskQueue::enqueue(std::shared_ptr<LocalTask> task) {
|
||||
MUTEX_LOCKER(locker, _mutex);
|
||||
_queue.push(task);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief enqueue a callback task to be run after all normal tasks finish;
|
||||
/// useful for cleanup tasks
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void LocalTaskQueue::enqueueCallback(std::shared_ptr<LocalCallbackTask> task) {
|
||||
MUTEX_LOCKER(locker, _mutex);
|
||||
_callbackQueue.push(task);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief join a single task. reduces the number of waiting tasks and wakes
|
||||
/// up the queue's dispatchAndWait() routine
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void LocalTaskQueue::join() {
|
||||
CONDITION_LOCKER(guard, _condition);
|
||||
TRI_ASSERT(_missing > 0);
|
||||
--_missing;
|
||||
_condition.signal();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief dispatch all tasks, including those that are queued while running,
|
||||
/// and wait for all tasks to join; then dispatch all callback tasks and wait
|
||||
/// for them to join
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void LocalTaskQueue::dispatchAndWait() {
|
||||
// regular task loop
|
||||
if (!_queue.empty()) {
|
||||
while (true) {
|
||||
CONDITION_LOCKER(guard, _condition);
|
||||
|
||||
{
|
||||
MUTEX_LOCKER(locker, _mutex);
|
||||
// dispatch all newly queued tasks
|
||||
if (_status == TRI_ERROR_NO_ERROR) {
|
||||
while (!_queue.empty()) {
|
||||
auto task = _queue.front();
|
||||
task->dispatch();
|
||||
_queue.pop();
|
||||
++_missing;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_missing == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
guard.wait(100000);
|
||||
}
|
||||
}
|
||||
|
||||
// callback task loop
|
||||
if (!_callbackQueue.empty()) {
|
||||
while (true) {
|
||||
CONDITION_LOCKER(guard, _condition);
|
||||
|
||||
{
|
||||
MUTEX_LOCKER(locker, _mutex);
|
||||
// dispatch all newly queued callbacks
|
||||
while (!_callbackQueue.empty()) {
|
||||
auto task = _callbackQueue.front();
|
||||
task->dispatch();
|
||||
_callbackQueue.pop();
|
||||
++_missing;
|
||||
}
|
||||
}
|
||||
|
||||
if (_missing == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
guard.wait(100000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief set status of queue
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void LocalTaskQueue::setStatus(int status) {
|
||||
MUTEX_LOCKER(locker, _mutex);
|
||||
_status = status;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief return overall status of queue tasks
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int LocalTaskQueue::status() {
|
||||
MUTEX_LOCKER(locker, _mutex);
|
||||
return _status;
|
||||
}
|
|
@ -0,0 +1,190 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// 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 Daniel H. Larkin
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef ARANGODB_BASICS_LOCAL_TASK_QUEUE_H
|
||||
#define ARANGODB_BASICS_LOCAL_TASK_QUEUE_H 1
|
||||
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
|
||||
#include "Basics/Common.h"
|
||||
#include "Basics/ConditionVariable.h"
|
||||
#include "Basics/Mutex.h"
|
||||
#include "Basics/asio-helper.h"
|
||||
|
||||
namespace arangodb {
|
||||
namespace basics {
|
||||
|
||||
class LocalTaskQueue;
|
||||
|
||||
class LocalTask : public std::enable_shared_from_this<LocalTask> {
|
||||
public:
|
||||
LocalTask() = delete;
|
||||
LocalTask(LocalTask const&) = delete;
|
||||
LocalTask& operator=(LocalTask const&) = delete;
|
||||
|
||||
explicit LocalTask(LocalTaskQueue* queue);
|
||||
virtual ~LocalTask() {}
|
||||
|
||||
virtual void run() = 0;
|
||||
void dispatch();
|
||||
|
||||
protected:
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief the underlying queue
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
LocalTaskQueue* _queue;
|
||||
};
|
||||
|
||||
class LocalCallbackTask
|
||||
: public std::enable_shared_from_this<LocalCallbackTask> {
|
||||
public:
|
||||
LocalCallbackTask() = delete;
|
||||
LocalCallbackTask(LocalCallbackTask const&) = delete;
|
||||
LocalCallbackTask& operator=(LocalCallbackTask const&) = delete;
|
||||
|
||||
LocalCallbackTask(LocalTaskQueue* queue, std::function<void()> cb);
|
||||
virtual ~LocalCallbackTask() {}
|
||||
|
||||
virtual void run();
|
||||
void dispatch();
|
||||
|
||||
protected:
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief the underlying queue
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
LocalTaskQueue* _queue;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief the callback executed by run() (any exceptions will be caught and
|
||||
/// ignored; must not call queue->setStatus() or queue->enqueue())
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
std::function<void()> _cb;
|
||||
};
|
||||
|
||||
class LocalTaskQueue {
|
||||
public:
|
||||
LocalTaskQueue(LocalTaskQueue const&) = delete;
|
||||
LocalTaskQueue& operator=(LocalTaskQueue const&) = delete;
|
||||
|
||||
explicit LocalTaskQueue(boost::asio::io_service*);
|
||||
|
||||
~LocalTaskQueue();
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief exposes underlying io_service
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
boost::asio::io_service* ioService();
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief enqueue a task to be run
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void enqueue(std::shared_ptr<LocalTask>);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief enqueue a callback task to be run after all normal tasks finish;
|
||||
/// useful for cleanup tasks
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void enqueueCallback(std::shared_ptr<LocalCallbackTask>);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief join a single task. reduces the number of waiting tasks and wakes
|
||||
/// up the queues's dispatchAndWait() routine
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void join();
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief dispatch all tasks, including those that are queued while running,
|
||||
/// and wait for all tasks to join; then dispatch all callback tasks and wait
|
||||
/// for them to join
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void dispatchAndWait();
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief set status of queue
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void setStatus(int);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief return overall status of queue tasks
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int status();
|
||||
|
||||
private:
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief io_service to dispatch tasks to
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
boost::asio::io_service* _ioService;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief internal task queue
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
std::queue<std::shared_ptr<LocalTask>> _queue;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief internal callback task queue
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
std::queue<std::shared_ptr<LocalCallbackTask>> _callbackQueue;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief condition variable
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
arangodb::basics::ConditionVariable _condition;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief internal mutex
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Mutex _mutex;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief number of dispatched, non-joined tasks
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
size_t _missing;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief overall status of queue tasks
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int _status;
|
||||
};
|
||||
|
||||
} // namespace arangodb::basics
|
||||
} // namespace arangodb
|
||||
|
||||
#endif
|
|
@ -1,104 +0,0 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// 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 "ThreadPool.h"
|
||||
|
||||
#include "Basics/WorkerThread.h"
|
||||
|
||||
using namespace arangodb::basics;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief create a pool with the specified size of worker threads
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ThreadPool::ThreadPool(size_t size, std::string const& name)
|
||||
: _condition(), _threads(), _tasks(), _name(name), _stopping(false) {
|
||||
_threads.reserve(size);
|
||||
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
auto workerThread = new WorkerThread(this);
|
||||
|
||||
try {
|
||||
_threads.emplace_back(workerThread);
|
||||
} catch (...) {
|
||||
// clean up
|
||||
delete workerThread;
|
||||
for (auto& it : _threads) {
|
||||
delete it;
|
||||
}
|
||||
_threads.clear();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
// now start them all
|
||||
for (auto& it : _threads) {
|
||||
it->start();
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief destroy the pool
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ThreadPool::~ThreadPool() {
|
||||
_stopping = true;
|
||||
{
|
||||
CONDITION_LOCKER(guard, _condition);
|
||||
_condition.broadcast();
|
||||
}
|
||||
|
||||
for (auto* it : _threads) {
|
||||
it->waitForDone();
|
||||
}
|
||||
|
||||
for (auto* it : _threads) {
|
||||
delete it;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief dequeue a task
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool ThreadPool::dequeue(std::function<void()>& result) {
|
||||
while (!_stopping) {
|
||||
CONDITION_LOCKER(guard, _condition);
|
||||
|
||||
if (_tasks.empty()) {
|
||||
guard.wait(1000000);
|
||||
}
|
||||
|
||||
if (_stopping) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!_tasks.empty()) {
|
||||
result = _tasks.front();
|
||||
_tasks.pop_front();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
|
@ -1,95 +0,0 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// 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
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef ARANGODB_BASICS_THREAD_POOL_H
|
||||
#define ARANGODB_BASICS_THREAD_POOL_H 1
|
||||
|
||||
#include "Basics/Common.h"
|
||||
#include "Basics/ConditionLocker.h"
|
||||
#include "Basics/ConditionVariable.h"
|
||||
|
||||
#include <deque>
|
||||
|
||||
namespace arangodb {
|
||||
namespace basics {
|
||||
|
||||
class WorkerThread;
|
||||
|
||||
class ThreadPool {
|
||||
public:
|
||||
ThreadPool(ThreadPool const&) = delete;
|
||||
ThreadPool& operator=(ThreadPool const&) = delete;
|
||||
|
||||
ThreadPool(size_t, std::string const&);
|
||||
|
||||
~ThreadPool();
|
||||
|
||||
public:
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief return the number of threads in the pool
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
size_t numThreads() const { return _threads.size(); }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief return the name of the pool
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
char const* name() const { return _name.c_str(); }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief dequeue a task
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool dequeue(std::function<void()>&);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief enqueue a task
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <typename T>
|
||||
void enqueue(T task) {
|
||||
{
|
||||
CONDITION_LOCKER(guard, _condition);
|
||||
_tasks.emplace_back(std::function<void()>(task));
|
||||
}
|
||||
|
||||
_condition.signal();
|
||||
}
|
||||
|
||||
private:
|
||||
arangodb::basics::ConditionVariable _condition;
|
||||
|
||||
std::vector<arangodb::basics::WorkerThread*> _threads;
|
||||
|
||||
std::deque<std::function<void()>> _tasks;
|
||||
|
||||
std::string _name;
|
||||
|
||||
std::atomic<bool> _stopping;
|
||||
};
|
||||
|
||||
} // namespace arangodb::basics
|
||||
} // namespace arangodb
|
||||
|
||||
#endif
|
|
@ -1,86 +0,0 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// 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
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef ARANGODB_BASICS_WORKER_THREAD_H
|
||||
#define ARANGODB_BASICS_WORKER_THREAD_H 1
|
||||
|
||||
#include "Basics/Common.h"
|
||||
#include "Basics/Thread.h"
|
||||
|
||||
namespace arangodb {
|
||||
namespace basics {
|
||||
|
||||
class WorkerThread : public Thread {
|
||||
public:
|
||||
WorkerThread(WorkerThread const&) = delete;
|
||||
WorkerThread operator=(WorkerThread const&) = delete;
|
||||
|
||||
WorkerThread(ThreadPool* pool)
|
||||
: Thread(pool->name()), _pool(pool), _status(0) {}
|
||||
|
||||
~WorkerThread() {
|
||||
waitForDone();
|
||||
shutdown();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief stops the worker thread
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void waitForDone() {
|
||||
int expected = 0;
|
||||
_status.compare_exchange_strong(expected, 1, std::memory_order_relaxed);
|
||||
|
||||
while (_status != 2) {
|
||||
usleep(5000);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
void run() {
|
||||
while (_status == 0) {
|
||||
if (isStopping()) {
|
||||
break;
|
||||
}
|
||||
|
||||
std::function<void()> task;
|
||||
|
||||
if (!_pool->dequeue(task)) {
|
||||
break;
|
||||
}
|
||||
|
||||
task();
|
||||
}
|
||||
|
||||
_status = 2;
|
||||
}
|
||||
|
||||
private:
|
||||
ThreadPool* _pool;
|
||||
|
||||
std::atomic<int> _status;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -21,7 +21,7 @@ if (USE_MAINTAINER_MODE AND NOT MSVC)
|
|||
|
||||
add_custom_target(clean_lib_autogenerated
|
||||
COMMAND rm -f ${CMAKE_SOURCE_DIR}/lib/V8/v8-json.cpp)
|
||||
|
||||
|
||||
list(APPEND CLEAN_AUTOGENERATED_FILES clean_lib_autogenerated)
|
||||
set(CLEAN_AUTOGENERATED_FILES ${CLEAN_AUTOGENERATED_FILES} PARENT_SCOPE)
|
||||
endif ()
|
||||
|
@ -133,6 +133,7 @@ add_library(${LIB_ARANGO} STATIC
|
|||
Basics/Exceptions.cpp
|
||||
Basics/FileUtils.cpp
|
||||
Basics/HybridLogicalClock.cpp
|
||||
Basics/LocalTaskQueue.cpp
|
||||
Basics/Mutex.cpp
|
||||
Basics/MutexLocker.cpp
|
||||
Basics/Nonce.cpp
|
||||
|
@ -144,7 +145,6 @@ add_library(${LIB_ARANGO} STATIC
|
|||
Basics/StringRef.cpp
|
||||
Basics/StringUtils.cpp
|
||||
Basics/Thread.cpp
|
||||
Basics/ThreadPool.cpp
|
||||
Basics/Timers.cpp
|
||||
Basics/Utf8Helper.cpp
|
||||
Basics/VelocyPackDumper.cpp
|
||||
|
|
|
@ -418,13 +418,16 @@ struct VectorParameter : public Parameter {
|
|||
|
||||
// a type that's useful for obsolete parameters that do nothing
|
||||
struct ObsoleteParameter : public Parameter {
|
||||
bool requiresValue() const override { return false; }
|
||||
explicit ObsoleteParameter(bool requiresValue) : required(requiresValue) {}
|
||||
bool requiresValue() const override { return required; }
|
||||
std::string name() const override { return "obsolete"; }
|
||||
std::string valueString() const override { return "-"; }
|
||||
std::string set(std::string const&) override { return ""; }
|
||||
void toVPack(VPackBuilder& builder) const override {
|
||||
builder.add(VPackValue(VPackValueType::Null));
|
||||
}
|
||||
|
||||
bool required;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -199,8 +199,9 @@ class ProgramOptions {
|
|||
|
||||
// adds an obsolete and hidden option to the program options
|
||||
void addObsoleteOption(std::string const& name,
|
||||
std::string const& description) {
|
||||
addOption(Option(name, description, new ObsoleteParameter(), true, true));
|
||||
std::string const& description,
|
||||
bool requiresValue) {
|
||||
addOption(Option(name, description, new ObsoleteParameter(requiresValue), true, true));
|
||||
}
|
||||
|
||||
// prints usage information
|
||||
|
|
Loading…
Reference in New Issue