1
0
Fork 0

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

This commit is contained in:
jsteemann 2016-06-06 22:33:01 +02:00
commit c2768d5a86
53 changed files with 338 additions and 171 deletions

View File

@ -267,9 +267,9 @@ RANDOM_TOKEN(8) // "zGl09z42"
RANDOM_TOKEN(8) // "m9w50Ft9" RANDOM_TOKEN(8) // "m9w50Ft9"
``` ```
!SUBSECTION REGEX() !SUBSECTION REGEX_MATCH()
`REGEX(text, search, caseInsensitive) → bool` `REGEX_MATCH(text, search, caseInsensitive) → bool`
Check whether the pattern *search* is contained in the string *text*, Check whether the pattern *search* is contained in the string *text*,
using regular expression matching. using regular expression matching.
@ -327,9 +327,9 @@ If the regular expression in *search* is invalid, a warning will be raised
and the function will return *false*. and the function will return *false*.
```js ```js
REGEX("the quick brown fox", "the.*fox") // true REGEX_MATCH("the quick brown fox", "the.*fox") // true
REGEX("the quick brown fox", "^(a|the)\s+(quick|slow).*f.x$") // true REGEX_MATCH("the quick brown fox", "^(a|the)\s+(quick|slow).*f.x$") // true
REGEX("the\nquick\nbrown\nfox", "^the(\n[a-w]+)+\nfox$") // true REGEX_MATCH("the\nquick\nbrown\nfox", "^the(\n[a-w]+)+\nfox$") // true
``` ```
!SUBSECTION REVERSE() !SUBSECTION REVERSE()

View File

