keuss 2.0.7 → 2.2.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,79 @@
1
+ const EventEmitter = require('events')
2
+ const MongoClient = require('mongodb').MongoClient
3
+ const Channel = require('./channel')
4
+ const noop = function () {}
5
+
6
+ class Connection extends EventEmitter {
7
+ constructor (uri, options) {
8
+ super()
9
+ const self = this
10
+
11
+ options || (options = {})
12
+
13
+ // It's a Db instance.
14
+ if (uri.collection) {
15
+ this.db = uri
16
+ } else {
17
+ const p = MongoClient.connect(uri, options)
18
+ p.then((client) => {
19
+ self.client = client
20
+ self.db = client.db()
21
+ self.emit('connect', self.db)
22
+ self.client.on('error', function (err) {
23
+ self.emit('error', err)
24
+ })
25
+ }).catch((err) => {
26
+ self.emit('error', err)
27
+ })
28
+ }
29
+
30
+ this.destroyed = false
31
+ this.channels = {}
32
+ }
33
+
34
+ get state () {
35
+ let state
36
+
37
+ // Using 'destroyed' to be compatible with the driver.
38
+ if (this.destroyed) {
39
+ state = 'destroyed'
40
+ } else if (
41
+ (this.db && !this.client) ||
42
+ this.client?.topology?.isConnected()
43
+ ) {
44
+ // https://github.com/mongodb/node-mongodb-native/blob/d266158c9e968c92e8041211ef99f1783025be40/src/operations/connect.ts#L18
45
+ state = 'connected'
46
+ } else {
47
+ state = 'connecting'
48
+ }
49
+
50
+ return state
51
+ }
52
+
53
+ channel (name, options) {
54
+ if (typeof name === 'object') {
55
+ options = name
56
+ name = 'mubsub'
57
+ }
58
+
59
+ if (!this.channels[name] || this.channels[name].closed) {
60
+ this.channels[name] = new Channel(this, name, options)
61
+ }
62
+
63
+ return this.channels[name]
64
+ }
65
+
66
+ close (callback) {
67
+ this.destroyed = true
68
+ if (this.client) {
69
+ callback || (callback = noop)
70
+ this.client
71
+ .close()
72
+ .then(() => callback())
73
+ .catch((e) => callback(e))
74
+ }
75
+ return this
76
+ }
77
+ }
78
+
79
+ module.exports = Connection
@@ -0,0 +1,10 @@
1
+ const Connection = require('./connection')
2
+ const Channel = require('./channel')
3
+ const mongodb = require('mongodb')
4
+
5
+ module.exports = exports = function (uri, options) {
6
+ return new Connection(uri, options)
7
+ }
8
+
9
+ exports.Connection = Connection
10
+ exports.Channel = Channel
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "keuss",
3
- "version": "2.0.7",
3
+ "version": "2.2.0",
4
4
  "keywords": [
5
5
  "queue",
6
6
  "persistent",
@@ -28,23 +28,21 @@
28
28
  },
29
29
  "license": "MIT",
30
30
  "dependencies": {
31
- "@nodebb/mubsub": "~1.8.0",
32
31
  "async": "~3.2.6",
33
32
  "async-lock": "~1.4.1",
34
33
  "debug": "~4.3.6",
35
34
  "ioredis": "~5.4.1",
36
35
  "lodash": "~4.17.21",
37
36
  "mitt": "~3.0.1",
38
- "mongodb": "~4.17.0",
37
+ "mongodb": "^6.0.0",
39
38
  "uuid": "~8.3.2",
40
39
  "pg": "~8.12.0"
41
40
  },
42
41
  "devDependencies": {
43
- "chance": "~1.1.12",
44
- "mocha": "~10.7.3",
42
+ "chance": "~1.1.13",
43
+ "mocha": "~11.7.5",
45
44
  "should": "~13.2.3",
46
- "nyc": "~17.0.0",
47
- "why-is-node-running": "~3.2.0"
45
+ "nyc": "~17.1.0"
48
46
  },
