pixl-server-storage 3.1.14 → 3.1.15

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.
Files changed (3) hide show
  1. package/engines/S3.js +63 -4
  2. package/indexer.js +11 -11
  3. package/package.json +2 -1
package/engines/S3.js CHANGED
@@ -10,6 +10,7 @@ var S3 = require("@aws-sdk/client-s3");
10
10
  var { Upload } = require("@aws-sdk/lib-storage");
11
11
  var { NodeHttpHandler } = require("@smithy/node-http-handler");
12
12
  var streamToBuffer = require("fast-stream-to-buffer");
13
+ var StreamMeter = require("stream-meter");
13
14
 
14
15
  module.exports = Class.create({
15
16
 
@@ -100,8 +101,6 @@ module.exports = Class.create({
100
101
 
101
102
  prepKey: function(key) {
102
103
  // prepare key for S3 based on config
103
- var md5 = Tools.digestHex(key, 'md5');
104
-
105
104
  var ns = '';
106
105
  if (key.match(/^([\w\-\.]+)\//)) ns = RegExp.$1;
107
106
 
@@ -110,6 +109,7 @@ module.exports = Class.create({
110
109
  }
111
110
 
112
111
  if (this.keyTemplate) {
112
+ var md5 = Tools.digestHex(key, 'md5');
113
113
  var idx = 0;
114
114
  var temp = this.keyTemplate.replace( /\#/g, function() {
115
115
  return md5.substr(idx++, 1);
@@ -153,6 +153,7 @@ module.exports = Class.create({
153
153
  this.s3.send( new S3.PutObjectCommand(params) )
154
154
  .then( function(data) {
155
155
  self.logDebug(9, "Store complete: " + key);
156
+ self.storage.emit('billing', 's3_put', 1);
156
157
 
157
158
  // possibly cache in LRU
158
159
  if (self.cache && !is_binary) {
@@ -162,6 +163,13 @@ module.exports = Class.create({
162
163
  if (callback) process.nextTick( function() { callback(null, data); });
163
164
  } )
164
165
  .catch( function(err) {
166
+ if (err.name == 'SlowDown') {
167
+ // special behavior for SlowDown errors
168
+ self.logDebug(6, "Received SlowDown from S3 put: " + orig_key + ": " + err + " (will retry)");
169
+ self.storage.emit('slowDown');
170
+ setTimeout( function() { self.put(orig_key, value, callback); }, 1000 );
171
+ return;
172
+ }
165
173
  self.logError('s3', "Failed to store object: " + key + ": " + (err.message || err), err);
166
174
  if (callback) process.nextTick( function() { callback(err); });
167
175
  } );
@@ -173,9 +181,12 @@ module.exports = Class.create({
173
181
  var orig_key = key;
174
182
  key = this.prepKey(key);
175
183
 
184
+ var meter = new StreamMeter();
185
+ inp.pipe(meter);
186
+
176
187
  var params = Tools.copyHash( this.s3Params );
177
188
  params.Key = this.extKey(key, orig_key);
178
- params.Body = inp;
189
+ params.Body = meter;
179
190
 
180
191
  this.logDebug(9, "Storing S3 Binary Stream: " + key);
181
192
 
@@ -187,6 +198,8 @@ module.exports = Class.create({
187
198
  upload.done()
188
199
  .then( function(data) {
189
200
  self.logDebug(9, "Stream store complete: " + key);
201
+ self.storage.emit('billing', 's3_put', 1);
202
+ self.storage.emit('billing', 's3_bytes_out', meter.bytes);
190
203
  if (callback) process.nextTick( function() { callback(null, data); });
191
204
  } )
192
205
  .catch( function(err) {
@@ -201,9 +214,12 @@ module.exports = Class.create({
201
214
  var orig_key = key;
202
215
  key = this.prepKey(key);
203
216
 
217
+ var meter = new StreamMeter();
218
+ inp.pipe(meter);
219
+
204
220
  var params = Tools.copyHash( this.s3Params );
205
221
  params.Key = this.extKey(key, orig_key);
206
- params.Body = inp;
222
+ params.Body = meter;
207
223
  if (opts) Tools.mergeHashInto(params, opts);
208
224
 
209
225
  this.logDebug(9, "Storing S3 Binary Stream: " + key);
@@ -216,6 +232,8 @@ module.exports = Class.create({
216
232
  upload.done()
217
233
  .then( function(data) {
218
234
  self.logDebug(9, "Stream store complete: " + key);
235
+ self.storage.emit('billing', 's3_put', 1);
236
+ self.storage.emit('billing', 's3_bytes_out', meter.bytes);
219
237
  if (callback) process.nextTick( function() { callback(null, data); });
220
238
  } )
221
239
  .catch( function(err) {
@@ -252,6 +270,7 @@ module.exports = Class.create({
252
270
  this.s3.send( new S3.HeadObjectCommand(params) )
253
271
  .then( function(data) {
254
272
  self.logDebug(9, "Head complete: " + key);
273
+ self.storage.emit('billing', 's3_head', 1);
255
274
 
256
275
  process.nextTick( function() {
257
276
  callback( null, {
@@ -267,6 +286,13 @@ module.exports = Class.create({
267
286
  err = new Error("Failed to head key: " + key + ": Not found");
268
287
  err.code = "NoSuchKey";
269
288
  }
289
+ else if (err.name == 'SlowDown') {
290
+ // special behavior for SlowDown errors
291
+ self.logDebug(6, "Received SlowDown from S3 head: " + orig_key + ": " + err + " (will retry)");
292
+ self.storage.emit('slowDown');
293
+ setTimeout( function() { self.head(orig_key, callback); }, 1000 );
294
+ return;
295
+ }
270
296
  else {
271
297
  // some other error
272
298
  self.logError('s3', "Failed to head key: " + key + ": " + (err.message || err), err);
@@ -314,6 +340,9 @@ module.exports = Class.create({
314
340
  return callback(err);
315
341
  }
316
342
 
343
+ self.storage.emit('billing', 's3_get', 1);
344
+ self.storage.emit('billing', 's3_bytes_in', body.length);
345
+
317
346
  if (is_binary) {
318
347
  self.logDebug(9, "Binary fetch complete: " + key, '' + body.length + ' bytes');
319
348
  }
@@ -347,6 +376,13 @@ module.exports = Class.create({
347
376
  err = new Error("Failed to fetch key: " + key + ": Not found");
348
377
  err.code = "NoSuchKey";
349
378
  }
379
+ else if (err.name == 'SlowDown') {
380
+ // special behavior for SlowDown errors
381
+ self.logDebug(6, "Received SlowDown from S3 get: " + orig_key + ": " + err + " (will retry)");
382
+ self.storage.emit('slowDown');
383
+ setTimeout( function() { self.get(orig_key, callback); }, 1000 );
384
+ return;
385
+ }
350
386
  else {
351
387
  // some other error
352
388
  self.logError('s3', "Failed to fetch key: " + key + ": " + (err.message || err), err);
@@ -376,6 +412,8 @@ module.exports = Class.create({
376
412
  }
377
413
 
378
414
  self.logDebug(9, "Binary fetch complete: " + key, '' + body.length + ' bytes');
415
+ self.storage.emit('billing', 's3_get', 1);
416
+ self.storage.emit('billing', 's3_bytes_in', body.length);
379
417
 
380
418
  callback( null, body, {
381
419
  mod: Math.floor((new Date(data.LastModified)).getTime() / 1000),
@@ -390,6 +428,13 @@ module.exports = Class.create({
390
428
  err = new Error("Failed to fetch key: " + key + ": Not found");
391
429
  err.code = "NoSuchKey";
392
430
  }
431
+ else if (err.name == 'SlowDown') {
432
+ // special behavior for SlowDown errors
433
+ self.logDebug(6, "Received SlowDown from S3 getBuffer: " + orig_key + ": " + err + " (will retry)");
434
+ self.storage.emit('slowDown');
435
+ setTimeout( function() { self.getBuffer(orig_key, callback); }, 1000 );
436
+ return;
437
+ }
393
438
  else {
394
439
  // some other error
395
440
  self.logError('s3', "Failed to fetch key: " + key + ": " + (err.message || err), err);
@@ -423,6 +468,9 @@ module.exports = Class.create({
423
468
  self.logDebug(9, "S3 stream download closed: " + key);
424
469
  } );
425
470
 
471
+ self.storage.emit('billing', 's3_get', 1);
472
+ self.storage.emit('billing', 's3_bytes_in', data.ContentLength);
473
+
426
474
  process.nextTick( function() {
427
475
  callback( null, download, {
428
476
  mod: Math.floor( (new Date(data.LastModified)).getTime() / 1000 ),
@@ -478,6 +526,9 @@ module.exports = Class.create({
478
526
  self.logDebug(9, "S3 stream download closed: " + key);
479
527
  } );
480
528
 
529
+ self.storage.emit('billing', 's3_get', 1);
530
+ self.storage.emit('billing', 's3_bytes_in', data.ContentRange);
531
+
481
532
  // get full length from the ContentRange header
482
533
  var len = 0;
483
534
  if (data.ContentRange && data.ContentRange.toString().match(/\/\s*(\d+)\s*$/)) {
@@ -521,6 +572,7 @@ module.exports = Class.create({
521
572
  this.s3.send( new S3.DeleteObjectCommand(params) )
522
573
  .then( function(data) {
523
574
  self.logDebug(9, "Delete complete: " + key);
575
+ self.storage.emit('billing', 's3_delete', 1);
524
576
 
525
577
  // possibly delete from LRU cache as well
526
578
  if (self.cache && self.cache.has(orig_key)) {
@@ -530,6 +582,13 @@ module.exports = Class.create({
530
582
  if (callback) process.nextTick( function() { callback(null, data); } );
531
583
  } )
532
584
  .catch( function(err) {
585
+ if (err.name == 'SlowDown') {
586
+ // special behavior for SlowDown errors
587
+ self.logDebug(6, "Received SlowDown from S3 delete: " + orig_key + ": " + err + " (will retry)");
588
+ self.storage.emit('slowDown');
589
+ setTimeout( function() { self.delete(orig_key, callback); }, 1000 );
590
+ return;
591
+ }
533
592
  self.logError('s3', "Failed to delete object: " + key + ": " + (err.message || err), err);
534
593
  if (callback) process.nextTick( function() { callback(err); } );
535
594
  } );
package/indexer.js CHANGED
@@ -75,7 +75,7 @@ module.exports = Class.create({
75
75
  validateIndexConfig: function(config) {
76
76
  // make sure index config is kosher
77
77
  // return false for success, or error on failure
78
- if (!config || !config.fields || !config.fields.length) {
78
+ if (!config || !config.fields || !Tools.isaArray(config.fields)) {
79
79
  return( new Error("Invalid index configuration object.") );
80
80
  }
81
81
  if (Tools.findObject(config.fields, { _primary: 1 })) {
@@ -104,6 +104,10 @@ module.exports = Class.create({
104
104
 
105
105
  // validate each sorter def
106
106
  if (config.sorters) {
107
+ if (!Tools.isaArray(config.sorters)) {
108
+ return( new Error("Invalid index sorters array.") );
109
+ }
110
+
107
111
  for (var idx = 0, len = config.sorters.length; idx < len; idx++) {
108
112
  var sorter = config.sorters[idx];
109
113
 
@@ -163,18 +167,12 @@ module.exports = Class.create({
163
167
  var fields = [];
164
168
 
165
169
  config.fields.forEach( function(def) {
166
- var value = def.source.match(/^\//) ? Tools.lookupPath(def.source, record) : Tools.sub(def.source, record, true);
170
+ var value = def.source.match(/\[.+\]/) ? Tools.sub(def.source, record, true) : Tools.getPath(record, def.source);
171
+ if (value === undefined) value = null;
167
172
  if ((value === null) && ("default_value" in def)) value = def.default_value;
168
173
  if (value !== null) fields.push(def);
169
174
  } );
170
175
 
171
- if (!fields.length) {
172
- // nothing to index!
173
- this.logDebug(6, "Nothing to index, skipping entire record");
174
- if (callback) callback();
175
- return;
176
- }
177
-
178
176
  // start index and track perf
179
177
  var pf = this.perf.begin('index');
180
178
 
@@ -211,7 +209,8 @@ module.exports = Class.create({
211
209
  return;
212
210
  }
213
211
 
214
- var value = def.source.match(/^\//) ? Tools.lookupPath(def.source, record) : Tools.sub(def.source, record, true);
212
+ var value = def.source.match(/\[.+\]/) ? Tools.sub(def.source, record, true) : Tools.getPath(record, def.source);
213
+ if (value === undefined) value = null;
215
214
  if ((value === null) && ("default_value" in def)) value = def.default_value;
216
215
  if (typeof(value) == 'object') value = JSON.stringify(value);
217
216
 
@@ -920,7 +919,8 @@ module.exports = Class.create({
920
919
  // add record to sorter index
921
920
  var config = state.config;
922
921
 
923
- var value = Tools.lookupPath(sorter.source, record);
922
+ var value = Tools.getPath(record, sorter.source);
923
+ if (value === undefined) value = null;
924
924
  if ((value === null) && ("default_value" in sorter)) value = sorter.default_value;
925
925
  if (value === null) {
926
926
  if (state.new_record) value = ((sorter.type == 'number') ? 0 : '');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pixl-server-storage",
3
- "version": "3.1.14",
3
+ "version": "3.1.15",
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",
@@ -36,6 +36,7 @@
36
36
  "pixl-server": "^1.0.39",
37
37
  "pixl-tools": "^1.0.33",
38
38
  "porter-stemmer": "0.9.1",
39
+ "stream-meter": "1.0.4",
39
40
  "unidecode": "0.1.8"
40
41
  },
41
42
  "devDependencies": {