pixl-server-storage 4.0.3 → 4.1.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/README.md +81 -10
- package/engines/Couchbase.js +0 -1
- package/engines/Filesystem.js +59 -28
- package/engines/Postgres.js +515 -0
- package/engines/Redis.js +129 -29
- package/engines/RedisCluster.js +128 -29
- package/engines/S3.js +53 -25
- package/engines/SQLite.js +65 -22
- package/package.json +1 -1
- package/test/config.json +19 -0
- package/test/data/backups/backup-2026-04-05-20-23-56.db.gz +0 -0
- package/test/data/backups/backup-2026-06-08-20-33-01.db.gz +0 -0
- package/test/data/backups/backup-2026-02-15-18-10-21.db.gz +0 -0
- package/test/data/backups/backup-2026-02-15-18-10-33.db.gz +0 -0
package/engines/Redis.js
CHANGED
|
@@ -9,6 +9,7 @@ const Class = require("pixl-class");
|
|
|
9
9
|
const Component = require("pixl-server/component");
|
|
10
10
|
const Redis = require('ioredis');
|
|
11
11
|
const Tools = require("pixl-tools");
|
|
12
|
+
const Cache = require("pixl-cache");
|
|
12
13
|
|
|
13
14
|
module.exports = Class.create({
|
|
14
15
|
|
|
@@ -47,6 +48,23 @@ module.exports = Class.create({
|
|
|
47
48
|
this.keyTemplate = (r_config.keyTemplate || '').replace(/^\//, '').replace(/\/$/, '');
|
|
48
49
|
delete r_config.keyTemplate;
|
|
49
50
|
|
|
51
|
+
// optional LRU cache
|
|
52
|
+
this.cache = null;
|
|
53
|
+
var cache_opts = r_config.cache;
|
|
54
|
+
if (cache_opts && cache_opts.enabled) {
|
|
55
|
+
this.logDebug(3, "Setting up LRU cache", cache_opts);
|
|
56
|
+
this.cache = new Cache( Tools.copyHashRemoveKeys(cache_opts, { enabled: 1 }) );
|
|
57
|
+
this.cache.on('expire', function(item, reason) {
|
|
58
|
+
self.logDebug(9, "Expiring LRU cache object: " + item.key + " due to: " + reason, {
|
|
59
|
+
key: item.key,
|
|
60
|
+
reason: reason,
|
|
61
|
+
totalCount: self.cache.count,
|
|
62
|
+
totalBytes: self.cache.bytes
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
delete r_config.cache;
|
|
67
|
+
|
|
50
68
|
if (!r_config.username.length) delete r_config.username;
|
|
51
69
|
if (!r_config.password.length) delete r_config.password;
|
|
52
70
|
|
|
@@ -93,13 +111,14 @@ module.exports = Class.create({
|
|
|
93
111
|
// store key+value in Redis
|
|
94
112
|
var self = this;
|
|
95
113
|
key = this.prepKey(key);
|
|
114
|
+
var is_binary = this.storage.isBinaryKey(key);
|
|
96
115
|
|
|
97
|
-
if (
|
|
116
|
+
if (is_binary) {
|
|
98
117
|
this.logDebug(9, "Storing Redis Binary Object: " + key, '' + value.length + ' bytes');
|
|
99
118
|
}
|
|
100
119
|
else {
|
|
101
120
|
this.logDebug(9, "Storing Redis JSON Object: " + key, this.debugLevel(10) ? value : null);
|
|
102
|
-
value = JSON.stringify( value );
|
|
121
|
+
value = Buffer.from( JSON.stringify( value ) );
|
|
103
122
|
}
|
|
104
123
|
|
|
105
124
|
this.redis.set( key, value, function(err) {
|
|
@@ -109,6 +128,11 @@ module.exports = Class.create({
|
|
|
109
128
|
}
|
|
110
129
|
else self.logDebug(9, "Store complete: " + key);
|
|
111
130
|
|
|
131
|
+
// possibly cache in LRU
|
|
132
|
+
if (!err && self.cache && !is_binary) {
|
|
133
|
+
self.cache.set( key, value, { date: Tools.timeNow(true) } );
|
|
134
|
+
}
|
|
135
|
+
|
|
112
136
|
if (callback) callback(err);
|
|
113
137
|
} );
|
|
114
138
|
},
|
|
@@ -138,7 +162,30 @@ module.exports = Class.create({
|
|
|
138
162
|
// The Redis API has no way to head / ping an object.
|
|
139
163
|
// So, we have to do this the RAM-hard way...
|
|
140
164
|
|
|
141
|
-
|
|
165
|
+
// check cache first
|
|
166
|
+
if (this.cache && this.cache.has(key)) {
|
|
167
|
+
var item = this.cache.getMeta(key);
|
|
168
|
+
|
|
169
|
+
if (item.value.length == 0) {
|
|
170
|
+
process.nextTick( function() {
|
|
171
|
+
var err = new Error("Failed to head key: " + key + ": Not found");
|
|
172
|
+
err.code = "NoSuchKey";
|
|
173
|
+
callback( err );
|
|
174
|
+
} );
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
process.nextTick( function() {
|
|
178
|
+
self.logDebug(9, "Cached head complete: " + key);
|
|
179
|
+
callback( null, {
|
|
180
|
+
mod: item.date,
|
|
181
|
+
len: item.value.length
|
|
182
|
+
} );
|
|
183
|
+
} );
|
|
184
|
+
}
|
|
185
|
+
return;
|
|
186
|
+
} // cache
|
|
187
|
+
|
|
188
|
+
this.redis.getBuffer( key, function(err, data) {
|
|
142
189
|
if (err) {
|
|
143
190
|
// an actual error
|
|
144
191
|
err.message = "Failed to head key: " + key + ": " + err;
|
|
@@ -150,7 +197,12 @@ module.exports = Class.create({
|
|
|
150
197
|
// always use "NoSuchKey" in error code
|
|
151
198
|
var err = new Error("Failed to head key: " + key + ": Not found");
|
|
152
199
|
err.code = "NoSuchKey";
|
|
153
|
-
|
|
200
|
+
|
|
201
|
+
if (self.cache && !self.storage.isBinaryKey(key)) {
|
|
202
|
+
// store 'empty' stub in cache
|
|
203
|
+
self.cache.set( key, Buffer.alloc(0), { date: 0 } );
|
|
204
|
+
}
|
|
205
|
+
|
|
154
206
|
callback( err, null );
|
|
155
207
|
}
|
|
156
208
|
else {
|
|
@@ -163,44 +215,79 @@ module.exports = Class.create({
|
|
|
163
215
|
// fetch Redis value given key
|
|
164
216
|
var self = this;
|
|
165
217
|
key = this.prepKey(key);
|
|
218
|
+
var is_binary = this.storage.isBinaryKey(key);
|
|
166
219
|
|
|
167
220
|
this.logDebug(9, "Fetching Redis Object: " + key);
|
|
168
221
|
|
|
169
|
-
|
|
170
|
-
this.
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
self.logError('redis', ''+err);
|
|
176
|
-
callback( err, null );
|
|
177
|
-
}
|
|
178
|
-
else {
|
|
179
|
-
// record not found
|
|
180
|
-
// always use "NoSuchKey" in error code
|
|
222
|
+
// check cache first
|
|
223
|
+
if (this.cache && !is_binary && this.cache.has(key)) {
|
|
224
|
+
var item = this.cache.getMeta(key);
|
|
225
|
+
|
|
226
|
+
if (item.value.length == 0) {
|
|
227
|
+
process.nextTick( function() {
|
|
181
228
|
var err = new Error("Failed to fetch key: " + key + ": Not found");
|
|
182
229
|
err.code = "NoSuchKey";
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
}
|
|
230
|
+
callback( err );
|
|
231
|
+
} );
|
|
186
232
|
}
|
|
187
233
|
else {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
else {
|
|
192
|
-
try { result = JSON.parse( result.toString() ); }
|
|
234
|
+
process.nextTick( function() {
|
|
235
|
+
var data = null;
|
|
236
|
+
try { data = JSON.parse( item.value.toString() ); }
|
|
193
237
|
catch (err) {
|
|
194
238
|
self.logError('redis', "Failed to parse JSON record: " + key + ": " + err);
|
|
195
239
|
callback( err, null );
|
|
196
240
|
return;
|
|
197
241
|
}
|
|
198
|
-
self.logDebug(9, "JSON fetch complete: " + key, self.debugLevel(10) ?
|
|
242
|
+
self.logDebug(9, "Cached JSON fetch complete: " + key, self.debugLevel(10) ? data : null);
|
|
243
|
+
|
|
244
|
+
callback( null, data );
|
|
245
|
+
} );
|
|
246
|
+
}
|
|
247
|
+
return;
|
|
248
|
+
} // cache
|
|
249
|
+
|
|
250
|
+
this.redis.getBuffer( key, function(err, result) {
|
|
251
|
+
if (err) {
|
|
252
|
+
// an actual error
|
|
253
|
+
err.message = "Failed to fetch key: " + key + ": " + err;
|
|
254
|
+
self.logError('redis', ''+err);
|
|
255
|
+
return callback( err, null );
|
|
256
|
+
}
|
|
257
|
+
else if (!result || !result.length) {
|
|
258
|
+
// record not found
|
|
259
|
+
// always use "NoSuchKey" in error code
|
|
260
|
+
var err = new Error("Failed to fetch key: " + key + ": Not found");
|
|
261
|
+
err.code = "NoSuchKey";
|
|
262
|
+
|
|
263
|
+
if (self.cache && !is_binary) {
|
|
264
|
+
// store 'empty' stub in cache
|
|
265
|
+
self.cache.set( key, Buffer.alloc(0), { date: 0 } );
|
|
199
266
|
}
|
|
200
267
|
|
|
201
|
-
callback(
|
|
268
|
+
return callback( err, null );
|
|
202
269
|
}
|
|
203
|
-
|
|
270
|
+
|
|
271
|
+
if (is_binary) {
|
|
272
|
+
self.logDebug(9, "Binary fetch complete: " + key, '' + result.length + ' bytes');
|
|
273
|
+
}
|
|
274
|
+
else {
|
|
275
|
+
// possibly cache in LRU
|
|
276
|
+
if (self.cache) {
|
|
277
|
+
self.cache.set( key, result, { date: Tools.timeNow(true) } );
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
try { result = JSON.parse( result.toString() ); }
|
|
281
|
+
catch (err) {
|
|
282
|
+
self.logError('redis', "Failed to parse JSON record: " + key + ": " + err);
|
|
283
|
+
callback( err, null );
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
self.logDebug(9, "JSON fetch complete: " + key, self.debugLevel(10) ? result : null);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
callback( null, result );
|
|
290
|
+
} ); // redis.getBuffer
|
|
204
291
|
},
|
|
205
292
|
|
|
206
293
|
getBuffer: function(key, callback) {
|
|
@@ -289,7 +376,6 @@ module.exports = Class.create({
|
|
|
289
376
|
end = buf.length ? buf.length - 1 : 0;
|
|
290
377
|
}
|
|
291
378
|
if (isNaN(start) || isNaN(end) || (start < 0) || (start >= buf.length) || (end < start) || (end >= buf.length)) {
|
|
292
|
-
download.destroy();
|
|
293
379
|
callback( new Error("Invalid byte range (" + start + '-' + end + ") for key: " + key + " (len: " + buf.length + ")"), null );
|
|
294
380
|
return;
|
|
295
381
|
}
|
|
@@ -304,6 +390,7 @@ module.exports = Class.create({
|
|
|
304
390
|
// delete Redis key given key
|
|
305
391
|
var self = this;
|
|
306
392
|
key = this.prepKey(key);
|
|
393
|
+
var is_binary = this.storage.isBinaryKey(key);
|
|
307
394
|
|
|
308
395
|
this.logDebug(9, "Deleting Redis Object: " + key);
|
|
309
396
|
|
|
@@ -311,11 +398,24 @@ module.exports = Class.create({
|
|
|
311
398
|
if (!err && !deleted) {
|
|
312
399
|
err = new Error("Failed to fetch key: " + key + ": Not found");
|
|
313
400
|
err.code = "NoSuchKey";
|
|
401
|
+
|
|
402
|
+
if (self.cache && !is_binary) {
|
|
403
|
+
// store 'empty' stub in cache
|
|
404
|
+
self.cache.set( key, Buffer.alloc(0), { date: 0 } );
|
|
405
|
+
}
|
|
314
406
|
}
|
|
315
407
|
if (err) {
|
|
316
408
|
self.logError('redis', "Failed to delete object: " + key + ": " + err);
|
|
317
409
|
}
|
|
318
|
-
else
|
|
410
|
+
else {
|
|
411
|
+
self.logDebug(9, "Delete complete: " + key);
|
|
412
|
+
|
|
413
|
+
// possibly "delete" from LRU cache as well
|
|
414
|
+
if (self.cache && !is_binary) {
|
|
415
|
+
// store 'empty' stub in cache
|
|
416
|
+
self.cache.set( key, Buffer.alloc(0), { date: 0 } );
|
|
417
|
+
}
|
|
418
|
+
}
|
|
319
419
|
|
|
320
420
|
callback(err);
|
|
321
421
|
} );
|
package/engines/RedisCluster.js
CHANGED
|
@@ -9,6 +9,7 @@ const Class = require("pixl-class");
|
|
|
9
9
|
const Component = require("pixl-server/component");
|
|
10
10
|
const Redis = require('ioredis');
|
|
11
11
|
const Tools = require("pixl-tools");
|
|
12
|
+
const Cache = require("pixl-cache");
|
|
12
13
|
|
|
13
14
|
module.exports = Class.create({
|
|
14
15
|
|
|
@@ -49,6 +50,22 @@ module.exports = Class.create({
|
|
|
49
50
|
|
|
50
51
|
this.keyTemplate = (r_config.keyTemplate || '').replace(/^\//, '').replace(/\/$/, '');
|
|
51
52
|
|
|
53
|
+
// optional LRU cache
|
|
54
|
+
this.cache = null;
|
|
55
|
+
var cache_opts = r_config.cache;
|
|
56
|
+
if (cache_opts && cache_opts.enabled) {
|
|
57
|
+
this.logDebug(3, "Setting up LRU cache", cache_opts);
|
|
58
|
+
this.cache = new Cache( Tools.copyHashRemoveKeys(cache_opts, { enabled: 1 }) );
|
|
59
|
+
this.cache.on('expire', function(item, reason) {
|
|
60
|
+
self.logDebug(9, "Expiring LRU cache object: " + item.key + " due to: " + reason, {
|
|
61
|
+
key: item.key,
|
|
62
|
+
reason: reason,
|
|
63
|
+
totalCount: self.cache.count,
|
|
64
|
+
totalBytes: self.cache.bytes
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
52
69
|
r_config.clusterOpts.clusterRetryStrategy = function(attempts) {
|
|
53
70
|
if (attempts > r_config.connectRetries) return false;
|
|
54
71
|
return attempts;
|
|
@@ -107,13 +124,14 @@ module.exports = Class.create({
|
|
|
107
124
|
// store key+value in Redis
|
|
108
125
|
var self = this;
|
|
109
126
|
key = this.prepKey(key);
|
|
127
|
+
var is_binary = this.storage.isBinaryKey(key);
|
|
110
128
|
|
|
111
|
-
if (
|
|
129
|
+
if (is_binary) {
|
|
112
130
|
this.logDebug(9, "Storing Redis Binary Object: " + key, '' + value.length + ' bytes');
|
|
113
131
|
}
|
|
114
132
|
else {
|
|
115
133
|
this.logDebug(9, "Storing Redis JSON Object: " + key, this.debugLevel(10) ? value : null);
|
|
116
|
-
value = JSON.stringify( value );
|
|
134
|
+
value = Buffer.from( JSON.stringify( value ) );
|
|
117
135
|
}
|
|
118
136
|
|
|
119
137
|
this.redis.set( key, value, function(err) {
|
|
@@ -123,6 +141,11 @@ module.exports = Class.create({
|
|
|
123
141
|
}
|
|
124
142
|
else self.logDebug(9, "Store complete: " + key);
|
|
125
143
|
|
|
144
|
+
// possibly cache in LRU
|
|
145
|
+
if (!err && self.cache && !is_binary) {
|
|
146
|
+
self.cache.set( key, value, { date: Tools.timeNow(true) } );
|
|
147
|
+
}
|
|
148
|
+
|
|
126
149
|
if (callback) callback(err);
|
|
127
150
|
} );
|
|
128
151
|
},
|
|
@@ -152,7 +175,30 @@ module.exports = Class.create({
|
|
|
152
175
|
// The Redis API has no way to head / ping an object.
|
|
153
176
|
// So, we have to do this the RAM-hard way...
|
|
154
177
|
|
|
155
|
-
|
|
178
|
+
// check cache first
|
|
179
|
+
if (this.cache && this.cache.has(key)) {
|
|
180
|
+
var item = this.cache.getMeta(key);
|
|
181
|
+
|
|
182
|
+
if (item.value.length == 0) {
|
|
183
|
+
process.nextTick( function() {
|
|
184
|
+
var err = new Error("Failed to head key: " + key + ": Not found");
|
|
185
|
+
err.code = "NoSuchKey";
|
|
186
|
+
callback( err );
|
|
187
|
+
} );
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
process.nextTick( function() {
|
|
191
|
+
self.logDebug(9, "Cached head complete: " + key);
|
|
192
|
+
callback( null, {
|
|
193
|
+
mod: item.date,
|
|
194
|
+
len: item.value.length
|
|
195
|
+
} );
|
|
196
|
+
} );
|
|
197
|
+
}
|
|
198
|
+
return;
|
|
199
|
+
} // cache
|
|
200
|
+
|
|
201
|
+
this.redis.getBuffer( key, function(err, data) {
|
|
156
202
|
if (err) {
|
|
157
203
|
// an actual error
|
|
158
204
|
err.message = "Failed to head key: " + key + ": " + err;
|
|
@@ -164,7 +210,12 @@ module.exports = Class.create({
|
|
|
164
210
|
// always use "NoSuchKey" in error code
|
|
165
211
|
var err = new Error("Failed to head key: " + key + ": Not found");
|
|
166
212
|
err.code = "NoSuchKey";
|
|
167
|
-
|
|
213
|
+
|
|
214
|
+
if (self.cache && !self.storage.isBinaryKey(key)) {
|
|
215
|
+
// store 'empty' stub in cache
|
|
216
|
+
self.cache.set( key, Buffer.alloc(0), { date: 0 } );
|
|
217
|
+
}
|
|
218
|
+
|
|
168
219
|
callback( err, null );
|
|
169
220
|
}
|
|
170
221
|
else {
|
|
@@ -177,44 +228,79 @@ module.exports = Class.create({
|
|
|
177
228
|
// fetch Redis value given key
|
|
178
229
|
var self = this;
|
|
179
230
|
key = this.prepKey(key);
|
|
231
|
+
var is_binary = this.storage.isBinaryKey(key);
|
|
180
232
|
|
|
181
233
|
this.logDebug(9, "Fetching Redis Object: " + key);
|
|
182
234
|
|
|
183
|
-
|
|
184
|
-
this.
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
self.logError('redis', ''+err);
|
|
190
|
-
callback( err, null );
|
|
191
|
-
}
|
|
192
|
-
else {
|
|
193
|
-
// record not found
|
|
194
|
-
// always use "NoSuchKey" in error code
|
|
235
|
+
// check cache first
|
|
236
|
+
if (this.cache && !is_binary && this.cache.has(key)) {
|
|
237
|
+
var item = this.cache.getMeta(key);
|
|
238
|
+
|
|
239
|
+
if (item.value.length == 0) {
|
|
240
|
+
process.nextTick( function() {
|
|
195
241
|
var err = new Error("Failed to fetch key: " + key + ": Not found");
|
|
196
242
|
err.code = "NoSuchKey";
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
}
|
|
243
|
+
callback( err );
|
|
244
|
+
} );
|
|
200
245
|
}
|
|
201
246
|
else {
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
else {
|
|
206
|
-
try { result = JSON.parse( result.toString() ); }
|
|
247
|
+
process.nextTick( function() {
|
|
248
|
+
var data = null;
|
|
249
|
+
try { data = JSON.parse( item.value.toString() ); }
|
|
207
250
|
catch (err) {
|
|
208
251
|
self.logError('redis', "Failed to parse JSON record: " + key + ": " + err);
|
|
209
252
|
callback( err, null );
|
|
210
253
|
return;
|
|
211
254
|
}
|
|
212
|
-
self.logDebug(9, "JSON fetch complete: " + key, self.debugLevel(10) ?
|
|
255
|
+
self.logDebug(9, "Cached JSON fetch complete: " + key, self.debugLevel(10) ? data : null);
|
|
256
|
+
|
|
257
|
+
callback( null, data );
|
|
258
|
+
} );
|
|
259
|
+
}
|
|
260
|
+
return;
|
|
261
|
+
} // cache
|
|
262
|
+
|
|
263
|
+
this.redis.getBuffer( key, function(err, result) {
|
|
264
|
+
if (err) {
|
|
265
|
+
// an actual error
|
|
266
|
+
err.message = "Failed to fetch key: " + key + ": " + err;
|
|
267
|
+
self.logError('redis', ''+err);
|
|
268
|
+
return callback( err, null );
|
|
269
|
+
}
|
|
270
|
+
else if (!result || !result.length) {
|
|
271
|
+
// record not found
|
|
272
|
+
// always use "NoSuchKey" in error code
|
|
273
|
+
var err = new Error("Failed to fetch key: " + key + ": Not found");
|
|
274
|
+
err.code = "NoSuchKey";
|
|
275
|
+
|
|
276
|
+
if (self.cache && !is_binary) {
|
|
277
|
+
// store 'empty' stub in cache
|
|
278
|
+
self.cache.set( key, Buffer.alloc(0), { date: 0 } );
|
|
213
279
|
}
|
|
214
280
|
|
|
215
|
-
callback(
|
|
281
|
+
return callback( err, null );
|
|
216
282
|
}
|
|
217
|
-
|
|
283
|
+
|
|
284
|
+
if (is_binary) {
|
|
285
|
+
self.logDebug(9, "Binary fetch complete: " + key, '' + result.length + ' bytes');
|
|
286
|
+
}
|
|
287
|
+
else {
|
|
288
|
+
// possibly cache in LRU
|
|
289
|
+
if (self.cache) {
|
|
290
|
+
self.cache.set( key, result, { date: Tools.timeNow(true) } );
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
try { result = JSON.parse( result.toString() ); }
|
|
294
|
+
catch (err) {
|
|
295
|
+
self.logError('redis', "Failed to parse JSON record: " + key + ": " + err);
|
|
296
|
+
callback( err, null );
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
self.logDebug(9, "JSON fetch complete: " + key, self.debugLevel(10) ? result : null);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
callback( null, result );
|
|
303
|
+
} ); // redis.getBuffer
|
|
218
304
|
},
|
|
219
305
|
|
|
220
306
|
getBuffer: function(key, callback) {
|
|
@@ -303,7 +389,6 @@ module.exports = Class.create({
|
|
|
303
389
|
end = buf.length ? buf.length - 1 : 0;
|
|
304
390
|
}
|
|
305
391
|
if (isNaN(start) || isNaN(end) || (start < 0) || (start >= buf.length) || (end < start) || (end >= buf.length)) {
|
|
306
|
-
download.destroy();
|
|
307
392
|
callback( new Error("Invalid byte range (" + start + '-' + end + ") for key: " + key + " (len: " + buf.length + ")"), null );
|
|
308
393
|
return;
|
|
309
394
|
}
|
|
@@ -318,6 +403,7 @@ module.exports = Class.create({
|
|
|
318
403
|
// delete Redis key given key
|
|
319
404
|
var self = this;
|
|
320
405
|
key = this.prepKey(key);
|
|
406
|
+
var is_binary = this.storage.isBinaryKey(key);
|
|
321
407
|
|
|
322
408
|
this.logDebug(9, "Deleting Redis Object: " + key);
|
|
323
409
|
|
|
@@ -325,11 +411,24 @@ module.exports = Class.create({
|
|
|
325
411
|
if (!err && !deleted) {
|
|
326
412
|
err = new Error("Failed to fetch key: " + key + ": Not found");
|
|
327
413
|
err.code = "NoSuchKey";
|
|
414
|
+
|
|
415
|
+
if (self.cache && !is_binary) {
|
|
416
|
+
// store 'empty' stub in cache
|
|
417
|
+
self.cache.set( key, Buffer.alloc(0), { date: 0 } );
|
|
418
|
+
}
|
|
328
419
|
}
|
|
329
420
|
if (err) {
|
|
330
421
|
self.logError('redis', "Failed to delete object: " + key + ": " + err);
|
|
331
422
|
}
|
|
332
|
-
else
|
|
423
|
+
else {
|
|
424
|
+
self.logDebug(9, "Delete complete: " + key);
|
|
425
|
+
|
|
426
|
+
// possibly "delete" from LRU cache as well
|
|
427
|
+
if (self.cache && !is_binary) {
|
|
428
|
+
// store 'empty' stub in cache
|
|
429
|
+
self.cache.set( key, Buffer.alloc(0), { date: 0 } );
|
|
430
|
+
}
|
|
431
|
+
}
|
|
333
432
|
|
|
334
433
|
callback(err);
|
|
335
434
|
} );
|
package/engines/S3.js
CHANGED
|
@@ -149,7 +149,7 @@ module.exports = Class.create({
|
|
|
149
149
|
}
|
|
150
150
|
else {
|
|
151
151
|
this.logDebug(9, "Storing S3 JSON Object: " + key, this.debugLevel(10) ? params.Body : null);
|
|
152
|
-
params.Body = this.pretty ? JSON.stringify( params.Body, null, "\t" ) : JSON.stringify( params.Body );
|
|
152
|
+
params.Body = Buffer.from( this.pretty ? JSON.stringify( params.Body, null, "\t" ) : JSON.stringify( params.Body ) );
|
|
153
153
|
params.ContentType = 'application/json';
|
|
154
154
|
}
|
|
155
155
|
|
|
@@ -257,13 +257,22 @@ module.exports = Class.create({
|
|
|
257
257
|
if (this.cache && this.cache.has(orig_key)) {
|
|
258
258
|
var item = this.cache.getMeta(orig_key);
|
|
259
259
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
260
|
+
if (item.value.length == 0) {
|
|
261
|
+
process.nextTick( function() {
|
|
262
|
+
var err = new Error("Failed to head key: " + key + ": Not found");
|
|
263
|
+
err.code = "NoSuchKey";
|
|
264
|
+
callback( err );
|
|
265
265
|
} );
|
|
266
|
-
}
|
|
266
|
+
}
|
|
267
|
+
else {
|
|
268
|
+
process.nextTick( function() {
|
|
269
|
+
self.logDebug(9, "Cached head complete: " + orig_key);
|
|
270
|
+
callback( null, {
|
|
271
|
+
mod: item.date,
|
|
272
|
+
len: item.value.length
|
|
273
|
+
} );
|
|
274
|
+
} );
|
|
275
|
+
}
|
|
267
276
|
return;
|
|
268
277
|
} // cache
|
|
269
278
|
|
|
@@ -288,6 +297,11 @@ module.exports = Class.create({
|
|
|
288
297
|
// always include "Not found" in error message
|
|
289
298
|
err = new Error("Failed to head key: " + key + ": Not found");
|
|
290
299
|
err.code = "NoSuchKey";
|
|
300
|
+
|
|
301
|
+
if (self.cache && !self.storage.isBinaryKey(orig_key)) {
|
|
302
|
+
// store 'empty' stub in cache
|
|
303
|
+
self.cache.set( orig_key, Buffer.alloc(0), { date: 0 } );
|
|
304
|
+
}
|
|
291
305
|
}
|
|
292
306
|
else if (err.name == 'SlowDown') {
|
|
293
307
|
// special behavior for SlowDown errors
|
|
@@ -315,19 +329,29 @@ module.exports = Class.create({
|
|
|
315
329
|
|
|
316
330
|
// check cache first
|
|
317
331
|
if (this.cache && !is_binary && this.cache.has(orig_key)) {
|
|
318
|
-
var
|
|
332
|
+
var item = this.cache.getMeta(orig_key);
|
|
319
333
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
callback(
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
334
|
+
if (item.value.length == 0) {
|
|
335
|
+
process.nextTick( function() {
|
|
336
|
+
var err = new Error("Failed to fetch key: " + key + ": Not found");
|
|
337
|
+
err.code = "NoSuchKey";
|
|
338
|
+
callback( err );
|
|
339
|
+
} );
|
|
340
|
+
}
|
|
341
|
+
else {
|
|
342
|
+
process.nextTick( function() {
|
|
343
|
+
var data = null;
|
|
344
|
+
try { data = JSON.parse( item.value.toString() ); }
|
|
345
|
+
catch (e) {
|
|
346
|
+
self.logError('file', "Failed to parse JSON record: " + orig_key + ": " + e);
|
|
347
|
+
callback( e, null );
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
self.logDebug(9, "Cached JSON fetch complete: " + orig_key, self.debugLevel(10) ? data : null);
|
|
351
|
+
|
|
352
|
+
callback( null, data );
|
|
353
|
+
} );
|
|
354
|
+
}
|
|
331
355
|
return;
|
|
332
356
|
} // cache
|
|
333
357
|
|
|
@@ -350,14 +374,12 @@ module.exports = Class.create({
|
|
|
350
374
|
self.logDebug(9, "Binary fetch complete: " + key, '' + body.length + ' bytes');
|
|
351
375
|
}
|
|
352
376
|
else {
|
|
353
|
-
body = body.toString();
|
|
354
|
-
|
|
355
377
|
// possibly cache in LRU
|
|
356
378
|
if (self.cache) {
|
|
357
379
|
self.cache.set( orig_key, body, { date: Tools.timeNow(true) } );
|
|
358
380
|
}
|
|
359
381
|
|
|
360
|
-
try { body = JSON.parse( body ); }
|
|
382
|
+
try { body = JSON.parse( body.toString() ); }
|
|
361
383
|
catch (e) {
|
|
362
384
|
self.logError('s3', "Failed to parse JSON record: " + key + ": " + e);
|
|
363
385
|
callback( e, null );
|
|
@@ -378,6 +400,11 @@ module.exports = Class.create({
|
|
|
378
400
|
// always include "Not found" in error message
|
|
379
401
|
err = new Error("Failed to fetch key: " + key + ": Not found");
|
|
380
402
|
err.code = "NoSuchKey";
|
|
403
|
+
|
|
404
|
+
if (self.cache && !is_binary) {
|
|
405
|
+
// store 'empty' stub in cache
|
|
406
|
+
self.cache.set( orig_key, Buffer.alloc(0), { date: 0 } );
|
|
407
|
+
}
|
|
381
408
|
}
|
|
382
409
|
else if (err.name == 'SlowDown') {
|
|
383
410
|
// special behavior for SlowDown errors
|
|
@@ -577,9 +604,10 @@ module.exports = Class.create({
|
|
|
577
604
|
self.logDebug(9, "Delete complete: " + key);
|
|
578
605
|
self.storage.emit('billing', 's3_delete', 1);
|
|
579
606
|
|
|
580
|
-
// possibly delete from LRU cache as well
|
|
581
|
-
if (self.cache && self.
|
|
582
|
-
|
|
607
|
+
// possibly "delete" from LRU cache as well
|
|
608
|
+
if (self.cache && !self.storage.isBinaryKey(orig_key)) {
|
|
609
|
+
// store 'empty' stub in cache
|
|
610
|
+
self.cache.set( orig_key, Buffer.alloc(0), { date: 0 } );
|
|
583
611
|
}
|
|
584
612
|
|
|
585
613
|
if (callback) process.nextTick( function() { callback(null, data); } );
|