feat(perf): cloud reporter should retry in case of a timeout

This commit is contained in:
Tobias Bosch 2015-01-16 17:36:24 -08:00
parent ed7d1cf060
commit 5f5ed06713

View File

@ -80,6 +80,8 @@ var TABLE_FIELDS = [
} }
]; ];
var RETRY_COUNT = 3;
class CloudReporter { class CloudReporter {
constructor(benchmarkConfig) { constructor(benchmarkConfig) {
this.tableConfig = createTableConfig(benchmarkConfig); this.tableConfig = createTableConfig(benchmarkConfig);
@ -95,12 +97,12 @@ class CloudReporter {
var self = this; var self = this;
var flow = browser.driver.controlFlow(); var flow = browser.driver.controlFlow();
flow.execute(function() { flow.execute(function() {
return authenticate(self.authConfig).then(function(authClient) { return authenticate(self.authConfig, RETRY_COUNT).then(function(authClient) {
self.authClient = authClient; self.authClient = authClient;
}); });
}); });
flow.execute(function() { flow.execute(function() {
return getOrCreateTable(self.authClient, self.tableConfig); return getOrCreateTable(self.authClient, self.tableConfig, RETRY_COUNT);
}); });
} }
add(data) { add(data) {
@ -112,12 +114,12 @@ class CloudReporter {
var allRows = this.allSample.map(function(data) { var allRows = this.allSample.map(function(data) {
return self._convertToTableRow(data, stableSample); return self._convertToTableRow(data, stableSample);
}); });
flow.execute(function() { return insertRows(this.authClient, this.tableConfig, allRows, RETRY_COUNT);
return insertRows(self.authClient, self.tableConfig, allRows)
});
} }
_convertToTableRow(benchpressRow, stableSample) { _convertToTableRow(benchpressRow, stableSample) {
var tableRow = { return {
insertId: this.benchmarkConfig.runId+'#'+benchpressRow.index,
json: {
runId: this.benchmarkConfig.runId, runId: this.benchmarkConfig.runId,
benchmarkId: this.benchmarkConfig.id, benchmarkId: this.benchmarkConfig.id,
index: benchpressRow.index, index: benchpressRow.index,
@ -144,8 +146,8 @@ class CloudReporter {
value: benchpressRow.values[index] value: benchpressRow.values[index]
}; };
}) })
}
}; };
return tableRow;
} }
} }
@ -160,14 +162,14 @@ function createTableConfig(benchmarkConfig) {
}; };
} }
function getOrCreateTable(authClient, tableConfig) { function getOrCreateTable(authClient, tableConfig, retryCount) {
return getTable(authClient, tableConfig).then(null, function(err) { return getTable(authClient, tableConfig, retryCount).then(null, function(err) {
// create the table if it does not exist // create the table if it does not exist
return createTable(authClient, tableConfig); return createTable(authClient, tableConfig, retryCount);
}); });
} }
function authenticate(authConfig) { function authenticate(authConfig, retryCount) {
var authClient = new google.auth.JWT( var authClient = new google.auth.JWT(
authConfig['client_email'], authConfig['client_email'],
null, null,
@ -178,12 +180,16 @@ function authenticate(authConfig) {
var defer = webdriver.promise.defer(); var defer = webdriver.promise.defer();
authClient.authorize(makeNodeJsResolver(defer)); authClient.authorize(makeNodeJsResolver(defer));
return defer.promise.then(function() { var resultPromise = defer.promise.then(function() {
return authClient; return authClient;
}); });
resultPromise = retryIfNeeded(resultPromise, retryCount, function(newRetryCount) {
return authenticate(authConfig, newRetryCount);
});
return resultPromise;
} }
function getTable(authClient, tableConfig) { function getTable(authClient, tableConfig, retryCount) {
// see https://cloud.google.com/bigquery/docs/reference/v2/tables/get // see https://cloud.google.com/bigquery/docs/reference/v2/tables/get
var params = { var params = {
auth: authClient, auth: authClient,
@ -193,10 +199,14 @@ function getTable(authClient, tableConfig) {
}; };
var defer = webdriver.promise.defer(); var defer = webdriver.promise.defer();
bigquery.tables.get(params, makeNodeJsResolver(defer)); bigquery.tables.get(params, makeNodeJsResolver(defer));
return defer.promise; var resultPromise = defer.promise;
resultPromise = retryIfNeeded(resultPromise, retryCount, function(newRetryCount) {
return getTable(authClient, tableConfig, newRetryCount);
});
return resultPromise;
} }
function createTable(authClient, tableConfig) { function createTable(authClient, tableConfig, retryCount) {
// see https://cloud.google.com/bigquery/docs/reference/v2/tables // see https://cloud.google.com/bigquery/docs/reference/v2/tables
// see https://cloud.google.com/bigquery/docs/reference/v2/tables#resource // see https://cloud.google.com/bigquery/docs/reference/v2/tables#resource
var params = { var params = {
@ -217,10 +227,23 @@ function createTable(authClient, tableConfig) {
}; };
var defer = webdriver.promise.defer(); var defer = webdriver.promise.defer();
bigquery.tables.insert(params, makeNodeJsResolver(defer)); bigquery.tables.insert(params, makeNodeJsResolver(defer));
return defer.promise; var resultPromise = defer.promise;
resultPromise = retryIfNeeded(resultPromise, retryCount, function(newRetryCount) {
return createTable(authClient, tableConfig, newRetryCount);
});
return resultPromise;
} }
function insertRows(authClient, tableConfig, rows) { function insertRows(authClient, tableConfig, rows, retryCount) {
// We need to split up the rows in batches as BigQuery
// has a size limit on requests.
// Note: executing the requests in parallel leads to timeouts sometime...
var recurseRows = null;
if (rows.length > 10) {
recurseRows = rows.slice(10);
rows = rows.slice(0, 10);
}
// see https://cloud.google.com/bigquery/docs/reference/v2/tabledata/insertAll // see https://cloud.google.com/bigquery/docs/reference/v2/tabledata/insertAll
var params = { var params = {
auth: authClient, auth: authClient,
@ -229,22 +252,41 @@ function insertRows(authClient, tableConfig, rows) {
tableId: tableConfig.table.id, tableId: tableConfig.table.id,
resource: { resource: {
"kind": "bigquery#tableDataInsertAllRequest", "kind": "bigquery#tableDataInsertAllRequest",
"rows": rows.map(function(row) { "rows": rows
return {
json: row
}
})
} }
}; };
var defer = webdriver.promise.defer(); var defer = webdriver.promise.defer();
bigquery.tabledata.insertAll(params, makeNodeJsResolver(defer)); bigquery.tabledata.insertAll(params, makeNodeJsResolver(defer));
return defer.promise.then(function(result) { var resultPromise = defer.promise.then(function(result) {
if (result.insertErrors) { if (result.insertErrors) {
throw result.insertErrors.map(function(err) { throw JSON.stringify(result.insertErrors, null, ' ');
return err.errors.map(function(err) { }
return err.message; });
}).join('\n'); resultPromise = retryIfNeeded(resultPromise, retryCount, function(newRetryCount) {
}).join('\n'); return insertRows(authClient, tableConfig, rows, newRetryCount);
});
if (recurseRows) {
resultPromise = resultPromise.then(function() {
return insertRows(authClient, tableConfig, recurseRows, retryCount);
});
}
return resultPromise;
}
function retryIfNeeded(promise, retryCount, retryCallback) {
if (!retryCount) {
return promise;
}
return promise.then(null, function(err) {
var errStr = err.toString();
if (typeof err === 'object') {
errStr += JSON.stringify(err, null, ' ');
}
if (errStr.indexOf('timeout') !== -1) {
console.log('Retrying', retryCallback.toString());
return retryCallback();
} else {
throw err;
} }
}); });
} }
@ -252,13 +294,8 @@ function insertRows(authClient, tableConfig, rows) {
function makeNodeJsResolver(defer) { function makeNodeJsResolver(defer) {
return function(err, result) { return function(err, result) {
if (err) { if (err) {
// Normalize errors messages from BigCloud so that they show up nicely // Format errors in a nice way
if (err.errors) { defer.reject(JSON.stringify(err, null, ' '));
err = err.errors.map(function(err) {
return err.message;
}).join('\n');
}
defer.reject(err);
} else { } else {
defer.fulfill(result); defer.fulfill(result);
} }