keuss 1.6.15 → 1.7.1
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/Queue.js +26 -0
- package/README.md +1 -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 +1 -1
- package/playground/stream-loops.js +114 -0
- 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 +35 -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
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/README.md
CHANGED
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
|
+
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/*
|
|
2
|
+
*
|
|
3
|
+
* very simple example of stream-mongo: one element pushed, consumed three times
|
|
4
|
+
*
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const async = require ('async');
|
|
8
|
+
const MQ = require ('../../backends/stream-mongo');
|
|
9
|
+
|
|
10
|
+
// initialize factory
|
|
11
|
+
MQ ({
|
|
12
|
+
url: 'mongodb://localhost/keuss_test_stream'
|
|
13
|
+
}, (err, factory) => {
|
|
14
|
+
if (err) return console.error(err);
|
|
15
|
+
|
|
16
|
+
// factory ready, create one queue
|
|
17
|
+
const q0 = factory.queue ('test_stream', {groups: 'G1, G2, G4'});
|
|
18
|
+
const q1 = factory.queue ('test_stream', {group: 'G1'});
|
|
19
|
+
const q2 = factory.queue ('test_stream', {group: 'G2'});
|
|
20
|
+
|
|
21
|
+
async.series ([
|
|
22
|
+
// push element
|
|
23
|
+
cb => q0.push (
|
|
24
|
+
{elem: 1, headline: 'something something', tags: {a: 1, b: 2}}, // this is the payload
|
|
25
|
+
{
|
|
26
|
+
hdrs: {h1: 'aaa', h2: 12, h3: false} // let's add some headers too
|
|
27
|
+
},
|
|
28
|
+
cb
|
|
29
|
+
),
|
|
30
|
+
cb => setTimeout (cb, 1000), // wait a bit
|
|
31
|
+
cb => q1.pop ('consumer-1', cb), // pop element in group G1
|
|
32
|
+
cb => q2.pop ('consumer-2', cb), // pop element in group G2
|
|
33
|
+
], (err, res) => {
|
|
34
|
+
if (err) {
|
|
35
|
+
console.error (err);
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
console.log ('element popped for group G1:', res[2]);
|
|
39
|
+
console.log ('element popped for group G2:', res[3]);
|
|
40
|
+
// this should print twice something like:
|
|
41
|
+
// {
|
|
42
|
+
// _id: <some id>,
|
|
43
|
+
// mature: <some date>,
|
|
44
|
+
// payload: { elem: 1, headline: 'something something', tags: { a: 1, b: 2 } },
|
|
45
|
+
// tries: 0
|
|
46
|
+
// hdrs: {h1: 'aaa', h2: 12, h3: false}
|
|
47
|
+
// }
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
factory.close ();
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|