pixl-server-storage 3.1.12 → 3.1.13
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 +1 -1
- package/docs/API.md +31 -1
- package/docs/Transactions.md +1 -0
- package/engines/Couchbase.js +31 -0
- package/engines/Filesystem.js +27 -0
- package/engines/Hybrid.js +10 -0
- package/engines/Redis.js +30 -0
- package/engines/RedisCluster.js +30 -0
- package/engines/S3.js +43 -0
- package/engines/SQLite.js +29 -0
- package/hash.js +78 -2
- package/package.json +1 -1
- package/storage.js +24 -0
- package/test/test-hash.js +14 -3
- package/test/test-main.js +27 -0
- package/transaction.js +1 -0
package/README.md
CHANGED
|
@@ -426,7 +426,7 @@ If you want to use [Amazon S3](http://aws.amazon.com/s3/) as a backing store, co
|
|
|
426
426
|
"region": "us-west-1",
|
|
427
427
|
"credentials": {
|
|
428
428
|
"accessKeyId": "YOUR_AMAZON_ACCESS_KEY",
|
|
429
|
-
"secretAccessKey": "YOUR_AMAZON_SECRET_KEY"
|
|
429
|
+
"secretAccessKey": "YOUR_AMAZON_SECRET_KEY"
|
|
430
430
|
}
|
|
431
431
|
},
|
|
432
432
|
"S3": {
|
package/docs/API.md
CHANGED
|
@@ -180,13 +180,27 @@ storage.getMulti( ['test1', 'test2', 'test3'], function(err, values) {
|
|
|
180
180
|
|
|
181
181
|
Note that if *any* of the records fail, the entire operation fails, and the first error is passed to your callback.
|
|
182
182
|
|
|
183
|
+
## getBuffer
|
|
184
|
+
|
|
185
|
+
```javascript
|
|
186
|
+
storage.getBuffer( KEY, CALLBACK );
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
The `getBuffer()` method retrieves a [Buffer](https://nodejs.org/api/buffer.html) to a given record's data, regardless if the key points to a JSON record or a binary record. Your callback function is passed an error if one occurred, and the buffer value for the given record. Example:
|
|
190
|
+
|
|
191
|
+
```javascript
|
|
192
|
+
storage.getBuffer( 'test1', function(err, buf) {
|
|
193
|
+
if (err) throw err;
|
|
194
|
+
} );
|
|
195
|
+
```
|
|
196
|
+
|
|
183
197
|
## getStream
|
|
184
198
|
|
|
185
199
|
```javascript
|
|
186
200
|
storage.getStream( KEY, CALLBACK );
|
|
187
201
|
```
|
|
188
202
|
|
|
189
|
-
The `getStream()` method retrieves a [readable stream](https://nodejs.org/api/stream.html#
|
|
203
|
+
The `getStream()` method retrieves a [readable stream](https://nodejs.org/api/stream.html#readable-streams) to a given record's data, so it can be read or piped to a writable stream. This is for very large records, so nothing is loaded into memory. Example of spooling to a local file:
|
|
190
204
|
|
|
191
205
|
```javascript
|
|
192
206
|
var fs = require('fs');
|
|
@@ -982,6 +996,22 @@ storage.hashGetMulti( 'users', ['bsanders', 'hclinton', 'dtrump'], function(err,
|
|
|
982
996
|
} );
|
|
983
997
|
```
|
|
984
998
|
|
|
999
|
+
## hashUpdate
|
|
1000
|
+
|
|
1001
|
+
```javascript
|
|
1002
|
+
storage.hashUpdate( PATH, KEY, UPDATES, CALLBACK );
|
|
1003
|
+
```
|
|
1004
|
+
|
|
1005
|
+
The `hashUpdate()` method updates an existing key/value pair in a hash. The `PATH` specifies the main storage path of the hash, and the hash key itself is identified by `KEY`, which should be a string. The `UPDATES` must be an object, but it can contain sparse keys. Furthermore, it can contain dot or slash delimited paths, to update inner nested keys. The updates are essentially applied atop the exiting record, merging and replacing (overwriting) where appropriate. Example:
|
|
1006
|
+
|
|
1007
|
+
```javascript
|
|
1008
|
+
storage.hashUpdate( 'users', 'bsanders', { age: 81 }, function(err) {
|
|
1009
|
+
if (err) throw err;
|
|
1010
|
+
} );
|
|
1011
|
+
```
|
|
1012
|
+
|
|
1013
|
+
This would update Bernie's age to 81 without affecting any of the other properties in his user record.
|
|
1014
|
+
|
|
985
1015
|
## hashEach
|
|
986
1016
|
|
|
987
1017
|
```javascript
|
package/docs/Transactions.md
CHANGED
|
@@ -199,6 +199,7 @@ When transactions are enabled, many compound storage methods (those that execute
|
|
|
199
199
|
- **Hash Methods**
|
|
200
200
|
- [hashCreate()](API.md#hashcreate)
|
|
201
201
|
- [hashPut()](API.md#hashput)
|
|
202
|
+
- [hashUpdate()](API.md#hashupdate)
|
|
202
203
|
- [hashPutMulti()](API.md#hashputmulti)
|
|
203
204
|
- [hashCopy()](API.md#hashcopy)
|
|
204
205
|
- [hashRename()](API.md#hashrename)
|
package/engines/Couchbase.js
CHANGED
|
@@ -203,6 +203,37 @@ module.exports = Class.create({
|
|
|
203
203
|
} );
|
|
204
204
|
},
|
|
205
205
|
|
|
206
|
+
getBuffer: function(key, callback) {
|
|
207
|
+
// fetch Couchbase buffer given key
|
|
208
|
+
var self = this;
|
|
209
|
+
key = this.prepKey(key);
|
|
210
|
+
|
|
211
|
+
this.logDebug(9, "Fetching Couchbase Object: " + key);
|
|
212
|
+
|
|
213
|
+
this.bucket.get( key, function(err, result) {
|
|
214
|
+
if (!result) {
|
|
215
|
+
if (err && (err.code != CouchbaseAPI.errors.keyNotFound)) {
|
|
216
|
+
// some other error
|
|
217
|
+
err.message = "Failed to fetch key: " + key + ": " + err.message;
|
|
218
|
+
self.logError('couchbase', err.message);
|
|
219
|
+
callback( err, null );
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
// record not found
|
|
223
|
+
// always use "NoSuchKey" in error code
|
|
224
|
+
var err = new Error("Failed to fetch key: " + key + ": Not found");
|
|
225
|
+
err.code = "NoSuchKey";
|
|
226
|
+
callback( err, null );
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
var body = result.value;
|
|
231
|
+
self.logDebug(9, "Binary fetch complete: " + key, '' + body.length + ' bytes');
|
|
232
|
+
callback( null, body );
|
|
233
|
+
}
|
|
234
|
+
} );
|
|
235
|
+
},
|
|
236
|
+
|
|
206
237
|
getStream: function(key, callback) {
|
|
207
238
|
// get readable stream to record value given key
|
|
208
239
|
var self = this;
|
package/engines/Filesystem.js
CHANGED
|
@@ -477,6 +477,33 @@ module.exports = Class.create({
|
|
|
477
477
|
} );
|
|
478
478
|
},
|
|
479
479
|
|
|
480
|
+
getBuffer: function(key, callback) {
|
|
481
|
+
// fetch buffer given key
|
|
482
|
+
var self = this;
|
|
483
|
+
var file = this.getFilePath(key);
|
|
484
|
+
|
|
485
|
+
this.logDebug(9, "Fetching Object: " + key, file);
|
|
486
|
+
|
|
487
|
+
fs.readFile(file, function (err, data) {
|
|
488
|
+
if (err) {
|
|
489
|
+
if (err.message.match(/ENOENT/)) {
|
|
490
|
+
err.message = "File not found";
|
|
491
|
+
err.code = "NoSuchKey";
|
|
492
|
+
}
|
|
493
|
+
else {
|
|
494
|
+
// log fs errors that aren't simple missing files (i.e. I/O errors)
|
|
495
|
+
self.logError('file', "Failed to read file: " + key + ": " + file + ": " + err.message);
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
err.message = "Failed to fetch key: " + key + ": " + err.message;
|
|
499
|
+
return callback( err, null );
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
self.logDebug(9, "Binary fetch complete: " + key, '' + data.length + ' bytes');
|
|
503
|
+
callback( null, data );
|
|
504
|
+
} );
|
|
505
|
+
},
|
|
506
|
+
|
|
480
507
|
getStream: function(key, callback) {
|
|
481
508
|
// get readable stream to record value given key
|
|
482
509
|
var self = this;
|
package/engines/Hybrid.js
CHANGED
|
@@ -73,6 +73,16 @@ module.exports = Class.create({
|
|
|
73
73
|
}
|
|
74
74
|
},
|
|
75
75
|
|
|
76
|
+
getBuffer: function(key, callback) {
|
|
77
|
+
// fetch hybrid buffer given key
|
|
78
|
+
if (this.storage.isBinaryKey(key)) {
|
|
79
|
+
this.binaryEngine.getBuffer( key, callback );
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
this.docEngine.getBuffer( key, callback );
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
|
|
76
86
|
getStream: function(key, callback) {
|
|
77
87
|
// get readable stream to record value given key
|
|
78
88
|
// streams are binary only!
|
package/engines/Redis.js
CHANGED
|
@@ -203,6 +203,36 @@ module.exports = Class.create({
|
|
|
203
203
|
} );
|
|
204
204
|
},
|
|
205
205
|
|
|
206
|
+
getBuffer: function(key, callback) {
|
|
207
|
+
// get buffer to record value given key
|
|
208
|
+
var self = this;
|
|
209
|
+
key = this.prepKey(key);
|
|
210
|
+
|
|
211
|
+
this.logDebug(9, "Fetching Redis Object: " + key);
|
|
212
|
+
|
|
213
|
+
this.redis.get( key, function(err, result) {
|
|
214
|
+
if (!result) {
|
|
215
|
+
if (err) {
|
|
216
|
+
// an actual error
|
|
217
|
+
err.message = "Failed to fetch key: " + key + ": " + err;
|
|
218
|
+
self.logError('redis', ''+err);
|
|
219
|
+
callback( err, null );
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
// record not found
|
|
223
|
+
// always use "NoSuchKey" in error code
|
|
224
|
+
var err = new Error("Failed to fetch key: " + key + ": Not found");
|
|
225
|
+
err.code = "NoSuchKey";
|
|
226
|
+
callback( err, null );
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
self.logDebug(9, "Binary fetch complete: " + key, '' + result.length + ' bytes');
|
|
231
|
+
callback( null, result );
|
|
232
|
+
}
|
|
233
|
+
} );
|
|
234
|
+
},
|
|
235
|
+
|
|
206
236
|
getStream: function(key, callback) {
|
|
207
237
|
// get readable stream to record value given key
|
|
208
238
|
var self = this;
|
package/engines/RedisCluster.js
CHANGED
|
@@ -220,6 +220,36 @@ module.exports = Class.create({
|
|
|
220
220
|
} );
|
|
221
221
|
},
|
|
222
222
|
|
|
223
|
+
getBuffer: function(key, callback) {
|
|
224
|
+
// fetch Redis buffer given key
|
|
225
|
+
var self = this;
|
|
226
|
+
key = this.prepKey(key);
|
|
227
|
+
|
|
228
|
+
this.logDebug(9, "Fetching Redis Object: " + key);
|
|
229
|
+
|
|
230
|
+
this.redis.getBuffer( key, function(err, result) {
|
|
231
|
+
if (!result) {
|
|
232
|
+
if (err) {
|
|
233
|
+
// an actual error
|
|
234
|
+
err.message = "Failed to fetch key: " + key + ": " + err;
|
|
235
|
+
self.logError('redis', ''+err);
|
|
236
|
+
callback( err, null );
|
|
237
|
+
}
|
|
238
|
+
else {
|
|
239
|
+
// record not found
|
|
240
|
+
// always use "NoSuchKey" in error code
|
|
241
|
+
var err = new Error("Failed to fetch key: " + key + ": Not found");
|
|
242
|
+
err.code = "NoSuchKey";
|
|
243
|
+
callback( err, null );
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
self.logDebug(9, "Binary fetch complete: " + key, '' + result.length + ' bytes');
|
|
248
|
+
callback( null, result );
|
|
249
|
+
}
|
|
250
|
+
} );
|
|
251
|
+
},
|
|
252
|
+
|
|
223
253
|
getStream: function(key, callback) {
|
|
224
254
|
// get readable stream to record value given key
|
|
225
255
|
var self = this;
|
package/engines/S3.js
CHANGED
|
@@ -355,6 +355,49 @@ module.exports = Class.create({
|
|
|
355
355
|
} );
|
|
356
356
|
},
|
|
357
357
|
|
|
358
|
+
getBuffer: function(key, callback) {
|
|
359
|
+
// fetch s3 buffer given key
|
|
360
|
+
var self = this;
|
|
361
|
+
var orig_key = key;
|
|
362
|
+
key = this.prepKey(key);
|
|
363
|
+
|
|
364
|
+
this.logDebug(9, "Fetching S3 Object: " + key);
|
|
365
|
+
|
|
366
|
+
var params = Tools.copyHash( this.s3Params );
|
|
367
|
+
params.Key = this.extKey(key, orig_key);
|
|
368
|
+
|
|
369
|
+
this.s3.send( new S3.GetObjectCommand(params) )
|
|
370
|
+
.then( function(data) {
|
|
371
|
+
// stream to buffer
|
|
372
|
+
streamToBuffer( data.Body, function (err, body) {
|
|
373
|
+
if (err) {
|
|
374
|
+
self.logError('s3', "Failed to fetch key: " + key + ": " + (err.message || err), err);
|
|
375
|
+
return callback(err);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
self.logDebug(9, "Binary fetch complete: " + key, '' + body.length + ' bytes');
|
|
379
|
+
|
|
380
|
+
callback( null, body, {
|
|
381
|
+
mod: Math.floor((new Date(data.LastModified)).getTime() / 1000),
|
|
382
|
+
len: data.ContentLength
|
|
383
|
+
} );
|
|
384
|
+
} ); // streamToBuffer
|
|
385
|
+
} )
|
|
386
|
+
.catch( function(err) {
|
|
387
|
+
if ((err.name == 'NoSuchKey') || (err.name == 'NotFound') || (err.code == 'NoSuchKey') || (err.code == 'NotFound')) {
|
|
388
|
+
// key not found, special case, don't log an error
|
|
389
|
+
// always include "Not found" in error message
|
|
390
|
+
err = new Error("Failed to fetch key: " + key + ": Not found");
|
|
391
|
+
err.code = "NoSuchKey";
|
|
392
|
+
}
|
|
393
|
+
else {
|
|
394
|
+
// some other error
|
|
395
|
+
self.logError('s3', "Failed to fetch key: " + key + ": " + (err.message || err), err);
|
|
396
|
+
}
|
|
397
|
+
process.nextTick( function() { callback( err ); } );
|
|
398
|
+
} );
|
|
399
|
+
},
|
|
400
|
+
|
|
358
401
|
getStream: function(key, callback) {
|
|
359
402
|
// get readable stream to record value given key
|
|
360
403
|
var self = this;
|
package/engines/SQLite.js
CHANGED
|
@@ -227,6 +227,35 @@ module.exports = Class.create({
|
|
|
227
227
|
}); // get
|
|
228
228
|
},
|
|
229
229
|
|
|
230
|
+
getBuffer: function(key, callback) {
|
|
231
|
+
// fetch SQLite buffer given key
|
|
232
|
+
var self = this;
|
|
233
|
+
key = this.prepKey(key);
|
|
234
|
+
|
|
235
|
+
this.logDebug(9, "Fetching SQLite Object: " + key);
|
|
236
|
+
|
|
237
|
+
this.commands.get.get({ $key: key }, function(err, row) {
|
|
238
|
+
if (err) {
|
|
239
|
+
// an actual error
|
|
240
|
+
err.message = "Failed to fetch key: " + key + ": " + err;
|
|
241
|
+
self.logError('sqlite', '' + err);
|
|
242
|
+
callback(err);
|
|
243
|
+
}
|
|
244
|
+
else if (!row) {
|
|
245
|
+
// record not found
|
|
246
|
+
// always use "NoSuchKey" in error code
|
|
247
|
+
var err = new Error("Failed to fetch key: " + key + ": Not found");
|
|
248
|
+
err.code = "NoSuchKey";
|
|
249
|
+
callback( err, null );
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
// success
|
|
253
|
+
self.logDebug(9, "Binary fetch complete: " + key, '' + row.value.length + ' bytes');
|
|
254
|
+
callback( null, row.value );
|
|
255
|
+
}
|
|
256
|
+
}); // get
|
|
257
|
+
},
|
|
258
|
+
|
|
230
259
|
getStream: function(key, callback) {
|
|
231
260
|
// get readable stream to record value given key
|
|
232
261
|
var self = this;
|
package/hash.js
CHANGED
|
@@ -311,7 +311,7 @@ module.exports = Class.create({
|
|
|
311
311
|
|
|
312
312
|
if (!(state.hkey in data.items)) {
|
|
313
313
|
// key not found
|
|
314
|
-
var err = new Error("Failed to fetch key: " + state.hkey + ": Not found");
|
|
314
|
+
var err = new Error("Failed to fetch hash key: " + state.path + ": " + state.hkey + ": Not found");
|
|
315
315
|
err.code = "NoSuchKey";
|
|
316
316
|
return callback(err);
|
|
317
317
|
}
|
|
@@ -325,7 +325,7 @@ module.exports = Class.create({
|
|
|
325
325
|
// fetch multiple hash records at once, given array of keys
|
|
326
326
|
// callback is provided an array of values in matching order to keys
|
|
327
327
|
var self = this;
|
|
328
|
-
var records =
|
|
328
|
+
var records = Object.create(null);
|
|
329
329
|
|
|
330
330
|
async.eachLimit(hkeys, this.concurrency,
|
|
331
331
|
function(hkey, callback) {
|
|
@@ -350,6 +350,82 @@ module.exports = Class.create({
|
|
|
350
350
|
);
|
|
351
351
|
},
|
|
352
352
|
|
|
353
|
+
hashUpdate: function(path, hkey, updates, callback) {
|
|
354
|
+
// update existing key/value pair in hash table
|
|
355
|
+
var self = this;
|
|
356
|
+
if (!path) return callback(new Error("Hash path must be a valid string."));
|
|
357
|
+
if (!hkey) return callback(new Error("Hash key must be a valid string."));
|
|
358
|
+
if (!Tools.isaHash(updates)) return callback(new Error("Hash updates must be an object."));
|
|
359
|
+
|
|
360
|
+
this.logDebug(9, "Updating hash key: " + path + ": " + hkey, this.debugLevel(10) ? updates : null);
|
|
361
|
+
|
|
362
|
+
// lock hash for this
|
|
363
|
+
this._hashLock(path, true, function() {
|
|
364
|
+
|
|
365
|
+
// load header, do not create new
|
|
366
|
+
self._hashLoad(path, false, function(err, hash) {
|
|
367
|
+
if (err) {
|
|
368
|
+
self._hashUnlock(path);
|
|
369
|
+
return callback(err);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
var state = {
|
|
373
|
+
path: path,
|
|
374
|
+
data_path: path + '/data',
|
|
375
|
+
hkey: ''+hkey,
|
|
376
|
+
updates: updates,
|
|
377
|
+
hash: hash,
|
|
378
|
+
index_depth: -1,
|
|
379
|
+
key_digest: Tools.digestHex(hkey, 'md5')
|
|
380
|
+
};
|
|
381
|
+
|
|
382
|
+
self._hashUpdateKey(state, function(err) {
|
|
383
|
+
// done
|
|
384
|
+
self._hashUnlock(path);
|
|
385
|
+
return callback(err);
|
|
386
|
+
}); // _hashPutKey
|
|
387
|
+
}); // load
|
|
388
|
+
}); // lock
|
|
389
|
+
},
|
|
390
|
+
|
|
391
|
+
_hashUpdateKey: function(state, callback) {
|
|
392
|
+
// internal hash update method, store at one hashing level
|
|
393
|
+
// recurse for deeper indexes
|
|
394
|
+
var self = this;
|
|
395
|
+
|
|
396
|
+
self.get(state.data_path, function(err, data) {
|
|
397
|
+
if (err) data = { type: 'hash_page', length: 0, items: {} };
|
|
398
|
+
|
|
399
|
+
if (data.type == 'hash_index') {
|
|
400
|
+
// recurse for deeper level
|
|
401
|
+
state.index_depth++;
|
|
402
|
+
state.data_path += '/' + state.key_digest.substring(state.index_depth, state.index_depth + 1);
|
|
403
|
+
return self._hashUpdateKey(state, callback);
|
|
404
|
+
}
|
|
405
|
+
else {
|
|
406
|
+
// got page, our key should be at this level
|
|
407
|
+
data.items = Tools.copyHashRemoveProto( data.items );
|
|
408
|
+
|
|
409
|
+
if (!(state.hkey in data.items)) {
|
|
410
|
+
// key not found
|
|
411
|
+
var err = new Error("Failed to fetch hash key: " + state.path + ": " + state.hkey + ": Not found");
|
|
412
|
+
err.code = "NoSuchKey";
|
|
413
|
+
return callback(err);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
var hvalue = data.items[state.hkey];
|
|
417
|
+
|
|
418
|
+
// apply updates directly to forehead
|
|
419
|
+
for (var key in state.updates) {
|
|
420
|
+
Tools.setPath( hvalue, key, state.updates[key] );
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// save page
|
|
424
|
+
self.put(state.data_path, data, callback);
|
|
425
|
+
} // hash_page
|
|
426
|
+
}); // get
|
|
427
|
+
},
|
|
428
|
+
|
|
353
429
|
hashEachPage: function(path, iterator, callback) {
|
|
354
430
|
// call user iterator for each populated hash page, data only
|
|
355
431
|
// iterator will be passed page items hash object
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pixl-server-storage",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.13",
|
|
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",
|
package/storage.js
CHANGED
|
@@ -376,6 +376,30 @@ module.exports = Class.create({
|
|
|
376
376
|
} );
|
|
377
377
|
},
|
|
378
378
|
|
|
379
|
+
getBuffer: function(key, callback) {
|
|
380
|
+
// fetch buffer given key
|
|
381
|
+
var self = this;
|
|
382
|
+
if (!this.started) return callback( new Error("Storage has not completed startup.") );
|
|
383
|
+
|
|
384
|
+
key = this.normalizeKey( key );
|
|
385
|
+
|
|
386
|
+
// invoke engine and track perf
|
|
387
|
+
var pf = this.perf.begin('get');
|
|
388
|
+
|
|
389
|
+
this.engine.getBuffer( key, function(err, value, info) {
|
|
390
|
+
// get complete
|
|
391
|
+
var elapsed = pf.end();
|
|
392
|
+
if (err) return callback(err);
|
|
393
|
+
|
|
394
|
+
self.logTransaction('get', key, {
|
|
395
|
+
elapsed_ms: elapsed
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
callback(null, value, info);
|
|
399
|
+
if (!err) self.emit('get', key, value, info);
|
|
400
|
+
} );
|
|
401
|
+
},
|
|
402
|
+
|
|
379
403
|
getStream: function(key, callback) {
|
|
380
404
|
// fetch value via stream pipe
|
|
381
405
|
var self = this;
|
package/test/test-hash.js
CHANGED
|
@@ -38,7 +38,18 @@ module.exports = {
|
|
|
38
38
|
var self = this;
|
|
39
39
|
test.expect(2);
|
|
40
40
|
|
|
41
|
-
this.storage.hashPut( 'hash1', 'key1', { foo: 'bar', number:
|
|
41
|
+
this.storage.hashPut( 'hash1', 'key1', { foo: 'bar', number: 122 }, function(err) {
|
|
42
|
+
test.ok( !err, "No error storing into hash: " + err );
|
|
43
|
+
test.ok( Object.keys(self.storage.locks).length == 0, "No more locks leftover in storage" );
|
|
44
|
+
test.done();
|
|
45
|
+
} );
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
function hashUpdate1(test) {
|
|
49
|
+
var self = this;
|
|
50
|
+
test.expect(2);
|
|
51
|
+
|
|
52
|
+
this.storage.hashUpdate( 'hash1', 'key1', { number: 123 }, function(err) {
|
|
42
53
|
test.ok( !err, "No error storing into hash: " + err );
|
|
43
54
|
test.ok( Object.keys(self.storage.locks).length == 0, "No more locks leftover in storage" );
|
|
44
55
|
test.done();
|
|
@@ -52,8 +63,8 @@ module.exports = {
|
|
|
52
63
|
this.storage.hashGet( 'hash1', 'key1', function(err, item) {
|
|
53
64
|
test.ok( !err, "No error fetching hash key: " + err );
|
|
54
65
|
test.ok( !!item, "Item is real" );
|
|
55
|
-
test.ok( item.number == 123, "
|
|
56
|
-
test.ok( item.foo == 'bar', "
|
|
66
|
+
test.ok( item.number == 123, "Hash item number matches" );
|
|
67
|
+
test.ok( item.foo == 'bar', "Hash item value matches" );
|
|
57
68
|
|
|
58
69
|
// check internals
|
|
59
70
|
self.storage.get( 'hash1', function(err, hash) {
|
package/test/test-main.js
CHANGED
|
@@ -374,6 +374,33 @@ module.exports = {
|
|
|
374
374
|
} );
|
|
375
375
|
},
|
|
376
376
|
|
|
377
|
+
function testBuffer(test) {
|
|
378
|
+
test.expect(8);
|
|
379
|
+
var self = this;
|
|
380
|
+
var key = 'buftest';
|
|
381
|
+
var value = { buf: "test" };
|
|
382
|
+
|
|
383
|
+
this.storage.put( key, value, function(err) {
|
|
384
|
+
test.ok( !err, "No error creating buftest: " + err );
|
|
385
|
+
|
|
386
|
+
self.storage.getBuffer( key, function(err, data) {
|
|
387
|
+
test.ok( !err, "No error fetching binary: " + err );
|
|
388
|
+
test.ok( !!data, "Data is true" );
|
|
389
|
+
test.ok( typeof(data) == 'object', "Data is an object (not a string)" );
|
|
390
|
+
test.ok( data.length > 0, "Data length is non-zero" );
|
|
391
|
+
|
|
392
|
+
var json = JSON.parse( data.toString() );
|
|
393
|
+
test.ok( !!json, "Parsed JSON object from buftest" );
|
|
394
|
+
test.ok( json.buf === "test", "Correct data inside JSON buftest" );
|
|
395
|
+
|
|
396
|
+
self.storage.delete( key, function(err) {
|
|
397
|
+
test.ok( !err, "No error deleting buftest key: " + err );
|
|
398
|
+
test.done();
|
|
399
|
+
} );
|
|
400
|
+
} );
|
|
401
|
+
} );
|
|
402
|
+
},
|
|
403
|
+
|
|
377
404
|
function testStream(test) {
|
|
378
405
|
test.expect(14);
|
|
379
406
|
var self = this;
|