pixl-server-storage 3.2.4 → 3.2.5
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/docs/API.md +1 -1
- package/engines/Filesystem.js +1 -0
- package/engines/Hybrid.js +2 -0
- package/engines/SQLite.js +92 -35
- package/indexer.js +1 -1
- package/package.json +1 -1
- package/test/config.json +7 -1
- package/test/data/sqlite.db +0 -0
package/docs/API.md
CHANGED
|
@@ -280,7 +280,7 @@ storage.head( 'test1', function(err, data) {
|
|
|
280
280
|
} );
|
|
281
281
|
```
|
|
282
282
|
|
|
283
|
-
Please note that as of this writing, the `Couchbase`, `Redis` and `RedisCluster` engines have no native head API, so the `head()` method has to load the entire record. It does return the record size in to the `len` property, but
|
|
283
|
+
Please note that as of this writing, the `Couchbase`, `Redis` and `RedisCluster` engines have no native head API, so the `head()` method has to load the entire record. It does return the record size in to the `len` property, but the `mod` property will always be set to `1`.
|
|
284
284
|
|
|
285
285
|
## headMulti
|
|
286
286
|
|
package/engines/Filesystem.js
CHANGED
|
@@ -689,6 +689,7 @@ module.exports = Class.create({
|
|
|
689
689
|
// run daily maintenance - delete old temp files
|
|
690
690
|
var self = this;
|
|
691
691
|
var now = Tools.timeNow(true);
|
|
692
|
+
this.logDebug(3, "Running filesystem maintenance");
|
|
692
693
|
|
|
693
694
|
fs.readdir( this.tempDir, function(err, files) {
|
|
694
695
|
if (err) return callback();
|
package/engines/Hybrid.js
CHANGED
|
@@ -107,6 +107,8 @@ module.exports = Class.create({
|
|
|
107
107
|
|
|
108
108
|
runMaintenance: function(callback) {
|
|
109
109
|
// run daily maintenance
|
|
110
|
+
this.logDebug(3, "Running hybrid maintenance");
|
|
111
|
+
|
|
110
112
|
async.series([
|
|
111
113
|
this.binaryEngine.runMaintenance.bind(this.binaryEngine),
|
|
112
114
|
this.docEngine.runMaintenance.bind(this.docEngine)
|
package/engines/SQLite.js
CHANGED
|
@@ -13,6 +13,7 @@ const Component = require("pixl-server/component");
|
|
|
13
13
|
const SQLite3 = require('sqlite3');
|
|
14
14
|
const Tools = require("pixl-tools");
|
|
15
15
|
const Perf = require("pixl-perf");
|
|
16
|
+
const Cache = require("pixl-cache");
|
|
16
17
|
const async = require('async');
|
|
17
18
|
const noop = function() {};
|
|
18
19
|
|
|
@@ -46,7 +47,12 @@ module.exports = Class.create({
|
|
|
46
47
|
this.keyTemplate = (sql_config.keyTemplate || '').replace(/^\//, '').replace(/\/$/, '');
|
|
47
48
|
|
|
48
49
|
this.baseDir = sql_config.base_dir || process.cwd();
|
|
49
|
-
this.commands = {
|
|
50
|
+
this.commands = {
|
|
51
|
+
get: 'SELECT value FROM items WHERE key = $key LIMIT 1',
|
|
52
|
+
head: 'SELECT modified, length(value) FROM items WHERE key = $key LIMIT 1',
|
|
53
|
+
put: 'INSERT INTO items VALUES($key, $value, $now) ON CONFLICT (key) DO UPDATE SET value = $value, modified = $now WHERE key = $key',
|
|
54
|
+
delete: 'DELETE FROM items WHERE key = $key'
|
|
55
|
+
};
|
|
50
56
|
|
|
51
57
|
// create initial data dir if necessary
|
|
52
58
|
try {
|
|
@@ -58,6 +64,22 @@ module.exports = Class.create({
|
|
|
58
64
|
throw new Error(msg);
|
|
59
65
|
}
|
|
60
66
|
|
|
67
|
+
// optional LRU cache
|
|
68
|
+
this.cache = null;
|
|
69
|
+
var cache_opts = this.config.get('cache');
|
|
70
|
+
if (cache_opts && cache_opts.enabled) {
|
|
71
|
+
this.logDebug(3, "Setting up LRU cache", cache_opts);
|
|
72
|
+
this.cache = new Cache( Tools.copyHashRemoveKeys(cache_opts, { enabled: 1 }) );
|
|
73
|
+
this.cache.on('expire', function(item, reason) {
|
|
74
|
+
self.logDebug(9, "Expiring LRU cache object: " + item.key + " due to: " + reason, {
|
|
75
|
+
key: item.key,
|
|
76
|
+
reason: reason,
|
|
77
|
+
totalCount: self.cache.count,
|
|
78
|
+
totalBytes: self.cache.bytes
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
61
83
|
var db_file = Path.join( this.baseDir, sql_config.filename );
|
|
62
84
|
this.logDebug(3, "Opening database file: " + db_file);
|
|
63
85
|
|
|
@@ -80,18 +102,6 @@ module.exports = Class.create({
|
|
|
80
102
|
// create our table if necessary
|
|
81
103
|
self.db.run( 'CREATE TABLE IF NOT EXISTS items( key TEXT PRIMARY KEY, value BLOB, modified INTEGER )', callback );
|
|
82
104
|
},
|
|
83
|
-
function(callback) {
|
|
84
|
-
self.commands.get = self.db.prepare( 'SELECT value FROM items WHERE key = $key LIMIT 1', callback );
|
|
85
|
-
},
|
|
86
|
-
function(callback) {
|
|
87
|
-
self.commands.head = self.db.prepare( 'SELECT modified, length(value) FROM items WHERE key = $key LIMIT 1', callback );
|
|
88
|
-
},
|
|
89
|
-
function(callback) {
|
|
90
|
-
self.commands.put = self.db.prepare( 'INSERT INTO items VALUES($key, $value, $now) ON CONFLICT (key) DO UPDATE SET value = $value, modified = $now WHERE key = $key', callback );
|
|
91
|
-
},
|
|
92
|
-
function(callback) {
|
|
93
|
-
self.commands.delete = self.db.prepare( 'DELETE FROM items WHERE key = $key', callback );
|
|
94
|
-
}
|
|
95
105
|
],
|
|
96
106
|
function(err) {
|
|
97
107
|
if (err) {
|
|
@@ -128,7 +138,9 @@ module.exports = Class.create({
|
|
|
128
138
|
var now = Tools.timeNow(true);
|
|
129
139
|
key = this.prepKey(key);
|
|
130
140
|
|
|
131
|
-
|
|
141
|
+
var is_binary = this.storage.isBinaryKey(key);
|
|
142
|
+
|
|
143
|
+
if (is_binary) {
|
|
132
144
|
this.logDebug(9, "Storing SQLite Binary Object: " + key, '' + value.length + ' bytes');
|
|
133
145
|
}
|
|
134
146
|
else {
|
|
@@ -136,13 +148,18 @@ module.exports = Class.create({
|
|
|
136
148
|
value = Buffer.from( JSON.stringify( value ) );
|
|
137
149
|
}
|
|
138
150
|
|
|
139
|
-
this.commands.put
|
|
151
|
+
this.db.run( this.commands.put, { $key: key, $value: value, $now: now }, function(err) {
|
|
140
152
|
if (err) {
|
|
141
153
|
err.message = "Failed to store object: " + key + ": " + err;
|
|
142
154
|
self.logError('sqlite', '' + err);
|
|
143
155
|
}
|
|
144
156
|
else self.logDebug(9, "Store complete: " + key);
|
|
145
157
|
|
|
158
|
+
// possibly cache in LRU
|
|
159
|
+
if (self.cache && !is_binary) {
|
|
160
|
+
self.cache.set( key, value, { date: Tools.timeNow(true) } );
|
|
161
|
+
}
|
|
162
|
+
|
|
146
163
|
if (callback) callback(err);
|
|
147
164
|
} ); // put
|
|
148
165
|
},
|
|
@@ -169,7 +186,21 @@ module.exports = Class.create({
|
|
|
169
186
|
var self = this;
|
|
170
187
|
key = this.prepKey(key);
|
|
171
188
|
|
|
172
|
-
|
|
189
|
+
// check cache first
|
|
190
|
+
if (this.cache && this.cache.has(key)) {
|
|
191
|
+
var item = this.cache.getMeta(key);
|
|
192
|
+
|
|
193
|
+
process.nextTick( function() {
|
|
194
|
+
self.logDebug(9, "Cached head complete: " + key);
|
|
195
|
+
callback( null, {
|
|
196
|
+
mod: item.date,
|
|
197
|
+
len: item.value.length
|
|
198
|
+
} );
|
|
199
|
+
} );
|
|
200
|
+
return;
|
|
201
|
+
} // cache
|
|
202
|
+
|
|
203
|
+
this.db.get( this.commands.head, { $key: key }, function(err, row) {
|
|
173
204
|
if (err) {
|
|
174
205
|
// an actual error
|
|
175
206
|
err.message = "Failed to head key: " + key + ": " + err;
|
|
@@ -194,9 +225,29 @@ module.exports = Class.create({
|
|
|
194
225
|
var self = this;
|
|
195
226
|
key = this.prepKey(key);
|
|
196
227
|
|
|
228
|
+
var is_binary = this.storage.isBinaryKey(key);
|
|
229
|
+
|
|
230
|
+
// check cache first
|
|
231
|
+
if (this.cache && !is_binary && this.cache.has(key)) {
|
|
232
|
+
var data = this.cache.get(key);
|
|
233
|
+
|
|
234
|
+
process.nextTick( function() {
|
|
235
|
+
try { data = JSON.parse( data.toString() ); }
|
|
236
|
+
catch (e) {
|
|
237
|
+
self.logError('file', "Failed to parse JSON record: " + key + ": " + e);
|
|
238
|
+
callback( e, null );
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
self.logDebug(9, "Cached JSON fetch complete: " + key, self.debugLevel(10) ? data : null);
|
|
242
|
+
|
|
243
|
+
callback( null, data );
|
|
244
|
+
} );
|
|
245
|
+
return;
|
|
246
|
+
} // cache
|
|
247
|
+
|
|
197
248
|
this.logDebug(9, "Fetching SQLite Object: " + key);
|
|
198
249
|
|
|
199
|
-
this.
|
|
250
|
+
this.db.get( this.commands.get, { $key: key }, function(err, row) {
|
|
200
251
|
if (err) {
|
|
201
252
|
// an actual error
|
|
202
253
|
err.message = "Failed to fetch key: " + key + ": " + err;
|
|
@@ -212,11 +263,16 @@ module.exports = Class.create({
|
|
|
212
263
|
}
|
|
213
264
|
else {
|
|
214
265
|
// success
|
|
215
|
-
if (
|
|
266
|
+
if (is_binary) {
|
|
216
267
|
self.logDebug(9, "Binary fetch complete: " + key, '' + row.value.length + ' bytes');
|
|
217
268
|
callback( null, row.value );
|
|
218
269
|
}
|
|
219
270
|
else {
|
|
271
|
+
// possibly cache in LRU
|
|
272
|
+
if (self.cache) {
|
|
273
|
+
self.cache.set( key, row.value, { date: Tools.timeNow(true) } );
|
|
274
|
+
}
|
|
275
|
+
|
|
220
276
|
var json = null;
|
|
221
277
|
try { json = JSON.parse( row.value.toString() ); }
|
|
222
278
|
catch (err) {
|
|
@@ -238,7 +294,7 @@ module.exports = Class.create({
|
|
|
238
294
|
|
|
239
295
|
this.logDebug(9, "Fetching SQLite Object: " + key);
|
|
240
296
|
|
|
241
|
-
this.
|
|
297
|
+
this.db.get( this.commands.get, { $key: key }, function(err, row) {
|
|
242
298
|
if (err) {
|
|
243
299
|
// an actual error
|
|
244
300
|
err.message = "Failed to fetch key: " + key + ": " + err;
|
|
@@ -334,7 +390,7 @@ module.exports = Class.create({
|
|
|
334
390
|
|
|
335
391
|
this.logDebug(9, "Deleting SQLite Object: " + key);
|
|
336
392
|
|
|
337
|
-
this.commands.delete
|
|
393
|
+
this.db.run( this.commands.delete, { $key: key }, function(err) {
|
|
338
394
|
// In sqlite3 callbacks `this` is special, and contains `changes`
|
|
339
395
|
if (!err && !this.changes) {
|
|
340
396
|
err = new Error("Not found");
|
|
@@ -346,6 +402,12 @@ module.exports = Class.create({
|
|
|
346
402
|
return callback(err);
|
|
347
403
|
}
|
|
348
404
|
self.logDebug(9, "Delete complete: " + key);
|
|
405
|
+
|
|
406
|
+
// possibly delete from LRU cache as well
|
|
407
|
+
if (self.cache && self.cache.has(key)) {
|
|
408
|
+
self.cache.delete(key);
|
|
409
|
+
}
|
|
410
|
+
|
|
349
411
|
callback();
|
|
350
412
|
}); // delete
|
|
351
413
|
},
|
|
@@ -354,6 +416,8 @@ module.exports = Class.create({
|
|
|
354
416
|
// run daily maintenance
|
|
355
417
|
// config.backups: { enabled, dir, filename, compress?, keep? }
|
|
356
418
|
var self = this;
|
|
419
|
+
this.logDebug(3, "Running SQLite maintenance");
|
|
420
|
+
|
|
357
421
|
var backups = this.config.get('backups') || null;
|
|
358
422
|
if (!backups || !backups.enabled || !backups.dir || !backups.filename) return callback();
|
|
359
423
|
|
|
@@ -506,21 +570,14 @@ module.exports = Class.create({
|
|
|
506
570
|
this.logDebug(2, "Shutting down SQLite");
|
|
507
571
|
|
|
508
572
|
if (this.db) {
|
|
509
|
-
//
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
else self.logDebug(3, "Shutdown complete");
|
|
518
|
-
callback();
|
|
519
|
-
} );
|
|
520
|
-
self.db = null;
|
|
521
|
-
self.commands = null;
|
|
522
|
-
}
|
|
523
|
-
); // eachSeries
|
|
573
|
+
// close db
|
|
574
|
+
self.db.close( function(err) {
|
|
575
|
+
if (err) self.logError('sqlite', "Failed to shutdown database cleanly: " + err);
|
|
576
|
+
else self.logDebug(3, "Shutdown complete");
|
|
577
|
+
callback();
|
|
578
|
+
} );
|
|
579
|
+
self.db = null;
|
|
580
|
+
self.commands = null;
|
|
524
581
|
}
|
|
525
582
|
}
|
|
526
583
|
|
package/indexer.js
CHANGED
|
@@ -129,7 +129,7 @@ module.exports = Class.create({
|
|
|
129
129
|
_indexRecord: function(id, record, config, callback) {
|
|
130
130
|
// index record (internal)
|
|
131
131
|
var self = this;
|
|
132
|
-
this.logDebug(8, "Indexing record: " + id
|
|
132
|
+
this.logDebug(8, "Indexing record: " + id);
|
|
133
133
|
|
|
134
134
|
var state = {
|
|
135
135
|
id: id,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pixl-server-storage",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.5",
|
|
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/test/config.json
CHANGED
|
Binary file
|