keuss 1.6.14 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/codeql-analysis.yml +72 -0
- package/Queue.js +26 -0
- package/Signal.js +6 -0
- package/backends/ps-mongo.js +20 -5
- package/backends/stream-mongo.js +476 -0
- package/examples/snippets/07-stream-simple.js +53 -0
- package/package.json +4 -3
- package/signal/local.js +76 -3
- package/signal/mongo-capped.js +68 -3
- package/signal/redis-pubsub.js +82 -3
- package/stats/mem.js +5 -8
- package/stats/redis.js +16 -2
- package/test/backends_deadletter.js +5 -4
- package/test/backends_payload.js +2 -1
- package/test/backends_reserve-commit-rollback.js +16 -15
- package/test/pause.js +5 -5
- package/test/signal.js +196 -0
- package/test/stats.js +44 -3
- package/test/stream-mongo.js +166 -0
- package/backends/bucket-mongo.js +0 -366
- package/test/backends_bucket-at-most-once.js +0 -151
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# For most projects, this workflow file will not need changing; you simply need
|
|
2
|
+
# to commit it to your repository.
|
|
3
|
+
#
|
|
4
|
+
# You may wish to alter this file to override the set of languages analyzed,
|
|
5
|
+
# or to provide custom queries or build logic.
|
|
6
|
+
#
|
|
7
|
+
# ******** NOTE ********
|
|
8
|
+
# We have attempted to detect the languages in your repository. Please check
|
|
9
|
+
# the `language` matrix defined below to confirm you have the correct set of
|
|
10
|
+
# supported CodeQL languages.
|
|
11
|
+
#
|
|
12
|
+
name: "CodeQL"
|
|
13
|
+
|
|
14
|
+
on:
|
|
15
|
+
push:
|
|
16
|
+
branches: [ master ]
|
|
17
|
+
pull_request:
|
|
18
|
+
# The branches below must be a subset of the branches above
|
|
19
|
+
branches: [ master ]
|
|
20
|
+
schedule:
|
|
21
|
+
- cron: '26 13 * * 1'
|
|
22
|
+
|
|
23
|
+
jobs:
|
|
24
|
+
analyze:
|
|
25
|
+
name: Analyze
|
|
26
|
+
runs-on: ubuntu-latest
|
|
27
|
+
permissions:
|
|
28
|
+
actions: read
|
|
29
|
+
contents: read
|
|
30
|
+
security-events: write
|
|
31
|
+
|
|
32
|
+
strategy:
|
|
33
|
+
fail-fast: false
|
|
34
|
+
matrix:
|
|
35
|
+
language: [ 'javascript' ]
|
|
36
|
+
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
|
37
|
+
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
|
|
38
|
+
|
|
39
|
+
steps:
|
|
40
|
+
- name: Checkout repository
|
|
41
|
+
uses: actions/checkout@v3
|
|
42
|
+
|
|
43
|
+
# Initializes the CodeQL tools for scanning.
|
|
44
|
+
- name: Initialize CodeQL
|
|
45
|
+
uses: github/codeql-action/init@v2
|
|
46
|
+
with:
|
|
47
|
+
languages: ${{ matrix.language }}
|
|
48
|
+
# If you wish to specify custom queries, you can do so here or in a config file.
|
|
49
|
+
# By default, queries listed here will override any specified in a config file.
|
|
50
|
+
# Prefix the list here with "+" to use these queries and those in the config file.
|
|
51
|
+
|
|
52
|
+
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
|
53
|
+
# queries: security-extended,security-and-quality
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
|
57
|
+
# If this step fails, then you should remove it and run the build manually (see below)
|
|
58
|
+
- name: Autobuild
|
|
59
|
+
uses: github/codeql-action/autobuild@v2
|
|
60
|
+
|
|
61
|
+
# ℹ️ Command-line programs to run using the OS shell.
|
|
62
|
+
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
|
63
|
+
|
|
64
|
+
# If the Autobuild fails above, remove it and uncomment the following three lines.
|
|
65
|
+
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
|
|
66
|
+
|
|
67
|
+
# - run: |
|
|
68
|
+
# echo "Run, Build Application using script"
|
|
69
|
+
# ./location_of_script_within_repo/buildscript.sh
|
|
70
|
+
|
|
71
|
+
- name: Perform CodeQL Analysis
|
|
72
|
+
uses: github/codeql-action/analyze@v2
|
package/Queue.js
CHANGED
|
@@ -98,6 +98,9 @@ class Queue {
|
|
|
98
98
|
// remove (and return if possible) by id
|
|
99
99
|
remove (id, callback) {callback(null, null);}
|
|
100
100
|
|
|
101
|
+
// extra information & status
|
|
102
|
+
extra_info (cb) {cb (null, {});}
|
|
103
|
+
|
|
101
104
|
// end of expected redefinitions on subclasses
|
|
102
105
|
////////////////////////////////////////////////////////////////////////////
|
|
103
106
|
|
|
@@ -113,6 +116,29 @@ class Queue {
|
|
|
113
116
|
capabilities () {
|
|
114
117
|
return this._factory.capabilities ();
|
|
115
118
|
}
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
info (cb) {
|
|
122
|
+
async.parallel ({
|
|
123
|
+
size: cb => this.size (cb),
|
|
124
|
+
totalSize: cb => this.totalSize (cb),
|
|
125
|
+
schedSize: cb => this.schedSize (cb),
|
|
126
|
+
resvSize: cb => this.resvSize (cb),
|
|
127
|
+
next_t: cb => this.next_t (cb),
|
|
128
|
+
stats: cb => this.stats (cb),
|
|
129
|
+
paused: cb => this.paused (cb),
|
|
130
|
+
extra: cb => this.extra_info (cb),
|
|
131
|
+
}, (err, res) => {
|
|
132
|
+
if (err) return cb (err);
|
|
133
|
+
|
|
134
|
+
res.name = this.name();
|
|
135
|
+
res.ns = this.ns();
|
|
136
|
+
res.type = this.type();
|
|
137
|
+
res.capabilities = this.capabilities();
|
|
138
|
+
|
|
139
|
+
cb (null, res);
|
|
140
|
+
});
|
|
141
|
+
}
|
|
116
142
|
|
|
117
143
|
// T of next mature
|
|
118
144
|
nextMatureDate () {return this._next_mature_t;}
|
package/Signal.js
CHANGED
|
@@ -73,6 +73,12 @@ class Signal {
|
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
|
|
76
|
+
// to be extended: generic pubsub service
|
|
77
|
+
subscribe_extra (topic, on_cb) {return false}
|
|
78
|
+
unsubscribe_extra (subscr) {}
|
|
79
|
+
emit_extra (topic, ev, cb) {if (cb) cb ();}
|
|
80
|
+
|
|
81
|
+
|
|
76
82
|
static _hrtimeAsMSecs (hrtime) {
|
|
77
83
|
return (hrtime[0] * 1000) + (hrtime[1] / 1e6);
|
|
78
84
|
}
|
package/backends/ps-mongo.js
CHANGED
|
@@ -14,6 +14,8 @@ class PersistentMongoQueue extends Queue {
|
|
|
14
14
|
constructor (name, factory, opts, orig_opts) {
|
|
15
15
|
super (name, factory, opts, orig_opts);
|
|
16
16
|
|
|
17
|
+
if (!this._opts.ttl) this._opts.ttl = 3600;
|
|
18
|
+
|
|
17
19
|
this._factory = factory;
|
|
18
20
|
this._col = factory._db.collection (name);
|
|
19
21
|
this.ensureIndexes (function (err) {});
|
|
@@ -50,7 +52,10 @@ class PersistentMongoQueue extends Queue {
|
|
|
50
52
|
};
|
|
51
53
|
|
|
52
54
|
var updt = {
|
|
53
|
-
$set: {
|
|
55
|
+
$set: {
|
|
56
|
+
processed: new Date (),
|
|
57
|
+
mature: Queue.nowPlusSecs (100 * this._opts.ttl)
|
|
58
|
+
}
|
|
54
59
|
};
|
|
55
60
|
|
|
56
61
|
var opts = {
|
|
@@ -78,7 +83,10 @@ class PersistentMongoQueue extends Queue {
|
|
|
78
83
|
};
|
|
79
84
|
|
|
80
85
|
var update = {
|
|
81
|
-
$set: {
|
|
86
|
+
$set: {
|
|
87
|
+
mature: Queue.nowPlusSecs (delay),
|
|
88
|
+
reserved: new Date ()
|
|
89
|
+
},
|
|
82
90
|
$inc: {tries: 1}
|
|
83
91
|
};
|
|
84
92
|
|
|
@@ -113,7 +121,10 @@ class PersistentMongoQueue extends Queue {
|
|
|
113
121
|
}
|
|
114
122
|
|
|
115
123
|
var updt = {
|
|
116
|
-
$set: {
|
|
124
|
+
$set: {
|
|
125
|
+
processed: new Date (),
|
|
126
|
+
mature: Queue.nowPlusSecs (100 * this._opts.ttl)
|
|
127
|
+
},
|
|
117
128
|
$unset: {reserved: ''}
|
|
118
129
|
};
|
|
119
130
|
|
|
@@ -226,7 +237,11 @@ class PersistentMongoQueue extends Queue {
|
|
|
226
237
|
}
|
|
227
238
|
|
|
228
239
|
var updt = {
|
|
229
|
-
$set: {
|
|
240
|
+
$set: {
|
|
241
|
+
processed: new Date (),
|
|
242
|
+
mature: Queue.nowPlusSecs (100 * this._opts.ttl),
|
|
243
|
+
removed: true
|
|
244
|
+
},
|
|
230
245
|
};
|
|
231
246
|
|
|
232
247
|
var opts = {};
|
|
@@ -261,7 +276,7 @@ class PersistentMongoQueue extends Queue {
|
|
|
261
276
|
ensureIndexes (cb) {
|
|
262
277
|
this._col.createIndex ({mature : 1}, err => {
|
|
263
278
|
if (err) return cb (err);
|
|
264
|
-
this._col.createIndex({processed: 1}, {expireAfterSeconds: this._opts.ttl
|
|
279
|
+
this._col.createIndex({processed: 1}, {expireAfterSeconds: this._opts.ttl}, err => cb (err));
|
|
265
280
|
});
|
|
266
281
|
}
|
|
267
282
|
}
|
|
@@ -0,0 +1,476 @@
|
|
|
1
|
+
const _ = require ('lodash');
|
|
2
|
+
const async = require ('async');
|
|
3
|
+
|
|
4
|
+
const MongoClient = require ('mongodb').MongoClient;
|
|
5
|
+
const mongo = require ('mongodb');
|
|
6
|
+
|
|
7
|
+
const Queue = require ('../Queue');
|
|
8
|
+
const QFactory_MongoDB_defaults = require ('../QFactory-MongoDB-defaults');
|
|
9
|
+
|
|
10
|
+
var debug = require('debug')('keuss:Queue:StreamMongo');
|
|
11
|
+
|
|
12
|
+
class StreamMongoQueue extends Queue {
|
|
13
|
+
|
|
14
|
+
//////////////////////////////////////////////
|
|
15
|
+
constructor (name, factory, opts, orig_opts) {
|
|
16
|
+
super (name, factory, opts, orig_opts);
|
|
17
|
+
|
|
18
|
+
if (!this._opts.ttl) this._opts.ttl = 3600;
|
|
19
|
+
|
|
20
|
+
this._factory = factory;
|
|
21
|
+
this._col = factory._db.collection (name);
|
|
22
|
+
this._groups_str = this._opts.groups || 'A,B:C';
|
|
23
|
+
this._groups_vector = this._groups_str.split (/[:,;.-]/).map (i => i.trim());
|
|
24
|
+
this._gid = this._opts.group || this._groups_vector[0];
|
|
25
|
+
|
|
26
|
+
this.ensureIndexes (err => {
|
|
27
|
+
if (err) {
|
|
28
|
+
console.error ('keuss:Queue:StreamMongo: index creation failed, queues performance will be severely impacted:', err);
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
debug ('indexes created');
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
debug ('created with groups %j and gid %s (used for pop/reserve only)', this._groups_vector, this._gid);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
/////////////////////////////////////////
|
|
40
|
+
static Type () {
|
|
41
|
+
return 'mongo:stream';
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
/////////////////////////////////////////
|
|
46
|
+
type () {
|
|
47
|
+
return 'mongo:stream';
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
/////////////////////////////////////////
|
|
52
|
+
_vector (item) {
|
|
53
|
+
const r = {};
|
|
54
|
+
this._groups_vector.forEach (i => r[i] = item);
|
|
55
|
+
return r;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
/////////////////////////////////////////
|
|
60
|
+
// add element to queue
|
|
61
|
+
insert (entry, callback) {
|
|
62
|
+
const mtr = entry.mature;
|
|
63
|
+
const tr = entry.tries;
|
|
64
|
+
|
|
65
|
+
entry.tries = this._vector (tr);
|
|
66
|
+
entry.mature = this._vector (mtr);
|
|
67
|
+
entry.processed = this._vector (false);
|
|
68
|
+
|
|
69
|
+
entry.t = new Date();
|
|
70
|
+
|
|
71
|
+
this._col.insertOne (entry, {}, (err, result) => {
|
|
72
|
+
if (err) return callback (err);
|
|
73
|
+
// TODO result.insertedCount must be 1
|
|
74
|
+
callback (null, result.insertedId);
|
|
75
|
+
this._groups_vector.forEach (i => this._stats.incr (`stream.${i}.put`));
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
/////////////////////////////////////////
|
|
81
|
+
// get element from queue
|
|
82
|
+
get (callback) {
|
|
83
|
+
const gid = this._gid;
|
|
84
|
+
const q = {};
|
|
85
|
+
|
|
86
|
+
q[`mature.${gid}`] = {$lte: Queue.nowPlusSecs (0)};
|
|
87
|
+
q[`processed.${gid}`] = false;
|
|
88
|
+
|
|
89
|
+
const updt = {
|
|
90
|
+
$set: {}
|
|
91
|
+
};
|
|
92
|
+
updt.$set[`processed.${gid}`] = new Date ();
|
|
93
|
+
updt.$set[`mature.${gid}`] = Queue.nowPlusSecs (100 * this._opts.ttl);
|
|
94
|
+
|
|
95
|
+
const opts = {
|
|
96
|
+
sort: {}
|
|
97
|
+
};
|
|
98
|
+
opts.sort[`mature.${gid}`] = 1;
|
|
99
|
+
|
|
100
|
+
debug ('get() with q %O, upd %O, opts %o', q, updt, opts);
|
|
101
|
+
|
|
102
|
+
this._col.findOneAndUpdate (q, updt, opts, (err, result) => {
|
|
103
|
+
if (err) return callback (err);
|
|
104
|
+
const v = result && result.value;
|
|
105
|
+
if (!v) return callback ();
|
|
106
|
+
if (v.payload._bsontype == 'Binary') v.payload = v.payload.buffer;
|
|
107
|
+
v.mature = v.mature[gid];
|
|
108
|
+
v.tries = v.tries[gid];
|
|
109
|
+
delete v.processed;
|
|
110
|
+
delete v.t;
|
|
111
|
+
callback (null, v);
|
|
112
|
+
this._stats.incr (`stream.${gid}.get`);
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
//////////////////////////////////
|
|
118
|
+
// reserve element: call cb (err, pl) where pl has an id
|
|
119
|
+
reserve (callback) {
|
|
120
|
+
const gid = this._gid;
|
|
121
|
+
const delay = this._opts.reserve_delay || 120;
|
|
122
|
+
|
|
123
|
+
const q = {};
|
|
124
|
+
|
|
125
|
+
q[`mature.${gid}`] = {$lte: Queue.nowPlusSecs (0)};
|
|
126
|
+
q[`processed.${gid}`] = false;
|
|
127
|
+
|
|
128
|
+
const updt = {
|
|
129
|
+
$set: {},
|
|
130
|
+
$inc: {}
|
|
131
|
+
};
|
|
132
|
+
updt.$set[`reserved.${gid}`] = new Date ();
|
|
133
|
+
updt.$set[`mature.${gid}`] = Queue.nowPlusSecs (delay);
|
|
134
|
+
updt.$inc[`tries.${gid}`] = 1;
|
|
135
|
+
|
|
136
|
+
const opts = {
|
|
137
|
+
sort: {},
|
|
138
|
+
returnDocument: 'before'
|
|
139
|
+
};
|
|
140
|
+
opts.sort[`mature.${gid}`] = 1;
|
|
141
|
+
|
|
142
|
+
debug ('reserve() with q %O, upd %O, opts %o', q, updt, opts);
|
|
143
|
+
|
|
144
|
+
this._col.findOneAndUpdate (q, updt, opts, (err, result) => {
|
|
145
|
+
if (err) return callback (err);
|
|
146
|
+
const v = result && result.value;
|
|
147
|
+
if (!v) return callback ();
|
|
148
|
+
if (v.payload._bsontype == 'Binary') v.payload = v.payload.buffer;
|
|
149
|
+
v.mature = v.mature[gid];
|
|
150
|
+
v.tries = v.tries[gid];
|
|
151
|
+
delete v.processed;
|
|
152
|
+
delete v.t;
|
|
153
|
+
callback (null, v);
|
|
154
|
+
this._stats.incr (`stream.${gid}.reserve`);
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
//////////////////////////////////
|
|
160
|
+
// commit previous reserve, by p.id
|
|
161
|
+
commit (id, callback) {
|
|
162
|
+
const gid = this._gid;
|
|
163
|
+
let q;
|
|
164
|
+
|
|
165
|
+
try {
|
|
166
|
+
q = {
|
|
167
|
+
_id: (_.isString(id) ? new mongo.ObjectID (id) : id),
|
|
168
|
+
};
|
|
169
|
+
q[`reserved.${gid}`] = {$exists: true};
|
|
170
|
+
}
|
|
171
|
+
catch (e) {
|
|
172
|
+
return callback ('id [' + id + '] can not be used as rollback id: ' + e);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const updt = {
|
|
176
|
+
$set: {},
|
|
177
|
+
$unset: {}
|
|
178
|
+
};
|
|
179
|
+
updt.$set[`processed.${gid}`] = new Date ();
|
|
180
|
+
updt.$set[`mature.${gid}`] = Queue.nowPlusSecs (100 * this._opts.ttl);
|
|
181
|
+
updt.$unset[`reserved.${gid}`] = '';
|
|
182
|
+
|
|
183
|
+
const opts = {};
|
|
184
|
+
|
|
185
|
+
debug ('commit() with q %O, upd %O, opts %o', q, updt, opts);
|
|
186
|
+
|
|
187
|
+
this._col.updateOne (q, updt, opts, (err, result) => {
|
|
188
|
+
if (err) return callback (err);
|
|
189
|
+
callback (null, result && (result.modifiedCount == 1));
|
|
190
|
+
this._stats.incr (`stream.${gid}.commit`);
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
//////////////////////////////////
|
|
196
|
+
// rollback previous reserve, by p.id
|
|
197
|
+
rollback (id, next_t, callback) {
|
|
198
|
+
const gid = this._gid;
|
|
199
|
+
let q;
|
|
200
|
+
|
|
201
|
+
if (_.isFunction (next_t)) {
|
|
202
|
+
callback = next_t;
|
|
203
|
+
next_t = null;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
try {
|
|
207
|
+
q = {
|
|
208
|
+
_id: (_.isString(id) ? new mongo.ObjectID (id) : id),
|
|
209
|
+
};
|
|
210
|
+
q[`reserved.${gid}`] = {$exists: true};
|
|
211
|
+
}
|
|
212
|
+
catch (e) {
|
|
213
|
+
return callback ('id [' + id + '] can not be used as rollback id: ' + e);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const updt = {
|
|
217
|
+
$set: {},
|
|
218
|
+
$unset: {}
|
|
219
|
+
};
|
|
220
|
+
updt.$set[`mature.${gid}`] = (next_t ? new Date (next_t) : Queue.now ());
|
|
221
|
+
updt.$unset[`reserved.${gid}`] = '';
|
|
222
|
+
|
|
223
|
+
const opts = {};
|
|
224
|
+
|
|
225
|
+
debug ('rollback() with q %O, upd %O, opts %o', q, updt, opts);
|
|
226
|
+
|
|
227
|
+
this._col.updateOne (q, updt, opts, (err, result) => {
|
|
228
|
+
if (err) return callback (err);
|
|
229
|
+
callback (null, result && (result.modifiedCount == 1));
|
|
230
|
+
this._stats.incr (`stream.${gid}.rollback`);
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
//////////////////////////////////
|
|
236
|
+
// queue size including non-mature elements
|
|
237
|
+
totalSize (callback, gid) {
|
|
238
|
+
const gr = gid || this._gid;
|
|
239
|
+
|
|
240
|
+
const q = {};
|
|
241
|
+
q[`processed.${gr}`] = false;
|
|
242
|
+
|
|
243
|
+
const opts = {};
|
|
244
|
+
this._col.countDocuments (q, opts, callback);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
//////////////////////////////////
|
|
249
|
+
// queue size NOT including non-mature elements
|
|
250
|
+
size (callback, gid) {
|
|
251
|
+
const gr = gid || this._gid;
|
|
252
|
+
|
|
253
|
+
const q = {};
|
|
254
|
+
q[`processed.${gr}`] = false;
|
|
255
|
+
q[`mature.${gr}`] = {$lte: Queue.now()};
|
|
256
|
+
|
|
257
|
+
const opts = {};
|
|
258
|
+
this._col.countDocuments (q, opts, callback);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
//////////////////////////////////
|
|
263
|
+
// queue size of non-mature elements only
|
|
264
|
+
schedSize (callback, gid) {
|
|
265
|
+
const gr = gid || this._gid;
|
|
266
|
+
|
|
267
|
+
const q = {};
|
|
268
|
+
q[`processed.${gr}`] = false;
|
|
269
|
+
q[`reserved.${gr}`] = {$exists: false};
|
|
270
|
+
q[`mature.${gr}`] = {$gt: Queue.now()};
|
|
271
|
+
|
|
272
|
+
const opts = {};
|
|
273
|
+
this._col.countDocuments (q, opts, callback);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
//////////////////////////////////
|
|
278
|
+
// queue size of reserved elements only
|
|
279
|
+
resvSize (callback, gid) {
|
|
280
|
+
const gr = gid || this._gid;
|
|
281
|
+
|
|
282
|
+
const q = {};
|
|
283
|
+
q[`processed.${gr}`] = false;
|
|
284
|
+
q[`reserved.${gr}`] = {$exists: true};
|
|
285
|
+
q[`mature.${gr}`] = {$gt: Queue.now()};
|
|
286
|
+
|
|
287
|
+
const opts = {};
|
|
288
|
+
this._col.countDocuments (q, opts, callback);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
/////////////////////////////////////////
|
|
293
|
+
// get element from queue
|
|
294
|
+
next_t (callback, gid) {
|
|
295
|
+
const gr = gid || this._gid;
|
|
296
|
+
|
|
297
|
+
const q = {};
|
|
298
|
+
q[`processed.${gr}`] = false;
|
|
299
|
+
|
|
300
|
+
const sort = {};
|
|
301
|
+
sort[`mature.${gr}`] = 1;
|
|
302
|
+
|
|
303
|
+
this._col
|
|
304
|
+
.find (q)
|
|
305
|
+
.limit(1)
|
|
306
|
+
.sort (sort)
|
|
307
|
+
.project ({mature:1})
|
|
308
|
+
.next ((err, result) => {
|
|
309
|
+
if (err) return callback (err);
|
|
310
|
+
debug ('next_t with git %s: got %o', gr, result);
|
|
311
|
+
callback (null, result && result.mature && result.mature[gr]);
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
//////////////////////////////////
|
|
317
|
+
// queue size of non-mature elements only.
|
|
318
|
+
// COMMENTED OUT: takes 1 sec per each 100K elements in collection
|
|
319
|
+
/*
|
|
320
|
+
extra_info (callback) {
|
|
321
|
+
const cursor = this._col.aggregate ([
|
|
322
|
+
{
|
|
323
|
+
$group : {
|
|
324
|
+
_id : "v",
|
|
325
|
+
r: {
|
|
326
|
+
$accumulator: {
|
|
327
|
+
init: `function() {
|
|
328
|
+
return { size: {}, totalSize: {}, resvSize: {}, schedSize: {}, processed: {} }
|
|
329
|
+
}`,
|
|
330
|
+
accumulate: `function(state, mature, reserved, processed) {
|
|
331
|
+
for (const gr in mature) {
|
|
332
|
+
if (!state.size[gr]) state.size[gr] = 0;
|
|
333
|
+
if (!state.totalSize[gr]) state.totalSize[gr] = 0;
|
|
334
|
+
if (!state.resvSize[gr]) state.resvSize[gr] = 0;
|
|
335
|
+
if (!state.schedSize[gr]) state.schedSize[gr] = 0;
|
|
336
|
+
if (!state.processed[gr]) state.processed[gr] = 0;
|
|
337
|
+
|
|
338
|
+
const mtr = (mature[gr].getTime() < new Date().getTime());
|
|
339
|
+
const rsv = (reserved && ((reserved[gr] != null) && (reserved[gr] != undefined)));
|
|
340
|
+
const prc = processed[gr];
|
|
341
|
+
|
|
342
|
+
if (prc) {
|
|
343
|
+
state.processed[gr]++;
|
|
344
|
+
}
|
|
345
|
+
else {
|
|
346
|
+
state.totalSize[gr]++;
|
|
347
|
+
|
|
348
|
+
if (mtr) {
|
|
349
|
+
state.size[gr]++;
|
|
350
|
+
}
|
|
351
|
+
else {
|
|
352
|
+
if (rsv) state.resvSize[gr]++;
|
|
353
|
+
else state.schedSize[gr]++;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
return state;
|
|
359
|
+
}`,
|
|
360
|
+
accumulateArgs: ['$mature', '$reserved', '$processed'],
|
|
361
|
+
merge: `function(state1, state2) {
|
|
362
|
+
const res = { size: {}, totalSize: {}, resvSize: {}, schedSize: {} }
|
|
363
|
+
for (const gr in state1) {
|
|
364
|
+
res.size[gr] = state1.size[gr] + (state2.size[gr] || 0)
|
|
365
|
+
res.totalSize[gr] = state1.totalSize[gr] + (state2.totalSize[gr] || 0)
|
|
366
|
+
res.resvSize[gr] = state1.resvSize[gr] + (state2.resvSize[gr] || 0)
|
|
367
|
+
res.schedSize[gr] = state1.schedSize[gr] + (state2.schedSize[gr] || 0)
|
|
368
|
+
res.processed[gr] = state1.processed[gr] + (state2.processed[gr] || 0)
|
|
369
|
+
}
|
|
370
|
+
for (const gr in state2) {
|
|
371
|
+
if (!state1.size[gr]) res.size[gr] = state2.size[gr]
|
|
372
|
+
if (!state1.totalSize[gr]) res.totalSize[gr] = state2.totalSize[gr]
|
|
373
|
+
if (!state1.schedSize[gr]) res.schedSize[gr] = state2.schedSize[gr]
|
|
374
|
+
if (!state1.resvSize[gr]) res.resvSize[gr] = state2.resvSize[gr]
|
|
375
|
+
if (!state1.processed[gr]) res.processed[gr] = state2.processed[gr]
|
|
376
|
+
}
|
|
377
|
+
return res
|
|
378
|
+
}`,
|
|
379
|
+
lang: "js"
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
]);
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
cursor.toArray ((err, res) => {
|
|
391
|
+
debug ('calculating resvSize: aggregation pipeline returns %o', err);
|
|
392
|
+
debug ('calculating resvSize: aggregation pipeline returns %o', res);
|
|
393
|
+
if (err) return callback (err);
|
|
394
|
+
if (res.length == 0) return callback (null, 0);
|
|
395
|
+
callback (null, res[0].r);
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
*/
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
402
|
+
// private parts
|
|
403
|
+
|
|
404
|
+
//////////////////////////////////////////////////////////////////
|
|
405
|
+
// create needed indexes for O(1) functioning
|
|
406
|
+
ensureIndexes (cb) {
|
|
407
|
+
const tasks = [];
|
|
408
|
+
|
|
409
|
+
this._groups_vector.forEach (i => {
|
|
410
|
+
const idx = {};
|
|
411
|
+
idx[`mature.${i}`] = 1;
|
|
412
|
+
tasks.push (cb => this._col.createIndex (idx, cb));
|
|
413
|
+
});
|
|
414
|
+
tasks.push (cb => this._col.createIndex ({t: 1}, {expireAfterSeconds: this._opts.ttl}, cb));
|
|
415
|
+
async.series (tasks, cb);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
class Factory extends QFactory_MongoDB_defaults {
|
|
421
|
+
constructor (opts, mongo_conn) {
|
|
422
|
+
super (opts);
|
|
423
|
+
this._mongo_conn = mongo_conn;
|
|
424
|
+
this._db = mongo_conn.db();
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
queue (name, opts) {
|
|
428
|
+
const full_opts = {};
|
|
429
|
+
_.merge(full_opts, this._opts, opts);
|
|
430
|
+
return new StreamMongoQueue (name, this, full_opts, opts);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
close (cb) {
|
|
434
|
+
super.close (() => {
|
|
435
|
+
if (this._mongo_conn) {
|
|
436
|
+
this._mongo_conn.close ();
|
|
437
|
+
this._mongo_conn = null;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
if (cb) return cb ();
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
type () {
|
|
445
|
+
return StreamMongoQueue.Type ();
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
capabilities () {
|
|
449
|
+
return {
|
|
450
|
+
sched: true,
|
|
451
|
+
reserve: true,
|
|
452
|
+
pipeline: false,
|
|
453
|
+
tape: true,
|
|
454
|
+
remove: false,
|
|
455
|
+
stream: true
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
function creator (opts, cb) {
|
|
461
|
+
const _opts = opts || {};
|
|
462
|
+
const m_url = _opts.url || 'mongodb://localhost:27017/keuss';
|
|
463
|
+
|
|
464
|
+
MongoClient.connect (m_url, { useNewUrlParser: true }, (err, cl) => {
|
|
465
|
+
if (err) return cb (err);
|
|
466
|
+
const F = new Factory (_opts, cl);
|
|
467
|
+
F.async_init (err => cb (null, F));
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
module.exports = creator;
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
|