mirror of https://gitee.com/bigwinds/arangodb
Merge branch 'devel' of https://github.com/triAGENS/ArangoDB into aql2
Conflicts: arangod/V8Server/v8-vocbase.cpp
This commit is contained in:
commit
f8cf5dc26d
|
@ -32,11 +32,11 @@ Subqueries may also include other subqueries themselves.
|
|||
!SUBSECTION Variable expansion
|
||||
|
||||
In order to access a named attribute from all elements in a list easily, AQL
|
||||
offers the shortcut operator *[\*]* for variable expansion.
|
||||
offers the shortcut operator [*] for variable expansion.
|
||||
|
||||
Using the *[\*]* operator with a variable will iterate over all elements in the
|
||||
Using the [*] operator with a variable will iterate over all elements in the
|
||||
variable thus allowing to access a particular attribute of each element. It is
|
||||
required that the expanded variable is a list. The result of the *[\*]*
|
||||
required that the expanded variable is a list. The result of the [*]
|
||||
operator is again a list.
|
||||
|
||||
FOR u IN users
|
||||
|
|
|
@ -0,0 +1,274 @@
|
|||
!CHAPTER Foxx Job Queues
|
||||
|
||||
Foxx allows defining job queues that let you perform slow or expensive actions asynchronously. These queues can be used to send e-mails, call external APIs or perform other actions that you do not want to perform directly or want to retry on failure.
|
||||
|
||||
For the low-level functionality see the section *Task Management* in the chapter *JavaScript Modules*.
|
||||
|
||||
*Examples*
|
||||
|
||||
The following Foxx route handler will enqueue a job whenever the *"/log"* route is accessed that prints "Hello World!" to the server log.
|
||||
|
||||
```js
|
||||
var Foxx = require("org/arangodb/foxx");
|
||||
var ctrl = new Foxx.Controller(applicationContext);
|
||||
var queue = Foxx.queues.create("my-queue");
|
||||
|
||||
Foxx.queues.registerJobType("log", function (data) {
|
||||
print(data);
|
||||
});
|
||||
|
||||
ctrl.get("/log", function () {
|
||||
queue.push("log", "Hello World!");
|
||||
});
|
||||
```
|
||||
|
||||
!SECTION Creating or updating a queue
|
||||
|
||||
Creates a queue with the given name and maximum number of workers.
|
||||
|
||||
`Foxx.queues.create(name, [maxWorkers])`
|
||||
|
||||
Returns the *Queue* instance for the given *name*. If the queue does not exist, a new queue with the given *name* will be created. If a queue with the given *name* already exists and *maxWorkers* is set, the queue's maximum number of workers will be updated.
|
||||
|
||||
*Parameter*
|
||||
|
||||
* *name*: the name of the queue to create.
|
||||
* *maxWorkers* (optional): the maximum number of workers. Default: *1*.
|
||||
|
||||
*Examples*
|
||||
|
||||
```js
|
||||
// Create a queue with the default number of workers (i.e. one)
|
||||
var queue1 = Foxx.queues.create("my-queue");
|
||||
// Create a queue with a given number of workers
|
||||
var queue2 = Foxx.queues.create("another-queue", 2);
|
||||
// Update the number of workers of an existing queue
|
||||
var queue3 = Foxx.queues.create("my-queue", 10);
|
||||
// queue1 and queue3 refer to the same queue
|
||||
assertEqual(queue1, queue3);
|
||||
```
|
||||
|
||||
!SECTION Fetching an existing queue
|
||||
|
||||
Fetches a queue with the given name.
|
||||
|
||||
`Foxx.queues.get(name)`
|
||||
|
||||
Returns the *Queue* instance for the given *name*. If the queue does not exist, an exception is thrown instead.
|
||||
|
||||
*Parameter*
|
||||
|
||||
* *name*: the name of the queue to fetch.
|
||||
|
||||
*Examples*
|
||||
|
||||
If the queue does not yet exist, an exception is thrown:
|
||||
|
||||
```js
|
||||
Foxx.queues.get("some-queue");
|
||||
// Error: Queue does not exist: some-queue
|
||||
// at ...
|
||||
```
|
||||
|
||||
Otherwise, the *Queue* instance will be returned:
|
||||
|
||||
```js
|
||||
var queue1 = Foxx.queues.create("some-queue");
|
||||
var queue2 = Foxx.queues.get("some-queue");
|
||||
assertEqual(queue1, queue2);
|
||||
```
|
||||
|
||||
!SECTION Deleting a queue
|
||||
|
||||
Deletes the queue with the given name from the database.
|
||||
|
||||
`Foxx.queues.delete(name)`
|
||||
|
||||
Returns *true* if the queue was deleted successfully. If the queue did not exist, it returns *false* instead.
|
||||
|
||||
When a queue is deleted, jobs on that queue will no longer be executed.
|
||||
|
||||
Deleting a queue will not delete any jobs on that queue.
|
||||
|
||||
*Parameter*
|
||||
|
||||
* *name*: the name of the queue to delete.
|
||||
|
||||
*Examples*
|
||||
|
||||
```js
|
||||
var queue = Foxx.queues.create("my-queue");
|
||||
Foxx.queues.delete("my-queue"); // true
|
||||
Foxx.queues.delete("my-queue"); // false
|
||||
```
|
||||
|
||||
!SECTION Registering a job type
|
||||
|
||||
Registers a job type with the queue manager.
|
||||
|
||||
`Foxx.queues.registerJobType(name, opts)`
|
||||
|
||||
If *opts* is a function, it will be treated as the *execute* function.
|
||||
|
||||
*Parameter*
|
||||
|
||||
* *name*: the name of the job type to register.
|
||||
* *opts*: an object with the following properties:
|
||||
* *execute*: a function to pass the job data to when a job is executed.
|
||||
* *maxFailures* (optional): the number of times a job will be re-tried before it is marked as *"failed"*. A negative value or *Infinity* means that the job will be re-tried on failure indefinitely. Default: *0*.
|
||||
* *schema* (optional): a [Joi](https://github.com/hapijs/joi) schema to validate a job's data against before accepting it.
|
||||
* *backOff* (optional): either a function that takes the number of times the job has failed before as input and returns the number of milliseconds to wait before trying the job again, or the delay to be used to calculate an [exponential back-off](https://en.wikipedia.org/wiki/Exponential_backoff), or *0* for no delay. Default: *1000*.
|
||||
|
||||
*Examples*
|
||||
|
||||
```js
|
||||
var Foxx = require("org/arangodb/foxx");
|
||||
Foxx.queues.registerJobType("log", function (data) {
|
||||
print(data);
|
||||
});
|
||||
```
|
||||
|
||||
!SECTION Adding a job to a queue
|
||||
|
||||
Adds a job of the given type to the given queue.
|
||||
|
||||
`Queue::push(name, data, [opts])`
|
||||
|
||||
Returns the job id.
|
||||
|
||||
*Parameter*
|
||||
|
||||
* *name*: the name of the job's job type.
|
||||
* *data*: the job data of the job; must be serializable to JSON.
|
||||
* *opts* (optional): an object with any of the following properties:
|
||||
* *success* (optional): a function to be called after the job has been completed successfully.
|
||||
* *failure* (optional): a function to be called after the job has failed too many times.
|
||||
* *delayUntil* (optional): a timestamp in milliseconds until which the execution of the job should be delayed. Default: *Date.now()*.
|
||||
* *backOff* (optional): either a function that takes the number of times the job has failed before as input and returns the number of milliseconds to wait before trying the job again, or the delay to be used to calculate an [exponential back-off](https://en.wikipedia.org/wiki/Exponential_backoff), or *0* for no delay. See [Registering a job type](#registering-a-job-type).
|
||||
* *allowUnknown* (optional): whether the job should be queued even if the job type name does not match a currently registered job type. Default: *false*.
|
||||
* *maxFailures* (optional): the number of times the job will be re-tried before it is marked as *"failed"*. A negative value or *Infinity* means that the job will be re-tried on failure indefinitely. See [Registering a job type](#registering-a-job-type).
|
||||
Note that if you pass a function for the *backOff* calculation, *success* callback or *failure* callback options the function must not rely on any external scope or external variables.
|
||||
|
||||
*Examples*
|
||||
|
||||
Basic usage example:
|
||||
|
||||
```js
|
||||
var Foxx = require("org/arangodb/foxx");
|
||||
var queue = Foxx.queues.create("my-queue");
|
||||
queue.push("log", "Hello World!");
|
||||
```
|
||||
|
||||
This will **not** work, because *console* was defined outside the callback function:
|
||||
|
||||
```js
|
||||
var Foxx = require("org/arangodb/foxx");
|
||||
var queue = Foxx.queues.create("my-queue");
|
||||
var console = require("console"); // outside the callback's function scope
|
||||
queue.push("log", "Hello World!", {
|
||||
success: function () {
|
||||
console.log("Yay!"); // throws "console is not defined"
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
!SECTION Fetching a job from the queue
|
||||
|
||||
Creates a proxy object representing a job with the given job id.
|
||||
|
||||
`Queue::get(jobId)`
|
||||
|
||||
Returns the *Job* instance for the given *jobId*. Properties of the job object will be fetched whenever they are referenced and can not be modified.
|
||||
|
||||
*Parameter*
|
||||
|
||||
* *jobId*: the id of the job to create a proxy object for.
|
||||
|
||||
*Examples*
|
||||
```js
|
||||
var jobId = queue.push("log", "Hello World!");
|
||||
var job = queue.get(jobId);
|
||||
assertEqual(job.id, jobId);
|
||||
```
|
||||
|
||||
!SECTION Deleting a job from the queue
|
||||
|
||||
Deletes a job with the given job id.
|
||||
|
||||
`Queue::delete(jobId)`
|
||||
|
||||
Returns *true* if the job was deleted successfully. If the job did not exist, it returns *false* instead.
|
||||
|
||||
!SECTION Fetching a list of jobs in a queue
|
||||
|
||||
*Examples*
|
||||
|
||||
```js
|
||||
queue.push("log", "Hello World!", {delayUntil: Date.now() + 50});
|
||||
assertEqual(queue.pending("log").length, 1);
|
||||
// 50 ms later...
|
||||
assertEqual(queue.pending("log").length, 0);
|
||||
assertEqual(queue.progress("log").length, 1);
|
||||
// even later...
|
||||
assertEqual(queue.progress("log").length, 0);
|
||||
assertEqual(queue.complete("log").length, 1);
|
||||
```
|
||||
|
||||
!SUBSECTION Fetching a list of pending jobs in a queue
|
||||
|
||||
`Queue::pending([type])`
|
||||
|
||||
Returns an array of job ids of jobs in the given queue with the status *"pending"*, optionally filtered by the given job type.
|
||||
|
||||
*Parameter*
|
||||
|
||||
* *type* (optional): the name of the job type to filter the results.
|
||||
|
||||
!SUBSECTION Fetching a list of jobs that are currently in progress
|
||||
|
||||
`Queue::progress([type])`
|
||||
|
||||
Returns an array of job ids of jobs in the given queue with the status *"progress"*, optionally filtered by the given job type.
|
||||
|
||||
*Parameter*
|
||||
|
||||
* *type* (optional): the name of the job type to filter the results.
|
||||
|
||||
!SUBSECTION Fetching a list of completed jobs in a queue
|
||||
|
||||
`Queue::complete([type])`
|
||||
|
||||
Returns an array of job ids of jobs in the given queue with the status *"complete"*, optionally filtered by the given job type.
|
||||
|
||||
*Parameter*
|
||||
|
||||
* *type* (optional): the name of the job type to filter the results.
|
||||
|
||||
!SUBSECTION Fetching a list of failed jobs in a queue
|
||||
|
||||
`Queue::failed([type])`
|
||||
|
||||
Returns an array of job ids of jobs in the given queue with the status *"failed"*, optionally filtered by the given job type.
|
||||
|
||||
*Parameter*
|
||||
|
||||
* *type* (optional): the name of the job type to filter the results.
|
||||
|
||||
!SUBSECTION Fetching a list of all jobs in a queue
|
||||
|
||||
`Queue::all([type])`
|
||||
|
||||
Returns an array of job ids of all jobs in the given queue, optionally filtered by the given job type.
|
||||
|
||||
*Parameter*
|
||||
|
||||
* *type* (optional): the name of the job type to filter the results.
|
||||
|
||||
!SECTION Aborting a job
|
||||
|
||||
Aborts a non-completed job.
|
||||
|
||||
`Job::abort()`
|
||||
|
||||
Sets a job's status to *"failed"* if it is not already *"complete"*, without calling the job's *onFailure* callback.
|
||||
|
|
@ -95,6 +95,7 @@
|
|||
* [Developing Applications](Foxx/DevelopingAnApplication.md)
|
||||
* [Dependency Injection](Foxx/FoxxInjection.md)
|
||||
* [Foxx Exports](Foxx/FoxxExports.md)
|
||||
* [Foxx Job Queues](Foxx/FoxxQueues.md)
|
||||
* [Optional Functionality](Foxx/FoxxOptional.md)
|
||||
<!-- 16 -->
|
||||
* [Foxx Manager](FoxxManager/README.md)
|
||||
|
|
|
@ -16,7 +16,7 @@ describe ArangoDB do
|
|||
doc.code.should eq(200)
|
||||
compatibility = doc.parsed_response['compatibility']
|
||||
compatibility.should be_kind_of(Integer)
|
||||
compatibility.should eq(20200)
|
||||
compatibility.should eq(20300)
|
||||
end
|
||||
|
||||
it "tests the compatibility value when a broken header is set" do
|
||||
|
@ -28,7 +28,7 @@ describe ArangoDB do
|
|||
doc.code.should eq(200)
|
||||
compatibility = doc.parsed_response['compatibility']
|
||||
compatibility.should be_kind_of(Integer)
|
||||
compatibility.should eq(20200)
|
||||
compatibility.should eq(20300)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -124,12 +124,12 @@ describe ArangoDB do
|
|||
end
|
||||
|
||||
it "tests the compatibility value when a too high version is set" do
|
||||
doc = ArangoDB.get("/_admin/echo", :headers => { "x-arango-version" => "2.3" })
|
||||
doc = ArangoDB.get("/_admin/echo", :headers => { "x-arango-version" => "2.4" })
|
||||
|
||||
doc.code.should eq(200)
|
||||
compatibility = doc.parsed_response['compatibility']
|
||||
compatibility.should be_kind_of(Integer)
|
||||
compatibility.should eq(20300)
|
||||
compatibility.should eq(20400)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -204,7 +204,7 @@ describe ArangoDB do
|
|||
doc.headers["x-arango-replication-lastincluded"].should_not eq("0")
|
||||
doc.headers["content-type"].should eq("application/x-arango-dump; charset=utf-8")
|
||||
|
||||
body = doc.response.body
|
||||
body = doc.response.body.split("\n")[0]
|
||||
document = JSON.parse(body)
|
||||
|
||||
document.should have_key("tick")
|
||||
|
@ -230,7 +230,7 @@ describe ArangoDB do
|
|||
c["waitForSync"].should eq(true)
|
||||
end
|
||||
|
||||
it "fetches some collection operations the follow log" do
|
||||
it "fetches some collection operations from the follow log" do
|
||||
ArangoDB.drop_collection("UnitTestsReplication")
|
||||
|
||||
sleep 1
|
||||
|
@ -282,28 +282,30 @@ describe ArangoDB do
|
|||
document = JSON.parse(part)
|
||||
|
||||
if i == 0
|
||||
# create collection
|
||||
document.should have_key("tick")
|
||||
document.should have_key("type")
|
||||
document.should have_key("cid")
|
||||
document.should have_key("collection")
|
||||
if document["type"] == 2000 and document["cid"] == cid
|
||||
# create collection
|
||||
document.should have_key("tick")
|
||||
document.should have_key("type")
|
||||
document.should have_key("cid")
|
||||
document.should have_key("collection")
|
||||
|
||||
document["tick"].should match(/^\d+$/)
|
||||
document["tick"].to_i.should >= fromTick.to_i
|
||||
document["type"].should eq(2000)
|
||||
document["cid"].should eq(cid)
|
||||
document["tick"].should match(/^\d+$/)
|
||||
document["tick"].to_i.should >= fromTick.to_i
|
||||
document["type"].should eq(2000)
|
||||
document["cid"].should eq(cid)
|
||||
|
||||
c = document["collection"]
|
||||
c.should have_key("version")
|
||||
c["type"].should eq(2)
|
||||
c["cid"].should eq(cid)
|
||||
c["deleted"].should eq(false)
|
||||
c["doCompact"].should eq(true)
|
||||
c.should have_key("maximalSize")
|
||||
c["maximalSize"].should be_kind_of(Integer)
|
||||
c["name"].should eq("UnitTestsReplication")
|
||||
c["isVolatile"].should eq(false)
|
||||
c["waitForSync"].should eq(true)
|
||||
c = document["collection"]
|
||||
c.should have_key("version")
|
||||
c["type"].should eq(2)
|
||||
c["cid"].should eq(cid)
|
||||
c["deleted"].should eq(false)
|
||||
c["doCompact"].should eq(true)
|
||||
c.should have_key("maximalSize")
|
||||
c["maximalSize"].should be_kind_of(Integer)
|
||||
c["name"].should eq("UnitTestsReplication")
|
||||
c["isVolatile"].should eq(false)
|
||||
c["waitForSync"].should eq(true)
|
||||
end
|
||||
|
||||
elsif i == 1
|
||||
# create document
|
||||
|
|
|
@ -4238,9 +4238,10 @@ static v8::Handle<v8::Value> JS_DatafileScanVocbaseCol (v8::Arguments const& arg
|
|||
return scope.Close(result);
|
||||
}
|
||||
|
||||
// .............................................................................
|
||||
// generate the TRI_vocbase_col_t template
|
||||
// .............................................................................
|
||||
// .............................................................................
|
||||
// generate the TRI_vocbase_col_t template
|
||||
// .............................................................................
|
||||
|
||||
void TRI_InitV8collection (v8::Handle<v8::Context> context,
|
||||
TRI_server_t* server,
|
||||
TRI_vocbase_t* vocbase,
|
||||
|
|
|
@ -1285,7 +1285,7 @@ static v8::Handle<v8::Value> CreateCollectionCoordinator (
|
|||
/// ],
|
||||
/// "isNewlyCreated" : true
|
||||
/// }
|
||||
/// ```js
|
||||
/// ```
|
||||
///
|
||||
/// @endDocuBlock
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -6,7 +6,7 @@ dnl ============================================================================
|
|||
dnl --SECTION-- triAGENS GmbH Build Environment
|
||||
dnl ============================================================================
|
||||
|
||||
AC_INIT([triAGENS ArangoDB], [2.2.0-devel], [info@triagens.de], [arangodb], [http://www.arangodb.org])
|
||||
AC_INIT([triAGENS ArangoDB], [2.3.0-devel], [info@triagens.de], [arangodb], [http://www.arangodb.org])
|
||||
|
||||
dnl ----------------------------------------------------------------------------
|
||||
dnl auxillary directory for install-sh and missing
|
||||
|
|
|
@ -323,30 +323,4 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Create Edge Modal -->
|
||||
<div id="edgeCreateModal" style="display:none" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3 id="myModalLabel">Create Edge</h3>
|
||||
</div>
|
||||
<div class="modal-body" id="createEdge">
|
||||
<p>Please complete your task:</p>
|
||||
<table>
|
||||
<tr>
|
||||
<th class="collectionTh">_from:</th>
|
||||
<th><input type="text" id="new-document-from" name="from" value=""/></th>
|
||||
<th><a class="modalTooltips" title="Document _id: document handle of the linked vertex (incoming relation)"><i class="icon-info-sign"></i></a></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="collectionTh">_to:</th>
|
||||
<th><input type="text" id="new-document-to" name="to" value=""/></th>
|
||||
<th><a class="modalTooltips" title="Document _id: document handle of the linked vertex (outgoing relation)"><i class="icon-info-sign"></i></a></th>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="button-close" data-dismiss="modal" aria-hidden="true">Close</button>
|
||||
<button id="confirmCreateEdge" class="button-success" style="float:right">Create</button>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
|
|
|
@ -118,4 +118,4 @@
|
|||
<%
|
||||
}
|
||||
%>
|
||||
</script>
|
||||
</script>
|
||||
|
|
|
@ -31,16 +31,16 @@ window.ApplicationsView = Backbone.View.extend({
|
|||
'Github information',
|
||||
'',
|
||||
'Your Github link comes here: username/application-name',
|
||||
undefined,
|
||||
"username/application-name",
|
||||
false,
|
||||
[
|
||||
{
|
||||
rule: Joi.string().required(),
|
||||
msg: "No github link given."
|
||||
},
|
||||
{
|
||||
rule: Joi.string().regex(/^[a-zA-Z0-9]+[\/]/),
|
||||
msg: "No valid github link given."
|
||||
},
|
||||
{
|
||||
rule: Joi.string().required(),
|
||||
msg: "No github link given."
|
||||
}
|
||||
]
|
||||
));
|
||||
|
@ -50,7 +50,7 @@ window.ApplicationsView = Backbone.View.extend({
|
|||
'Version (optional)',
|
||||
'',
|
||||
'Example: v1.1.2 for Version 1.1.2 - if no version is commited, master is used',
|
||||
undefined,
|
||||
'master',
|
||||
false,
|
||||
/[<>&'"]/
|
||||
));
|
||||
|
|
|
@ -172,16 +172,20 @@
|
|||
"",
|
||||
true,
|
||||
[
|
||||
{
|
||||
rule: Joi.string().required(),
|
||||
msg: "No collection name given."
|
||||
},
|
||||
{
|
||||
rule: Joi.string().regex(/^[a-zA-Z]/),
|
||||
msg: "Collection name must always start with a letter."
|
||||
},
|
||||
{
|
||||
rule: Joi.string().regex(/^[a-zA-Z0-9\-_]*$/),
|
||||
msg: 'Only Symbols "_" and "-" are allowed.'
|
||||
},
|
||||
{
|
||||
rule: Joi.string().required(),
|
||||
msg: "No collection name given."
|
||||
}
|
||||
]
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
}, this);
|
||||
|
||||
//if type in collectionsDropdown2 is changed,
|
||||
// the page will be rerendered, so check the toggel button
|
||||
//the page will be rerendered, so check the toggel button
|
||||
if($('#collectionsDropdown2').css('display') === 'none') {
|
||||
$('#collectionsToggle').removeClass('activated');
|
||||
|
||||
|
@ -304,6 +304,10 @@
|
|||
rule: Joi.string().regex(/^[a-zA-Z]/),
|
||||
msg: "Collection name must always start with a letter."
|
||||
},
|
||||
{
|
||||
rule: Joi.string().regex(/^[a-zA-Z0-9\-_]*$/),
|
||||
msg: 'Only Symbols "_" and "-" are allowed.'
|
||||
},
|
||||
{
|
||||
rule: Joi.string().required(),
|
||||
msg: "No collection name given."
|
||||
|
|
|
@ -246,14 +246,17 @@
|
|||
[
|
||||
{
|
||||
rule: Joi.string().regex(/^[a-zA-Z]/),
|
||||
msg: "Database name must always start with a letter."
|
||||
msg: "Database name must start with a letter."
|
||||
},
|
||||
{
|
||||
rule: Joi.string().regex(/^[a-zA-Z0-9\-_]*$/),
|
||||
msg: 'Only Symbols "_" and "-" are allowed.'
|
||||
},
|
||||
{
|
||||
rule: Joi.string().required(),
|
||||
msg: "No database name given."
|
||||
}
|
||||
]
|
||||
|
||||
)
|
||||
);
|
||||
tableContent.push(
|
||||
|
@ -266,7 +269,13 @@
|
|||
+ "you will not be able to see the database. "
|
||||
+ "If there is a failure you will be informed.",
|
||||
"Database Owner",
|
||||
true
|
||||
true,
|
||||
[
|
||||
{
|
||||
rule: Joi.string().required(),
|
||||
msg: "No username given."
|
||||
}
|
||||
]
|
||||
)
|
||||
);
|
||||
tableContent.push(
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*jslint indent: 2, nomen: true, maxlen: 100, vars: true, white: true, plusplus: true */
|
||||
/*global require, arangoHelper, _, $, window, arangoHelper, templateEngine */
|
||||
/*global require, arangoHelper, _, $, window, arangoHelper, templateEngine, Joi*/
|
||||
|
||||
(function() {
|
||||
"use strict";
|
||||
|
@ -50,7 +50,6 @@
|
|||
"click #filterSend" : "sendFilter",
|
||||
"click #addFilterItem" : "addFilterItem",
|
||||
"click .removeFilterItem" : "removeFilterItem",
|
||||
"click #confirmCreateEdge" : "addEdge",
|
||||
"click #documentsTableID tr" : "clicked",
|
||||
"click #deleteDoc" : "remove",
|
||||
"click #addDocumentButton" : "addDocument",
|
||||
|
@ -108,9 +107,6 @@
|
|||
},
|
||||
|
||||
listenKey: function (e) {
|
||||
if(e.keyCode === 13){
|
||||
this.addEdge();
|
||||
}
|
||||
},
|
||||
|
||||
resetView: function () {
|
||||
|
@ -342,8 +338,56 @@
|
|||
// second parameter is "true" to disable caching of collection type
|
||||
doctype = arangoHelper.collectionApiType(collid, true);
|
||||
if (doctype === 'edge') {
|
||||
$('#edgeCreateModal').modal('show');
|
||||
arangoHelper.fixTooltips(".modalTooltips", "left");
|
||||
//$('#edgeCreateModal').modal('show');
|
||||
//arangoHelper.fixTooltips(".modalTooltips", "left");
|
||||
|
||||
var buttons = [], tableContent = [];
|
||||
|
||||
tableContent.push(
|
||||
window.modalView.createTextEntry(
|
||||
'new-edge-from-attr',
|
||||
'_from',
|
||||
'',
|
||||
"document _id: document handle of the linked vertex (incoming relation)",
|
||||
undefined,
|
||||
false,
|
||||
[
|
||||
{
|
||||
rule: Joi.string().required(),
|
||||
msg: "No _from attribute given."
|
||||
}
|
||||
]
|
||||
)
|
||||
);
|
||||
|
||||
tableContent.push(
|
||||
window.modalView.createTextEntry(
|
||||
'new-edge-to',
|
||||
'_to',
|
||||
'',
|
||||
"document _id: document handle of the linked vertex (outgoing relation)",
|
||||
undefined,
|
||||
false,
|
||||
[
|
||||
{
|
||||
rule: Joi.string().required(),
|
||||
msg: "No _to attribute given."
|
||||
}
|
||||
]
|
||||
)
|
||||
);
|
||||
|
||||
buttons.push(
|
||||
window.modalView.createSuccessButton('Create', this.addEdge.bind(this))
|
||||
);
|
||||
|
||||
window.modalView.show(
|
||||
'modalTable.ejs',
|
||||
'Create edge',
|
||||
buttons,
|
||||
tableContent
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -359,20 +403,13 @@
|
|||
|
||||
addEdge: function () {
|
||||
var collid = window.location.hash.split("/")[1];
|
||||
var from = $('#new-document-from').val();
|
||||
var to = $('#new-document-to').val();
|
||||
if (from === '') {
|
||||
//Heiko: Form-Validator - from is missing
|
||||
return;
|
||||
}
|
||||
if (to === '') {
|
||||
//Heiko: Form-Validator - to is missing
|
||||
return;
|
||||
}
|
||||
var from = $('.modal-body #new-edge-from-attr').last().val();
|
||||
var to = $('.modal-body #new-edge-to').last().val();
|
||||
var result = this.documentStore.createTypeEdge(collid, from, to);
|
||||
|
||||
if (result !== false) {
|
||||
$('#edgeCreateModal').modal('hide');
|
||||
//$('#edgeCreateModal').modal('hide');
|
||||
window.modalView.hide();
|
||||
window.location.hash = "collection/"+result;
|
||||
}
|
||||
//Error
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*jslint indent: 2, nomen: true, maxlen: 100, vars: true, white: true, plusplus: true */
|
||||
/*global Backbone, $, window, EJS, arangoHelper, _, templateEngine*/
|
||||
/*global Backbone, $, window, EJS, arangoHelper, _, templateEngine, Joi*/
|
||||
|
||||
(function() {
|
||||
"use strict";
|
||||
|
@ -89,7 +89,13 @@
|
|||
"change-mount-point", "Mount", this.model.get("mount"),
|
||||
"The path where the app can be reached.",
|
||||
"mount-path",
|
||||
true
|
||||
true,
|
||||
[
|
||||
{
|
||||
rule: Joi.string().required(),
|
||||
msg: "No mount-path given."
|
||||
}
|
||||
]
|
||||
));
|
||||
/*
|
||||
* For the future, update apps to available newer versions
|
||||
|
|
|
@ -285,8 +285,17 @@
|
|||
var ind = buttons.indexOf(this.closeButton);
|
||||
buttons.splice(ind, 1);
|
||||
|
||||
var completeTableContent = tableContent;
|
||||
try {
|
||||
_.each(advancedContent.content, function(x) {
|
||||
completeTableContent.push(x);
|
||||
});
|
||||
}
|
||||
catch(ignore) {
|
||||
}
|
||||
|
||||
//handle select2
|
||||
_.each(tableContent, function(r) {
|
||||
_.each(completeTableContent, function(r) {
|
||||
if (r.type === self.tables.SELECT2) {
|
||||
$('#'+r.id).select2({
|
||||
tags: r.tags || [],
|
||||
|
@ -299,11 +308,11 @@
|
|||
});//handle select2
|
||||
|
||||
self.testInput = (function(){
|
||||
_.each(tableContent,function(r){
|
||||
_.each(completeTableContent,function(r){
|
||||
|
||||
if(r.validateInput) {
|
||||
//catch result of validation and act
|
||||
$('#' + r.id).on('keyup', function(){
|
||||
$('#' + r.id).on('keyup focusout', function(e){
|
||||
|
||||
var validation = r.validateInput($('#' + r.id));
|
||||
var error = false, msg;
|
||||
|
@ -314,8 +323,14 @@
|
|||
toCheck: validator.rule
|
||||
});
|
||||
|
||||
var valueToCheck = $('#' + r.id).val();
|
||||
|
||||
if (valueToCheck === '' && e.type === "keyup") {
|
||||
return;
|
||||
}
|
||||
|
||||
Joi.validate({
|
||||
toCheck: $('#' + r.id).val()
|
||||
toCheck: valueToCheck
|
||||
},
|
||||
schema,
|
||||
function (err, value) {
|
||||
|
@ -331,13 +346,15 @@
|
|||
if(error === true){
|
||||
// if validation throws an error
|
||||
$('#' + r.id).addClass('invalid-input');
|
||||
$('.modal-footer .button-success').prop('disabled', true);
|
||||
$('.modal-footer .button-success').addClass('disabled');
|
||||
|
||||
if (errorElement) {
|
||||
//error element available
|
||||
$(errorElement).text(msg);
|
||||
}
|
||||
else {
|
||||
//render error element
|
||||
//error element not available
|
||||
$('#' + r.id).after('<p class="errorMessage">' + msg+ '</p>');
|
||||
}
|
||||
|
||||
|
@ -345,6 +362,8 @@
|
|||
else {
|
||||
//validation throws success
|
||||
$('#' + r.id).removeClass('invalid-input');
|
||||
$('.modal-footer .button-success').prop('disabled', false);
|
||||
$('.modal-footer .button-success').removeClass('disabled');
|
||||
if (errorElement) {
|
||||
$(errorElement).remove();
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*jslint indent: 2, nomen: true, maxlen: 100, vars: true, white: true, plusplus: true */
|
||||
/*global require, exports, Backbone, EJS, $, setTimeout, localStorage, ace, Storage, window, _ */
|
||||
/*global arangoHelper, templateEngine, jQuery*/
|
||||
/*global arangoHelper, templateEngine, jQuery, Joi*/
|
||||
|
||||
(function () {
|
||||
"use strict";
|
||||
|
@ -59,7 +59,12 @@
|
|||
undefined,
|
||||
undefined,
|
||||
false,
|
||||
/[<>&'"]/
|
||||
[
|
||||
{
|
||||
rule: Joi.string().required(),
|
||||
msg: "No query name given."
|
||||
}
|
||||
]
|
||||
)
|
||||
);
|
||||
buttons.push(
|
||||
|
|
|
@ -27,6 +27,8 @@
|
|||
self.resize();
|
||||
});
|
||||
|
||||
this.executeJs("start_pretty_print()");
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*jslint indent: 2, nomen: true, maxlen: 100, vars: true, white: true, plusplus: true */
|
||||
/*global window, document, Backbone, EJS, SwaggerUi, hljs, $, arangoHelper, templateEngine,
|
||||
CryptoJS */
|
||||
CryptoJS, Joi */
|
||||
(function() {
|
||||
|
||||
"use strict";
|
||||
|
@ -406,7 +406,20 @@
|
|||
tableContent = [];
|
||||
|
||||
tableContent.push(
|
||||
window.modalView.createTextEntry("newUsername", "Username", "", false, "Username", true)
|
||||
window.modalView.createTextEntry(
|
||||
"newUsername",
|
||||
"Username",
|
||||
"",
|
||||
false,
|
||||
"Username",
|
||||
true,
|
||||
[
|
||||
{
|
||||
rule: Joi.string().required(),
|
||||
msg: "No username given."
|
||||
}
|
||||
]
|
||||
)
|
||||
);
|
||||
tableContent.push(
|
||||
window.modalView.createTextEntry("newName", "Name", "", false, "Name", false)
|
||||
|
|
|
@ -38,7 +38,10 @@
|
|||
.button-success {
|
||||
@extend %btn;
|
||||
@extend %positive;
|
||||
}
|
||||
|
||||
.button-success:disabled {
|
||||
@extend %neutral;
|
||||
}
|
||||
|
||||
.button-danger {
|
||||
|
|
|
@ -159,6 +159,10 @@
|
|||
width: 398px;
|
||||
}
|
||||
|
||||
.collectionTh {
|
||||
height: 55px;
|
||||
}
|
||||
|
||||
.tab-content {
|
||||
min-height: 200px;
|
||||
}
|
||||
|
@ -167,7 +171,8 @@
|
|||
color: red;
|
||||
font-size: 9pt;
|
||||
margin-bottom: 5px;
|
||||
margin-top: -5px;
|
||||
margin-top: -9px;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
.navbar {
|
||||
|
||||
-webkit-font-smoothing: subpixel-antialiased;
|
||||
|
||||
.nav {
|
||||
li.dropdown {
|
||||
.active > .dropdown-toggle,
|
||||
|
|
|
@ -1420,9 +1420,9 @@ nav.navbar, footer.footer {
|
|||
.button-warning:hover, .button-warning:focus {
|
||||
background-color: #f89406; }
|
||||
|
||||
.button-neutral, .button-close {
|
||||
.button-neutral, .button-success:disabled, .button-close {
|
||||
background-color: #8f8d8c; }
|
||||
.button-neutral:hover, .button-close:hover, .button-neutral:focus, .button-close:focus {
|
||||
.button-neutral:hover, .button-success:hover:disabled, .button-close:hover, .button-neutral:focus, .button-success:focus:disabled, .button-close:focus {
|
||||
background-color: #736b68; }
|
||||
|
||||
.dashboard-sub-bar-menu {
|
||||
|
@ -1512,10 +1512,12 @@ ul.link-dropdown-menu, ul.user-dropdown-menu, ul.gv-dropdown-menu {
|
|||
right: 8px;
|
||||
top: -6px; }
|
||||
|
||||
.navbar .nav li.dropdown .active > .dropdown-toggle,
|
||||
.navbar .nav li.dropdown .open > .dropdown-toggle,
|
||||
.navbar .nav li.dropdown .open.active > .dropdown-toggle {
|
||||
background: #788f3d; }
|
||||
.navbar {
|
||||
-webkit-font-smoothing: subpixel-antialiased; }
|
||||
.navbar .nav li.dropdown .active > .dropdown-toggle,
|
||||
.navbar .nav li.dropdown .open > .dropdown-toggle,
|
||||
.navbar .nav li.dropdown .open.active > .dropdown-toggle {
|
||||
background: #788f3d; }
|
||||
|
||||
nav.navbar {
|
||||
height: 38px;
|
||||
|
@ -4456,13 +4458,16 @@ div.breadcrumb a.disabledBread {
|
|||
width: 384px; }
|
||||
.modal-body select {
|
||||
width: 398px; }
|
||||
.modal-body .collectionTh {
|
||||
height: 55px; }
|
||||
.modal-body .tab-content {
|
||||
min-height: 200px; }
|
||||
.modal-body .errorMessage {
|
||||
color: red;
|
||||
font-size: 9pt;
|
||||
margin-bottom: 5px;
|
||||
margin-top: -5px; }
|
||||
margin-top: -9px;
|
||||
position: absolute; }
|
||||
|
||||
.modal-text {
|
||||
font-weight: 300;
|
||||
|
|
|
@ -42,7 +42,7 @@ jasmine.getGlobal().clearTimeout = function (timeoutId) {
|
|||
|
||||
exports.executeTestSuite = function (specFileNames, options) {
|
||||
'use strict';
|
||||
var sandbox = jasmine.getEnv(),
|
||||
var sandbox = new jasmine.Env(),
|
||||
format = options.format || 'progress';
|
||||
|
||||
// Explicitly add require
|
||||
|
|
|
@ -52,6 +52,9 @@
|
|||
// autoload all modules and reload routing information in all threads
|
||||
internal.executeGlobalContextFunction("bootstrapCoordinator");
|
||||
|
||||
// start the queue manager once
|
||||
require('org/arangodb/foxx/queues/manager').run();
|
||||
|
||||
console.info("bootstraped coordinator %s", ArangoServerState.id());
|
||||
return true;
|
||||
};
|
||||
|
|
|
@ -57,6 +57,9 @@
|
|||
require("org/arangodb/statistics").startup();
|
||||
}
|
||||
|
||||
// start the queue manager once
|
||||
require('org/arangodb/foxx/queues/manager').run();
|
||||
|
||||
console.info("bootstraped DB server %s", ArangoServerState.id());
|
||||
return true;
|
||||
};
|
||||
|
|
|
@ -32,8 +32,10 @@ var Controller = require("org/arangodb/foxx/controller").Controller,
|
|||
Model = require("org/arangodb/foxx/model").Model,
|
||||
Repository = require("org/arangodb/foxx/repository").Repository,
|
||||
manager = require("org/arangodb/foxx/manager"),
|
||||
queues = require("org/arangodb/foxx/queues"),
|
||||
arangodb = require("org/arangodb");
|
||||
|
||||
exports.queues = queues;
|
||||
exports.Controller = Controller;
|
||||
exports.Model = Model;
|
||||
exports.Repository = Repository;
|
||||
|
|
|
@ -1487,7 +1487,8 @@ exports.appRoutes = function () {
|
|||
|
||||
return arangodb.db._executeTransaction({
|
||||
collections: {
|
||||
read: [ aal.name() ]
|
||||
read: [ '_queues', '_jobs', aal.name() ],
|
||||
write: [ '_queues', '_jobs' ]
|
||||
},
|
||||
params: {
|
||||
aal : aal
|
||||
|
|
|
@ -0,0 +1,308 @@
|
|||
/*jslint es5: true, indent: 2, nomen: true, maxlen: 120 */
|
||||
/*global module, require */
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief Foxx queues
|
||||
///
|
||||
/// @file
|
||||
///
|
||||
/// DISCLAIMER
|
||||
///
|
||||
/// 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 triAGENS GmbH, Cologne, Germany
|
||||
///
|
||||
/// @author Alan Plum
|
||||
/// @author Copyright 2014, triAGENS GmbH, Cologne, Germany
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
var _ = require('underscore'),
|
||||
flatten = require('internal').flatten,
|
||||
arangodb = require('org/arangodb'),
|
||||
console = require('console'),
|
||||
db = arangodb.db,
|
||||
failImmutable,
|
||||
queueMap,
|
||||
jobMap,
|
||||
queues,
|
||||
getJobs,
|
||||
Job,
|
||||
Queue;
|
||||
|
||||
failImmutable = function (name) {
|
||||
'use strict';
|
||||
return function () {
|
||||
throw new Error(name + ' is not mutable');
|
||||
};
|
||||
};
|
||||
|
||||
queueMap = Object.create(null);
|
||||
jobMap = Object.create(null);
|
||||
|
||||
queues = {
|
||||
_jobTypes: Object.create(null),
|
||||
get: function (key) {
|
||||
'use strict';
|
||||
|
||||
if (!queueMap[key]) {
|
||||
if (!db._queues.exists(key)) {
|
||||
throw new Error('Queue does not exist: ' + key);
|
||||
}
|
||||
queueMap[key] = new Queue(key);
|
||||
}
|
||||
return queueMap[key];
|
||||
},
|
||||
create: function (key, maxWorkers) {
|
||||
'use strict';
|
||||
try {
|
||||
db._queues.save({_key: key, maxWorkers: maxWorkers || 1});
|
||||
} catch (err) {
|
||||
if (!err instanceof arangodb.ArangoError ||
|
||||
err.errorNum !== arangodb.ERROR_ARANGO_UNIQUE_CONSTRAINT_VIOLATED) {
|
||||
throw err;
|
||||
}
|
||||
if (maxWorkers) {
|
||||
db._queues.update(key, {maxWorkers: maxWorkers});
|
||||
}
|
||||
}
|
||||
if (!queueMap[key]) {
|
||||
queueMap[key] = new Queue(key);
|
||||
}
|
||||
return queueMap[key];
|
||||
},
|
||||
delete: function (key) {
|
||||
'use strict';
|
||||
var result = false;
|
||||
db._executeTransaction({
|
||||
collections: {
|
||||
read: ['_queues'],
|
||||
write: ['_queues']
|
||||
},
|
||||
action: function () {
|
||||
if (db._queues.exists(key)) {
|
||||
db._queues.remove(key);
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
return result;
|
||||
},
|
||||
registerJobType: function (type, opts) {
|
||||
'use strict';
|
||||
if (typeof opts === 'function') {
|
||||
opts = {execute: opts};
|
||||
}
|
||||
if (typeof opts.execute !== 'function') {
|
||||
throw new Error('Must provide a function to execute!');
|
||||
}
|
||||
if (opts.schema && typeof opts.schema.validate !== 'function') {
|
||||
throw new Error('Schema must be a joi schema!');
|
||||
}
|
||||
var cfg = _.extend({maxFailures: 0}, opts);
|
||||
queues._jobTypes[type] = cfg;
|
||||
}
|
||||
};
|
||||
|
||||
getJobs = function (queue, status, type) {
|
||||
'use strict';
|
||||
var vars = {},
|
||||
aql = 'FOR job IN _jobs';
|
||||
if (queue !== undefined) {
|
||||
aql += ' FILTER job.queue == @queue';
|
||||
vars.queue = queue;
|
||||
}
|
||||
if (status !== undefined) {
|
||||
aql += ' FILTER job.status == @status';
|
||||
vars.status = status;
|
||||
}
|
||||
if (type !== undefined) {
|
||||
aql += ' FILTER job.type == @type';
|
||||
vars.type = type;
|
||||
}
|
||||
aql += ' SORT job.delayUntil ASC RETURN job._id';
|
||||
return db._createStatement({
|
||||
query: aql,
|
||||
bindVars: vars
|
||||
}).execute().toArray();
|
||||
};
|
||||
|
||||
Job = function Job(id) {
|
||||
'use strict';
|
||||
var self = this;
|
||||
Object.defineProperty(self, 'id', {
|
||||
get: function () {
|
||||
return id;
|
||||
},
|
||||
configurable: false,
|
||||
enumerable: true
|
||||
});
|
||||
_.each(['data', 'status', 'type', 'failures'], function (key) {
|
||||
Object.defineProperty(self, key, {
|
||||
get: function () {
|
||||
var value = db._jobs.document(this.id)[key];
|
||||
return (value && typeof value === 'object') ? Object.freeze(value) : value;
|
||||
},
|
||||
set: failImmutable(key),
|
||||
configurable: false,
|
||||
enumerable: true
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
_.extend(Job.prototype, {
|
||||
abort: function () {
|
||||
'use strict';
|
||||
var self = this;
|
||||
db._executeTransaction({
|
||||
collections: {
|
||||
read: ['_jobs'],
|
||||
write: ['_jobs']
|
||||
},
|
||||
action: function () {
|
||||
var job = db._jobs.document(self.id);
|
||||
if (job.status !== 'completed') {
|
||||
db._jobs.update(job, {
|
||||
status: 'failed',
|
||||
modified: Date.now(),
|
||||
failures: job.failures.concat([
|
||||
flatten(new Error('Job aborted.'))
|
||||
])
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
reset: function () {
|
||||
'use strict';
|
||||
db._jobs.update(this.id, {
|
||||
status: 'pending'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
Queue = function Queue(name) {
|
||||
'use strict';
|
||||
Object.defineProperty(this, 'name', {
|
||||
get: function () {
|
||||
return name;
|
||||
},
|
||||
set: failImmutable('name'),
|
||||
configurable: false,
|
||||
enumerable: true
|
||||
});
|
||||
};
|
||||
|
||||
_.extend(Queue.prototype, {
|
||||
push: function (name, data, opts) {
|
||||
'use strict';
|
||||
var type, result, now;
|
||||
if (typeof name !== 'string') {
|
||||
throw new Error('Must pass a job type!');
|
||||
}
|
||||
if (!opts) {
|
||||
opts = {};
|
||||
}
|
||||
|
||||
type = queues._jobTypes[name];
|
||||
if (type !== undefined) {
|
||||
if (type.schema) {
|
||||
result = type.schema.validate(data);
|
||||
if (result.error) {
|
||||
throw result.error;
|
||||
}
|
||||
data = result.value;
|
||||
}
|
||||
} else if (opts.allowUnknown) {
|
||||
console.warn('Unknown job type: ' + name);
|
||||
} else {
|
||||
throw new Error('Unknown job type: ' + name);
|
||||
}
|
||||
now = Date.now();
|
||||
return db._jobs.save({
|
||||
status: 'pending',
|
||||
queue: this.name,
|
||||
type: name,
|
||||
failures: [],
|
||||
data: data,
|
||||
created: now,
|
||||
modified: now,
|
||||
maxFailures: opts.maxFailures === Infinity ? -1 : opts.maxFailures,
|
||||
backOff: typeof opts.backOff === 'function' ? opts.backOff.toString() : opts.backOff,
|
||||
delayUntil: opts.delayUntil || now,
|
||||
onSuccess: opts.success ? opts.success.toString() : null,
|
||||
onFailure: opts.failure ? opts.failure.toString() : null
|
||||
})._id;
|
||||
},
|
||||
get: function (id) {
|
||||
'use strict';
|
||||
if (!id.match(/^_jobs\//)) {
|
||||
id = '_jobs/' + id;
|
||||
}
|
||||
if (!jobMap[id]) {
|
||||
jobMap[id] = new Job(id);
|
||||
}
|
||||
return jobMap[id];
|
||||
},
|
||||
delete: function (id) {
|
||||
'use strict';
|
||||
return db._executeTransaction({
|
||||
collections: {
|
||||
read: ['_jobs'],
|
||||
write: ['_jobs']
|
||||
},
|
||||
action: function () {
|
||||
try {
|
||||
db._jobs.remove(id);
|
||||
return true;
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
pending: function (jobType) {
|
||||
'use strict';
|
||||
return getJobs(this.name, 'pending', jobType);
|
||||
},
|
||||
complete: function (jobType) {
|
||||
'use strict';
|
||||
return getJobs(this.name, 'complete', jobType);
|
||||
},
|
||||
failed: function (jobType) {
|
||||
'use strict';
|
||||
return getJobs(this.name, 'failed', jobType);
|
||||
},
|
||||
progress: function (jobType) {
|
||||
'use strict';
|
||||
return getJobs(this.name, 'progress', jobType);
|
||||
},
|
||||
all: function (jobType) {
|
||||
'use strict';
|
||||
return getJobs(this.name, undefined, jobType);
|
||||
}
|
||||
});
|
||||
|
||||
queues.create('default');
|
||||
|
||||
module.exports = queues;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- END-OF-FILE
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// Local Variables:
|
||||
// mode: outline-minor
|
||||
// outline-regexp: "/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @}\\|/\\*jslint"
|
||||
// End:
|
|
@ -0,0 +1,91 @@
|
|||
/*jslint es5: true, indent: 2, nomen: true, maxlen: 120 */
|
||||
/*global exports, require */
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief Foxx queues manager
|
||||
///
|
||||
/// @file
|
||||
///
|
||||
/// DISCLAIMER
|
||||
///
|
||||
/// 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 triAGENS GmbH, Cologne, Germany
|
||||
///
|
||||
/// @author Alan Plum
|
||||
/// @author Copyright 2014, triAGENS GmbH, Cologne, Germany
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
var QUEUE_MANAGER_PERIOD = 1000, // in ms
|
||||
_ = require('underscore'),
|
||||
tasks = require('org/arangodb/tasks'),
|
||||
db = require('org/arangodb').db;
|
||||
|
||||
exports.manage = function () {
|
||||
'use strict';
|
||||
db._executeTransaction({
|
||||
collections: {
|
||||
read: ['_queues', '_jobs'],
|
||||
write: ['_jobs']
|
||||
},
|
||||
action: function () {
|
||||
db._queues.all().toArray().forEach(function (queue) {
|
||||
var numBusy = db._jobs.byExample({
|
||||
queue: queue._key,
|
||||
status: 'progress'
|
||||
}).count();
|
||||
if (numBusy >= queue.maxWorkers) {
|
||||
return;
|
||||
}
|
||||
db._createStatement({
|
||||
query: (
|
||||
'FOR job IN _jobs'
|
||||
+ ' FILTER job.queue == @queue'
|
||||
+ ' && job.status == "pending"'
|
||||
+ ' && job.delayUntil <= @now'
|
||||
+ ' SORT job.delayUntil ASC'
|
||||
+ ' LIMIT @max'
|
||||
+ ' RETURN job'
|
||||
),
|
||||
bindVars: {
|
||||
queue: queue._key,
|
||||
now: Date.now(),
|
||||
max: queue.maxWorkers - numBusy
|
||||
}
|
||||
}).execute().toArray().forEach(function (doc) {
|
||||
db._jobs.update(doc, {status: 'progress'});
|
||||
tasks.register({
|
||||
command: function (job) {
|
||||
require('org/arangodb/foxx/queues/worker').work(job);
|
||||
},
|
||||
params: _.extend({}, doc, {status: 'progress'}),
|
||||
offset: 0
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
exports.run = function () {
|
||||
'use strict';
|
||||
db._jobs.updateByExample({status: 'progress'}, {status: 'pending'});
|
||||
return tasks.register({
|
||||
command: function () {
|
||||
require('org/arangodb/foxx/queues/manager').manage();
|
||||
},
|
||||
period: QUEUE_MANAGER_PERIOD / 1000
|
||||
});
|
||||
};
|
|
@ -0,0 +1,134 @@
|
|||
/*jslint es5: true, indent: 2, nomen: true, maxlen: 120, evil: true */
|
||||
/*global exports, require */
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief Foxx queues workers
|
||||
///
|
||||
/// @file
|
||||
///
|
||||
/// DISCLAIMER
|
||||
///
|
||||
/// 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 triAGENS GmbH, Cologne, Germany
|
||||
///
|
||||
/// @author Alan Plum
|
||||
/// @author Copyright 2014, triAGENS GmbH, Cologne, Germany
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
var db = require('org/arangodb').db,
|
||||
flatten = require('internal').flatten,
|
||||
exponentialBackOff = require('internal').exponentialBackOff,
|
||||
console = require('console'),
|
||||
queues = require('org/arangodb/foxx').queues,
|
||||
getBackOffDelay;
|
||||
|
||||
getBackOffDelay = function (job, cfg) {
|
||||
'use strict';
|
||||
var n = job.failures.length - 1;
|
||||
if (typeof job.backOff === 'string') {
|
||||
try {
|
||||
return eval('(' + job.backOff + ')(' + n + ')');
|
||||
} catch (jobStrErr) {
|
||||
console.error('Failed to call backOff function of job ' + job._key, jobStrErr);
|
||||
}
|
||||
} else if (typeof job.backOff === 'number' && job.backOff >= 0 && job.backOff < Infinity) {
|
||||
return exponentialBackOff(n, job.backOff);
|
||||
}
|
||||
if (typeof cfg.backOff === 'function') {
|
||||
try {
|
||||
return cfg.backOff(n);
|
||||
} catch (cfgFnErr) {
|
||||
console.error('Failed to call backOff function of job type ' + job.type, cfgFnErr);
|
||||
}
|
||||
} else if (typeof cfg.backOff === 'string') {
|
||||
try {
|
||||
return eval('(' + cfg.backOff + ')(' + n + ')');
|
||||
} catch (cfgStrErr) {
|
||||
console.error('Failed to call backOff function of job type ' + job.type, cfgStrErr);
|
||||
}
|
||||
}
|
||||
if (typeof cfg.backOff === 'number' && cfg.backOff >= 0 && cfg.backOff < Infinity) {
|
||||
return exponentialBackOff(n, cfg.backOff);
|
||||
}
|
||||
return exponentialBackOff(n, 1000);
|
||||
};
|
||||
|
||||
exports.work = function (job) {
|
||||
'use strict';
|
||||
var cfg = queues._jobTypes[job.type],
|
||||
success = true,
|
||||
callback = null,
|
||||
now = Date.now(),
|
||||
maxFailures;
|
||||
|
||||
if (!cfg) {
|
||||
console.warn('Unknown job type for job ' + job._key + ':', job.type);
|
||||
db._jobs.update(job._key, {status: 'pending'});
|
||||
return;
|
||||
}
|
||||
|
||||
maxFailures = (
|
||||
typeof job.maxFailures === 'number'
|
||||
? (job.maxFailures < 0 ? Infinity : job.maxFailures)
|
||||
: (cfg.maxFailures < 0 ? Infinity : cfg.maxFailures)
|
||||
) || 0;
|
||||
|
||||
try {
|
||||
cfg.execute(job.data, job._id);
|
||||
} catch (executeErr) {
|
||||
console.error('Job ' + job._key + ' failed:', executeErr);
|
||||
job.failures.push(flatten(executeErr));
|
||||
success = false;
|
||||
}
|
||||
if (success) {
|
||||
// mark complete
|
||||
callback = job.onSuccess;
|
||||
db._jobs.update(job._key, {modified: Date.now(), status: 'complete'});
|
||||
} else if (job.failures.length > maxFailures) {
|
||||
// mark failed
|
||||
callback = job.onFailure;
|
||||
db._jobs.update(job._key, {
|
||||
modified: now,
|
||||
failures: job.failures,
|
||||
status: 'failed'
|
||||
});
|
||||
} else {
|
||||
// queue for retry
|
||||
db._jobs.update(job._key, {
|
||||
modified: now,
|
||||
delayUntil: now + getBackOffDelay(job, cfg),
|
||||
failures: job.failures,
|
||||
status: 'pending'
|
||||
});
|
||||
}
|
||||
|
||||
if (callback) {
|
||||
try {
|
||||
eval('(' + callback + ')(' + [
|
||||
JSON.stringify(job._id),
|
||||
JSON.stringify(job.data),
|
||||
JSON.stringify(job.failures)
|
||||
].join(', ') + ')');
|
||||
} catch (callbackErr) {
|
||||
console.error(
|
||||
'Failed to execute '
|
||||
+ (success ? 'success' : 'failure')
|
||||
+ ' callback for job ' + job._key + ':',
|
||||
callbackErr
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
|
@ -45,7 +45,7 @@
|
|||
var internal = require("internal");
|
||||
var db = internal.db;
|
||||
|
||||
// one the cluster the kickstarter will call boostrap-role.js
|
||||
// in the cluster the kickstarter will call boostrap-role.js
|
||||
if (ArangoAgency.prefix() !== "") {
|
||||
return true;
|
||||
}
|
||||
|
@ -66,6 +66,11 @@
|
|||
// reload routing information
|
||||
internal.loadStartup("server/bootstrap/routing.js").startup();
|
||||
|
||||
// start the queue manager once
|
||||
if (internal.enableStatistics && internal.threadNumber === 0) {
|
||||
require('org/arangodb/foxx/queues/manager').run();
|
||||
}
|
||||
|
||||
return true;
|
||||
}());
|
||||
|
||||
|
|
|
@ -1298,6 +1298,44 @@
|
|||
}
|
||||
});
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief setupQueues
|
||||
///
|
||||
/// set up the collection _queues
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
addTask({
|
||||
name: "setupQueues",
|
||||
description: "setup _queues collection",
|
||||
|
||||
mode: [ MODE_PRODUCTION, MODE_DEVELOPMENT ],
|
||||
cluster: [ CLUSTER_NONE, CLUSTER_COORDINATOR_GLOBAL ],
|
||||
database: [ DATABASE_INIT, DATABASE_UPGRADE ],
|
||||
|
||||
task: function () {
|
||||
return createSystemCollection("_queues");
|
||||
}
|
||||
});
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief setupJobs
|
||||
///
|
||||
/// set up the collection _jobs
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
addTask({
|
||||
name: "setupJobs",
|
||||
description: "setup _jobs collection",
|
||||
|
||||
mode: [ MODE_PRODUCTION, MODE_DEVELOPMENT ],
|
||||
cluster: [ CLUSTER_NONE, CLUSTER_COORDINATOR_GLOBAL ],
|
||||
database: [ DATABASE_INIT, DATABASE_UPGRADE ],
|
||||
|
||||
task: function () {
|
||||
return createSystemCollection("_jobs");
|
||||
}
|
||||
});
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// --SECTION-- public methods
|
||||
// -----------------------------------------------------------------------------
|
||||
|
|
Loading…
Reference in New Issue