@ -1,52 +1,154 @@
!SECTION Distributed deployment using Apache Mesos !SECTION Distributed deployment using Apache Mesos
Philosophie: Es muss extrem einfach aussehen, trotzdem sollte die Section TODO: Intro (vom Rechner)
hinreichend viel Detailinformation liefern. Ich weiß, dass das ein
Widerspruch ist.
!SUBSECTION DC/OS !SUBSECTION DC/OS
Explain that ArangoDB is in "the universe" and can be launched under DC/OS is the recommended way to install a cluster as it eases much of the process to install a Mesos cluster. You can deploy it very quickly on a variety of cloud hosters or setup your own DC/OS locally. DC/OS is a set of tools built on top of Apache Mesos. Apache Mesos is a so called "Distributed Cluster Operation System" and the core of DC/OS. Apache Mesos has the concept of so called [persistent volumes](http://mesos.apache.org/documentation/latest/persistent-volume/) which make it perfectly suitable for a database.
"Services" in the UI.
Show how one reaches the coordinator UI via the DC/OS UI after deployment. !SUBSUBSECTION Installing
Explain about scaling up and down via the UI. DC/OS comes with its own package management. Packages can be installed from the so called "Universe". As an official DC/OS partner ArangoDB can be installed from there straight away.
Explain configuration options or link to some place where they are 1. Installing via DC/OS UI
explained. 1. Go to https://dcos.io and prepare a cluster
2. Open your browser and go to the DC/OS admin interface
3. Open the "Universe" tab
4. Locate arangodb and hit "Install Package"
5. Optionally review the settings (Advanced Installation)
6. Press "Install Package"
Explain about the `dcos` CLI and the ArangoDB `dcos`-subcommand. 2. Installing via DC/OS Commandline
Explain how to shut down an ArangoDB cluster cleanly. 1. Install the [dcos cli](https://docs.mesosphere.com/usage/cli/)
2. Open a terminal and issue `dcos install arangodb`
Both options are essentially doing the same in the background. Both are starting ArangoDB with its default options set. To review the default options click "Advanced Installation" in the webinterface or type `dcos package describe --config arangodb`.
Say that ArangoDB uses persistent volumes, explain advantage. To get an explanation of the various command line options please check the latest options here (choose the most recent number and have a look at config.json):
Say that this makes it impossible to deploy ArangoDB to "public Mesos Agents". https://github.com/mesosphere/universe/tree/version-3.x/repo/packages/A/arangodb
Explain how to setup a marathon-lb load balancer to reach the coordinators
either from within the DC/OS cluster or from the outside (via public slaves).
Explain authentication setup (once we have it). Alternatively check the DC/OS webinterface. Hit installing ArangoDB on the "Services" tab and examine "Advanced Installation".
Staubsauger? After installing DC/OS will start deploying the ArangoDB cluster on the DC/OS cluster. You can watch ArangoDB starting on the "Services" tab in the webinterface. Once it is listed as healthy click the link next to it and you should see the ArangoDB webinterface.
!SUBSUBSECTION While running
!SUBSUBSUBSECTION ArangoDB Mesos framework
While ArangoDB is deployed Mesos will keep your cluster running. The web interface has many monitoring facilities so be sure to make yourself familiar with the DC/OS webinterface. As a fault tolerant system Mesos will take care of most failure scenarios automatically. Mesos does that by running ArangoDB as a so called "framework". This framework has been specifically built to keep ArangoDB running in a healthy condition on the Mesos cluster. From time to time a task might fail. The ArangoDB framework will then take care of rescheduling the failed task. As it knows about the very specifics of each cluster task and its role it will automatically take care of most failure scenarios.
To inspect what the framework is doing go to `WEBINTERFACEURL`/mesos in your browser. Locate the task "arangodb" and inspect stderr in the "Sandbox". This can be of interest for example when a slave got lost and the framework is rescheduling the task.
!SUBSUBSUBSECTION Using ArangoDB
To use ArangoDB as a datastore in your DC/OS cluster you can facilitate the service discovery of DC/OS. Assuming you deployed a standard ArangoDB cluster the [mesos dns](https://github.com/mesosphere/mesos-dns) will know about `arangodb.mesos`. By doing a SRV DNS request (check the documentation of mesos dns) you can find out the port where the internal HAProxy of ArangoDB is running. This will offer a round robin load balancer to access all ArangoDB coordinators.
!SUBSUBSUBSECTION Scaling ArangoDB
To change the settings of your ArangoDB Cluster access the ArangoDB UI and hit "Nodes". On the scale tab you will have the ability to scale your cluster up and down.
After changing the settings the ArangoDB framework will take care of the rest. Scaling your cluster up is generally a straightforward operation as Mesos will simply launch another task and be done with it. Scaling down is a bit more complicated as the data first has to be moved to some other place so that will naturally take somewhat longer.
Please note that scaling operations might not always work. For example if the underlying Mesos cluster is completely saturated with tasks scaling up will not be possible. Scaling down might also fail due to not being able to move all shards of a DBServer to a new destination because of size limitations. Be sure to check the output of the ArangoDB framework.
!SUBSUBSECTION Deinstallation
Deinstalling ArangoDB is a bit more difficult as there is much state being kept in the Mesos cluster which is not automatically cleaned up. To deinstall from the command line use the following one liner:
`dcos arangodb uninstall ; dcos package uninstall arangodb`
This will first cleanup the state in the cluster and then uninstall arangodb.
!SUBSUBSUBSECTION arangodb-cleanup-framework
Should you forget to cleanup the state you can do so later by using the [arangodb-cleanup-framework](https://github.com/arangodb/arangodb-cleanup-framework/) container. Otherwise you might not be able to deploy a new arangodb installation.
The cleanup framework will announce itself as a normal ArangoDB. Mesos will recognize this and offer all persistent volumes it still has for ArangoDB to this framework. The cleanup framework will then properly free the persistent volumes. Finally it will clean up any state left in zookeeper (the central configuration manager in a Mesos cluster).
To deploy follow the instructions in the github repository. After deploying watch the output in the sandbox of the Mesos webinterface. After a while there shouldn't be any persistent resource offers anymore as everything was cleaned up. After that you can delete the cleanup framework again via marathon.
!SUBSECTION Apache Mesos and Marathon !SUBSECTION Apache Mesos and Marathon
Explain how to launch an ArangoDB cluster via Marathon in an Apache You can also install ArangoDB on a bare Apache Mesos cluster provided that marathon is running on it.
Mesos cluster.
Explain configuration options or link to some place where they are Doing so has the following downsides:
explained.
Show how one reaches the coordinator UI, HOW DOES THIS ACTUALLY WORK? 1. Manual Mesos cluster setup
DO WE NEED EXPLAIN ABOUT sshuttle? 1. You need to implement your own service discovery
1. You are missing the dcos cli
1. Install and Deinstall are tedious
1. You need to setup some kind of proxy tunnel to access arangodb from the outside
1. Sparse monitoring capabilities
Mention scaling up and down as in DC/OS case. However these are things which do not influence ArangoDB itself and operating your cluster like this is fully supported.
Explain how to shut down an ArangoDB cluster cleanly. !SUBSUBSECTION Installing via Marathon
Explain authentication setup (once we have it) or say it is exactly as To install ArangoDB via marathon you need a proper config file:
in the DC/OS case.
Staubsauger? Deployment via Marathon. ```
{
"id": "arangodb",
"cpus": 0.25,
"mem": 256.0,
"ports": [0, 0, 0],
"instances": 1,
"args": [
"framework",
"--framework_name=arangodb",
"--master=zk://172.17.0.2:2181/mesos",
"--zk=zk://172.17.0.2:2181/arangodb",
"--user=",
"--principal=pri",
"--role=arangodb",
"--mode=cluster",
"--async_replication=true",
"--minimal_resources_agent=mem(*):512;cpus(*):0.25;disk(*):512",
"--minimal_resources_dbserver=mem(*):512;cpus(*):0.25;disk(*):1024",
"--minimal_resources_secondary=mem(*):512;cpus(*):0.25;disk(*):1024",
"--minimal_resources_coordinator=mem(*):512;cpus(*):0.25;disk(*):1024",
"--nr_agents=1",
"--nr_dbservers=2",
"--nr_coordinators=2",
"--failover_timeout=86400",
"--arangodb_image=arangodb/arangodb-mesos:3.0",
"--secondaries_with_dbservers=false",
"--coordinators_with_dbservers=false"
],
"container": {
"type": "DOCKER",
"docker": {
"image": "arangodb/arangodb-mesos-framework:3.0",
"network": "HOST"
}
},
"healthChecks": [
{
"protocol": "HTTP",
"path": "/framework/v1/health.json",
"gracePeriodSeconds": 3,
"intervalSeconds": 10,
"portIndex": 0,
"timeoutSeconds": 10,
"maxConsecutiveFailures": 0
}
]
}
```
Carefully review the settings (especially the IPs and the resources). Then you can deploy to marathon:
`curl -X POST -H "Content-Type: application/json" http://url-of-marathon/v2/apps -d @arangodb3.json`
Alternatively use the webinterface of marathon to deploy ArangoDB.
!SUBSUBSECTION Deinstallation via Marathon
As with DC/OS you first need to properly cleanup any state leftovers.
The easiest is to simply delete arangodb and then deploy the cleanup-framework (see section arangodb-cleanup-framework).
!SUBSECTION Configuration options
The Arangodb Mesos framework has a ton of different options which are listed and described here: https://github.com/arangodb/arangodb-mesos-framework/tree/3.0

View File

@ -65,7 +65,7 @@ void ActionFeature::start() {
nullptr); nullptr);
} }
void ActionFeature::stop() { void ActionFeature::unprepare() {
TRI_CleanupActions(); TRI_CleanupActions();
ACTION = nullptr; ACTION = nullptr;

View File

@ -36,7 +36,7 @@ class ActionFeature final : public application_features::ApplicationFeature {
public: public:
void collectOptions(std::shared_ptr<options::ProgramOptions>) override final; void collectOptions(std::shared_ptr<options::ProgramOptions>) override final;
void start() override final; void start() override final;
void stop() override final; void unprepare() override final;
public: public:
bool allowUseDatabase() { return _allowUseDatabase; } bool allowUseDatabase() { return _allowUseDatabase; }

View File

@ -177,8 +177,8 @@ struct FunctionDefiner {
false, true, true, &Functions::Contains}); false, true, true, &Functions::Contains});
add({"LIKE", "AQL_LIKE", "s,r|b", true, true, false, true, add({"LIKE", "AQL_LIKE", "s,r|b", true, true, false, true,
true, &Functions::Like}); true, &Functions::Like});
add({"REGEX", "AQL_REGEX", "s,r|b", true, true, false, true, add({"REGEX_MATCH", "AQL_REGEX_MATCH", "s,r|b", true, true, false, true,
true, &Functions::Regex}); true, &Functions::RegexMatch});
add({"LEFT", "AQL_LEFT", "s,n", true, true, false, true, true}); add({"LEFT", "AQL_LEFT", "s,n", true, true, false, true, true});
add({"RIGHT", "AQL_RIGHT", "s,n", true, true, false, true, true}); add({"RIGHT", "AQL_RIGHT", "s,n", true, true, false, true, true});
add({"TRIM", "AQL_TRIM", "s|ns", true, true, false, true, true}); add({"TRIM", "AQL_TRIM", "s|ns", true, true, false, true, true});

View File

@ -1419,11 +1419,11 @@ AqlValue Functions::Like(arangodb::aql::Query* query,
return AqlValue(result); return AqlValue(result);
} }
/// @brief function REGEX /// @brief function REGEX_MATCH
AqlValue Functions::Regex(arangodb::aql::Query* query, AqlValue Functions::RegexMatch(arangodb::aql::Query* query,
arangodb::AqlTransaction* trx, arangodb::AqlTransaction* trx,
VPackFunctionParameters const& parameters) { VPackFunctionParameters const& parameters) {
ValidateParameters(parameters, "REGEX", 2, 3); ValidateParameters(parameters, "REGEX_MATCH", 2, 3);
bool const caseInsensitive = GetBooleanParameter(trx, parameters, 2, false); bool const caseInsensitive = GetBooleanParameter(trx, parameters, 2, false);
StringBufferLeaser buffer(trx); StringBufferLeaser buffer(trx);
arangodb::basics::VPackStringBufferAdapter adapter(buffer->stringBuffer()); arangodb::basics::VPackStringBufferAdapter adapter(buffer->stringBuffer());
@ -1464,7 +1464,7 @@ AqlValue Functions::Regex(arangodb::aql::Query* query,
if (matcher == nullptr) { if (matcher == nullptr) {
// compiling regular expression failed // compiling regular expression failed
RegisterWarning(query, "REGEX", TRI_ERROR_QUERY_INVALID_REGEX); RegisterWarning(query, "REGEX_MATCH", TRI_ERROR_QUERY_INVALID_REGEX);
return AqlValue(arangodb::basics::VelocyPackHelper::NullValue()); return AqlValue(arangodb::basics::VelocyPackHelper::NullValue());
} }
@ -1479,7 +1479,7 @@ AqlValue Functions::Regex(arangodb::aql::Query* query,
if (error) { if (error) {
// compiling regular expression failed // compiling regular expression failed
RegisterWarning(query, "REGEX", TRI_ERROR_QUERY_INVALID_REGEX); RegisterWarning(query, "REGEX_MATCH", TRI_ERROR_QUERY_INVALID_REGEX);
return AqlValue(arangodb::basics::VelocyPackHelper::NullValue()); return AqlValue(arangodb::basics::VelocyPackHelper::NullValue());
} }

View File

@ -97,7 +97,7 @@ struct Functions {
VPackFunctionParameters const&); VPackFunctionParameters const&);
static AqlValue Like(arangodb::aql::Query*, arangodb::AqlTransaction*, static AqlValue Like(arangodb::aql::Query*, arangodb::AqlTransaction*,
VPackFunctionParameters const&); VPackFunctionParameters const&);
static AqlValue Regex(arangodb::aql::Query*, arangodb::AqlTransaction*, static AqlValue RegexMatch(arangodb::aql::Query*, arangodb::AqlTransaction*,
VPackFunctionParameters const&); VPackFunctionParameters const&);
static AqlValue Passthru(arangodb::aql::Query*, arangodb::AqlTransaction*, static AqlValue Passthru(arangodb::aql::Query*, arangodb::AqlTransaction*,
VPackFunctionParameters const&); VPackFunctionParameters const&);

View File

@ -157,7 +157,9 @@ void DispatcherFeature::stop() {
} }
_dispatcher->shutdown(); _dispatcher->shutdown();
}
void DispatcherFeature::unprepare() {
DISPATCHER = nullptr; DISPATCHER = nullptr;
} }

