keuss 1.7.1 → 1.7.2
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/QFactory-MongoDB-defaults.js +1 -1
- package/Queue.js +2 -2
- package/TODO +8 -0
- package/backends/intraorder.js +374 -0
- package/docker-compose.yaml +18 -0
- package/package.json +8 -8
- package/playground/irc.js +53 -0
- package/test/backends_deadletter.js +7 -6
- package/test/backends_payload.js +8 -7
- package/test/backends_reserve-commit-rollback.js +32 -13
- package/test/intraorder.js +325 -0
- package/test/pause.js +1 -0
|
@@ -11,7 +11,7 @@ class QFactory_MongoDB_defaults extends QFactory {
|
|
|
11
11
|
super (opts);
|
|
12
12
|
|
|
13
13
|
if (!this._opts.url) {
|
|
14
|
-
this._opts.url = '
|
|
14
|
+
this._opts.url = 'mongodb://localhost:27017/keuss';
|
|
15
15
|
debug ('added url default to %s: %o', this._name, this._opts.url);
|
|
16
16
|
}
|
|
17
17
|
|
package/Queue.js
CHANGED
package/TODO
CHANGED
|
@@ -8,6 +8,14 @@ prio 1
|
|
|
8
8
|
* pipelines
|
|
9
9
|
+ add some commonplace transforms (etl-like)
|
|
10
10
|
+ create a server for that, allow processor code defined externally
|
|
11
|
+
* documentation
|
|
12
|
+
+ blog entries:
|
|
13
|
+
- motivations on using mongodb+redis. comparatives, pros&cons
|
|
14
|
+
- buckets
|
|
15
|
+
- pipelines
|
|
16
|
+
- tape
|
|
17
|
+
- redis-oq
|
|
18
|
+
- exotic experiments: intraorder
|
|
11
19
|
|
|
12
20
|
prio 0
|
|
13
21
|
-----------------------------------------------------------------
|
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
const _ = require ('lodash');
|
|
2
|
+
const async = require ('async');
|
|
3
|
+
const uuid = require ('uuid');
|
|
4
|
+
|
|
5
|
+
const MongoClient = require ('mongodb').MongoClient;
|
|
6
|
+
const mongo = require ('mongodb');
|
|
7
|
+
|
|
8
|
+
const Queue = require ('../Queue');
|
|
9
|
+
const QFactory_MongoDB_defaults = require ('../QFactory-MongoDB-defaults');
|
|
10
|
+
|
|
11
|
+
const debug = require('debug')('keuss:Queue:intraqueue');
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class IntraOrderedQueue extends Queue {
|
|
15
|
+
|
|
16
|
+
//////////////////////////////////////////////
|
|
17
|
+
constructor (name, factory, opts, orig_opts) {
|
|
18
|
+
super (name, factory, opts, orig_opts);
|
|
19
|
+
|
|
20
|
+
this._factory = factory;
|
|
21
|
+
this._col = factory._db.collection (name);
|
|
22
|
+
this.ensureIndexes (err => {});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
/////////////////////////////////////////
|
|
27
|
+
static Type () {
|
|
28
|
+
return 'mongo:intraorder';
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
/////////////////////////////////////////
|
|
33
|
+
type () {
|
|
34
|
+
return 'mongo:intraorder';
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
/////////////////////////////////////////
|
|
39
|
+
// add element to queue
|
|
40
|
+
insert (entry, callback) {
|
|
41
|
+
const q = { _id: entry.payload.iid || uuid.v4 () };
|
|
42
|
+
const upd = {
|
|
43
|
+
$push: { q: entry },
|
|
44
|
+
$inc: { qcnt: 1 },
|
|
45
|
+
$set: { mature: entry.mature, tries: entry.tries },
|
|
46
|
+
};
|
|
47
|
+
const opts = { upsert: true };
|
|
48
|
+
|
|
49
|
+
this._col.updateOne (q, upd, opts, (err, result) => {
|
|
50
|
+
debug ('insert: updateOne (%j, %j, %j) => (%j, %j)', q, upd, opts, err, result);
|
|
51
|
+
if (err) return callback (err);
|
|
52
|
+
// TODO result.insertedCount must be 1
|
|
53
|
+
callback (null, result.upsertedId || q._id);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
/////////////////////////////////////////
|
|
59
|
+
// get element from queue
|
|
60
|
+
get (callback) {
|
|
61
|
+
// actually, a reserve followed by a commit
|
|
62
|
+
this.reserve ((err, elem) => {
|
|
63
|
+
if (err) return callback (err);
|
|
64
|
+
if (!elem) return callback ();
|
|
65
|
+
|
|
66
|
+
this.commit (elem._id, (err, res) => {
|
|
67
|
+
if (err) return callback (err);
|
|
68
|
+
|
|
69
|
+
// clear _env: not needed here
|
|
70
|
+
delete elem._env;
|
|
71
|
+
callback (null, elem);
|
|
72
|
+
}, elem);
|
|
73
|
+
})
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
//////////////////////////////////
|
|
78
|
+
// reserve element: call cb (err, pl) where pl has an id
|
|
79
|
+
reserve (callback) {
|
|
80
|
+
const delay = this._opts.reserve_delay || 120;
|
|
81
|
+
|
|
82
|
+
const q = {
|
|
83
|
+
mature: {$lte: Queue.nowPlusSecs (0)},
|
|
84
|
+
qcnt: { $gt: 0}
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const upd = {
|
|
88
|
+
$set: {
|
|
89
|
+
mature: Queue.nowPlusSecs (delay),
|
|
90
|
+
reserved: new Date ()
|
|
91
|
+
},
|
|
92
|
+
$inc: {tries: 1}
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const opts = {
|
|
96
|
+
sort: {mature : 1},
|
|
97
|
+
returnDocument: 'before'
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
this._col.findOneAndUpdate (q, upd, opts, (err, result) => {
|
|
101
|
+
debug ('reserve: findOneAndUpdate (%j, %j, %j) => (%j, %j)', q, upd, opts, err, result);
|
|
102
|
+
if (err) return callback (err);
|
|
103
|
+
const v = result && result.value;
|
|
104
|
+
if (!v) return callback ();
|
|
105
|
+
|
|
106
|
+
// construct the real object to return
|
|
107
|
+
const vq = v.q[0];
|
|
108
|
+
vq._id = v._id; // use the whole obj's _id
|
|
109
|
+
vq.tries = v.tries
|
|
110
|
+
vq._env = v; // pass along the whole obj too
|
|
111
|
+
if (vq.payload._bsontype == 'Binary') vq.payload = vq.payload.buffer;
|
|
112
|
+
callback (null, vq);
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
//////////////////////////////////
|
|
118
|
+
// commit previous reserve, by p.id
|
|
119
|
+
commit (id, callback, obj) {
|
|
120
|
+
if (!(obj || (obj && obj._id))) {
|
|
121
|
+
// full obj must be passed to commit
|
|
122
|
+
return callback ('full obj must be passed to commit');
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const q = {
|
|
126
|
+
_id: id,
|
|
127
|
+
reserved: {$exists: true}
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
const upd = {
|
|
131
|
+
// do not alter mature on commit: leave it be
|
|
132
|
+
// $set: {
|
|
133
|
+
// mature: Queue.nowPlusSecs (100 * this._opts.ttl)
|
|
134
|
+
// },
|
|
135
|
+
$pop: {q: -1}, // pop entry from queue
|
|
136
|
+
$inc: { qcnt: -1 }, // one less element
|
|
137
|
+
$unset: {reserved: ''}
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
if ((obj._env && obj._env.qcnt) > 1) {
|
|
141
|
+
debug ('it is certain there are still entries in the intraqueue: set mature and tries');
|
|
142
|
+
upd.$set = {
|
|
143
|
+
mature: obj._env.q[1].mature,
|
|
144
|
+
tries: obj._env.q[1].tries,
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
// last in queue: set mature to distant future to get it out of the way while it's GCed
|
|
149
|
+
// not really, it'd impact if there were an insert in between
|
|
150
|
+
// upd.$set.mature = Queue.nowPlusSecs (60*60*24*1000);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const opts = {};
|
|
154
|
+
|
|
155
|
+
this._col.updateOne (q, upd, opts, (err, result) => {
|
|
156
|
+
debug ('commit: updateOne (%j, %j, %j) => (%j, %j)', q, upd, opts, err, result);
|
|
157
|
+
if (err) return callback (err);
|
|
158
|
+
callback (null, result && (result.modifiedCount == 1));
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
//////////////////////////////////
|
|
164
|
+
// rollback previous reserve, by p.id
|
|
165
|
+
rollback (id, next_t, callback) {
|
|
166
|
+
if (_.isFunction (next_t)) {
|
|
167
|
+
callback = next_t;
|
|
168
|
+
next_t = null;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const q = {
|
|
172
|
+
_id: id,
|
|
173
|
+
reserved: {$exists: true}
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
const upd = {
|
|
177
|
+
$set: {mature: (next_t ? new Date (next_t) : Queue.now ())},
|
|
178
|
+
$unset: {reserved: ''}
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
const opts = {};
|
|
182
|
+
|
|
183
|
+
this._col.updateOne (q, upd, opts, (err, result) => {
|
|
184
|
+
debug ('rollback: updateOne (%j, %j, %j) => (%j, %j)', q, upd, opts, err, result);
|
|
185
|
+
if (err) return callback (err);
|
|
186
|
+
callback (null, result && (result.modifiedCount == 1));
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
//////////////////////////////////
|
|
192
|
+
// queue size of non-mature elements only
|
|
193
|
+
totalSize (callback) {
|
|
194
|
+
const cursor = this._col.aggregate ([
|
|
195
|
+
{$match: {
|
|
196
|
+
qcnt: {$gt: 0}
|
|
197
|
+
}},
|
|
198
|
+
{$group:{_id:'t', v: {$sum: '$qcnt'}}}
|
|
199
|
+
]);
|
|
200
|
+
|
|
201
|
+
cursor.toArray ((err, res) => {
|
|
202
|
+
debug ('calculating schedSize: aggregation pipeline returns %o', res);
|
|
203
|
+
if (err) return callback (err);
|
|
204
|
+
if (res.length == 0) return callback (null, 0);
|
|
205
|
+
callback (null, res[0].v);
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
//////////////////////////////////
|
|
211
|
+
// queue size NOT including non-mature elements
|
|
212
|
+
size (callback) {
|
|
213
|
+
const cursor = this._col.aggregate ([
|
|
214
|
+
{$match: {
|
|
215
|
+
mature: {$lte: Queue.now ()},
|
|
216
|
+
qcnt: {$gt: 0}
|
|
217
|
+
}},
|
|
218
|
+
{$group:{_id:'t', v: {$sum: '$qcnt'}}}
|
|
219
|
+
]);
|
|
220
|
+
|
|
221
|
+
cursor.toArray ((err, res) => {
|
|
222
|
+
debug ('calculating schedSize: aggregation pipeline returns %o', res);
|
|
223
|
+
if (err) return callback (err);
|
|
224
|
+
if (res.length == 0) return callback (null, 0);
|
|
225
|
+
callback (null, res[0].v);
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
//////////////////////////////////
|
|
231
|
+
// queue size of non-mature elements only
|
|
232
|
+
schedSize (callback) {
|
|
233
|
+
const cursor = this._col.aggregate ([
|
|
234
|
+
{$match: {
|
|
235
|
+
mature: {$gt: Queue.now ()},
|
|
236
|
+
reserved: {$exists: false},
|
|
237
|
+
qcnt: {$gt: 0}
|
|
238
|
+
}},
|
|
239
|
+
{$group:{_id:'t', v: {$sum: '$qcnt'}}}
|
|
240
|
+
]);
|
|
241
|
+
|
|
242
|
+
cursor.toArray ((err, res) => {
|
|
243
|
+
debug ('calculating schedSize: aggregation pipeline returns %o', res);
|
|
244
|
+
if (err) return callback (err);
|
|
245
|
+
if (res.length == 0) return callback (null, 0);
|
|
246
|
+
callback (null, res[0].v);
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
//////////////////////////////////
|
|
252
|
+
// queue size of reserved elements only
|
|
253
|
+
resvSize (callback) {
|
|
254
|
+
const cursor = this._col.aggregate ([
|
|
255
|
+
{$match: {
|
|
256
|
+
mature: {$gt: Queue.now ()},
|
|
257
|
+
reserved: {$exists: true},
|
|
258
|
+
qcnt: {$gt: 0}
|
|
259
|
+
}},
|
|
260
|
+
{$group:{_id:'t', v: {$sum: '$qcnt'}}}
|
|
261
|
+
]);
|
|
262
|
+
|
|
263
|
+
cursor.toArray ((err, res) => {
|
|
264
|
+
debug ('calculating schedSize: aggregation pipeline returns %o', res);
|
|
265
|
+
if (err) return callback (err);
|
|
266
|
+
if (res.length == 0) return callback (null, 0);
|
|
267
|
+
callback (null, res[0].v);
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
//////////////////////////////////////////////
|
|
273
|
+
// remove by id
|
|
274
|
+
remove (id, callback) {
|
|
275
|
+
const q = {
|
|
276
|
+
_id: id,
|
|
277
|
+
qcnt: { $eq: 1 }, // allow deletion ONLY if it has just one element in the intraqueue
|
|
278
|
+
reserved: {$exists: false}
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
const opts = {};
|
|
282
|
+
|
|
283
|
+
this._col.deleteOne (q, opts, (err, result) => {
|
|
284
|
+
debug ('remove: deleteOne (%j, %j) => (%j, %j)', q, opts, err, result);
|
|
285
|
+
if (err) return callback (err);
|
|
286
|
+
callback (null, result && (result.deletedCount == 1));
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
/////////////////////////////////////////
|
|
292
|
+
// get element from queue
|
|
293
|
+
next_t (callback) {
|
|
294
|
+
this._col
|
|
295
|
+
.find ()
|
|
296
|
+
.limit(1)
|
|
297
|
+
.sort ({mature:1})
|
|
298
|
+
.project ({mature:1})
|
|
299
|
+
.next ((err, result) => {
|
|
300
|
+
if (err) return callback (err);
|
|
301
|
+
callback (null, result && result.mature);
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
307
|
+
// private parts
|
|
308
|
+
|
|
309
|
+
//////////////////////////////////////////////////////////////////
|
|
310
|
+
// create needed indexes for O(1) functioning
|
|
311
|
+
ensureIndexes (cb) {
|
|
312
|
+
async.series ([
|
|
313
|
+
cb => this._col.createIndex ({mature : 1, qcnt: 1}, cb),
|
|
314
|
+
], cb);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
class Factory extends QFactory_MongoDB_defaults {
|
|
320
|
+
constructor (opts, mongo_conn) {
|
|
321
|
+
super (opts);
|
|
322
|
+
this._mongo_conn = mongo_conn;
|
|
323
|
+
this._db = mongo_conn.db();
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
queue (name, opts) {
|
|
327
|
+
var full_opts = {};
|
|
328
|
+
_.merge(full_opts, this._opts, opts);
|
|
329
|
+
return new IntraOrderedQueue (name, this, full_opts, opts);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
close (cb) {
|
|
333
|
+
super.close (() => {
|
|
334
|
+
if (this._mongo_conn) {
|
|
335
|
+
this._mongo_conn.close ();
|
|
336
|
+
this._mongo_conn = null;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
if (cb) return cb ();
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
type () {
|
|
344
|
+
return IntraOrderedQueue.Type ();
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
capabilities () {
|
|
348
|
+
return {
|
|
349
|
+
sched: true,
|
|
350
|
+
reserve: true,
|
|
351
|
+
pipeline: false,
|
|
352
|
+
tape: false,
|
|
353
|
+
remove: false
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
function creator (opts, cb) {
|
|
359
|
+
var _opts = opts || {};
|
|
360
|
+
var m_url = _opts.url || 'mongodb://localhost:27017/keuss';
|
|
361
|
+
|
|
362
|
+
MongoClient.connect (m_url, { useNewUrlParser: true }, (err, cl) => {
|
|
363
|
+
if (err) return cb (err);
|
|
364
|
+
var F = new Factory (_opts, cl);
|
|
365
|
+
F.async_init (err => cb (null, F));
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
module.exports = creator;
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "keuss",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.2",
|
|
4
4
|
"keywords": [
|
|
5
5
|
"queue",
|
|
6
6
|
"persistent",
|
|
@@ -27,23 +27,23 @@
|
|
|
27
27
|
"license": "GPL-3.0",
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@nodebb/mubsub": "~1.8.0",
|
|
30
|
-
"async": "~3.2.
|
|
30
|
+
"async": "~3.2.4",
|
|
31
31
|
"async-lock": "~1.3.1",
|
|
32
32
|
"debug": "~4.3.4",
|
|
33
33
|
"ioredis": "~5.0.4",
|
|
34
34
|
"lodash": "~4.17.21",
|
|
35
35
|
"mitt": "~3.0.0",
|
|
36
36
|
"mongodb": "~4.5.0",
|
|
37
|
-
"uuid": "~8.3.2"
|
|
38
|
-
"why-is-node-running": "^2.2.2"
|
|
37
|
+
"uuid": "~8.3.2"
|
|
39
38
|
},
|
|
40
39
|
"devDependencies": {
|
|
41
40
|
"chance": "~1.1.8",
|
|
42
|
-
"mocha": "~
|
|
43
|
-
"should": "~13.2.3"
|
|
41
|
+
"mocha": "~10.2.0",
|
|
42
|
+
"should": "~13.2.3",
|
|
43
|
+
"why-is-node-running": "^2.2.2"
|
|
44
44
|
},
|
|
45
45
|
"scripts": {
|
|
46
|
-
"test": "mocha --reporter spec --check-leaks --no-timeouts --exit test/",
|
|
47
|
-
"test-with-coverage": "nyc --reporter=html -- mocha --reporter spec --check-leaks --no-timeouts --exit test/"
|
|
46
|
+
"test": "docker compose up -d; sleep 5; mocha --reporter spec --check-leaks --no-timeouts --exit test/ ; docker compose down",
|
|
47
|
+
"test-with-coverage": "docker compose up -d; sleep 5; nyc --reporter=html -- mocha --reporter spec --check-leaks --no-timeouts --exit ; test/docker compose down"
|
|
48
48
|
}
|
|
49
49
|
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
const async = require ('async');
|
|
2
|
+
const MQ = require ('../backends/intraorder');
|
|
3
|
+
|
|
4
|
+
// initialize factory
|
|
5
|
+
MQ ({
|
|
6
|
+
url: 'mongodb://localhost/keuss'
|
|
7
|
+
}, (err, factory) => {
|
|
8
|
+
if (err) return console.error(err);
|
|
9
|
+
|
|
10
|
+
const q = factory.queue ('test_queue', {});
|
|
11
|
+
/*
|
|
12
|
+
async.waterfall ([
|
|
13
|
+
cb => q.push ({iid: 123, elem: 1, headline: 'something something', tags: {a: 1, b: 2}}, cb),
|
|
14
|
+
(item_id, cb) => q.push ({iid: 123, elem: 2, headline: 'other other', tags: {a: 3, b: 4}}, cb),
|
|
15
|
+
(item_id, cb) => q.pop ('consumer-1', {reserve: true}, cb),
|
|
16
|
+
(item, cb) => q.ko (item, new Date().getTime () + 1500, cb),
|
|
17
|
+
(item_id, cb) => q.pop ('consumer-1', {reserve: true}, cb),
|
|
18
|
+
(item, cb) => {console.log ('%s: got %o', new Date().toISOString (), item.payload); q.ok (item, cb); },
|
|
19
|
+
(item_id, cb) => q.pop ('consumer-1', {reserve: true}, cb),
|
|
20
|
+
(item, cb) => q.ko (item, new Date().getTime () + 1500, cb),
|
|
21
|
+
(item_id, cb) => q.pop ('consumer-1', {reserve: true}, cb),
|
|
22
|
+
(item, cb) => {console.log ('%s: got %o', new Date().toISOString (), item.payload); q.ok (item, cb); },
|
|
23
|
+
(i, cb) => setTimeout (cb, 100),
|
|
24
|
+
cb => q.status (cb),
|
|
25
|
+
], (err, res) => {
|
|
26
|
+
if (err) console.error (err);
|
|
27
|
+
console.log (res)
|
|
28
|
+
factory.close ();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
async.series ([
|
|
35
|
+
cb => q.status (cb),
|
|
36
|
+
cb => q.push ({iid: 123, elem: 1, headline: 'something something', tags: {a: 1, b: 2}}, cb),
|
|
37
|
+
cb => q.status (cb),
|
|
38
|
+
cb => q.push ({iid: 123, elem: 2, headline: 'other other', tags: {a: 3, b: 4}}, cb),
|
|
39
|
+
cb => q.status (cb),
|
|
40
|
+
cb => q.pop ('consumer-1', cb),
|
|
41
|
+
cb => q.status (cb),
|
|
42
|
+
cb => q.pop ('consumer-1', cb),
|
|
43
|
+
cb => q.status (cb),
|
|
44
|
+
cb => setTimeout (cb, 100),
|
|
45
|
+
cb => q.status (cb),
|
|
46
|
+
], (err, res) => {
|
|
47
|
+
if (err) console.error (err);
|
|
48
|
+
factory.close ();
|
|
49
|
+
res.forEach ((v, i) => console.log ('%d:', i, v ));
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
});
|
|
53
|
+
|
|
@@ -77,12 +77,13 @@ function release_mq_factory (q, factory, cb) {
|
|
|
77
77
|
|
|
78
78
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
79
79
|
[
|
|
80
|
-
{label: 'Simple MongoDB', mq: require ('../backends/mongo')},
|
|
81
|
-
{label: 'Pipelined MongoDB', mq: require ('../backends/pl-mongo')},
|
|
82
|
-
{label: 'Tape MongoDB', mq: require ('../backends/ps-mongo')},
|
|
83
|
-
{label: 'Stream MongoDB', mq: require ('../backends/stream-mongo')},
|
|
84
|
-
{label: 'Redis OrderedQueue', mq: require ('../backends/redis-oq')},
|
|
85
|
-
{label: 'MongoDB SafeBucket', mq: require ('../backends/bucket-mongo-safe')}
|
|
80
|
+
// {label: 'Simple MongoDB', mq: require ('../backends/mongo')},
|
|
81
|
+
// {label: 'Pipelined MongoDB', mq: require ('../backends/pl-mongo')},
|
|
82
|
+
// {label: 'Tape MongoDB', mq: require ('../backends/ps-mongo')},
|
|
83
|
+
// {label: 'Stream MongoDB', mq: require ('../backends/stream-mongo')},
|
|
84
|
+
// {label: 'Redis OrderedQueue', mq: require ('../backends/redis-oq')},
|
|
85
|
+
// {label: 'MongoDB SafeBucket', mq: require ('../backends/bucket-mongo-safe')},
|
|
86
|
+
{label: 'Mongo IntraOrder', mq: require ('../backends/intraorder')},
|
|
86
87
|
].forEach(function (MQ_item) {
|
|
87
88
|
describe('rollback and deadletters with ' + MQ_item.label + ' queue backend', function () {
|
|
88
89
|
const MQ = MQ_item.mq;
|
package/test/backends_payload.js
CHANGED
|
@@ -10,13 +10,14 @@ var MongoClient = require ('mongodb').MongoClient;
|
|
|
10
10
|
var factory = null;
|
|
11
11
|
|
|
12
12
|
[
|
|
13
|
-
{label: 'Simple MongoDB',
|
|
14
|
-
{label: 'Pipelined MongoDB',
|
|
15
|
-
{label: 'Tape MongoDB',
|
|
16
|
-
{label: 'Stream MongoDB',
|
|
17
|
-
{label: 'Safe MongoDB Buckets',
|
|
18
|
-
{label: 'Redis List',
|
|
19
|
-
{label: 'Redis OrderedQueue',
|
|
13
|
+
{label: 'Simple MongoDB', mq: require ('../backends/mongo')},
|
|
14
|
+
{label: 'Pipelined MongoDB', mq: require ('../backends/pl-mongo')},
|
|
15
|
+
{label: 'Tape MongoDB', mq: require ('../backends/ps-mongo')},
|
|
16
|
+
{label: 'Stream MongoDB', mq: require ('../backends/stream-mongo')},
|
|
17
|
+
{label: 'Safe MongoDB Buckets', mq: require ('../backends/bucket-mongo-safe')},
|
|
18
|
+
{label: 'Redis List', mq: require ('../backends/redis-list')},
|
|
19
|
+
{label: 'Redis OrderedQueue', mq: require ('../backends/redis-oq')},
|
|
20
|
+
{label: 'Mongo IntraOrder', mq: require ('../backends/intraorder')},
|
|
20
21
|
].forEach(function (MQ_item) {
|
|
21
22
|
describe('payload aspects on ' + MQ_item.label + ' queue backend, round 1', function () {
|
|
22
23
|
var MQ = MQ_item.mq;
|
|
@@ -15,7 +15,8 @@ var factory = null;
|
|
|
15
15
|
{label: 'Tape MongoDB', mq: require ('../backends/ps-mongo')},
|
|
16
16
|
{label: 'Stream MongoDB', mq: require ('../backends/stream-mongo')},
|
|
17
17
|
// {label: 'Safe MongoDB Buckets', mq: require ('../backends/bucket-mongo-safe')},
|
|
18
|
-
{label: 'Redis OrderedQueue', mq: require ('../backends/redis-oq')}
|
|
18
|
+
{label: 'Redis OrderedQueue', mq: require ('../backends/redis-oq')},
|
|
19
|
+
{label: 'Mongo IntraOrder', mq: require ('../backends/intraorder')},
|
|
19
20
|
].forEach(function (MQ_item) {
|
|
20
21
|
describe('reserve-commit-rollback with ' + MQ_item.label + ' queue backend', function () {
|
|
21
22
|
var MQ = MQ_item.mq;
|
|
@@ -130,7 +131,9 @@ var factory = null;
|
|
|
130
131
|
cb();
|
|
131
132
|
}),
|
|
132
133
|
cb => q.next_t((err, res) => {
|
|
133
|
-
|
|
134
|
+
if (q.type () != 'mongo:intraorder') {
|
|
135
|
+
should.equal(res, null);
|
|
136
|
+
}
|
|
134
137
|
cb();
|
|
135
138
|
})
|
|
136
139
|
], (err, results) => {
|
|
@@ -252,7 +255,9 @@ var factory = null;
|
|
|
252
255
|
},
|
|
253
256
|
function (cb) {
|
|
254
257
|
q.next_t(function (err, res) {
|
|
255
|
-
|
|
258
|
+
if (q.type () != 'mongo:intraorder') {
|
|
259
|
+
should.equal(res, null);
|
|
260
|
+
}
|
|
256
261
|
cb();
|
|
257
262
|
});
|
|
258
263
|
}
|
|
@@ -604,6 +609,7 @@ var factory = null;
|
|
|
604
609
|
it('should do raw reserve & commit as expected', function (done) {
|
|
605
610
|
var q = factory.queue('test_queue_7');
|
|
606
611
|
var id = null;
|
|
612
|
+
var obj = null;
|
|
607
613
|
|
|
608
614
|
async.series([
|
|
609
615
|
cb => q.push({elem: 1, pl: 'twetrwte'}, cb),
|
|
@@ -617,6 +623,7 @@ var factory = null;
|
|
|
617
623
|
}),
|
|
618
624
|
cb => q.reserve((err, res) => {
|
|
619
625
|
id = res._id;
|
|
626
|
+
obj = res;
|
|
620
627
|
res.payload.should.eql({
|
|
621
628
|
elem: 1,
|
|
622
629
|
pl: 'twetrwte'
|
|
@@ -639,7 +646,7 @@ var factory = null;
|
|
|
639
646
|
cb => q.commit(id, (err, res) => {
|
|
640
647
|
res.should.equal(true);
|
|
641
648
|
cb();
|
|
642
|
-
}),
|
|
649
|
+
}, obj),
|
|
643
650
|
cb => q.size((err, size) => {
|
|
644
651
|
size.should.equal(0);
|
|
645
652
|
cb();
|
|
@@ -649,17 +656,20 @@ var factory = null;
|
|
|
649
656
|
cb();
|
|
650
657
|
}),
|
|
651
658
|
cb => q.next_t((err, res) => {
|
|
652
|
-
|
|
659
|
+
if (q.type () != 'mongo:intraorder') {
|
|
660
|
+
should.equal(res, null);
|
|
661
|
+
}
|
|
653
662
|
cb();
|
|
654
663
|
}),
|
|
655
664
|
], (err, results) => {
|
|
656
|
-
done();
|
|
665
|
+
done(err);
|
|
657
666
|
});
|
|
658
667
|
});
|
|
659
668
|
|
|
660
669
|
it('should do raw reserve & rollback as expected', function (done) {
|
|
661
670
|
var q = factory.queue('test_queue_8');
|
|
662
671
|
var id = null;
|
|
672
|
+
var obj = null;
|
|
663
673
|
|
|
664
674
|
async.series([
|
|
665
675
|
function (cb) {
|
|
@@ -683,6 +693,7 @@ var factory = null;
|
|
|
683
693
|
function (cb) {
|
|
684
694
|
q.reserve(function (err, res) {
|
|
685
695
|
id = res._id;
|
|
696
|
+
obj = res;
|
|
686
697
|
res.payload.should.eql({
|
|
687
698
|
elem: 1,
|
|
688
699
|
pl: 'twetrwte'
|
|
@@ -737,7 +748,7 @@ var factory = null;
|
|
|
737
748
|
q.commit(id, function (err, res) {
|
|
738
749
|
res.should.equal(false);
|
|
739
750
|
cb();
|
|
740
|
-
})
|
|
751
|
+
}, obj)
|
|
741
752
|
},
|
|
742
753
|
function (cb) {
|
|
743
754
|
q.size(function (err, size) {
|
|
@@ -782,7 +793,9 @@ var factory = null;
|
|
|
782
793
|
},
|
|
783
794
|
function (cb) {
|
|
784
795
|
q.next_t(function (err, res) {
|
|
785
|
-
|
|
796
|
+
if (q.type () != 'mongo:intraorder') {
|
|
797
|
+
should.equal(res, null);
|
|
798
|
+
}
|
|
786
799
|
cb();
|
|
787
800
|
})
|
|
788
801
|
},
|
|
@@ -794,6 +807,7 @@ var factory = null;
|
|
|
794
807
|
it('should do get.reserve & ok as expected', function (done) {
|
|
795
808
|
var q = factory.queue('test_queue_9');
|
|
796
809
|
var id = null;
|
|
810
|
+
var obj = null;
|
|
797
811
|
|
|
798
812
|
async.series([
|
|
799
813
|
function (cb) {
|
|
@@ -807,6 +821,7 @@ var factory = null;
|
|
|
807
821
|
reserve: true
|
|
808
822
|
}, function (err, res) {
|
|
809
823
|
id = res._id;
|
|
824
|
+
obj = res;
|
|
810
825
|
res.payload.should.eql({
|
|
811
826
|
elem: 1,
|
|
812
827
|
pl: 'twetrwte'
|
|
@@ -834,7 +849,7 @@ var factory = null;
|
|
|
834
849
|
})
|
|
835
850
|
},
|
|
836
851
|
function (cb) {
|
|
837
|
-
q.ok(
|
|
852
|
+
q.ok(obj, function (err, res) {
|
|
838
853
|
res.should.equal(true);
|
|
839
854
|
cb();
|
|
840
855
|
})
|
|
@@ -853,7 +868,9 @@ var factory = null;
|
|
|
853
868
|
},
|
|
854
869
|
function (cb) {
|
|
855
870
|
q.next_t(function (err, res) {
|
|
856
|
-
|
|
871
|
+
if (q.type () != 'mongo:intraorder') {
|
|
872
|
+
should.equal(res, null);
|
|
873
|
+
}
|
|
857
874
|
cb();
|
|
858
875
|
})
|
|
859
876
|
},
|
|
@@ -875,10 +892,11 @@ var factory = null;
|
|
|
875
892
|
});
|
|
876
893
|
|
|
877
894
|
it('should manage rollback on invalid id as expected', function (done) {
|
|
878
|
-
if (MQ_item.label == 'Redis OrderedQueue') return done ();
|
|
879
|
-
|
|
880
895
|
var q = factory.queue('test_queue_10');
|
|
881
896
|
|
|
897
|
+
if (q.type () == 'redis:oq') return done ();
|
|
898
|
+
if (q.type () == 'mongo:intraorder') return done ();
|
|
899
|
+
|
|
882
900
|
async.series([
|
|
883
901
|
function (cb) {
|
|
884
902
|
q.rollback('invalid-id', function (err, res) {
|
|
@@ -924,10 +942,11 @@ var factory = null;
|
|
|
924
942
|
cb => q.pop ('me', {reserve: true}, (err, res) => {
|
|
925
943
|
if (err) return db (err);
|
|
926
944
|
state.reserved_id = res._id;
|
|
945
|
+
state.reserved_obj = res;
|
|
927
946
|
cb (null, res._id);
|
|
928
947
|
}),
|
|
929
948
|
cb => _get_all_sizes (q, cb),
|
|
930
|
-
cb => q.ok (state.reserved_id, cb),
|
|
949
|
+
cb => q.ok (state.reserved_id, cb, state.reserved_obj),
|
|
931
950
|
cb => _get_all_sizes (q, cb),
|
|
932
951
|
cb => q.pop ('me', cb),
|
|
933
952
|
cb => q.pop ('me', cb),
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
const async = require ('async');
|
|
2
|
+
const should = require ('should');
|
|
3
|
+
const _ = require ('lodash');
|
|
4
|
+
|
|
5
|
+
const LocalSignal = require ('../signal/local');
|
|
6
|
+
const MemStats = require ('../stats/mem');
|
|
7
|
+
|
|
8
|
+
const MongoClient = require ('mongodb').MongoClient;
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
const MQ = require ('../backends/intraorder');
|
|
12
|
+
|
|
13
|
+
var factory = null;
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
describe('IntraOrder backend: specific operations', () => {
|
|
17
|
+
|
|
18
|
+
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
19
|
+
before(done => {
|
|
20
|
+
var opts = {
|
|
21
|
+
url: 'mongodb://localhost/keuss_test_intraorder',
|
|
22
|
+
signaller: {provider: LocalSignal},
|
|
23
|
+
stats: {provider: MemStats}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
MQ(opts, (err, fct) => {
|
|
27
|
+
if (err) return done(err);
|
|
28
|
+
factory = fct;
|
|
29
|
+
done();
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
35
|
+
after (done => async.series ([
|
|
36
|
+
cb => setTimeout (cb, 1000),
|
|
37
|
+
cb => factory.close (cb),
|
|
38
|
+
cb => MongoClient.connect ('mongodb://localhost/keuss_test_intraorder', (err, cl) => {
|
|
39
|
+
if (err) return done (err);
|
|
40
|
+
cl.db().dropDatabase (() => cl.close (cb))
|
|
41
|
+
})
|
|
42
|
+
], done));
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
46
|
+
it('sequential push & pops with no retries preserves order', done => {
|
|
47
|
+
const q = factory.queue('test_queue_1');
|
|
48
|
+
|
|
49
|
+
async.series([
|
|
50
|
+
cb => q.push({elem: 1, iid: 'twetrwte', pl: {d: 't-', a: 56}}, cb),
|
|
51
|
+
cb => q.push({elem: 2, iid: 'twetrwte', pl: {d: 't--', a: 156}}, cb),
|
|
52
|
+
cb => q.push({elem: 3, iid: 'twetrwte', pl: {d: 't---', a: 256}}, cb),
|
|
53
|
+
cb => q.push({elem: 4, iid: 'twetrwte', pl: {d: 't----', a: 356}}, cb),
|
|
54
|
+
cb => q.push({elem: 5, iid: 'twetrwte', pl: {d: 't-----', a: 456}}, cb),
|
|
55
|
+
cb => {
|
|
56
|
+
q.size ((err, size) => {
|
|
57
|
+
if (err) return done (err);
|
|
58
|
+
size.should.equal(5);
|
|
59
|
+
cb();
|
|
60
|
+
})
|
|
61
|
+
},
|
|
62
|
+
cb => q.stats((err, res) => {
|
|
63
|
+
if (err) return done (err);
|
|
64
|
+
res.should.match({
|
|
65
|
+
get: 0,
|
|
66
|
+
put: 5,
|
|
67
|
+
reserve: 0,
|
|
68
|
+
commit: 0,
|
|
69
|
+
rollback: 0,
|
|
70
|
+
deadletter: 0
|
|
71
|
+
});
|
|
72
|
+
cb();
|
|
73
|
+
}),
|
|
74
|
+
cb => q.pop('c1', cb),
|
|
75
|
+
cb => q.pop('c1', cb),
|
|
76
|
+
cb => q.pop('c1', cb),
|
|
77
|
+
cb => q.pop('c1', cb),
|
|
78
|
+
cb => q.pop('c1', cb),
|
|
79
|
+
cb => q.size((err, size) => {
|
|
80
|
+
if (err) return done (err);
|
|
81
|
+
size.should.equal(0);
|
|
82
|
+
cb();
|
|
83
|
+
}),
|
|
84
|
+
cb => q.stats((err, res) => {
|
|
85
|
+
if (err) return done (err);
|
|
86
|
+
res.should.match({
|
|
87
|
+
get: 5,
|
|
88
|
+
put: 5,
|
|
89
|
+
reserve: 0,
|
|
90
|
+
commit: 0,
|
|
91
|
+
rollback: 0,
|
|
92
|
+
deadletter: 0
|
|
93
|
+
});
|
|
94
|
+
cb();
|
|
95
|
+
}),
|
|
96
|
+
], (err, results) => {
|
|
97
|
+
if (err) return done (err);
|
|
98
|
+
results[7].payload.should.eql ({ elem: 1, iid: 'twetrwte', pl: { d: 't-', a: 56 } });
|
|
99
|
+
results[8].payload.should.eql ({ elem: 2, iid: 'twetrwte', pl: { d: 't--', a: 156 } });
|
|
100
|
+
results[9].payload.should.eql ({ elem: 3, iid: 'twetrwte', pl: { d: 't---', a: 256 } });
|
|
101
|
+
results[10].payload.should.eql ({ elem: 4, iid: 'twetrwte', pl: { d: 't----', a: 356 } });
|
|
102
|
+
results[11].payload.should.eql ({ elem: 5, iid: 'twetrwte', pl: { d: 't-----', a: 456 } });
|
|
103
|
+
done();
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
109
|
+
it('sequential reserve-commit with retries preserves order', done => {
|
|
110
|
+
const q = factory.queue('test_queue_2');
|
|
111
|
+
const t0 = process.hrtime();
|
|
112
|
+
async.series([
|
|
113
|
+
cb => q.push({elem: 1, iid: 'twetrwte', pl: {d: 't-', a: 56}}, cb),
|
|
114
|
+
cb => q.push({elem: 2, iid: 'twetrwte', pl: {d: 't--', a: 156}}, cb),
|
|
115
|
+
cb => q.push({elem: 3, iid: 'twetrwte', pl: {d: 't---', a: 256}}, cb),
|
|
116
|
+
cb => q.push({elem: 4, iid: 'twetrwte', pl: {d: 't----', a: 356}}, cb),
|
|
117
|
+
cb => q.push({elem: 5, iid: 'twetrwte', pl: {d: 't-----', a: 456}}, cb),
|
|
118
|
+
|
|
119
|
+
cb => q.pop('c1', {reserve: true}, (err, res) => {
|
|
120
|
+
if (err) return done (err);
|
|
121
|
+
res.payload.should.eql ({ elem: 1, iid: 'twetrwte', pl: { d: 't-', a: 56 } });
|
|
122
|
+
q.ko (res, new Date().getTime () + 2000, cb)
|
|
123
|
+
}),
|
|
124
|
+
|
|
125
|
+
cb => q.pop('c1', {reserve: true}, (err, res) => {
|
|
126
|
+
if (err) return done (err);
|
|
127
|
+
res.payload.should.eql ({ elem: 1, iid: 'twetrwte', pl: { d: 't-', a: 56 } });
|
|
128
|
+
q.ko (res, new Date().getTime () + 3000, cb)
|
|
129
|
+
}),
|
|
130
|
+
|
|
131
|
+
cb => q.pop('c1', {reserve: true}, (err, res) => {
|
|
132
|
+
if (err) return done (err);
|
|
133
|
+
res.payload.should.eql ({ elem: 1, iid: 'twetrwte', pl: { d: 't-', a: 56 } });
|
|
134
|
+
q.ko (res, new Date().getTime () + 1000, cb)
|
|
135
|
+
}),
|
|
136
|
+
|
|
137
|
+
// cb => q.pop('c1', (err, res) => {console.log (new Date().toISOString (), res.payload); cb (err, res)}),
|
|
138
|
+
|
|
139
|
+
cb => q.pop('c1', cb),
|
|
140
|
+
cb => q.pop('c1', cb),
|
|
141
|
+
cb => q.pop('c1', cb),
|
|
142
|
+
cb => q.pop('c1', cb),
|
|
143
|
+
cb => q.pop('c1', cb),
|
|
144
|
+
|
|
145
|
+
cb => q.size((err, size) => {
|
|
146
|
+
if (err) return done (err);
|
|
147
|
+
size.should.equal(0);
|
|
148
|
+
cb();
|
|
149
|
+
}),
|
|
150
|
+
], (err, results) => {
|
|
151
|
+
if (err) return done (err);
|
|
152
|
+
|
|
153
|
+
// duration must be about 6 secs
|
|
154
|
+
const delta = process.hrtime(t0);
|
|
155
|
+
delta[0].should.eql (6);
|
|
156
|
+
results[8].payload.should.eql ({ elem: 1, iid: 'twetrwte', pl: { d: 't-', a: 56 } });
|
|
157
|
+
results[9].payload.should.eql ({ elem: 2, iid: 'twetrwte', pl: { d: 't--', a: 156 } });
|
|
158
|
+
results[10].payload.should.eql ({ elem: 3, iid: 'twetrwte', pl: { d: 't---', a: 256 } });
|
|
159
|
+
results[11].payload.should.eql ({ elem: 4, iid: 'twetrwte', pl: { d: 't----', a: 356 } });
|
|
160
|
+
results[12].payload.should.eql ({ elem: 5, iid: 'twetrwte', pl: { d: 't-----', a: 456 } });
|
|
161
|
+
done();
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
167
|
+
it('parallel reserve-commit on same iid blocks until commit', done => {
|
|
168
|
+
const q = factory.queue('test_queue_3');
|
|
169
|
+
const t0 = process.hrtime();
|
|
170
|
+
const pops = [];
|
|
171
|
+
|
|
172
|
+
async.series([
|
|
173
|
+
cb => q.push({elem: 1, iid: 'twetrwte', pl: {d: 't-', a: 56}}, cb),
|
|
174
|
+
cb => q.push({elem: 2, iid: 'twetrwte', pl: {d: 't--', a: 156}}, cb),
|
|
175
|
+
cb => q.push({elem: 3, iid: 'twetrwte', pl: {d: 't---', a: 256}}, cb),
|
|
176
|
+
|
|
177
|
+
cb => async.parallel ([
|
|
178
|
+
cb => {
|
|
179
|
+
let elem;
|
|
180
|
+
async.series ([
|
|
181
|
+
cb => setTimeout (cb, 500),
|
|
182
|
+
cb => q.pop('c1', {reserve: true}, (err, res) => {elem = res; pops.push (res); cb (err); }),
|
|
183
|
+
cb => setTimeout (cb, 1500),
|
|
184
|
+
cb => q.ok (elem, cb),
|
|
185
|
+
], cb);
|
|
186
|
+
},
|
|
187
|
+
cb => {
|
|
188
|
+
let elem;
|
|
189
|
+
async.series ([
|
|
190
|
+
cb => setTimeout (cb, 1000),
|
|
191
|
+
cb => q.pop('c1', {reserve: true}, (err, res) => {elem = res; pops.push (res);; cb (err); }),
|
|
192
|
+
cb => setTimeout (cb, 1500),
|
|
193
|
+
cb => q.ok (elem, cb),
|
|
194
|
+
], cb);
|
|
195
|
+
},
|
|
196
|
+
cb => {
|
|
197
|
+
let elem;
|
|
198
|
+
async.series ([
|
|
199
|
+
cb => setTimeout (cb, 1500),
|
|
200
|
+
cb => q.pop('c1', {reserve: true}, (err, res) => {elem = res; pops.push (res);; cb (err); }),
|
|
201
|
+
cb => setTimeout (cb, 1500),
|
|
202
|
+
cb => q.ok (elem, cb),
|
|
203
|
+
], cb);
|
|
204
|
+
},
|
|
205
|
+
], cb)
|
|
206
|
+
], (err, results) => {
|
|
207
|
+
if (err) return done (err);
|
|
208
|
+
|
|
209
|
+
pops[0].payload.should.eql ({ elem: 1, iid: 'twetrwte', pl: { d: 't-', a: 56 } });
|
|
210
|
+
pops[1].payload.should.eql ({ elem: 2, iid: 'twetrwte', pl: { d: 't--', a: 156 } });
|
|
211
|
+
pops[2].payload.should.eql ({ elem: 3, iid: 'twetrwte', pl: { d: 't---', a: 256 } });
|
|
212
|
+
|
|
213
|
+
// duration must be about 6 secs
|
|
214
|
+
const delta = process.hrtime(t0);
|
|
215
|
+
|
|
216
|
+
// queue pop won't rearm after commit... so it will timeout after a min and only then rearm again for next pop/reserve
|
|
217
|
+
// delta[0].should.eql (7);
|
|
218
|
+
done();
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
224
|
+
it('parallel reserve-commit on same iid blocks until rollback', done => {
|
|
225
|
+
const q = factory.queue('test_queue_4');
|
|
226
|
+
const t0 = process.hrtime();
|
|
227
|
+
const pops = [];
|
|
228
|
+
|
|
229
|
+
async.series([
|
|
230
|
+
cb => q.push({elem: 1, iid: 'twetrwte', pl: {d: 't-', a: 56}}, cb),
|
|
231
|
+
cb => q.push({elem: 2, iid: 'twetrwte', pl: {d: 't--', a: 156}}, cb),
|
|
232
|
+
cb => q.push({elem: 3, iid: 'twetrwte', pl: {d: 't---', a: 256}}, cb),
|
|
233
|
+
|
|
234
|
+
cb => async.parallel ([
|
|
235
|
+
cb => {
|
|
236
|
+
let elem;
|
|
237
|
+
async.series ([
|
|
238
|
+
cb => setTimeout (cb, 500),
|
|
239
|
+
cb => q.pop('c1', {reserve: true}, (err, res) => {elem = res; pops.push (res); cb (err); }),
|
|
240
|
+
cb => q.ko (elem, new Date().getTime () + 1000, cb),
|
|
241
|
+
], cb);
|
|
242
|
+
},
|
|
243
|
+
cb => {
|
|
244
|
+
let elem;
|
|
245
|
+
async.series ([
|
|
246
|
+
cb => setTimeout (cb, 700),
|
|
247
|
+
cb => q.pop('c1', {reserve: true}, (err, res) => {elem = res; pops.push (res);; cb (err); }),
|
|
248
|
+
cb => q.ko (elem, new Date().getTime () + 1000, cb),
|
|
249
|
+
], cb);
|
|
250
|
+
},
|
|
251
|
+
cb => {
|
|
252
|
+
let elem;
|
|
253
|
+
async.series ([
|
|
254
|
+
cb => setTimeout (cb, 1500),
|
|
255
|
+
cb => q.pop('c1', {reserve: true}, (err, res) => {elem = res; pops.push (res);; cb (err); }),
|
|
256
|
+
cb => setTimeout (cb, 1500),
|
|
257
|
+
cb => q.ok (elem, cb),
|
|
258
|
+
], cb);
|
|
259
|
+
},
|
|
260
|
+
cb => {
|
|
261
|
+
let elem;
|
|
262
|
+
async.series ([
|
|
263
|
+
cb => setTimeout (cb, 2000),
|
|
264
|
+
cb => q.pop('c1', {reserve: true}, (err, res) => {elem = res; pops.push (res);; cb (err); }),
|
|
265
|
+
cb => setTimeout (cb, 1500),
|
|
266
|
+
cb => q.ok (elem, cb),
|
|
267
|
+
], cb);
|
|
268
|
+
},
|
|
269
|
+
cb => {
|
|
270
|
+
let elem;
|
|
271
|
+
async.series ([
|
|
272
|
+
cb => setTimeout (cb, 2500),
|
|
273
|
+
cb => q.pop('c1', {reserve: true}, (err, res) => {elem = res; pops.push (res);; cb (err); }),
|
|
274
|
+
cb => setTimeout (cb, 1500),
|
|
275
|
+
cb => q.ok (elem, cb),
|
|
276
|
+
], cb);
|
|
277
|
+
},
|
|
278
|
+
], cb)
|
|
279
|
+
], (err, results) => {
|
|
280
|
+
if (err) return done (err);
|
|
281
|
+
|
|
282
|
+
pops[0].payload.should.eql ({ elem: 1, iid: 'twetrwte', pl: { d: 't-', a: 56 } });
|
|
283
|
+
pops[1].payload.should.eql ({ elem: 1, iid: 'twetrwte', pl: { d: 't-', a: 56 } });
|
|
284
|
+
pops[2].payload.should.eql ({ elem: 1, iid: 'twetrwte', pl: { d: 't-', a: 56 } });
|
|
285
|
+
pops[3].payload.should.eql ({ elem: 2, iid: 'twetrwte', pl: { d: 't--', a: 156 } });
|
|
286
|
+
pops[4].payload.should.eql ({ elem: 3, iid: 'twetrwte', pl: { d: 't---', a: 256 } });
|
|
287
|
+
|
|
288
|
+
// duration must be about 6 secs
|
|
289
|
+
const delta = process.hrtime(t0);
|
|
290
|
+
|
|
291
|
+
// queue pop won't rearm after commit... so it will timeout after a min and only then rearm again for next pop/reserve
|
|
292
|
+
// delta[0].should.eql (7);
|
|
293
|
+
done();
|
|
294
|
+
});
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
299
|
+
it('delayed push preserves order', done => {
|
|
300
|
+
const q = factory.queue('test_queue_1');
|
|
301
|
+
|
|
302
|
+
async.series([
|
|
303
|
+
cb => q.push({elem: 1, iid: 'twetrwte', pl: {d: 't-', a: 56}}, {delay: 5}, cb),
|
|
304
|
+
cb => q.push({elem: 2, iid: 'twetrwte', pl: {d: 't--', a: 156}}, {delay: 4}, cb),
|
|
305
|
+
cb => q.push({elem: 3, iid: 'twetrwte', pl: {d: 't---', a: 256}}, {delay: 3}, cb),
|
|
306
|
+
cb => q.push({elem: 4, iid: 'twetrwte', pl: {d: 't----', a: 356}}, {delay: 2}, cb),
|
|
307
|
+
cb => q.push({elem: 5, iid: 'twetrwte', pl: {d: 't-----', a: 456}}, {delay: 1}, cb),
|
|
308
|
+
cb => q.pop('c1', cb),
|
|
309
|
+
cb => q.pop('c1', cb),
|
|
310
|
+
cb => q.pop('c1', cb),
|
|
311
|
+
cb => q.pop('c1', cb),
|
|
312
|
+
cb => q.pop('c1', cb),
|
|
313
|
+
], (err, results) => {
|
|
314
|
+
if (err) return done (err);
|
|
315
|
+
results[5].payload.should.eql ({ elem: 1, iid: 'twetrwte', pl: { d: 't-', a: 56 } });
|
|
316
|
+
results[6].payload.should.eql ({ elem: 2, iid: 'twetrwte', pl: { d: 't--', a: 156 } });
|
|
317
|
+
results[7].payload.should.eql ({ elem: 3, iid: 'twetrwte', pl: { d: 't---', a: 256 } });
|
|
318
|
+
results[8].payload.should.eql ({ elem: 4, iid: 'twetrwte', pl: { d: 't----', a: 356 } });
|
|
319
|
+
results[9].payload.should.eql ({ elem: 5, iid: 'twetrwte', pl: { d: 't-----', a: 456 } });
|
|
320
|
+
done();
|
|
321
|
+
});
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
});
|
package/test/pause.js
CHANGED
|
@@ -14,6 +14,7 @@ var factory = null;
|
|
|
14
14
|
{label: 'Stream MongoDB', backend: require ('../backends/stream-mongo')},
|
|
15
15
|
{label: 'plain MongoDB', backend: require ('../backends/mongo')},
|
|
16
16
|
{label: 'Safe MongoDB Bucket', backend: require ('../backends/bucket-mongo-safe')},
|
|
17
|
+
{label: 'Mongo IntraOrder', backend: require ('../backends/intraorder')},
|
|
17
18
|
].forEach (backend_item => {
|
|
18
19
|
[
|
|
19
20
|
{label: 'mem', stats: require('../stats/mem')},
|