pixl-server-storage 3.0.21 → 3.0.24
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/S3.js +21 -5
- package/index_types/Date.js +5 -0
- package/index_types/Number.js +5 -0
- package/indexer.js +61 -10
- package/package.json +1 -1
- package/storage.js +1 -0
- package/transaction.js +5 -2
package/engines/S3.js
CHANGED
|
@@ -275,7 +275,7 @@ module.exports = Class.create({
|
|
|
275
275
|
if (done) return; else done = true;
|
|
276
276
|
|
|
277
277
|
if (err) {
|
|
278
|
-
if (err.code == 'NoSuchKey') {
|
|
278
|
+
if ((err.code == 'NoSuchKey') || (err.code == 'NotFound')) {
|
|
279
279
|
// key not found, special case, don't log an error
|
|
280
280
|
// always include "Not found" in error message
|
|
281
281
|
err = new Error("Failed to fetch key: " + key + ": Not found");
|
|
@@ -346,9 +346,17 @@ module.exports = Class.create({
|
|
|
346
346
|
if (done) return; else done = true;
|
|
347
347
|
|
|
348
348
|
if (err) {
|
|
349
|
-
if ((err.code
|
|
350
|
-
|
|
349
|
+
if ((err.code == 'NoSuchKey') || (err.code == 'NotFound')) {
|
|
350
|
+
// key not found, special case, don't log an error
|
|
351
|
+
// always include "Not found" in error message
|
|
352
|
+
err = new Error("Failed to fetch key: " + key + ": Not found");
|
|
353
|
+
err.code = "NoSuchKey";
|
|
354
|
+
}
|
|
355
|
+
else {
|
|
356
|
+
// some other error
|
|
357
|
+
self.logError('s3', "Failed to fetch key: " + key + ": " + (err.message || err), err);
|
|
351
358
|
}
|
|
359
|
+
|
|
352
360
|
download.destroy();
|
|
353
361
|
callback( err, null );
|
|
354
362
|
return;
|
|
@@ -397,9 +405,17 @@ module.exports = Class.create({
|
|
|
397
405
|
if (done) return; else done = true;
|
|
398
406
|
|
|
399
407
|
if (err) {
|
|
400
|
-
if ((err.code
|
|
401
|
-
|
|
408
|
+
if ((err.code == 'NoSuchKey') || (err.code == 'NotFound')) {
|
|
409
|
+
// key not found, special case, don't log an error
|
|
410
|
+
// always include "Not found" in error message
|
|
411
|
+
err = new Error("Failed to fetch key: " + key + ": Not found");
|
|
412
|
+
err.code = "NoSuchKey";
|
|
402
413
|
}
|
|
414
|
+
else {
|
|
415
|
+
// some other error
|
|
416
|
+
self.logError('s3', "Failed to fetch key: " + key + ": " + (err.message || err), err);
|
|
417
|
+
}
|
|
418
|
+
|
|
403
419
|
download.destroy();
|
|
404
420
|
callback( err, null );
|
|
405
421
|
return;
|
package/index_types/Date.js
CHANGED
|
@@ -159,7 +159,9 @@ module.exports = Class.create({
|
|
|
159
159
|
}
|
|
160
160
|
|
|
161
161
|
// load index summary for list of all populated dates
|
|
162
|
+
var dspf = state.perf.begin('date_summary');
|
|
162
163
|
this.get( sum_path, function(err, summary) {
|
|
164
|
+
dspf.end();
|
|
163
165
|
if (err || !summary) {
|
|
164
166
|
summary = { id: query.def.id, values: {} };
|
|
165
167
|
}
|
|
@@ -190,6 +192,7 @@ module.exports = Class.create({
|
|
|
190
192
|
}
|
|
191
193
|
|
|
192
194
|
// now perform OR search for all applicable words
|
|
195
|
+
var drpf = state.perf.begin('date_range');
|
|
193
196
|
async.eachLimit( words, self.concurrency,
|
|
194
197
|
function(word, callback) {
|
|
195
198
|
// for each word, iterate over record ids
|
|
@@ -203,6 +206,8 @@ module.exports = Class.create({
|
|
|
203
206
|
},
|
|
204
207
|
function(err) {
|
|
205
208
|
// all done, perform final merge
|
|
209
|
+
drpf.end();
|
|
210
|
+
state.perf.count('date_buckets', words.length);
|
|
206
211
|
if (err) return callback(err);
|
|
207
212
|
self.mergeIndex( record_ids, temp_results, state.first ? 'or' : state.mode );
|
|
208
213
|
state.first = false;
|
package/index_types/Number.js
CHANGED
|
@@ -119,7 +119,9 @@ module.exports = Class.create({
|
|
|
119
119
|
}
|
|
120
120
|
|
|
121
121
|
// load index summary for list of all populated numbers
|
|
122
|
+
var nspf = state.perf.begin('number_summary');
|
|
122
123
|
this.get( sum_path, function(err, summary) {
|
|
124
|
+
nspf.end();
|
|
123
125
|
if (err || !summary) {
|
|
124
126
|
summary = { id: query.def.id, values: {} };
|
|
125
127
|
}
|
|
@@ -150,6 +152,7 @@ module.exports = Class.create({
|
|
|
150
152
|
}
|
|
151
153
|
|
|
152
154
|
// now perform OR search for all applicable words
|
|
155
|
+
var nrpf = state.perf.begin('number_range');
|
|
153
156
|
async.eachLimit( words, self.concurrency,
|
|
154
157
|
function(word, callback) {
|
|
155
158
|
// for each word, iterate over record ids
|
|
@@ -163,6 +166,8 @@ module.exports = Class.create({
|
|
|
163
166
|
},
|
|
164
167
|
function(err) {
|
|
165
168
|
// all done, perform final merge
|
|
169
|
+
nrpf.end();
|
|
170
|
+
state.perf.count('number_buckets', words.length);
|
|
166
171
|
if (err) return callback(err);
|
|
167
172
|
self.mergeIndex( record_ids, temp_results, state.first ? 'or' : state.mode );
|
|
168
173
|
state.first = false;
|
package/indexer.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
var async = require('async');
|
|
6
6
|
var Class = require("pixl-class");
|
|
7
7
|
var Tools = require("pixl-tools");
|
|
8
|
+
var Perf = require("pixl-perf");
|
|
8
9
|
var nearley = require("nearley");
|
|
9
10
|
var pxql_grammar = require("./pxql.js");
|
|
10
11
|
var stemmer = require('porter-stemmer').stemmer;
|
|
@@ -1220,15 +1221,16 @@ module.exports = Class.create({
|
|
|
1220
1221
|
var path = config.base_path;
|
|
1221
1222
|
var pf = this.perf.begin('search');
|
|
1222
1223
|
|
|
1224
|
+
var orig_query = query;
|
|
1225
|
+
if (typeof(query) == 'object') query = Tools.copyHash(query, true);
|
|
1226
|
+
|
|
1223
1227
|
this.shareLock( 'C|'+path, true, function(err, lock) {
|
|
1224
1228
|
// got shared lock
|
|
1225
1229
|
self._searchRecords( query, config, function(err, results, state) {
|
|
1226
1230
|
// search complete
|
|
1227
|
-
var elapsed = pf.end();
|
|
1228
|
-
|
|
1229
1231
|
if (!err) self.logTransaction('search', path, {
|
|
1230
|
-
query:
|
|
1231
|
-
|
|
1232
|
+
query: orig_query,
|
|
1233
|
+
perf: state.perf ? state.perf.metrics() : {},
|
|
1232
1234
|
results: (self.logEventTypes.search || self.logEventTypes.all) ? Tools.numKeys(results) : 0
|
|
1233
1235
|
});
|
|
1234
1236
|
|
|
@@ -1251,9 +1253,15 @@ module.exports = Class.create({
|
|
|
1251
1253
|
if (query == '*') {
|
|
1252
1254
|
// fetch all records
|
|
1253
1255
|
this.logDebug(8, "Fetching all records: " + config.base_path);
|
|
1256
|
+
var apf = new Perf();
|
|
1257
|
+
apf.begin();
|
|
1258
|
+
apf.begin('all');
|
|
1259
|
+
|
|
1254
1260
|
return this.hashGetAll( config.base_path + '/_id', function(err, results) {
|
|
1255
1261
|
// ignore error, just return empty hash
|
|
1256
|
-
|
|
1262
|
+
apf.end('all');
|
|
1263
|
+
apf.end();
|
|
1264
|
+
callback( null, results || {}, { perf: apf } );
|
|
1257
1265
|
} );
|
|
1258
1266
|
}
|
|
1259
1267
|
else if (query.match(/^\([\s\S]+\)$/)) {
|
|
@@ -1282,6 +1290,12 @@ module.exports = Class.create({
|
|
|
1282
1290
|
state.record_ids = Object.create(null);
|
|
1283
1291
|
state.first = true;
|
|
1284
1292
|
|
|
1293
|
+
// track detailed perf of search operations
|
|
1294
|
+
if (!state.perf) {
|
|
1295
|
+
state.perf = new Perf();
|
|
1296
|
+
state.perf.begin();
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1285
1299
|
// first, split criteria into subs (sub-queries),
|
|
1286
1300
|
// stds (standard queries) and negs (negative queries)
|
|
1287
1301
|
var subs = [], stds = [], negs = [];
|
|
@@ -1302,11 +1316,14 @@ module.exports = Class.create({
|
|
|
1302
1316
|
}
|
|
1303
1317
|
|
|
1304
1318
|
// stds need to be weighed and sorted by weight ascending
|
|
1319
|
+
var wpf = state.perf.begin('weigh');
|
|
1305
1320
|
async.eachLimit( (query.mode == 'and') ? stds : [], this.concurrency,
|
|
1306
1321
|
function(crit, callback) {
|
|
1307
1322
|
self.weighCriterion(crit, config, callback);
|
|
1308
1323
|
},
|
|
1309
1324
|
function(err) {
|
|
1325
|
+
wpf.end();
|
|
1326
|
+
|
|
1310
1327
|
// sort stds by weight ascending (only needed in AND mode)
|
|
1311
1328
|
if (query.mode == 'and') {
|
|
1312
1329
|
stds = stds.sort( function(a, b) { return a.weight - b.weight; } );
|
|
@@ -1317,9 +1334,12 @@ module.exports = Class.create({
|
|
|
1317
1334
|
var tasks = [].concat( subs, stds, negs );
|
|
1318
1335
|
async.eachSeries( tasks,
|
|
1319
1336
|
function(task, callback) {
|
|
1337
|
+
task.perf = state.perf;
|
|
1338
|
+
|
|
1320
1339
|
if (task.criteria) {
|
|
1321
|
-
// sub-query
|
|
1340
|
+
// sub-query
|
|
1322
1341
|
self._searchRecords( task, config, function(err, records) {
|
|
1342
|
+
state.perf.count('subs', 1);
|
|
1323
1343
|
self.mergeIndex( state.record_ids, records, state.first ? 'or' : state.mode );
|
|
1324
1344
|
state.first = false;
|
|
1325
1345
|
callback();
|
|
@@ -1333,13 +1353,31 @@ module.exports = Class.create({
|
|
|
1333
1353
|
// custom index type, e.g. date, time, number
|
|
1334
1354
|
var func = 'searchIndex_' + task.def.type;
|
|
1335
1355
|
if (!self[func]) return callback( new Error("Unknown index type: " + task.def.type) );
|
|
1336
|
-
|
|
1356
|
+
|
|
1357
|
+
var cpf = state.perf.begin('search_' + task.def.id + '_' + task.def.type);
|
|
1358
|
+
self[func]( task, state, function(err) {
|
|
1359
|
+
cpf.end();
|
|
1360
|
+
state.perf.count(task.def.type + 's', 1);
|
|
1361
|
+
callback(err);
|
|
1362
|
+
} );
|
|
1337
1363
|
}
|
|
1338
1364
|
else if (task.literal) {
|
|
1339
|
-
|
|
1365
|
+
// literal multi-word phrase
|
|
1366
|
+
var spf = state.perf.begin('search_' + task.def.id + '_literal');
|
|
1367
|
+
self.searchWordIndexLiteral(task, state, function(err) {
|
|
1368
|
+
spf.end();
|
|
1369
|
+
state.perf.count('literals', 1);
|
|
1370
|
+
callback(err);
|
|
1371
|
+
});
|
|
1340
1372
|
}
|
|
1341
1373
|
else {
|
|
1342
|
-
|
|
1374
|
+
// single word search
|
|
1375
|
+
var spf = state.perf.begin('search_' + task.def.id + '_word');
|
|
1376
|
+
self.searchWordIndex(task, state, function(err) {
|
|
1377
|
+
spf.end();
|
|
1378
|
+
state.perf.count('words', 1);
|
|
1379
|
+
callback(err);
|
|
1380
|
+
});
|
|
1343
1381
|
}
|
|
1344
1382
|
},
|
|
1345
1383
|
function(err) {
|
|
@@ -1347,6 +1385,7 @@ module.exports = Class.create({
|
|
|
1347
1385
|
if (err) {
|
|
1348
1386
|
self.logError('index', "Index search failed: " + err);
|
|
1349
1387
|
state.record_ids = {};
|
|
1388
|
+
state.err = err;
|
|
1350
1389
|
}
|
|
1351
1390
|
self.logDebug(10, "Search complete", state.record_ids);
|
|
1352
1391
|
callback(null, state.record_ids, Tools.copyHashRemoveKeys(state, { config:1, record_ids:1, first:1 }));
|
|
@@ -1357,7 +1396,7 @@ module.exports = Class.create({
|
|
|
1357
1396
|
},
|
|
1358
1397
|
|
|
1359
1398
|
searchWordIndex: function(query, state, callback) {
|
|
1360
|
-
// run one
|
|
1399
|
+
// run one word query (single word against one index)
|
|
1361
1400
|
var self = this;
|
|
1362
1401
|
var config = state.config;
|
|
1363
1402
|
var def = query.def;
|
|
@@ -1385,6 +1424,7 @@ module.exports = Class.create({
|
|
|
1385
1424
|
// then it would probably be faster to apply the logic using _data getMulti (a.k.a row scan).
|
|
1386
1425
|
// Otherwise, perform a normal hash merge (which has to read every hash page).
|
|
1387
1426
|
var hash_page_size = config.hash_page_size || 1000;
|
|
1427
|
+
var wpf = state.perf.begin('word_' + query.word);
|
|
1388
1428
|
|
|
1389
1429
|
if ((mode == 'and') && query.weight && (query.weight / hash_page_size > num_cur_items)) {
|
|
1390
1430
|
this.logDebug(10, "Performing row scan on " + num_cur_items + " items", query);
|
|
@@ -1394,7 +1434,9 @@ module.exports = Class.create({
|
|
|
1394
1434
|
return config.base_path + '/_data/' + record_id;
|
|
1395
1435
|
} );
|
|
1396
1436
|
|
|
1437
|
+
var rspf = state.perf.begin('row_scan');
|
|
1397
1438
|
this.getMulti( data_paths, function(err, datas) {
|
|
1439
|
+
rspf.end();
|
|
1398
1440
|
if (err) return callback(err);
|
|
1399
1441
|
|
|
1400
1442
|
datas.forEach( function(data, idx) {
|
|
@@ -1404,11 +1446,15 @@ module.exports = Class.create({
|
|
|
1404
1446
|
}
|
|
1405
1447
|
} );
|
|
1406
1448
|
|
|
1449
|
+
state.perf.count('rows_scanned', datas.length);
|
|
1450
|
+
wpf.end();
|
|
1407
1451
|
callback();
|
|
1408
1452
|
} ); // getMulti
|
|
1409
1453
|
} // row scan
|
|
1410
1454
|
else {
|
|
1411
1455
|
this.logDebug(10, "Performing '" + mode + "' hash merge on " + num_cur_items + " items", query);
|
|
1456
|
+
|
|
1457
|
+
var hmpf = state.perf.begin('hash_merge');
|
|
1412
1458
|
this.hashEachPage( path,
|
|
1413
1459
|
function(items, callback) {
|
|
1414
1460
|
switch (mode) {
|
|
@@ -1430,9 +1476,12 @@ module.exports = Class.create({
|
|
|
1430
1476
|
}
|
|
1431
1477
|
break;
|
|
1432
1478
|
}
|
|
1479
|
+
state.perf.count('hash_pages', 1);
|
|
1433
1480
|
callback();
|
|
1434
1481
|
},
|
|
1435
1482
|
function(err) {
|
|
1483
|
+
hmpf.end();
|
|
1484
|
+
wpf.end();
|
|
1436
1485
|
if (mode == 'and') state.record_ids = new_items;
|
|
1437
1486
|
callback(err);
|
|
1438
1487
|
}
|
|
@@ -1460,6 +1509,7 @@ module.exports = Class.create({
|
|
|
1460
1509
|
function(word, callback) {
|
|
1461
1510
|
// for each word, iterate over record ids
|
|
1462
1511
|
var keepers = Object.create(null);
|
|
1512
|
+
var wpf = state.perf.begin('literal_' + word);
|
|
1463
1513
|
|
|
1464
1514
|
self.hashEachSync( path_prefix + word,
|
|
1465
1515
|
function(record_id, raw_value) {
|
|
@@ -1495,6 +1545,7 @@ module.exports = Class.create({
|
|
|
1495
1545
|
else keepers[record_id] = 1;
|
|
1496
1546
|
},
|
|
1497
1547
|
function(err) {
|
|
1548
|
+
wpf.end();
|
|
1498
1549
|
// If in a subsequent word pass, make sure all temp_results
|
|
1499
1550
|
// ids are still matched in the latest word
|
|
1500
1551
|
if (temp_idx > 0) self.mergeIndex( temp_results, keepers, 'and' );
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pixl-server-storage",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.24",
|
|
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
package/transaction.js
CHANGED
|
@@ -638,14 +638,17 @@ module.exports = Class.create({
|
|
|
638
638
|
}
|
|
639
639
|
|
|
640
640
|
// complete, unlock and remove transaction from memory
|
|
641
|
-
self.unlock( 'C|'+path );
|
|
642
|
-
self._transUnlock(path);
|
|
643
641
|
self.transactions[path].keys = {}; // release memory
|
|
644
642
|
self.transactions[path].values = {}; // release memory
|
|
645
643
|
self.transactions[path].queue = []; // release memory
|
|
646
644
|
delete self.transactions[path];
|
|
647
645
|
|
|
648
646
|
self.logDebug(3, "Transaction rollback complete: " + trans.id, { path: path });
|
|
647
|
+
|
|
648
|
+
// unlock at the VERY end, as a new transaction may be waiting on the same path
|
|
649
|
+
self.unlock( 'C|'+path );
|
|
650
|
+
self._transUnlock(path);
|
|
651
|
+
|
|
649
652
|
callback();
|
|
650
653
|
}); // fs.unlink
|
|
651
654
|
} // done with log
|