View File

@ -49,6 +49,7 @@ class DispatcherFeature final
void start() override final; void start() override final;
void beginShutdown() override final; void beginShutdown() override final;
void stop() override final; void stop() override final;
void unprepare() override final;
private: private:
uint64_t _nrStandardThreads; uint64_t _nrStandardThreads;

View File

@ -257,7 +257,7 @@ void RocksDBFeature::start() {
} }
} }
void RocksDBFeature::stop() { void RocksDBFeature::unprepare() {
if (!isEnabled()) { if (!isEnabled()) {
return; return;
} }

View File

@ -45,7 +45,7 @@ class RocksDBFeature final : public application_features::ApplicationFeature {
void collectOptions(std::shared_ptr<options::ProgramOptions>) override final; void collectOptions(std::shared_ptr<options::ProgramOptions>) override final;
void validateOptions(std::shared_ptr<options::ProgramOptions>) override final; void validateOptions(std::shared_ptr<options::ProgramOptions>) override final;
void start() override final; void start() override final;
void stop() override final; void unprepare() override final;
inline rocksdb::OptimisticTransactionDB* db() const { return _db; } inline rocksdb::OptimisticTransactionDB* db() const { return _db; }
inline RocksDBKeyComparator* comparator() const { return _comparator; } inline RocksDBKeyComparator* comparator() const { return _comparator; }

View File

@ -34,6 +34,7 @@ class BootstrapFeature final : public application_features::ApplicationFeature {
void collectOptions(std::shared_ptr<options::ProgramOptions>) override final; void collectOptions(std::shared_ptr<options::ProgramOptions>) override final;
void start() override final; void start() override final;
void stop() override final; void stop() override final;
bool isReady() const { bool isReady() const {
return _isReady; return _isReady;
} }

View File

@ -73,6 +73,8 @@ void ConsoleFeature::stop() {
while (_consoleThread->isRunning() && ++iterations < 30) { while (_consoleThread->isRunning() && ++iterations < 30) {
usleep(100 * 1000); // spin while console is still needed usleep(100 * 1000); // spin while console is still needed
} }
}
void ConsoleFeature::unprepare() {
std::cout << std::endl << TRI_BYE_MESSAGE << std::endl; std::cout << std::endl << TRI_BYE_MESSAGE << std::endl;
} }

View File

@ -37,6 +37,7 @@ class ConsoleFeature final : public application_features::ApplicationFeature {
public: public:
void start() override final; void start() override final;
void stop() override final; void stop() override final;
void unprepare() override final;
private: private:
OperationMode _operationMode; OperationMode _operationMode;

View File

@ -171,7 +171,7 @@ void DatabaseFeature::start() {
} }
} }
void DatabaseFeature::stop() { void DatabaseFeature::unprepare() {
// close all databases // close all databases
closeDatabases(); closeDatabases();

View File

@ -39,7 +39,7 @@ class DatabaseFeature final : public application_features::ApplicationFeature {
void collectOptions(std::shared_ptr<options::ProgramOptions>) override final; void collectOptions(std::shared_ptr<options::ProgramOptions>) override final;
void validateOptions(std::shared_ptr<options::ProgramOptions>) override final; void validateOptions(std::shared_ptr<options::ProgramOptions>) override final;
void start() override final; void start() override final;
void stop() override final; void unprepare() override final;
public: public:
TRI_vocbase_t* vocbase() const { return _vocbase; } TRI_vocbase_t* vocbase() const { return _vocbase; }

View File

@ -82,7 +82,7 @@ void DatabaseServerFeature::start() {
} }
} }
void DatabaseServerFeature::stop() { void DatabaseServerFeature::unprepare() {
// turn off index threads // turn off index threads
INDEX_POOL = nullptr; INDEX_POOL = nullptr;
_indexPool.reset(); _indexPool.reset();

View File

@ -47,7 +47,7 @@ class DatabaseServerFeature final
void validateOptions(std::shared_ptr<options::ProgramOptions>) override final; void validateOptions(std::shared_ptr<options::ProgramOptions>) override final;
void prepare() override final; void prepare() override final;
void start() override final; void start() override final;
void stop() override final; void unprepare() override final;
private: private:
uint64_t _indexThreads = 2; uint64_t _indexThreads = 2;

View File

@ -85,7 +85,7 @@ void QueryRegistryFeature::start() {
DatabaseServerFeature::SERVER->_queryRegistry = _queryRegistry.get(); DatabaseServerFeature::SERVER->_queryRegistry = _queryRegistry.get();
} }
void QueryRegistryFeature::stop() { void QueryRegistryFeature::unprepare() {
// clear the query registery // clear the query registery
DatabaseServerFeature::SERVER->_queryRegistry = nullptr; DatabaseServerFeature::SERVER->_queryRegistry = nullptr;
// TODO: reset QUERY_REGISTRY as well? // TODO: reset QUERY_REGISTRY as well?

View File

@ -42,7 +42,7 @@ class QueryRegistryFeature final : public application_features::ApplicationFeatu
void validateOptions(std::shared_ptr<options::ProgramOptions>) override final; void validateOptions(std::shared_ptr<options::ProgramOptions>) override final;
void prepare() override final; void prepare() override final;
void start() override final; void start() override final;
void stop() override final; void unprepare() override final;
private: private:
bool _queryTracking = true; bool _queryTracking = true;

View File

@ -315,13 +315,14 @@ void RestServerFeature::stop() {
for (auto& server : _servers) { for (auto& server : _servers) {
server->stop(); server->stop();
} }
}
void RestServerFeature::unprepare() {
for (auto& server : _servers) { for (auto& server : _servers) {
delete server; delete server;
} }
_httpOptions._vocbase = nullptr; _httpOptions._vocbase = nullptr;
RESTSERVER = nullptr; RESTSERVER = nullptr;
} }

View File

@ -78,6 +78,7 @@ class RestServerFeature final
void prepare() override final; void prepare() override final;
void start() override final; void start() override final;
void stop() override final; void stop() override final;
void unprepare() override final;
private: private:
double _keepAliveTimeout; double _keepAliveTimeout;

View File

@ -141,8 +141,11 @@ void SchedulerFeature::stop() {
} }
_scheduler->shutdown(); _scheduler->shutdown();
}
}
// delete the scheduler void SchedulerFeature::unprepare() {
if (_scheduler != nullptr) {
delete _scheduler; delete _scheduler;
_scheduler = nullptr; _scheduler = nullptr;
SCHEDULER = nullptr; SCHEDULER = nullptr;

View File

@ -46,6 +46,7 @@ class SchedulerFeature final : public application_features::ApplicationFeature {
void validateOptions(std::shared_ptr<options::ProgramOptions>) override final; void validateOptions(std::shared_ptr<options::ProgramOptions>) override final;
void start() override final; void start() override final;
void stop() override final; void stop() override final;
void unprepare() override final;
private: private:
uint64_t _nrSchedulerThreads; uint64_t _nrSchedulerThreads;

View File

@ -224,7 +224,7 @@ void V8DealerFeature::start() {
startGarbageCollection(); startGarbageCollection();
} }
void V8DealerFeature::stop() { void V8DealerFeature::unprepare() {
shutdownContexts(); shutdownContexts();
// delete GC thread after all action threads have been stopped // delete GC thread after all action threads have been stopped

View File

@ -45,7 +45,7 @@ class V8DealerFeature final : public application_features::ApplicationFeature {
void collectOptions(std::shared_ptr<options::ProgramOptions>) override final; void collectOptions(std::shared_ptr<options::ProgramOptions>) override final;
void validateOptions(std::shared_ptr<options::ProgramOptions>) override final; void validateOptions(std::shared_ptr<options::ProgramOptions>) override final;
void start() override final; void start() override final;
void stop() override final; void unprepare() override final;
private: private:
double _gcFrequency; double _gcFrequency;

View File

@ -314,6 +314,6 @@ void BenchFeature::start() {
*_result = ret; *_result = ret;
} }
void BenchFeature::stop() { void BenchFeature::unprepare() {
ARANGOBENCH = nullptr; ARANGOBENCH = nullptr;
} }

View File

@ -32,8 +32,8 @@ class BenchFeature final : public application_features::ApplicationFeature {
public: public:
void collectOptions(std::shared_ptr<options::ProgramOptions>) override; void collectOptions(std::shared_ptr<options::ProgramOptions>) override;
void start() override; void start() override final;
void stop() override; void unprepare() override final;
public: public:
bool async() const { return _async; } bool async() const { return _async; }

View File

@ -136,7 +136,7 @@ void ConsoleFeature::start() {
#endif #endif
} }
void ConsoleFeature::stop() { void ConsoleFeature::unprepare() {
closeLog(); closeLog();
} }

View File

@ -36,7 +36,7 @@ class ConsoleFeature final : public application_features::ApplicationFeature {
void collectOptions(std::shared_ptr<options::ProgramOptions>) override final; void collectOptions(std::shared_ptr<options::ProgramOptions>) override final;
void prepare() override final; void prepare() override final;
void start() override final; void start() override final;
void stop() override final; void unprepare() override final;
public: public:
bool quiet() const { return _quiet; } bool quiet() const { return _quiet; }

View File

@ -130,7 +130,7 @@ void V8ShellFeature::start() {
initGlobals(); initGlobals();
} }
void V8ShellFeature::stop() { void V8ShellFeature::unprepare() {
{ {
v8::Locker locker{_isolate}; v8::Locker locker{_isolate};

View File

@ -44,8 +44,8 @@ class V8ShellFeature final : public application_features::ApplicationFeature {
void collectOptions(std::shared_ptr<options::ProgramOptions>) override; void collectOptions(std::shared_ptr<options::ProgramOptions>) override;
void validateOptions( void validateOptions(
std::shared_ptr<options::ProgramOptions> options) override; std::shared_ptr<options::ProgramOptions> options) override;
void start() override; void start() override final;
void stop() override; void unprepare() override final;
private: private:
std::string _startupDirectory; std::string _startupDirectory;

View File

@ -2301,7 +2301,7 @@ function AQL_LIKE (value, regex, caseInsensitive) {
/// @brief searches a substring in a string, using a regex /// @brief searches a substring in a string, using a regex
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
function AQL_REGEX (value, regex, caseInsensitive) { function AQL_REGEX_MATCH (value, regex, caseInsensitive) {
'use strict'; 'use strict';
var modifiers = ''; var modifiers = '';
@ -8358,7 +8358,7 @@ exports.AQL_UPPER = AQL_UPPER;
exports.AQL_SUBSTRING = AQL_SUBSTRING; exports.AQL_SUBSTRING = AQL_SUBSTRING;
exports.AQL_CONTAINS = AQL_CONTAINS; exports.AQL_CONTAINS = AQL_CONTAINS;
exports.AQL_LIKE = AQL_LIKE; exports.AQL_LIKE = AQL_LIKE;
exports.AQL_REGEX = AQL_REGEX; exports.AQL_REGEX_MATCH = AQL_REGEX_MATCH;
exports.AQL_LEFT = AQL_LEFT; exports.AQL_LEFT = AQL_LEFT;
exports.AQL_RIGHT = AQL_RIGHT; exports.AQL_RIGHT = AQL_RIGHT;
exports.AQL_TRIM = AQL_TRIM; exports.AQL_TRIM = AQL_TRIM;

View File

@ -62,18 +62,18 @@ function ahuacatlStringFunctionsTestSuite () {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
testRegexInvalid : function () { testRegexInvalid : function () {
assertQueryError(errors.ERROR_QUERY_FUNCTION_ARGUMENT_NUMBER_MISMATCH.code, "RETURN REGEX()"); assertQueryError(errors.ERROR_QUERY_FUNCTION_ARGUMENT_NUMBER_MISMATCH.code, "RETURN REGEX_MATCH()");
assertQueryError(errors.ERROR_QUERY_FUNCTION_ARGUMENT_NUMBER_MISMATCH.code, "RETURN REGEX(\"test\")"); assertQueryError(errors.ERROR_QUERY_FUNCTION_ARGUMENT_NUMBER_MISMATCH.code, "RETURN REGEX_MATCH(\"test\")");
assertQueryError(errors.ERROR_QUERY_FUNCTION_ARGUMENT_NUMBER_MISMATCH.code, "RETURN REGEX(\"test\", \"meow\", \"foo\", \"bar\")"); assertQueryError(errors.ERROR_QUERY_FUNCTION_ARGUMENT_NUMBER_MISMATCH.code, "RETURN REGEX_MATCH(\"test\", \"meow\", \"foo\", \"bar\")");
assertQueryWarningAndFalse(errors.ERROR_QUERY_INVALID_REGEX.code, "RETURN REGEX(\"test\", \"[\")"); assertQueryWarningAndFalse(errors.ERROR_QUERY_INVALID_REGEX.code, "RETURN REGEX_MATCH(\"test\", \"[\")");
assertQueryWarningAndFalse(errors.ERROR_QUERY_INVALID_REGEX.code, "RETURN REGEX(\"test\", \"[^\")"); assertQueryWarningAndFalse(errors.ERROR_QUERY_INVALID_REGEX.code, "RETURN REGEX_MATCH(\"test\", \"[^\")");
assertQueryWarningAndFalse(errors.ERROR_QUERY_INVALID_REGEX.code, "RETURN REGEX(\"test\", \"a.(\")"); assertQueryWarningAndFalse(errors.ERROR_QUERY_INVALID_REGEX.code, "RETURN REGEX_MATCH(\"test\", \"a.(\")");
assertQueryWarningAndFalse(errors.ERROR_QUERY_INVALID_REGEX.code, "RETURN REGEX(\"test\", \"(a\")"); assertQueryWarningAndFalse(errors.ERROR_QUERY_INVALID_REGEX.code, "RETURN REGEX_MATCH(\"test\", \"(a\")");
assertQueryWarningAndFalse(errors.ERROR_QUERY_INVALID_REGEX.code, "RETURN REGEX(\"test\", \"(a]\")"); assertQueryWarningAndFalse(errors.ERROR_QUERY_INVALID_REGEX.code, "RETURN REGEX_MATCH(\"test\", \"(a]\")");
assertQueryWarningAndFalse(errors.ERROR_QUERY_INVALID_REGEX.code, "RETURN REGEX(\"test\", \"**\")"); assertQueryWarningAndFalse(errors.ERROR_QUERY_INVALID_REGEX.code, "RETURN REGEX_MATCH(\"test\", \"**\")");
assertQueryWarningAndFalse(errors.ERROR_QUERY_INVALID_REGEX.code, "RETURN REGEX(\"test\", \"?\")"); assertQueryWarningAndFalse(errors.ERROR_QUERY_INVALID_REGEX.code, "RETURN REGEX_MATCH(\"test\", \"?\")");
assertQueryWarningAndFalse(errors.ERROR_QUERY_INVALID_REGEX.code, "RETURN REGEX(\"test\", \"*\")"); assertQueryWarningAndFalse(errors.ERROR_QUERY_INVALID_REGEX.code, "RETURN REGEX_MATCH(\"test\", \"*\")");
}, },
testRegex : function () { testRegex : function () {
@ -217,13 +217,13 @@ function ahuacatlStringFunctionsTestSuite () {
]; ];
values.forEach(function(v) { values.forEach(function(v) {
var query = "RETURN REGEX(@what, @re)"; var query = "RETURN REGEX_MATCH(@what, @re)";
assertEqual(v[2], getQueryResults(query, { what: v[0], re: v[1] })[0], v); assertEqual(v[2], getQueryResults(query, { what: v[0], re: v[1] })[0], v);
query = "RETURN NOOPT(REGEX(@what, @re))"; query = "RETURN NOOPT(REGEX_MATCH(@what, @re))";
assertEqual(v[2], getQueryResults(query, { what: v[0], re: v[1] })[0], v); assertEqual(v[2], getQueryResults(query, { what: v[0], re: v[1] })[0], v);
query = "RETURN NOOPT(V8(REGEX(@what, @re)))"; query = "RETURN NOOPT(V8(REGEX_MATCH(@what, @re)))";
assertEqual(v[2], getQueryResults(query, { what: v[0], re: v[1] })[0], v); assertEqual(v[2], getQueryResults(query, { what: v[0], re: v[1] })[0], v);
}); });

View File

@ -72,9 +72,12 @@ void ApplicationFeature::start() {}
// notify the feature about a shutdown request // notify the feature about a shutdown request
void ApplicationFeature::beginShutdown() {} void ApplicationFeature::beginShutdown() {}
// stop and shut down the feature // stop the feature
void ApplicationFeature::stop() {} void ApplicationFeature::stop() {}
// shut down the feature
void ApplicationFeature::unprepare() {}
// determine all direct and indirect ancestors of a feature // determine all direct and indirect ancestors of a feature
std::unordered_set<std::string> ApplicationFeature::ancestors() const { std::unordered_set<std::string> ApplicationFeature::ancestors() const {
TRI_ASSERT(_ancestorsDetermined); TRI_ASSERT(_ancestorsDetermined);

View File

@ -126,9 +126,12 @@ class ApplicationFeature {
// notify the feature about a shutdown request // notify the feature about a shutdown request
virtual void beginShutdown(); virtual void beginShutdown();
// stop and shut down the feature // stop the feature
virtual void stop(); virtual void stop();
// shut down the feature
virtual void unprepare();
protected: protected:
// return the ApplicationServer instance // return the ApplicationServer instance
ApplicationServer* server() const { return _server; } ApplicationServer* server() const { return _server; }

View File

@ -205,6 +205,11 @@ void ApplicationServer::run(int argc, char* argv[]) {
reportServerProgress(_state); reportServerProgress(_state);
stop(); stop();
// unprepare all features
_state = ServerState::IN_UNPREPARE;
reportServerProgress(_state);
unprepare();
// stopped // stopped
_state = ServerState::STOPPED; _state = ServerState::STOPPED;
reportServerProgress(_state); reportServerProgress(_state);
@ -540,12 +545,25 @@ void ApplicationServer::stop() {
auto feature = *it; auto feature = *it;
LOG_TOPIC(TRACE, Logger::STARTUP) << feature->name() << "::stop"; LOG_TOPIC(TRACE, Logger::STARTUP) << feature->name() << "::stop";
feature->stop(); // feature->stop();
feature->state(FeatureState::STOPPED); feature->state(FeatureState::STOPPED);
reportFeatureProgress(_state, feature->name()); reportFeatureProgress(_state, feature->name());
} }
} }
void ApplicationServer::unprepare() {
LOG_TOPIC(TRACE, Logger::STARTUP) << "ApplicationServer::unprepare";
for (auto it = _orderedFeatures.rbegin(); it != _orderedFeatures.rend(); ++it) {
auto feature = *it;
LOG_TOPIC(TRACE, Logger::STARTUP) << feature->name() << "::unprepare";
feature->unprepare();
feature->state(FeatureState::UNPREPARED);
reportFeatureProgress(_state, feature->name());
}
}
void ApplicationServer::wait() { void ApplicationServer::wait() {
LOG_TOPIC(TRACE, Logger::STARTUP) << "ApplicationServer::wait"; LOG_TOPIC(TRACE, Logger::STARTUP) << "ApplicationServer::wait";

View File

@ -44,6 +44,7 @@ enum class ServerState {
IN_START, IN_START,
IN_WAIT, IN_WAIT,
IN_STOP, IN_STOP,
IN_UNPREPARE,
STOPPED, STOPPED,
ABORT ABORT
}; };
@ -103,6 +104,11 @@ class ProgressHandler {
// `stop` // `stop`
// //
// Stops the features. The `stop` methods are called in reversed `start` order. // Stops the features. The `stop` methods are called in reversed `start` order.
// This must stop all threads, but not destroy the features.
//
// `unprepare`
//
// This destroys the features.
class ApplicationServer { class ApplicationServer {
ApplicationServer(ApplicationServer const&) = delete; ApplicationServer(ApplicationServer const&) = delete;
@ -115,7 +121,8 @@ class ApplicationServer {
VALIDATED, VALIDATED,
PREPARED, PREPARED,
STARTED, STARTED,
STOPPED STOPPED,
UNPREPARED
}; };
static ApplicationServer* server; static ApplicationServer* server;
@ -252,6 +259,9 @@ class ApplicationServer {
// stops features // stops features
void stop(); void stop();
// destroys features
void unprepare();
// after start, the server will wait in this method until // after start, the server will wait in this method until
// beginShutdown is called // beginShutdown is called
void wait(); void wait();

View File

@ -118,7 +118,7 @@ void DaemonFeature::daemonize() {
} }
} }
void DaemonFeature::stop() { void DaemonFeature::unprepare() {
if (!_daemon) { if (!_daemon) {
return; return;
} }

View File

@ -36,7 +36,7 @@ class DaemonFeature final : public application_features::ApplicationFeature {
void collectOptions(std::shared_ptr<options::ProgramOptions>) override final; void collectOptions(std::shared_ptr<options::ProgramOptions>) override final;
void validateOptions(std::shared_ptr<options::ProgramOptions>) override final; void validateOptions(std::shared_ptr<options::ProgramOptions>) override final;
void daemonize() override final; void daemonize() override final;
void stop() override final; void unprepare() override final;
public: public:
void setDaemon(bool value) { _daemon = value; } void setDaemon(bool value) { _daemon = value; }

View File

@ -51,10 +51,6 @@ void NonceFeature::prepare() {
} }
} }
void NonceFeature::start() { void NonceFeature::unprepare() {
LOG(DEBUG) << "setting nonce hash size to " << _size;
}
void NonceFeature::stop() {
Nonce::destroy(); Nonce::destroy();
} }

View File

@ -36,8 +36,7 @@ class NonceFeature : public application_features::ApplicationFeature {
public: public:
void collectOptions(std::shared_ptr<options::ProgramOptions>) override final; void collectOptions(std::shared_ptr<options::ProgramOptions>) override final;
void prepare() override final; void prepare() override final;
void start() override final; void unprepare() override final;
void stop() override final;
private: private:
uint64_t _size; uint64_t _size;

View File

@ -33,6 +33,15 @@ using namespace arangodb::application_features;
using namespace arangodb::basics; using namespace arangodb::basics;
using namespace arangodb::options; using namespace arangodb::options;
static bool DONE = false;
static int CLIENT_PID = false;
static void StopHandler(int) {
LOG_TOPIC(INFO, Logger::STARTUP) << "received SIGINT for supervisor";
kill(CLIENT_PID, SIGTERM);
DONE = true;
}
SupervisorFeature::SupervisorFeature( SupervisorFeature::SupervisorFeature(
application_features::ApplicationServer* server) application_features::ApplicationServer* server)
: ApplicationFeature(server, "Supervisor"), _supervisor(false) { : ApplicationFeature(server, "Supervisor"), _supervisor(false) {
@ -104,11 +113,8 @@ void SupervisorFeature::daemonize() {
// parent (supervisor) // parent (supervisor)
if (0 < _clientPid) { if (0 < _clientPid) {
LOG_TOPIC(DEBUG, Logger::STARTUP) << "supervisor mode: within parent";
TRI_SetProcessTitle("arangodb [supervisor]"); TRI_SetProcessTitle("arangodb [supervisor]");
ArangoGlobalContext::CONTEXT->unmaskStandardSignals();
std::for_each(supervisorFeatures.begin(), supervisorFeatures.end(), std::for_each(supervisorFeatures.begin(), supervisorFeatures.end(),
[](ApplicationFeature* feature) { [](ApplicationFeature* feature) {
LoggerFeature* logger = LoggerFeature* logger =
@ -126,81 +132,98 @@ void SupervisorFeature::daemonize() {
std::for_each(supervisorFeatures.begin(), supervisorFeatures.end(), std::for_each(supervisorFeatures.begin(), supervisorFeatures.end(),
[](ApplicationFeature* feature) { feature->start(); }); [](ApplicationFeature* feature) { feature->start(); });
LOG_TOPIC(DEBUG, Logger::STARTUP) << "supervisor mode: within parent";
ArangoGlobalContext::CONTEXT->unmaskStandardSignals();
signal(SIGINT, StopHandler);
signal(SIGTERM, StopHandler);
CLIENT_PID = _clientPid;
DONE = false;
int status; int status;
waitpid(_clientPid, &status, 0); int res = waitpid(_clientPid, &status, 0);
bool horrible = true; bool horrible = true;
if (WIFEXITED(status)) { if (!DONE) {
// give information about cause of death done = true;
if (WEXITSTATUS(status) == 0) { horrible = false;
LOG_TOPIC(INFO, Logger::STARTUP) << "child " << _clientPid }
<< " died of natural causes"; else {
done = true; LOG_TOPIC(DEBUG, Logger::STARTUP) << "waitpid woke up with return value "
horrible = false; << res << " and status " << status;
} else {
t = time(0) - startTime;
LOG_TOPIC(ERR, Logger::STARTUP) if (WIFEXITED(status)) {
<< "child " << _clientPid // give information about cause of death
<< " died a horrible death, exit status " << WEXITSTATUS(status); if (WEXITSTATUS(status) == 0) {
LOG_TOPIC(INFO, Logger::STARTUP) << "child " << _clientPid
<< " died of natural causes";
done = true;
horrible = false;
} else {
t = time(0) - startTime;
if (t < MIN_TIME_ALIVE_IN_SEC) { LOG_TOPIC(ERR, Logger::STARTUP)
LOG_TOPIC(ERR, Logger::STARTUP) << "child " << _clientPid
<< "child only survived for " << t << " died a horrible death, exit status " << WEXITSTATUS(status);
<< " seconds, this will not work - please fix the error "
"first";
done = true;
} else {
done = false;
}
}
} else if (WIFSIGNALED(status)) {
switch (WTERMSIG(status)) {
case 2:
case 9:
case 15:
LOG_TOPIC(INFO, Logger::STARTUP)
<< "child " << _clientPid
<< " died of natural causes, exit status " << WTERMSIG(status);
done = true;
horrible = false;
break;
default: if (t < MIN_TIME_ALIVE_IN_SEC) {
t = time(0) - startTime; LOG_TOPIC(ERR, Logger::STARTUP)
<< "child only survived for " << t
<< " seconds, this will not work - please fix the error "
"first";
done = true;
} else {
done = false;
}
}
} else if (WIFSIGNALED(status)) {
switch (WTERMSIG(status)) {
case 2:
case 9:
case 15:
LOG_TOPIC(INFO, Logger::STARTUP)
<< "child " << _clientPid
<< " died of natural causes, exit status " << WTERMSIG(status);
done = true;
horrible = false;
break;
LOG_TOPIC(ERR, Logger::STARTUP) << "child " << _clientPid default:
<< " died a horrible death, signal " t = time(0) - startTime;
<< WTERMSIG(status);
if (t < MIN_TIME_ALIVE_IN_SEC) { LOG_TOPIC(ERR, Logger::STARTUP) << "child " << _clientPid
LOG_TOPIC(ERR, Logger::STARTUP) << " died a horrible death, signal "
<< "child only survived for " << t << WTERMSIG(status);
<< " seconds, this will not work - please fix the "
"error first"; if (t < MIN_TIME_ALIVE_IN_SEC) {
done = true; LOG_TOPIC(ERR, Logger::STARTUP)
<< "child only survived for " << t
<< " seconds, this will not work - please fix the "
"error first";
done = true;
#ifdef WCOREDUMP #ifdef WCOREDUMP
if (WCOREDUMP(status)) { if (WCOREDUMP(status)) {
LOG_TOPIC(WARN, Logger::STARTUP) << "child process " LOG_TOPIC(WARN, Logger::STARTUP) << "child process "
<< _clientPid << _clientPid
<< " produced a core dump"; << " produced a core dump";
} }
#endif #endif
} else { } else {
done = false; done = false;
} }
break; break;
} }
} else { } else {
LOG_TOPIC(ERR, Logger::STARTUP) LOG_TOPIC(ERR, Logger::STARTUP)
<< "child " << _clientPid << "child " << _clientPid
<< " died a horrible death, unknown cause"; << " died a horrible death, unknown cause";
done = false; done = false;
}
} }
// remove pid file
if (horrible) { if (horrible) {
result = EXIT_FAILURE; result = EXIT_FAILURE;
} }
@ -223,5 +246,8 @@ void SupervisorFeature::daemonize() {
std::for_each(supervisorFeatures.rbegin(), supervisorFeatures.rend(), std::for_each(supervisorFeatures.rbegin(), supervisorFeatures.rend(),
[](ApplicationFeature* feature) { feature->stop(); }); [](ApplicationFeature* feature) { feature->stop(); });
std::for_each(supervisorFeatures.rbegin(), supervisorFeatures.rend(),
[](ApplicationFeature* feature) { feature->unprepare(); });
exit(result); exit(result);
} }

View File

@ -80,7 +80,7 @@ void V8PlatformFeature::start() {
_allocator.reset(new ArrayBufferAllocator); _allocator.reset(new ArrayBufferAllocator);
} }
void V8PlatformFeature::stop() { void V8PlatformFeature::unprepare() {
v8::V8::Dispose(); v8::V8::Dispose();
v8::V8::ShutdownPlatform(); v8::V8::ShutdownPlatform();
_platform.reset(); _platform.reset();

View File

@ -37,7 +37,7 @@ class V8PlatformFeature final
public: public:
void collectOptions(std::shared_ptr<options::ProgramOptions>) override final; void collectOptions(std::shared_ptr<options::ProgramOptions>) override final;
void start() override final; void start() override final;
void stop() override final; void unprepare() override final;
v8::ArrayBuffer::Allocator* arrayBufferAllocator() const { v8::ArrayBuffer::Allocator* arrayBufferAllocator() const {
return _allocator.get(); return _allocator.get();

View File

@ -35,4 +35,4 @@ WorkMonitorFeature::WorkMonitorFeature(
void WorkMonitorFeature::start() { InitializeWorkMonitor(); } void WorkMonitorFeature::start() { InitializeWorkMonitor(); }
void WorkMonitorFeature::stop() { ShutdownWorkMonitor(); } void WorkMonitorFeature::unprepare() { ShutdownWorkMonitor(); }

View File

@ -33,7 +33,7 @@ class WorkMonitorFeature final
public: public:
void start() override final; void start() override final;
void stop() override final; void unprepare() override final;
}; };
} }

View File

@ -177,7 +177,7 @@ void LoggerFeature::prepare() {
} }
} }
void LoggerFeature::stop() { void LoggerFeature::unprepare() {
Logger::flush(); Logger::flush();
Logger::shutdown(true); Logger::shutdown(true);
} }

View File

@ -35,7 +35,7 @@ class LoggerFeature final : public application_features::ApplicationFeature {
void loadOptions(std::shared_ptr<options::ProgramOptions>) override final; void loadOptions(std::shared_ptr<options::ProgramOptions>) override final;
void validateOptions(std::shared_ptr<options::ProgramOptions>) override final; void validateOptions(std::shared_ptr<options::ProgramOptions>) override final;
void prepare() override final; void prepare() override final;
void stop() override final; void unprepare() override final;
public: public:
void setBackgrounded(bool backgrounded) { _backgrounded = backgrounded; } void setBackgrounded(bool backgrounded) { _backgrounded = backgrounded; }

View File

@ -145,7 +145,7 @@ void SslFeature::prepare() {
opensslSetup(); opensslSetup();
} }
void SslFeature::stop() { void SslFeature::unprepare() {
opensslCleanup(); opensslCleanup();
ERR_free_strings(); ERR_free_strings();

View File

@ -34,7 +34,7 @@ class SslFeature final : public application_features::ApplicationFeature {
public: public:
void prepare() override final; void prepare() override final;
void stop() override final; void unprepare() override final;
}; };
} }

View File

@ -94,9 +94,7 @@ void SslServerFeature::collectOptions(std::shared_ptr<ProgramOptions> options) {
void SslServerFeature::prepare() { void SslServerFeature::prepare() {
createSslContext(); createSslContext();
}
void SslServerFeature::start() {
LOG(INFO) << "using SSL options: " << stringifySslOptions(_options); LOG(INFO) << "using SSL options: " << stringifySslOptions(_options);
if (!_cipherList.empty()) { if (!_cipherList.empty()) {
@ -104,7 +102,7 @@ void SslServerFeature::start() {
} }
} }
void SslServerFeature::stop() { void SslServerFeature::unprepare() {
if (_sslContext != nullptr) { if (_sslContext != nullptr) {
SSL_CTX_free(_sslContext); SSL_CTX_free(_sslContext);
_sslContext = nullptr; _sslContext = nullptr;

View File

@ -35,8 +35,7 @@ class SslServerFeature final : public application_features::ApplicationFeature {
public: public:
void collectOptions(std::shared_ptr<options::ProgramOptions>) override final; void collectOptions(std::shared_ptr<options::ProgramOptions>) override final;
void prepare() override final; void prepare() override final;
void start() override final; void unprepare() override final;
void stop() override final;
public: public:
SSL_CTX* sslContext() const { return _sslContext; } SSL_CTX* sslContext() const { return _sslContext; }