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.
@@ -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
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "keuss",
3
- "version": "1.6.14",
3
+ "version": "1.7.0",
4
4
  "keywords": [
5
5
  "queue",
6
6
  "persistent",
@@ -34,7 +34,8 @@
34
34
  "lodash": "~4.17.21",
35
35
  "mitt": "~3.0.0",
36
36
  "mongodb": "~4.5.0",
37
- "uuid": "~8.3.2"
37
+ "uuid": "~8.3.2",
38
+ "why-is-node-running": "^2.2.2"
38
39
  },
39
40
  "devDependencies": {
40
41
  "chance": "~1.1.8",
@@ -43,6 +44,6 @@
43
44
  },
44
45
  "scripts": {
45
46
  "test": "mocha --reporter spec --check-leaks --no-timeouts --exit test/",
46
- "test-with-coverage": "nyc --reporter=text -- 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/"
47
48
  }
48
49
  }
package/signal/local.js CHANGED
@@ -4,6 +4,7 @@ var Signal = require ('../Signal');
4
4
  var debug = require('debug')('keuss:Signal:local');
5
5
 
6
6
 
7
+ //////////////////////////////////////////////////////////////////////
7
8
  class LocalSignal extends Signal {
8
9
  constructor (queue, factory, opts) {
9
10
  super (queue, opts);
@@ -39,38 +40,110 @@ class LocalSignal extends Signal {
39
40
  debug ('created LocalSignal for channel %s', this._channel);
40
41
  }
41
42
 
42
- type () {return LocalSignalFactory.Type ()}
43
43
 
44
+ //////////////////////////////////////////////////////////////////////
45
+ type () {
46
+ return LocalSignalFactory.Type ();
47
+ }
48
+
49
+
50
+ //////////////////////////////////////////////////////////////////////
44
51
  emitInsertion (mature, cb) {
45
52
  debug ('got insertion event [%o], relay on local mitt', mature);
46
53
  this._factory._emitter.emit (this._channel, mature.getTime () + '');
47
54
  }
48
55
 
56
+ //////////////////////////////////////////////////////////////////////
49
57
  emitPaused (paused, cb) {
50
58
  debug ('got paused event [%d], relay on local mitt', paused);
51
59
  this._factory._emitter.emit (this._channel, `p ${paused}`);
52
60
  }
61
+
62
+
63
+ //////////////////////////////////////////////////////////////////////
64
+ subscribe_extra (topic, on_cb) {
65
+ return this._factory.subscribe_extra (this._master.ns (), topic, on_cb);
66
+ }
67
+
68
+
69
+ //////////////////////////////////////////////////////////////////////
70
+ unsubscribe_extra (subscr) {
71
+ this._factory.unsubscribe_extra (subscr);
72
+ }
73
+
74
+
75
+ //////////////////////////////////////////////////////////////////////
76
+ emit_extra (topic, ev, cb) {
77
+ this._factory.emit_extra (this._master.ns (), topic, ev, cb);
78
+ }
53
79
  }
54
80
 
55
81
 
82
+ //////////////////////////////////////////////////////////////////////
56
83
  class LocalSignalFactory {
57
84
  constructor (opts) {
58
85
  this._emitter = mitt();
86
+ debug ('created local factory');
87
+ }
88
+
89
+
90
+ //////////////////////////////////////////////////////////////////////
91
+ static Type () {
92
+ return 'signal:local';
59
93
  }
60
94
 
61
- static Type () {return 'signal:local'}
62
- type () {return LocalSignalFactory.Type ()}
63
95
 
96
+ //////////////////////////////////////////////////////////////////////
97
+ type () {
98
+ return LocalSignalFactory.Type ();
99
+ }
100
+
101
+
102
+ //////////////////////////////////////////////////////////////////////
64
103
  signal (queue, opts) {
65
104
  return new LocalSignal (queue, this, opts);
66
105
  }
67
106
 
107
+
108
+ //////////////////////////////////////////////////////////////////////
109
+ subscribe_extra (ns, topic, on_cb) {
110
+ const t = `keuss:signal:${ns}:extra:${topic}`;
111
+ debug ('subscribing to ns [%s], topic [%s]', ns, t);
112
+
113
+ const s = {
114
+ n: ns,
115
+ t: t,
116
+ f: (msg => on_cb (msg))
117
+ };
118
+
119
+ this._emitter.on (s.t, s.f);
120
+ return s;
121
+ }
122
+
123
+
124
+ //////////////////////////////////////////////////////////////////////
125
+ unsubscribe_extra (s) {
126
+ this._emitter.off (s.t, s.f);
127
+ debug ('unsubscribed on %s', s.t);
128
+ }
129
+
130
+
131
+ //////////////////////////////////////////////////////////////////////
132
+ emit_extra (ns, topic, ev, cb) {
133
+ const t = `keuss:signal:${ns}:extra:${topic}`;
134
+ debug ('emit extra on topic [%s], value [%j]', t, ev);
135
+ this._emitter.emit (t, ev);
136
+ }
137
+
138
+
139
+ //////////////////////////////////////////////////////////////////////
68
140
  close (cb) {
69
141
  cb ();
70
142
  }
71
143
  }
72
144
 
73
145
 
146
+ //////////////////////////////////////////////////////////////////////
74
147
  function creator (opts, cb) {
75
148
  return cb (null, new LocalSignalFactory (opts));
76
149
  }
@@ -5,7 +5,7 @@ var Signal = require ('../Signal');
5
5
 
6
6
  var debug = require('debug')('keuss:Signal:MongoCapped');
7
7
 
8
-
8
+ //////////////////////////////////////////////////////////////////////
9
9
  class MCSignal extends Signal {
10
10
  constructor (queue, factory, opts) {
11
11
  super (queue, opts);
@@ -45,24 +45,52 @@ class MCSignal extends Signal {
45
45
  debug ('created mongo-capped signaller for topic %s with opts %o', this._topic_name, opts);
46
46
  }
47
47
 
48
+
49
+ //////////////////////////////////////////////////////////////////////
48
50
  type () {return MCSignalFactory.Type ()}
49
51
 
52
+
53
+ //////////////////////////////////////////////////////////////////////
50
54
  emitInsertion (mature, cb) {
51
55
  debug ('emit insertion on topic [%s] value [%d])', this._topic_name, mature);
52
56
  this._factory._channel.publish (this._topic_name, mature.getTime());
53
57
  }
54
58
 
59
+
60
+ //////////////////////////////////////////////////////////////////////
55
61
  emitPaused (paused, cb) {
56
62
  debug ('emit paused on topic [%s], value [%b]', this._topic_name, paused);
57
63
  this._factory._channel.publish (this._topic_name, `p ${paused}`);
58
64
  }
59
65
 
66
+
67
+ //////////////////////////////////////////////////////////////////////
60
68
  _insertionEvent (mature) {
61
69
  debug ('got insertion event on ch [%s], mature is %s, calling master.emitInsertion()', this._channel, mature);
62
70
  this._master.signalInsertion (new Date (mature));
63
71
  }
72
+
73
+
74
+ //////////////////////////////////////////////////////////////////////
75
+ subscribe_extra (topic, on_cb) {
76
+ return this._factory.subscribe_extra (this._master.ns (), topic, on_cb);
77
+ }
78
+
79
+
80
+ //////////////////////////////////////////////////////////////////////
81
+ unsubscribe_extra (subscr) {
82
+ this._factory.unsubscribe_extra (subscr);
83
+ }
84
+
85
+
86
+ //////////////////////////////////////////////////////////////////////
87
+ emit_extra (topic, ev, cb) {
88
+ this._factory.emit_extra (this._master.ns (), topic, ev, cb);
89
+ }
64
90
  }
65
91
 
92
+
93
+ //////////////////////////////////////////////////////////////////////
66
94
  class MCSignalFactory {
67
95
  constructor (opts) {
68
96
  var defaults = {
@@ -79,19 +107,56 @@ class MCSignalFactory {
79
107
  debug ('created mongo-capped factory with opts %o', opts);
80
108
  }
81
109
 
82
- static Type () {return 'signal:mongo-capped'}
83
- type () {return MCSignalFactory.Type ()}
84
110
 
111
+ //////////////////////////////////////////////////////////////////////
112
+ static Type () {
113
+ return 'signal:mongo-capped';
114
+ }
115
+
116
+
117
+ //////////////////////////////////////////////////////////////////////
118
+ type () {
119
+ return MCSignalFactory.Type ();
120
+ }
121
+
122
+
123
+ //////////////////////////////////////////////////////////////////////
85
124
  signal (channel, opts) {
86
125
  return new MCSignal (channel, this, opts);
87
126
  }
88
127
 
128
+
129
+ //////////////////////////////////////////////////////////////////////
130
+ subscribe_extra (ns, topic, on_cb) {
131
+ const t = `keuss:signal:${ns}:extra:${topic}`;
132
+ debug ('subscribing to ns [%s], topic [%s]', ns, t);
133
+ return this._channel.subscribe (t, on_cb);
134
+ }
135
+
136
+
137
+ //////////////////////////////////////////////////////////////////////
138
+ unsubscribe_extra (subscr) {
139
+ subscr.unsubscribe ();
140
+ debug ('unsubscribed on %j', subscr);
141
+ }
142
+
143
+
144
+ //////////////////////////////////////////////////////////////////////
145
+ emit_extra (ns, topic, ev, cb) {
146
+ const t = `keuss:signal:${ns}:extra:${topic}`;
147
+ debug ('emit extra on ns [%s], topic [%s], value [%j]', ns, t, ev);
148
+ this._channel.publish (t, ev);
149
+ }
150
+
151
+
152
+ //////////////////////////////////////////////////////////////////////
89
153
  close (cb) {
90
154
  this._mubsub.close (cb);
91
155
  }
92
156
  }
93
157
 
94
158
 
159
+ //////////////////////////////////////////////////////////////////////
95
160
  function creator (opts, cb) {
96
161
  return cb (null, new MCSignalFactory (opts));
97
162
  }
@@ -8,6 +8,7 @@ var Signal = require ('../Signal');
8
8
  var debug = require('debug')('keuss:Signal:RedisPubsub');
9
9
 
10
10
 
11
+ //////////////////////////////////////////////////////////////////////
11
12
  class RPSSignal extends Signal {
12
13
  constructor (queue, factory, opts) {
13
14
  super (queue, opts);
@@ -53,24 +54,54 @@ class RPSSignal extends Signal {
53
54
  debug ('created redis-pubsub signaller for topic %s with opts %o', this._topic_name, opts);
54
55
  }
55
56
 
56
- type () {return RPSSignalFactory.Type ()}
57
57
 
58
+ //////////////////////////////////////////////////////////////////////
59
+ type () {
60
+ return RPSSignalFactory.Type ();
61
+ }
62
+
63
+
64
+ //////////////////////////////////////////////////////////////////////
58
65
  emitInsertion (mature, cb) {
59
66
  debug ('emit insertion on channel [%s] value [%d])', this._channel, mature);
60
67
  this._rediscl_pub.publish (this._channel, mature.getTime());
61
68
  }
62
69
 
70
+
71
+ //////////////////////////////////////////////////////////////////////
63
72
  emitPaused (paused, cb) {
64
73
  debug ('emit paused on channel [%s], value [%b]', this._channel, paused);
65
74
  this._rediscl_pub.publish (this._channel, `p ${paused}`);
66
75
  }
67
76
 
77
+
78
+ //////////////////////////////////////////////////////////////////////
68
79
  _insertionEvent (mature) {
69
80
  debug ('got insertion event on ch [%s], mature is %s, calling master.emitInsertion()', this._channel, mature);
70
81
  this._master.signalInsertion (new Date (mature));
71
82
  }
83
+
84
+
85
+ //////////////////////////////////////////////////////////////////////
86
+ subscribe_extra (topic, on_cb) {
87
+ return this._factory.subscribe_extra (this._master.ns (), topic, on_cb);
88
+ }
89
+
90
+
91
+ //////////////////////////////////////////////////////////////////////
92
+ unsubscribe_extra (subscr) {
93
+ this._factory.unsubscribe_extra (subscr);
94
+ }
95
+
96
+
97
+ //////////////////////////////////////////////////////////////////////
98
+ emit_extra (topic, ev, cb) {
99
+ this._factory.emit_extra (this._master.ns (), topic, ev, cb);
100
+ }
72
101
  }
73
102
 
103
+
104
+ //////////////////////////////////////////////////////////////////////
74
105
  class RPSSignalFactory {
75
106
  constructor (opts) {
76
107
  this._opts = opts || {};
@@ -86,14 +117,61 @@ class RPSSignalFactory {
86
117
  debug ('created redis-pubsub factory with opts %o', opts);
87
118
  }
88
119
 
89
- static Type () {return 'signal:redis-pubsub'}
90
- type () {return RPSSignalFactory.Type ()}
91
120
 
121
+ //////////////////////////////////////////////////////////////////////
122
+ static Type () {
123
+ return 'signal:redis-pubsub';
124
+ }
125
+
126
+
127
+ //////////////////////////////////////////////////////////////////////
128
+ type () {
129
+ return RPSSignalFactory.Type ();
130
+ }
131
+
132
+
133
+ //////////////////////////////////////////////////////////////////////
92
134
  signal (channel, opts) {
93
135
  debug ('creating redis-pubsub signaller with opts %o', opts);
94
136
  return new RPSSignal (channel, this, opts);
95
137
  }
96
138
 
139
+
140
+ //////////////////////////////////////////////////////////////////////
141
+ subscribe_extra (ns, topic, on_cb) {
142
+ const t = `keuss:signal:${ns}:extra:${topic}`;
143
+ debug ('subscribing to ns [%s], topic [%s]', ns, t);
144
+
145
+ const s = {
146
+ n: ns,
147
+ t: t,
148
+ f: (msg => on_cb (JSON.parse (msg)))
149
+ };
150
+
151
+ this._emitter.on (s.t, s.f);
152
+ this._rediscl_sub.subscribe (s.t);
153
+ return s;
154
+ }
155
+
156
+
157
+ //////////////////////////////////////////////////////////////////////
158
+ unsubscribe_extra (subscr) {
159
+ this._rediscl_sub.unsubscribe (subscr.t);
160
+ this._emitter.off (subscr.t, subscr.f);
161
+ debug ('unsubscribed on %j', subscr);
162
+ }
163
+
164
+
165
+ //////////////////////////////////////////////////////////////////////
166
+ emit_extra (ns, topic, ev, cb) {
167
+ const t = `keuss:signal:${ns}:extra:${topic}`;
168
+ const v = JSON.stringify(ev);
169
+ debug ('emit extra on ns [%s], topic [%s], value [%j]', ns, t, ev);
170
+ this._rediscl_pub.publish (t, v);
171
+ }
172
+
173
+
174
+ //////////////////////////////////////////////////////////////////////
97
175
  close (cb) {
98
176
  async.parallel ([
99
177
  cb => this._rediscl_pub.quit (cb),
@@ -103,6 +181,7 @@ class RPSSignalFactory {
103
181
  }
104
182
 
105
183
 
184
+ //////////////////////////////////////////////////////////////////////
106
185
  function creator (opts, cb) {
107
186
  return cb (null, new RPSSignalFactory (opts));
108
187
  }
package/stats/mem.js CHANGED
@@ -36,15 +36,12 @@ class MemStats extends Stats {
36
36
  }
37
37
 
38
38
  incr (v, delta, cb) {
39
- if (!this._s.counters[v]) {
40
- this._s.counters[v] = 0;
41
- }
42
-
43
39
  if ((delta == null) || (delta == undefined)) delta = 1;
44
- var old_v = this._s.counters[v]
45
- debug ('incr %s by %d: %d --> %d', v, delta, old_v, this._s.counters[v]);
46
- this._s.counters[v] = this._s.counters[v] + delta;
47
- if (cb) cb(null, this._s.counters[v]);
40
+ const old_v = _.get (this._s.counters, v, 0);
41
+ _.set (this._s.counters, v, old_v + delta);
42
+ const new_v = _.get (this._s.counters, v);
43
+ debug ('incr %s by %d: %d --> %d', v, delta, old_v, new_v);
44
+ if (cb) cb(null, new_v);
48
45
  }
49
46
 
50
47
  decr (v, delta, cb) {
package/stats/redis.js CHANGED
@@ -37,13 +37,27 @@ class RedisStats extends Stats {
37
37
  var ret = {};
38
38
  for (let k in v) {
39
39
  // filter counters
40
- if (k.startsWith ('counter_')) ret[k.substr (8)] = parseInt(v[k]);
40
+ if (k.startsWith ('counter_')) _.set (ret, k.substring (8), parseInt(v[k]));
41
41
  }
42
42
 
43
43
  cb (null, ret);
44
44
  });
45
45
  }
46
46
 
47
+
48
+ _raw_values (cb) {
49
+ this._rediscl.hgetall (this._id, (err, v) => {
50
+ if (err) return cb(err);
51
+ var ret = {};
52
+ for (let k in v) {
53
+ if (k.startsWith ('counter_')) ret[k.substring (8)] = v[k];
54
+ }
55
+
56
+ cb (null, ret);
57
+ });
58
+ }
59
+
60
+
47
61
  paused (val, cb) {
48
62
  if (!cb) {
49
63
  // get, val is cb
@@ -144,7 +158,7 @@ class RedisStats extends Stats {
144
158
  cb => this._rediscl.hdel (this._id, 'opts', cb)
145
159
  ];
146
160
 
147
- this.values ((err, vals) => {
161
+ this._raw_values ((err, vals) => {
148
162
  _.forEach (vals, (v, k) => {
149
163
  tasks.push (cb => this._rediscl.hdel (this._id, 'counter_' + k, cb));
150
164
  });
@@ -80,6 +80,7 @@ function release_mq_factory (q, factory, cb) {
80
80
  {label: 'Simple MongoDB', mq: require ('../backends/mongo')},
81
81
  {label: 'Pipelined MongoDB', mq: require ('../backends/pl-mongo')},
82
82
  {label: 'Tape MongoDB', mq: require ('../backends/ps-mongo')},
83
+ {label: 'Stream MongoDB', mq: require ('../backends/stream-mongo')},
83
84
  {label: 'Redis OrderedQueue', mq: require ('../backends/redis-oq')},
84
85
  {label: 'MongoDB SafeBucket', mq: require ('../backends/bucket-mongo-safe')}
85
86
  ].forEach(function (MQ_item) {
@@ -159,8 +160,8 @@ function release_mq_factory (q, factory, cb) {
159
160
  cb => stats (q, cb),
160
161
  cb => stats (factory.deadletter_queue(), cb),
161
162
  ], (err, res) => {
162
- res[1].tsize.should.equal (0);
163
- res[2].tsize.should.equal (0);
163
+ res[1].tsize.should.equal (0);
164
+ res[2].tsize.should.equal (0);
164
165
 
165
166
  cb (err, q, factory);
166
167
  });
@@ -209,8 +210,8 @@ function release_mq_factory (q, factory, cb) {
209
210
  cb => stats (q, cb),
210
211
  cb => stats (factory.deadletter_queue(), cb),
211
212
  ], (err, res) => {
212
- res[1].tsize.should.equal (0);
213
- res[2].tsize.should.equal (0);
213
+ res[1].tsize.should.equal (0);
214
+ res[2].tsize.should.equal (0);
214
215
 
215
216
  cb (err, q, factory);
216
217
  });
@@ -13,7 +13,7 @@ var factory = null;
13
13
  {label: 'Simple MongoDB', mq: require ('../backends/mongo')},
14
14
  {label: 'Pipelined MongoDB', mq: require ('../backends/pl-mongo')},
15
15
  {label: 'Tape MongoDB', mq: require ('../backends/ps-mongo')},
16
- {label: 'Simple MongoDB Buckets', mq: require ('../backends/bucket-mongo')},
16
+ {label: 'Stream MongoDB', mq: require ('../backends/stream-mongo')},
17
17
  {label: 'Safe MongoDB Buckets', mq: require ('../backends/bucket-mongo-safe')},
18
18
  {label: 'Redis List', mq: require ('../backends/redis-list')},
19
19
  {label: 'Redis OrderedQueue', mq: require ('../backends/redis-oq')},
@@ -264,6 +264,7 @@ var factory = null;
264
264
  {label: 'Simple MongoDB', mq: require ('../backends/mongo')},
265
265
  {label: 'Pipelined MongoDB', mq: require ('../backends/pl-mongo')},
266
266
  {label: 'Tape MongoDB', mq: require ('../backends/ps-mongo')},
267
+ {label: 'Stream MongoDB', mq: require ('../backends/stream-mongo')},
267
268
  {label: 'Safe MongoDB Buckets', mq: require ('../backends/bucket-mongo-safe')},
268
269
  {label: 'Redis OrderedQueue', mq: require ('../backends/redis-oq')},
269
270
  ].forEach(function (MQ_item) {