49
47
  "scripts": {
50
48
  "test": "docker compose up -d; sleep 5; mocha --reporter spec --check-leaks --no-timeouts --exit test/ ; docker compose down",
@@ -1,4 +1,4 @@
1
- var mubsub = require('@nodebb/mubsub');
1
+ var mubsub = require('../lib/mubsub');
2
2
  var _ = require('lodash');
3
3
 
4
4
  var Signal = require ('../Signal');
package/stats/mongo.js CHANGED
@@ -1,11 +1,13 @@
1
- var _ = require ('lodash');
2
- var async = require ('async');
3
- var MongoClient = require ('mongodb').MongoClient;
1
+ const _ = require ('lodash');
2
+ const async = require ('async');
3
+ const MongoClient = require ('mongodb').MongoClient;
4
4
 
5
- var Stats = require ('../Stats');
5
+ const Stats = require ('../Stats');
6
6
 
7
- var debug = require('debug')('keuss:Stats:Mongo');
7
+ const debug = require('debug')('keuss:Stats:Mongo');
8
8
 
9
+
10
+ //////////////////////////////////////////////////////////////////////////////////
9
11
  /*
10
12
  * plain into a single mongo coll
11
13
  */
@@ -16,52 +18,62 @@ class MongoStats extends Stats {
16
18
  this._opts = opts || {};
17
19
  this._cache = {};
18
20
 
19
- var upd = {
21
+ const upd = {
20
22
  $set: {
21
23
  ns: this._ns,
22
24
  name: this._name,
23
25
  }
24
26
  };
25
27
 
26
- this._coll().updateOne ({_id: this._id}, upd, {upsert: true}, (err, ret) => {
28
+ this._coll().updateOne ({_id: this._id}, upd, {upsert: true})
29
+ .finally (() => {
27
30
  debug ('mongo stats created, ns %s, name %s, opts %j', ns, name, opts);
28
31
  });
29
32
  }
30
33
 
31
- values(cb) {
32
- this._coll().findOne ({_id: this._id}, {projection: {counters: 1}}, (err, res) => {
33
- if (err) return cb (err);
34
34
 
35
+ //////////////////////////////////////////////////////////////////////////////////
36
+ values(cb) {
37
+ this._coll().findOne ({_id: this._id}, {projection: {counters: 1}})
38
+ .then (res => {
35
39
  debug ('mongo stats: get %s -> %j', this._name, res);
36
40
  cb (null, (res && res.counters) || {});
37
- });
41
+ })
42
+ .catch (cb);
38
43
  }
39
44
 
45
+
46
+ //////////////////////////////////////////////////////////////////////////////////
40
47
  paused (val, cb) {
41
48
  if (!cb) {
42
49
  // get, val is cb
43
50
  cb = val;
44
51
  val = undefined;
45
52
 
46
- this._coll().findOne ({_id: this._id}, {projection: {paused: 1}}, (err, res) => {
47
- if (err) return cb (err);
53
+ this._coll().findOne ({_id: this._id}, {projection: {paused: 1}})
54
+ .then (res => {
48
55
  debug ('mongo stats - paused: get %s -> %j', this._name, res);
49
56
  cb (null, (res && res.paused) || false);
50
- });
57
+ })
58
+ .catch (cb)
51
59
  }
52
60
  else {
53
61
  // set
54
- var upd = {$set: {paused : val}};
55
- this._coll().updateOne ({_id: this._id}, upd, {upsert: true}, (err) => {
62
+ const upd = {$set: {paused : val}};
63
+ this._coll().updateOne ({_id: this._id}, upd, {upsert: true})
64
+ .then (res => {
56
65
  debug ('mongo stats: updated %s -> %j', this._name, upd);
57
- cb (err);
58
- });
66
+ cb ();
67
+ })
68
+ .catch (cb);
59
69
  }
60
70
  }
61
71
 
72
+
73
+ //////////////////////////////////////////////////////////////////////////////////
62
74
  _flush (cb) {
63
- var upd = {$inc: {}};
64
- var some_added = false;
75
+ const upd = {$inc: {}};
76
+ let some_added = false;
65
77
 
66
78
  _.forEach(this._cache, (value, key) => {
67
79
  if (value) {
@@ -72,9 +84,13 @@ class MongoStats extends Stats {
72
84
  });
73
85
 
74
86
  if (some_added) {
75
- this._coll().updateOne ({_id: this._id}, upd, {upsert: true}, (err) => {
87
+ this._coll().updateOne ({_id: this._id}, upd, {upsert: true})
88
+ .then (res => {
76
89
  debug ('mongo stats: updated %s -> %j', this._name, upd);
77
- if (cb) cb (err);
90
+ if (cb) cb ();
91
+ })
92
+ .catch (err => {
93
+ if (cb) return cb(err);
78
94
  });
79
95
  }
80
96
  else {
@@ -83,6 +99,7 @@ class MongoStats extends Stats {
83
99
  }
84
100
 
85
101
 
102
+ //////////////////////////////////////////////////////////////////////////////////
86
103
  _ensureFlush() {
87
104
  if (this._flusher) return;
88
105
 
@@ -93,6 +110,7 @@ class MongoStats extends Stats {
93
110
  }
94
111
 
95
112
 
113
+ //////////////////////////////////////////////////////////////////////////////////
96
114
  _cancelFlush() {
97
115
  if (this._flusher) {
98
116
  clearTimeout(this._flusher);
@@ -101,16 +119,19 @@ class MongoStats extends Stats {
101
119
  }
102
120
 
103
121
 
122
+ //////////////////////////////////////////////////////////////////////////////////
104
123
  _mongocl () {
105
124
  return this._factory._mongocl;
106
125
  }
107
126
 
108
127
 
128
+ //////////////////////////////////////////////////////////////////////////////////
109
129
  _coll () {
110
130
  return this._factory._coll;
111
131
  }
112
132
 
113
133
 
134
+ //////////////////////////////////////////////////////////////////////////////////
114
135
  incr(v, delta, cb) {
115
136
  if ((delta === null) || (delta === undefined)) delta = 1;
116
137
 
@@ -125,48 +146,57 @@ class MongoStats extends Stats {
125
146
  }
126
147
 
127
148
 
149
+ //////////////////////////////////////////////////////////////////////////////////
128
150
  decr(v, delta, cb) {
129
151
  if ((delta === null) || (delta === undefined)) delta = 1;
130
152
  this.incr(v, -delta, cb);
131
153
  }
132
154
 
155
+
156
+ //////////////////////////////////////////////////////////////////////////////////
133
157
  opts (opts, cb) {
134
158
  if (!cb) {
135
159
  // get
136
160
  cb = opts;
137
- this._coll().findOne ({_id: this._id}, {projection: {opts: 1}}, (err, res) => {
138
- if (err) return cb (err);
161
+ this._coll().findOne ({_id: this._id}, {projection: {opts: 1}})
162
+ .then (res => {
139
163
  debug ('mongo stats - opts: get %s -> %j', this._name, res);
140
164
  cb (null, (res && res.opts) || {});
141
- });
165
+ })
166
+ .catch (cb);
142
167
  }
143
168
  else {
144
169
  // set
145
- var upd = {$set: {opts : opts}};
146
- this._coll().updateOne ({_id: this._id}, upd, {upsert: true}, (err) => {
170
+ const upd = {$set: {opts : opts}};
171
+ this._coll().updateOne ({_id: this._id}, upd, {upsert: true})
172
+ .then (res => {
147
173
  debug ('mongo stats: updated %s -> %j', this._name, upd);
148
- cb (err);
149
- });
174
+ cb ();
175
+ })
176
+ .catch (cb);
150
177
  }
151
178
  }
152
179
 
180
+
181
+ //////////////////////////////////////////////////////////////////////////////////
153
182
  clear(cb) {
154
183
  this._cancelFlush();
155
184
  this._cache = {};
156
185
 
157
- var upd = {
186
+ const upd = {
158
187
  $unset: {
159
188
  counters: 1,
160
189
  opts: 1
161
190
  }
162
191
  };
163
192
 
164
- this._coll().updateOne ({_id: this._id}, upd, (err) => {
165
- cb (err);
166
- });
193
+ this._coll().updateOne ({_id: this._id}, upd)
194
+ .then (() => cb ())
195
+ .catch(cb);
167
196
  }
168
197
 
169
198
 
199
+ //////////////////////////////////////////////////////////////////////////////////
170
200
  close (cb) {
171
201
  this._cancelFlush();
172
202
  this._flush (cb);
@@ -175,6 +205,7 @@ class MongoStats extends Stats {
175
205
 
176
206
 
177
207
 
208
+ //////////////////////////////////////////////////////////////////////////////////
178
209
  class MongoStatsFactory {
179
210
  constructor(cl, coll, opts) {
180
211
  this._opts = opts || {};
@@ -190,6 +221,7 @@ class MongoStatsFactory {
190
221
  type() { return MongoStatsFactory.Type() }
191
222
 
192
223
 
224
+ //////////////////////////////////////////////////////////////////////////////////
193
225
  stats(ns, name, opts) {
194
226
  var k = name + '@' + ns;
195
227
  if (!this._instances [k]) {
@@ -200,6 +232,7 @@ class MongoStatsFactory {
200
232
  return this._instances [k];
201
233
  }
202
234
 
235
+ //////////////////////////////////////////////////////////////////////////////////
203
236
  queues (ns, opts, cb) {
204
237
  if (!cb) {
205
238
  cb = opts;
@@ -207,11 +240,11 @@ class MongoStatsFactory {
207
240
  }
208
241
 
209
242
  if (opts.full) {
210
- this._coll.find({_id: {$regex: '^keuss:stats:' + ns}}).toArray (function (err, arr) {
211
- if (err) return cb (err);
212
-
213
- var res = {};
214
- arr.forEach (function (elem){
243
+ this._coll.find({_id: {$regex: '^keuss:stats:' + ns}}).
244
+ toArray ()
245
+ .then (arr => {
246
+ const res = {};
247
+ arr.forEach (elem => {
215
248
  res [elem.name] = {
216
249
  ns: elem.ns,
217
250
  name: elem.name,
@@ -222,24 +255,25 @@ class MongoStatsFactory {
222
255
  });
223
256
 
224
257
  cb (null, res);
225
- });
258
+ })
259
+ .catch (cb);
226
260
  }
227
261
  else {
228
- this._coll.find({_id: {$regex: '^keuss:stats:' + ns}}).project ({_id: 1, name: 1}).toArray (function (err, arr) {
229
- if (err) return cb (err);
230
-
231
- var res = [];
232
- arr.forEach (function (elem){
233
- res.push (elem.name);
234
- });
235
-
262
+ this._coll.find({_id: {$regex: '^keuss:stats:' + ns}})
263
+ .project ({_id: 1, name: 1})
264
+ .toArray ()
265
+ .then (arr => {
266
+ const res = [];
267
+ arr.forEach (elem => res.push (elem.name));
236
268
  cb (null, res);
237
- });
269
+ })
270
+ .catch (cb);
238
271
  }
239
272
  }
240
273
 
274
+ //////////////////////////////////////////////////////////////////////////////////
241
275
  close (cb) {
242
- var tasks = [];
276
+ const tasks = [];
243
277
 
244
278
  // flush pending stats
245
279
  _.each (this._instances, (v, k) => {
@@ -253,12 +287,15 @@ class MongoStatsFactory {
253
287
  (cb) => async.parallel (tasks, cb),
254
288
  (cb) => {
255
289
  debug (`closing MongoStatsFactory mongodb conn`);
256
- this._mongocl.close (cb);
290
+ this._mongocl.close ()
291
+ .then (res => cb (null, res))
292
+ .catch (cb);
257
293
  }
258
294
  ], cb);
259
295
  }
260
296
  }
261
297
 
298
+ //////////////////////////////////////////////////////////////////////////////////
262
299
  function creator (opts, cb) {
263
300
  if (!cb) {
264
301
  cb = opts;
@@ -274,13 +311,13 @@ function creator (opts, cb) {
274
311
 
275
312
  debug ('initializing creator of MongoStatsFactory, connecting to %s', m_url);
276
313
 
277
- MongoClient.connect (m_url, { useNewUrlParser: true }, function (err, cl) {
278
- if (err) return cb (err);
279
-
314
+ MongoClient.connect (m_url)
315
+ .then (cl => {
280
316
  debug ('initializing creator of MongoStatsFactory, connected to %s', m_url);
281
317
  var coll = cl.db().collection (m_coll);
282
318
  cb (null, new MongoStatsFactory (cl, coll, opts));
283
- });
319
+ })
320
+ .catch (cb);
284
321
  }
285
322
 
286
323
  module.exports = creator;
@@ -7,6 +7,13 @@ const _s_lua_code_push = `
7
7
  -- mature-t in ARGV[2]
8
8
  -- val in ARGV[3]
9
9
 
10
+ -- check id exists
11
+ local exists = redis.call ('HGET', 'keuss:q:ordered_queue:hash:' .. KEYS[1], ARGV[1])
12
+
13
+ if (exists) then
14
+ return redis.error_reply('EDUP duplicated _id ' .. ARGV[1])
15
+ end
16
+
10
17
  -- insert obj in hash by id
11
18
  redis.call ('HSET', 'keuss:q:ordered_queue:hash:' .. KEYS[1], ARGV[1], ARGV[3])
12
19
 
@@ -174,20 +181,33 @@ class RedisOrderedQueue {
174
181
  push (entry, done) {
175
182
  //////////////////////////////////
176
183
  var pl = {
177
- _id: entry.id || uuid.v4(),
184
+ _id: entry._id || uuid.v4(),
178
185
  payload: entry.payload,
179
186
  tries: entry.tries,
180
187
  hdrs: entry.hdrs || {},
181
188
  mature: (entry.mature || new Date ()).getTime ()
182
189
  };
183
190
 
184
-
185
191
  if (Buffer.isBuffer (pl.payload)) {
186
192
  pl.payload = pl.payload.toString ('base64');
187
193
  pl.type = 'buffer';
188
194
  }
189
195
 
190
- this._rediscl.roq_push (this._name, pl._id, pl.mature, JSON.stringify (pl), done);
196
+ this._rediscl.roq_push (this._name, pl._id, pl.mature, JSON.stringify (pl), (err, res) => {
197
+ if (err) {
198
+ if (err.message.startsWith ('EDUP ')) {
199
+ const e = new Error (`duplicated entry with _id ${pl._id}`)
200
+ e.code = 'EDUP';
201
+ done(e);
202
+ }
203
+ else {
204
+ done (err)
205
+ }
206
+ }
207
+ else {
208
+ done (null, res);
209
+ }
210
+ });
191
211
  }
192
212
 
193
213