keuss 1.6.15 → 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.
@@ -1,366 +0,0 @@
1
- var async = require ('async');
2
- var _ = require ('lodash');
3
- var AsyncLock = require ('async-lock');
4
-
5
- var debug = require('debug')('keuss:backend:BucketMongo');
6
-
7
- var MongoClient = require ('mongodb').MongoClient;
8
- var mongo = require ('mongodb');
9
-
10
- var Queue = require ('../Queue');
11
- var QFactory_MongoDB_defaults = require ('../QFactory-MongoDB-defaults');
12
-
13
- class BucketMongoQueue extends Queue {
14
-
15
- //////////////////////////////////////////////
16
- constructor (name, factory, opts, orig_opts) {
17
- //////////////////////////////////////////////
18
- super (name, factory, opts, orig_opts);
19
-
20
- this._factory = factory;
21
- this._col = factory._db.collection (name);
22
-
23
- this._insert_bucket = {
24
- _id: new mongo.ObjectID (),
25
- b: []
26
- };
27
-
28
- this._read_bucket = {
29
- b: []
30
- }
31
-
32
- this._bucket_max_size = opts.bucket_max_size || 1024;
33
- this._bucket_max_wait = opts.bucket_max_wait || 500;
34
-
35
- this._lock = new AsyncLock ();
36
-
37
- debug ('created BucketMongoSafe %s', name);
38
- }
39
-
40
-
41
- /////////////////////////////////////////
42
- static Type () {
43
- /////////////////////////////////////////
44
- return 'mongo:bucket';
45
- }
46
-
47
- /////////////////////////////////////////
48
- type () {
49
- /////////////////////////////////////////
50
- return 'mongo:bucket';
51
- }
52
-
53
- /////////////////////////////////////////
54
- // add element to queue
55
- insert (entry, callback) {
56
- if (this._insert_bucket.b.length == 0) this._insert_bucket.mature = entry.mature;
57
-
58
- this._insert_bucket.b.push ({
59
- __p: entry.payload,
60
- __h: (entry.hdrs || {})
61
- });
62
-
63
- var id = this._insert_bucket._id.toString () + '--' + this._insert_bucket.b.length;
64
- debug ('added to bucket, %s', id);
65
-
66
- if (this._insert_bucket.b.length >= this._bucket_max_size) {
67
- if (this._flush_timer) clearTimeout (this._flush_timer);
68
- this._flush_timer = null;
69
-
70
- debug ('cancelled periodic_flush');
71
-
72
- this._flush_bucket (callback);
73
- }
74
- else {
75
- if (this._insert_bucket.b.length == 1) {
76
- debug ('first insert of bucket, set periodic_flush');
77
- this._set_periodic_flush ();
78
- }
79
-
80
- setImmediate (() => callback (null, id));
81
- }
82
- }
83
-
84
-
85
- /////////////////////////////////////////
86
- // get element from queue
87
- get (callback) {
88
- if (this._in_drain) {
89
- if (this._read_bucket.b.length == 0) {
90
- debug ('in_drain_read: read_buffer empty, calling _drain_read_cb');
91
- this._drain_read_cb ();
92
- this._drain_read_cb = undefined;
93
- return setImmediate (() => callback (null, null));
94
- }
95
- else {
96
- debug ('in_drain_read: %d pending in read_buffer', this._read_bucket.b.length);
97
- }
98
- }
99
-
100
- debug ('_ensure_bucket: acquire lock');
101
-
102
- this._lock.acquire ('ensure-bucket', done => {
103
- debug ('_ensure_bucket: lock acquired');
104
-
105
- if (this._read_bucket.b.length) {
106
- debug ('_ensure_bucket: end (already present)');
107
- return done ();
108
- }
109
-
110
- this._get_bucket ((err, res) => {
111
- if (err) {
112
- debug ('_ensure_bucket: end (error) %o', err);
113
- return done (err);
114
- }
115
-
116
- if (!res) {
117
- debug ('_ensure_bucket: end (no bucket)');
118
- this._read_bucket = {b: []};
119
- return done ();
120
- }
121
-
122
- debug ('_ensure_bucket: end (bucket read)');
123
- this._read_bucket = res;
124
- done (null, res);
125
- });
126
- }, (err, ret) => {
127
- debug ('BucketSet:_ensure_bucket: lock released');
128
- if (err) return callback (err);
129
-
130
- if (this._read_bucket.b.length) {
131
- var pl = this._read_bucket.b.shift ();
132
- var elem = {};
133
-
134
- if (pl.__p) {
135
- // add-headers: array contains both payload and headers, breaks backwards compat
136
- elem.payload = pl.__p;
137
- elem.hdrs = pl.__h;
138
- }
139
- else {
140
- // backwards compat, pre-headers: array contains whole payloads
141
- elem.payload = pl;
142
- }
143
-
144
- if (elem.payload._bsontype == 'Binary') elem.payload = elem.payload.buffer;
145
- elem.tries = 0;
146
- elem.mature = this._read_bucket.mature;
147
- setImmediate (() => callback (null, elem));
148
- }
149
- else {
150
- setImmediate (callback);
151
- }
152
- });
153
- }
154
-
155
-
156
- /////////////////////////////////////////
157
- _drain_read (cb) {
158
- if (this._read_bucket.b.length == 0) {
159
- debug ('no read_buffer, drain_read done');
160
- cb ();
161
- }
162
- else {
163
- debug ('drain_read: %d pending in bucket, %d consumers. Setting cb for later', this._read_bucket.b.length, this.nConsumers ());
164
- this._drain_read_cb = cb;
165
- }
166
- }
167
-
168
-
169
- /////////////////////////////////////////
170
- _drain_insert (cb) {
171
- debug ('drain_insert called');
172
-
173
- if (this._insert_bucket.b.length) {
174
- if (this._flush_timer) clearTimeout (this._flush_timer);
175
- this._flush_timer = null;
176
-
177
- debug ('drain_insert flushing _insert_bucket');
178
-
179
- this._flush_bucket (cb);
180
- }
181
- else {
182
- debug ('drain_insert: nothing pending insertion, completed');
183
- cb ();
184
- }
185
- }
186
-
187
- /////////////////////////////////////////
188
- // empty local buffers
189
- drain (callback) {
190
- async.series ([
191
- cb => {this._in_drain = true; cb ();},
192
- cb => async.parallel ([
193
- cb => this._drain_read (cb),
194
- cb => this._drain_insert (cb),
195
- ], cb),
196
- cb => {debug ('drain stages completed'), cb ()},
197
- cb => {this._in_drain = false; this._drained = true; cb ()},
198
- cb => {this.cancel (); cb ()},
199
- cb => {debug ('drain completed'), cb ()}
200
- ], callback);
201
- }
202
-
203
-
204
- //////////////////////////////////
205
- // queue size including non-mature elements
206
- totalSize (callback) {
207
- //////////////////////////////////
208
- const cursor = this._col.aggregate ([
209
- {$group:{_id:'t', v: {$sum: '$n'}}}
210
- ]);
211
-
212
- cursor.toArray ((err, res) => {
213
- if (err) return callback (err);
214
- if (res.length == 0) return callback (null, 0);
215
- callback (null, res[0].v);
216
- });
217
- }
218
-
219
-
220
- //////////////////////////////////
221
- // queue size NOT including non-mature elements
222
- size (callback) {
223
- //////////////////////////////////
224
- this.totalSize (callback);
225
- }
226
-
227
-
228
- /////////////////////////////////////////
229
- _set_periodic_flush () {
230
- /////////////////////////////////////////
231
- if (this._flush_timer) return;
232
-
233
- this._flush_timer = setTimeout (() => {
234
- this._flush_timer = null;
235
-
236
- debug ('flush_timer went off');
237
-
238
- if (this._insert_bucket.b.length) {
239
- this._flush_bucket ((err, res) => {
240
- if (err) {
241
- // keep retrying
242
- this._set_periodic_flush ();
243
- }
244
- });
245
- }
246
- else {
247
- // nothing to insert, stop
248
- }
249
- }, this._bucket_max_wait);
250
-
251
- debug ('_set_periodic_flush set, wait %d msecs', this._bucket_max_wait);
252
- }
253
-
254
-
255
- /////////////////////////////////////////
256
- _flush_bucket (callback) {
257
- /////////////////////////////////////////
258
- var bucket = this._insert_bucket;
259
- bucket.n = bucket.b.length;
260
-
261
- this._insert_bucket = {
262
- _id: new mongo.ObjectID (),
263
- b: []
264
- };
265
-
266
- debug ('flushing bucket %s with %d elems', bucket._id.toString(), bucket.b.length);
267
-
268
- this._col.insertOne (bucket, {}, (err, result) => {
269
- if (err) return callback (err);
270
- this._signal_insertion_own (bucket.mature);
271
-
272
- callback (null, bucket);
273
- });
274
- }
275
-
276
-
277
- /////////////////////////////////////////
278
- // get element from queue
279
- _get_bucket (callback) {
280
- /////////////////////////////////////////
281
- debug ('need to read a bucket');
282
-
283
- this._col.findOneAndDelete ({}, {sort: {_id : 1}}, (err, result) => {
284
- if (err) return callback (err);
285
-
286
- var val = result && result.value;
287
-
288
- if (val) {
289
- debug ('read a bucket %s with %d elems', val._id.toString(), val.n);
290
- }
291
-
292
- callback (null, val);
293
- });
294
- }
295
-
296
-
297
- ////////////////////////////////////////
298
- // redefine signalling of insertion:
299
- //
300
- // inhibit inherited one
301
- _signal_insertion (t) {
302
- }
303
-
304
- // and define own one
305
- _signal_insertion_own (t) {
306
- this._signaller.signalInsertion (t);
307
- }
308
- };
309
-
310
-
311
- class Factory extends QFactory_MongoDB_defaults {
312
- constructor (opts, mongo_conn) {
313
- super (opts);
314
- this._mongo_conn = mongo_conn;
315
- this._db = mongo_conn.db();
316
- }
317
-
318
- queue (name, opts) {
319
- var full_opts = {}
320
- _.merge(full_opts, this._opts, opts);
321
- return new BucketMongoQueue (name, this, full_opts, opts);
322
- }
323
-
324
- close (cb) {
325
- super.close (() => {
326
- if (this._mongo_conn) {
327
- this._mongo_conn.close ();
328
- this._mongo_conn = null;
329
- }
330
-
331
- if (cb) return cb ();
332
- });
333
- }
334
-
335
- type () {
336
- return BucketMongoQueue.Type ();
337
- }
338
-
339
- capabilities () {
340
- return {
341
- sched: false,
342
- reserve: false,
343
- pipeline: false,
344
- tape: false,
345
- remove: true
346
- };
347
- }
348
- }
349
-
350
- function creator (opts, cb) {
351
- var _opts = opts || {};
352
- var m_url = _opts.url || 'mongodb://localhost:27017/keuss';
353
-
354
- MongoClient.connect (m_url, { useNewUrlParser: true }, (err, cl) => {
355
- if (err) return cb (err);
356
- var F = new Factory (_opts, cl);
357
- F.async_init (err => cb (null, F));
358
- });
359
- }
360
-
361
- module.exports = creator;
362
-
363
-
364
-
365
-
366
-
@@ -1,151 +0,0 @@
1
-
2
- var async = require ('async');
3
- var should = require ('should');
4
-
5
- var LocalSignal = require ('../signal/local');
6
- var MemStats = require ('../stats/mem');
7
-
8
- const MongoClient = require ('mongodb').MongoClient;
9
-
10
- var factory = null;
11
-
12
- [
13
- {label: 'MongoDB Bucket', mq: require ('../backends/bucket-mongo')}
14
- ].forEach (function (MQ_item) {
15
-
16
- describe ('bucket-at-most-once with ' + MQ_item.label + ' queue backend', function () {
17
- var MQ = MQ_item.mq;
18
-
19
- before (function (done) {
20
- var opts = {
21
- url: 'mongodb://localhost/keuss_test_bucket_at_most_once',
22
- signaller: { provider: LocalSignal},
23
- stats: {provider: MemStats}
24
- };
25
-
26
- MQ (opts, function (err, fct) {
27
- if (err) return done (err);
28
- factory = fct;
29
- done();
30
- });
31
- });
32
-
33
-
34
- after (done => async.series ([
35
- cb => setTimeout (cb, 1000),
36
- cb => factory.close (cb),
37
- cb => MongoClient.connect ('mongodb://localhost/keuss_test_bucket_at_most_once', (err, cl) => {
38
- if (err) return done (err);
39
- cl.db().dropDatabase (() => cl.close (cb))
40
- })
41
- ], done));
42
-
43
- it ('queue is created empty and ok', function (done){
44
- var q = factory.queue('test_queue');
45
- should.equal (q.nextMatureDate (), null);
46
- q.name ().should.equal ('test_queue');
47
-
48
- async.series([
49
- function (cb) {q.stats(cb)},
50
- function (cb) {q.size (cb)},
51
- function (cb) {q.totalSize (cb)},
52
- function (cb) {q.next_t (cb)},
53
- ], function(err, results) {
54
- results.should.eql ([{get: 0, put: 0}, 0, 0, null])
55
- done();
56
- });
57
- });
58
-
59
-
60
- it ('sequential push & pops, go as expected', function (done){
61
- var q = factory.queue('test_queue');
62
-
63
- async.series([
64
- function (cb) {q.push ({elem:1, pl:'twetrwte'}, cb)},
65
- function (cb) {q.push ({elem:2, pl:'twetrwte'}, cb)},
66
- function (cb) {setTimeout (cb, 1111)},
67
- function (cb) {q.size (function (err, size) {
68
- size.should.equal (2);
69
- cb();
70
- })},
71
- function (cb) {q.stats (function (err, res) {
72
- res.should.eql ({get: 0, put: 2});
73
- cb();
74
- })},
75
- function (cb) {q.next_t (function (err, res) {
76
- should.equal (res, null);
77
- cb();
78
- })},
79
- function (cb) {q.pop ('c1', function (err, ret) {
80
- ret.payload.should.eql ({elem:1, pl:'twetrwte'});
81
- cb (err);
82
- })},
83
- function (cb) {q.size (function (err, size) {
84
- size.should.equal (0);
85
- cb();
86
- })},
87
- function (cb) {q.stats (function (err, res) {
88
- res.should.eql ({get: 1, put: 2});
89
- cb();
90
- })},
91
- function (cb) {q.pop ('c2', function (err, ret) {
92
- ret.payload.should.eql ({elem:2, pl:'twetrwte'});
93
- cb (err);
94
- })},
95
- function (cb) {q.size (function (err, size) {
96
- size.should.equal (0);
97
- cb();
98
- })},
99
- function (cb) {q.stats (function (err, res) {
100
- res.should.eql ({get: 2, put: 2});
101
- cb();
102
- })},
103
- ], function(err, results) {
104
- q.drain();
105
- done();
106
- });
107
- });
108
-
109
-
110
- it ('pop cancellation works as expected', function (done){
111
- var q = factory.queue('test_queue');
112
-
113
- async.series([
114
- function (cb) {
115
- var tid1 = q.pop ('c1', {timeout: 2000}, function (err, ret) {err.should.equal('cancel')});
116
- q.consumers().length.should.equal (1);
117
- var tid2 = q.pop ('c2', {timeout: 2000}, function (err, ret) {err.should.equal('cancel')});
118
- q.nConsumers().should.equal (2);
119
- q.cancel (tid1);
120
- q.nConsumers().should.equal (1);
121
- q.cancel (tid2);
122
- q.nConsumers().should.equal (0);
123
- cb();
124
- },
125
- function (cb) {q.push ({elem:2, pl:'twetrwte'}, cb)},
126
- function (cb) {q.push ({elem:1, pl:'twetrwte'}, cb)},
127
- function (cb) {setTimeout (cb, 1111)},
128
- function (cb) {q.pop ('c3', {timeout: 15000}, function (err, ret) {
129
- ret.payload.should.eql ({elem:2, pl:'twetrwte'});
130
- cb (err, ret);
131
- })},
132
- function (cb) {q.pop ('c4', {timeout: 15000}, function (err, ret) {
133
- ret.payload.should.eql ({elem:1, pl:'twetrwte'});
134
- cb (err, ret);
135
- })},
136
- function (cb) {q.size (function (err, size) {
137
- size.should.equal (0);
138
- cb();
139
- })},
140
- function (cb) {q.stats (function (err, res) {
141
- res.should.eql ({get: 2, put: 2});
142
- cb();
143
- })},
144
- ], function(err, results) {
145
- q.drain();
146
- done();
147
- });
148
- });
149
-
150
- });
151
- });