pixl-server-storage 3.1.18 → 3.2.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/engines/Redis.js CHANGED
@@ -1,14 +1,14 @@
1
1
  // Redis Storage Plugin
2
- // Copyright (c) 2015 - 2019 Joseph Huckaby
2
+ // Copyright (c) 2015 - 2024 Joseph Huckaby
3
3
  // Released under the MIT License
4
4
 
5
- // Requires the 'redis' module from npm
6
- // npm install redis
5
+ // Requires the 'ioredis' module from npm
6
+ // npm install --save ioredis
7
7
 
8
- var Class = require("pixl-class");
9
- var Component = require("pixl-server/component");
10
- var Redis = require('redis');
11
- var Tools = require("pixl-tools");
8
+ const Class = require("pixl-class");
9
+ const Component = require("pixl-server/component");
10
+ const Redis = require('ioredis');
11
+ const Tools = require("pixl-tools");
12
12
 
13
13
  module.exports = Class.create({
14
14
 
@@ -16,8 +16,14 @@ module.exports = Class.create({
16
16
  __parent: Component,
17
17
 
18
18
  defaultConfig: {
19
- host: "127.0.0.1",
19
+
20
+ host: 'localhost',
20
21
  port: 6379,
22
+ commandTimeout: 5000,
23
+ connectTimeout: 5000,
24
+ username: "",
25
+ password: "",
26
+
21
27
  keyPrefix: "",
22
28
  keyTemplate: ""
23
29
  },
@@ -25,10 +31,7 @@ module.exports = Class.create({
25
31
  startup: function(callback) {
26
32
  // setup Redis connection
27
33
  var self = this;
28
-
29
- this.logDebug(2, "Setting up Redis",
30
- Tools.copyHashRemoveKeys( this.config.get(), { password:1 }) );
31
-
34
+ this.logDebug(2, "Setting up Redis", this.config.get() );
32
35
  this.setup(callback);
33
36
  },
34
37
 
@@ -39,35 +42,31 @@ module.exports = Class.create({
39
42
 
40
43
  this.keyPrefix = (r_config.keyPrefix || '').replace(/^\//, '');
41
44
  if (this.keyPrefix && !this.keyPrefix.match(/\/$/)) this.keyPrefix += '/';
45
+ delete r_config.keyPrefix;
42
46
 
43
47
  this.keyTemplate = (r_config.keyTemplate || '').replace(/^\//, '').replace(/\/$/, '');
48
+ delete r_config.keyTemplate;
44
49
 
45
- r_config.return_buffers = true;
46
- r_config.retry_strategy = function(opts) {
47
- // simple backoff strategy
48
- return Math.min(opts.attempt * 100, 3000);
49
- };
50
+ if (!r_config.username.length) delete r_config.username;
51
+ if (!r_config.password.length) delete r_config.password;
50
52
 
51
- this.redis = Redis.createClient( Tools.copyHashRemoveKeys(r_config, { keyPrefix:1, keyTemplate:1 }) );
53
+ r_config.lazyConnect = true;
54
+ r_config.reconnectOnError = function(err) { return true; };
55
+
56
+ this.redis = new Redis(r_config);
52
57
 
53
58
  this.redis.on('error', function(err) {
54
- if (!self.storage.started) return callback(err);
59
+ if (!self.storage.started) {
60
+ return callback( new Error("Redis Startup Error: " + (err.message || err)) );
61
+ }
55
62
 
56
63
  // error after startup? Just log it I guess
57
64
  self.logError('redis', ''+err);
58
- });
65
+ }); // error
59
66
 
60
- this.redis.on('connect', function() {
61
- self.logDebug(3, "Redis connected successfully");
62
- if (!self.storage.started) return callback();
63
- });
64
-
65
- this.redis.on('reconnecting', function(opts) {
66
- self.logDebug(3, "Redis is reconnecting", opts);
67
- });
68
-
69
- this.redis.on('end', function() {
70
- self.logDebug(3, "Redis disconnected");
67
+ this.redis.connect(function() {
68
+ self.logDebug(8, "Successfully connected to Redis");
69
+ callback();
71
70
  });
72
71
  },
73
72
 
@@ -167,7 +166,8 @@ module.exports = Class.create({
167
166
 
168
167
  this.logDebug(9, "Fetching Redis Object: " + key);
169
168
 
170
- this.redis.get( key, function(err, result) {
169
+ var func = this.storage.isBinaryKey(key) ? 'getBuffer' : 'get';
170
+ this.redis[func]( key, function(err, result) {
171
171
  if (!result) {
172
172
  if (err) {
173
173
  // an actual error
@@ -204,13 +204,13 @@ module.exports = Class.create({
204
204
  },
205
205
 
206
206
  getBuffer: function(key, callback) {
207
- // get buffer to record value given key
207
+ // fetch Redis buffer given key
208
208
  var self = this;
209
209
  key = this.prepKey(key);
210
210
 
211
211
  this.logDebug(9, "Fetching Redis Object: " + key);
212
212
 
213
- this.redis.get( key, function(err, result) {
213
+ this.redis.getBuffer( key, function(err, result) {
214
214
  if (!result) {
215
215
  if (err) {
216
216
  // an actual error
@@ -329,8 +329,11 @@ module.exports = Class.create({
329
329
  shutdown: function(callback) {
330
330
  // shutdown storage
331
331
  this.logDebug(2, "Shutting down Redis");
332
- if (this.redis) this.redis.quit();
333
- callback();
332
+ if (this.redis) {
333
+ this.redis.quit(callback);
334
+ this.redis = null;
335
+ }
336
+ else callback();
334
337
  }
335
338
 
336
339
  });
@@ -1,14 +1,13 @@
1
1
  // RedisCluster Storage Plugin
2
- // Copyright (c) 2015 - 2020 Joseph Huckaby
2
+ // Copyright (c) 2015 - 2024 Joseph Huckaby
3
3
  // Released under the MIT License
4
4
 
5
- // Requires the 'ioredis' and 'ioredis-timeout' modules from npm
6
- // npm install --save ioredis ioredis-timeout
5
+ // Requires the 'ioredis' module from npm
6
+ // npm install --save ioredis
7
7
 
8
8
  const Class = require("pixl-class");
9
9
  const Component = require("pixl-server/component");
10
10
  const Redis = require('ioredis');
11
- const RedisTimeout = require('ioredis-timeout');
12
11
  const Tools = require("pixl-tools");
13
12
 
14
13
  module.exports = Class.create({
@@ -20,10 +19,13 @@ module.exports = Class.create({
20
19
 
21
20
  host: 'localhost',
22
21
  port: 6379,
23
- timeout: 1000,
24
22
  connectRetries: 5,
25
23
  clusterOpts: {
26
- scaleReads: "master"
24
+ scaleReads: "master",
25
+ redisOptions: {
26
+ commandTimeout: 5000,
27
+ connectTimeout: 5000
28
+ }
27
29
  },
28
30
 
29
31
  keyPrefix: "",
@@ -33,7 +35,7 @@ module.exports = Class.create({
33
35
  startup: function(callback) {
34
36
  // setup Redis connection
35
37
  var self = this;
36
- this.logDebug(2, "Setting up RedisClustr", this.config.get() );
38
+ this.logDebug(2, "Setting up RedisCluster", this.config.get() );
37
39
  this.setup(callback);
38
40
  },
39
41
 
@@ -77,12 +79,7 @@ module.exports = Class.create({
77
79
  }); // error
78
80
 
79
81
  this.redis.once('ready', function() {
80
- self.logDebug(8, "Successfully connected to Redis");
81
-
82
- if (r_config.timeout) {
83
- RedisTimeout(self.redis, parseInt(r_config.timeout));
84
- }
85
-
82
+ self.logDebug(8, "Successfully connected to Redis cluster");
86
83
  callback();
87
84
  });
88
85
  },
package/engines/S3.js CHANGED
@@ -2,6 +2,7 @@
2
2
  // Copyright (c) 2015 - 2022 Joseph Huckaby
3
3
  // Released under the MIT License
4
4
 
5
+ var Path = require('path');
5
6
  var Class = require("pixl-class");
6
7
  var Component = require("pixl-server/component");
7
8
  var Tools = require("pixl-tools");
@@ -596,6 +597,93 @@ module.exports = Class.create({
596
597
  } );
597
598
  },
598
599
 
600
+ list: function(opts, callback) {
601
+ // generate list of objects in S3 given prefix
602
+ // this repeatedly calls ListObjectsV2 for lists > 1000
603
+ // opts: { remotePath, filespec, filter }
604
+ // result: { files([{ key, size, mtime }, ...]), total_bytes }
605
+ let self = this;
606
+ let done = false;
607
+ let files = [];
608
+ let total_bytes = 0;
609
+ let num_calls = 0;
610
+ let now = Tools.timeNow(true);
611
+
612
+ if (typeof(opts) == 'string') opts = { remotePath: opts };
613
+ if (!opts.remotePath) opts.remotePath = '';
614
+ if (!opts.filespec) opts.filespec = /.*/;
615
+ if (!opts.filter) opts.filter = function() { return true; };
616
+
617
+ if (opts.older) {
618
+ // convert older to filter func with mtime
619
+ if (typeof(opts.older) == 'string') opts.older = Tools.getSecondsFromText( opts.older );
620
+ opts.filter = function(file) { return file.mtime <= now - opts.older; };
621
+ }
622
+
623
+ let params = Tools.mergeHashes( this.s3Params || {}, opts.params || {} );
624
+ params.Prefix = this.keyPrefix + opts.remotePath;
625
+ params.MaxKeys = 1000;
626
+
627
+ let PREFIX_RE = new RegExp('^' + Tools.escapeRegExp(this.keyPrefix));
628
+
629
+ this.logDebug(8, "Listing S3 files with prefix: " + params.Prefix, opts);
630
+ let tracker = this.perf ? this.perf.begin('s3_list') : null;
631
+
632
+ Tools.async.whilst(
633
+ function() {
634
+ return !done;
635
+ },
636
+ function(callback) {
637
+ self.logDebug(9, "Listing chunk", params);
638
+
639
+ self.s3.send( new S3.ListObjectsV2Command(params) )
640
+ .then( function(data) {
641
+ let items = data.Contents || [];
642
+ for (let idx = 0, len = items.length; idx < len; idx++) {
643
+ let item = items[idx];
644
+ let key = item.Key.replace(PREFIX_RE, '');
645
+ let bytes = item.Size;
646
+ let mtime = item.LastModified.getTime() / 1000;
647
+ let file = { key: key, size: bytes, mtime: mtime };
648
+
649
+ // optional filter and filespec
650
+ if (opts.filter(file) && Path.basename(key).match(opts.filespec)) {
651
+ total_bytes += bytes;
652
+ files.push(file);
653
+ }
654
+ }
655
+
656
+ // check for end of key list
657
+ if (!data.IsTruncated || !items.length) done = true;
658
+ else {
659
+ // advance to next chunk
660
+ params.StartAfter = items[ items.length - 1 ].Key;
661
+ }
662
+
663
+ num_calls++;
664
+ callback();
665
+ } )
666
+ .catch( function(err) {
667
+ callback( err );
668
+ } );
669
+ },
670
+ function(err) {
671
+ if (tracker) tracker.end();
672
+ if (err) return process.nextTick( function() { callback(err, null, null); });
673
+
674
+ self.logDebug(9, "S3 listing complete (" + Tools.commify(files.length) + " objects, " + Tools.getTextFromBytes(total_bytes) + ")", {
675
+ prefix: params.Prefix,
676
+ count: files.length,
677
+ bytes: total_bytes,
678
+ calls: num_calls
679
+ });
680
+
681
+ // break out of promise context
682
+ process.nextTick( function() { callback( null, files, total_bytes ); } );
683
+ }
684
+ ); // whilst
685
+ },
686
+
599
687
  runMaintenance: function(callback) {
600
688
  // run daily maintenance
601
689
  callback();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pixl-server-storage",
3
- "version": "3.1.18",
3
+ "version": "3.2.1",
4
4
  "description": "A key/value/list storage component for the pixl-server framework.",
5
5
  "author": "Joseph Huckaby <jhuckaby@gmail.com>",
6
6
  "homepage": "https://github.com/jhuckaby/pixl-server-storage",