Browse Source

added retry support for ES requests that timeout

pull/782/head
Stephen Hess 8 years ago
parent
commit
4ed4b8b357
  1. 2
      app.js
  2. 37
      controller/search.js
  3. 2
      package.json
  4. 36
      routes/v1.js

2
app.js

@ -23,7 +23,7 @@ var legacy = require('./routes/legacy');
legacy.addRoutes(app, peliasConfig.api); legacy.addRoutes(app, peliasConfig.api);
var v1 = require('./routes/v1'); var v1 = require('./routes/v1');
v1.addRoutes(app, peliasConfig.api); v1.addRoutes(app, peliasConfig);
/** ----------------------- error middleware ----------------------- **/ /** ----------------------- error middleware ----------------------- **/

37
controller/search.js

@ -1,8 +1,11 @@
'use strict';
var _ = require('lodash'); var _ = require('lodash');
var service = { search: require('../service/search') }; var service = { search: require('../service/search') };
var logger = require('pelias-logger').get('api'); var logger = require('pelias-logger').get('api');
var logging = require( '../helper/logging' ); var logging = require( '../helper/logging' );
const retry = require('retry');
function setup( config, esclient, query ){ function setup( config, esclient, query ){
function controller( req, res, next ){ function controller( req, res, next ){
@ -33,6 +36,23 @@ function setup( config, esclient, query ){
return next(); return next();
} }
logger.debug( '[ES req]', cmd );
// options for retry
// default number of retries to 3 (seems reasonable)
// factor of 1 means that each retry attempt will esclient requestTimeout
const operationOptions = {
retries: _.get(esclient, 'transport.maxRetries', 3),
factor: 1,
minTimeout: _.get(esclient, 'transport.requestTimeout')
};
// prepend the config timeout since the total number of attempts is maxRetries+1
const timeouts = [operationOptions.minTimeout].concat(retry.timeouts(operationOptions));
// setup a new operation
const operation = retry.operation(operationOptions);
// elasticsearch command // elasticsearch command
var cmd = { var cmd = {
index: config.indexName, index: config.indexName,
@ -40,10 +60,15 @@ function setup( config, esclient, query ){
body: renderedQuery.body body: renderedQuery.body
}; };
logger.debug( '[ES req]', cmd ); operation.attempt((currentAttempt) => {
// query elasticsearch // query elasticsearch
service.search( esclient, cmd, function( err, docs, meta ){ service.search( esclient, cmd, function( err, docs, meta ){
// returns true if the operation should be attempted again
// (handles bookkeeping of maxRetries)
if (operation.retry(err)) {
logger.info('request timed out, retrying');
return;
}
// error handler // error handler
if( err ){ if( err ){
@ -55,6 +80,12 @@ function setup( config, esclient, query ){
} }
// set response data // set response data
else { else {
// log that a retry was successful
// most requests succeed on first attempt so this declutters log files
if (currentAttempt > 1) {
logger.info(`succeeded on retry ${currentAttempt-1}`);
}
res.data = docs; res.data = docs;
res.meta = meta || {}; res.meta = meta || {};
// store the query_type for subsequent middleware // store the query_type for subsequent middleware
@ -67,6 +98,8 @@ function setup( config, esclient, query ){
next(); next();
}); });
});
} }
return controller; return controller;

2
package.json

@ -58,6 +58,8 @@
"pelias-model": "4.4.0", "pelias-model": "4.4.0",
"pelias-query": "8.11.0", "pelias-query": "8.11.0",
"pelias-text-analyzer": "1.7.0", "pelias-text-analyzer": "1.7.0",
"performance-now": "^2.0.0",
"retry": "^0.10.1",
"stats-lite": "2.0.3", "stats-lite": "2.0.3",
"through2": "^2.0.3" "through2": "^2.0.3"
}, },

36
routes/v1.js

@ -68,22 +68,22 @@ function addRoutes(app, peliasConfig) {
var routers = { var routers = {
index: createRouter([ index: createRouter([
controllers.mdToHTML(peliasConfig, './public/apiDoc.md') controllers.mdToHTML(peliasConfig.api, './public/apiDoc.md')
]), ]),
attribution: createRouter([ attribution: createRouter([
controllers.mdToHTML(peliasConfig, './public/attribution.md') controllers.mdToHTML(peliasConfig.api, './public/attribution.md')
]), ]),
search: createRouter([ search: createRouter([
sanitizers.search.middleware, sanitizers.search.middleware,
middleware.calcSize(), middleware.calcSize(),
// 3rd parameter is which query module to use, use fallback/geodisambiguation // 3rd parameter is which query module to use, use fallback/geodisambiguation
// first, then use original search strategy if first query didn't return anything // first, then use original search strategy if first query didn't return anything
controllers.search(peliasConfig, esclient, queries.libpostal), controllers.search(peliasConfig.api, esclient, queries.libpostal),
sanitizers.search_fallback.middleware, sanitizers.search_fallback.middleware,
controllers.search(peliasConfig, esclient, queries.fallback_to_old_prod), controllers.search(peliasConfig.api, esclient, queries.fallback_to_old_prod),
postProc.trimByGranularity(), postProc.trimByGranularity(),
postProc.distances('focus.point.'), postProc.distances('focus.point.'),
postProc.confidenceScores(peliasConfig), postProc.confidenceScores(peliasConfig.api),
postProc.confidenceScoresFallback(), postProc.confidenceScoresFallback(),
postProc.dedupe(), postProc.dedupe(),
postProc.accuracy(), postProc.accuracy(),
@ -92,16 +92,16 @@ function addRoutes(app, peliasConfig) {
postProc.parseBoundingBox(), postProc.parseBoundingBox(),
postProc.normalizeParentIds(), postProc.normalizeParentIds(),
postProc.assignLabels(), postProc.assignLabels(),
postProc.geocodeJSON(peliasConfig, base), postProc.geocodeJSON(peliasConfig.api, base),
postProc.sendJSON postProc.sendJSON
]), ]),
structured: createRouter([ structured: createRouter([
sanitizers.structured_geocoding.middleware, sanitizers.structured_geocoding.middleware,
middleware.calcSize(), middleware.calcSize(),
controllers.search(peliasConfig, esclient, queries.structured_geocoding), controllers.search(peliasConfig.api, esclient, queries.structured_geocoding),
postProc.trimByGranularityStructured(), postProc.trimByGranularityStructured(),
postProc.distances('focus.point.'), postProc.distances('focus.point.'),
postProc.confidenceScores(peliasConfig), postProc.confidenceScores(peliasConfig.api),
postProc.confidenceScoresFallback(), postProc.confidenceScoresFallback(),
postProc.dedupe(), postProc.dedupe(),
postProc.accuracy(), postProc.accuracy(),
@ -110,14 +110,14 @@ function addRoutes(app, peliasConfig) {
postProc.parseBoundingBox(), postProc.parseBoundingBox(),
postProc.normalizeParentIds(), postProc.normalizeParentIds(),
postProc.assignLabels(), postProc.assignLabels(),
postProc.geocodeJSON(peliasConfig, base), postProc.geocodeJSON(peliasConfig.api, base),
postProc.sendJSON postProc.sendJSON
]), ]),
autocomplete: createRouter([ autocomplete: createRouter([
sanitizers.autocomplete.middleware, sanitizers.autocomplete.middleware,
controllers.search(peliasConfig, esclient, queries.autocomplete), controllers.search(peliasConfig.api, esclient, queries.autocomplete),
postProc.distances('focus.point.'), postProc.distances('focus.point.'),
postProc.confidenceScores(peliasConfig), postProc.confidenceScores(peliasConfig.api),
postProc.dedupe(), postProc.dedupe(),
postProc.accuracy(), postProc.accuracy(),
postProc.localNamingConventions(), postProc.localNamingConventions(),
@ -125,13 +125,13 @@ function addRoutes(app, peliasConfig) {
postProc.parseBoundingBox(), postProc.parseBoundingBox(),
postProc.normalizeParentIds(), postProc.normalizeParentIds(),
postProc.assignLabels(), postProc.assignLabels(),
postProc.geocodeJSON(peliasConfig, base), postProc.geocodeJSON(peliasConfig.api, base),
postProc.sendJSON postProc.sendJSON
]), ]),
reverse: createRouter([ reverse: createRouter([
sanitizers.reverse.middleware, sanitizers.reverse.middleware,
middleware.calcSize(), middleware.calcSize(),
controllers.search(peliasConfig, esclient, queries.reverse), controllers.search(peliasConfig.api, esclient, queries.reverse),
postProc.distances('point.'), postProc.distances('point.'),
// reverse confidence scoring depends on distance from origin // reverse confidence scoring depends on distance from origin
// so it must be calculated first // so it must be calculated first
@ -143,13 +143,13 @@ function addRoutes(app, peliasConfig) {
postProc.parseBoundingBox(), postProc.parseBoundingBox(),
postProc.normalizeParentIds(), postProc.normalizeParentIds(),
postProc.assignLabels(), postProc.assignLabels(),
postProc.geocodeJSON(peliasConfig, base), postProc.geocodeJSON(peliasConfig.api, base),
postProc.sendJSON postProc.sendJSON
]), ]),
nearby: createRouter([ nearby: createRouter([
sanitizers.nearby.middleware, sanitizers.nearby.middleware,
middleware.calcSize(), middleware.calcSize(),
controllers.search(peliasConfig, esclient, queries.reverse), controllers.search(peliasConfig.api, esclient, queries.reverse),
postProc.distances('point.'), postProc.distances('point.'),
// reverse confidence scoring depends on distance from origin // reverse confidence scoring depends on distance from origin
// so it must be calculated first // so it must be calculated first
@ -161,19 +161,19 @@ function addRoutes(app, peliasConfig) {
postProc.parseBoundingBox(), postProc.parseBoundingBox(),
postProc.normalizeParentIds(), postProc.normalizeParentIds(),
postProc.assignLabels(), postProc.assignLabels(),
postProc.geocodeJSON(peliasConfig, base), postProc.geocodeJSON(peliasConfig.api, base),
postProc.sendJSON postProc.sendJSON
]), ]),
place: createRouter([ place: createRouter([
sanitizers.place.middleware, sanitizers.place.middleware,
controllers.place(peliasConfig, esclient), controllers.place(peliasConfig.api, esclient),
postProc.accuracy(), postProc.accuracy(),
postProc.localNamingConventions(), postProc.localNamingConventions(),
postProc.renamePlacenames(), postProc.renamePlacenames(),
postProc.parseBoundingBox(), postProc.parseBoundingBox(),
postProc.normalizeParentIds(), postProc.normalizeParentIds(),
postProc.assignLabels(), postProc.assignLabels(),
postProc.geocodeJSON(peliasConfig, base), postProc.geocodeJSON(peliasConfig.api, base),
postProc.sendJSON postProc.sendJSON
]), ]),
status: createRouter([ status: createRouter([

Loading…
Cancel
Save