motorq-documentdb
Version:
Azure Cosmos DB Service Node.js SDK for SQL API
141 lines (128 loc) • 8.35 kB
JavaScript
/*
The MIT License (MIT)
Copyright (c) 2017 Microsoft Corporation
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
;
var Base = require("./base"),
Constants = require("./constants"),
EndpointDiscoveryRetryPolicy = require("./endpointDiscoveryRetryPolicy"),
ResourceThrottleRetryPolicy = require("./resourceThrottleRetryPolicy"),
SessionReadRetryPolicy = require("./sessionReadRetryPolicy"),
DefaultRetryPolicy = require("./defaultRetryPolicy"),
StatusCodes = require("./statusCodes").StatusCodes,
SubStatusCodes = require("./statusCodes").SubStatusCodes,
log = require("./log")("request");
//SCRIPT START
var RetryUtility = {
/**
* Executes the retry policy for the created request object.
* @param {object} globalEndpointManager - an instance of GlobalEndpointManager class.
* @param {object} body - a dictionary containing 'buffer' and 'stream' keys to hold corresponding buffer or stream body, null otherwise.
* @param {function} createRequestObjectStub - stub function that creates the request object.
* @param {object} connectionPolicy - an instance of ConnectionPolicy that has the connection configs.
* @param {RequestOptions} requestOptions - The request options.
* @param {function} callback - the callback that will be called when the request is finished executing.
*/
execute: function (globalEndpointManager, body, createRequestObjectFunc, connectionPolicy, requestOptions, request, callback) {
var request = typeof request !== 'string' ? request : { "path": "", "operationType": "nonReadOps", "client": null };
var endpointDiscoveryRetryPolicy = new EndpointDiscoveryRetryPolicy(globalEndpointManager);
var resourceThrottleRetryPolicy = new ResourceThrottleRetryPolicy(connectionPolicy.RetryOptions.MaxRetryAttemptCount,
connectionPolicy.RetryOptions.FixedRetryIntervalInMilliseconds,
connectionPolicy.RetryOptions.MaxWaitTimeInSeconds);
var sessionReadRetryPolicy = new SessionReadRetryPolicy(globalEndpointManager, request)
var defaultRetryPolicy = new DefaultRetryPolicy(request.operationType)
this.apply(body, createRequestObjectFunc, connectionPolicy, requestOptions, endpointDiscoveryRetryPolicy, resourceThrottleRetryPolicy, sessionReadRetryPolicy, defaultRetryPolicy, callback);
},
/**
* Applies the retry policy for the created request object.
* @param {object} body - a dictionary containing 'buffer' and 'stream' keys to hold corresponding buffer or stream body, null otherwise.
* @param {function} createRequestObjectFunc - function that creates the request object.
* @param {object} connectionPolicy - an instance of ConnectionPolicy that has the connection configs.
* @param {RequestOptions} requestOptions - The request options.
* @param {EndpointDiscoveryRetryPolicy} endpointDiscoveryRetryPolicy - The endpoint discovery retry policy instance.
* @param {ResourceThrottleRetryPolicy} resourceThrottleRetryPolicy - The resource throttle retry policy instance.
* @param {function} callback - the callback that will be called when the response is retrieved and processed.
*/
apply: function (body, createRequestObjectFunc, connectionPolicy, requestOptions, endpointDiscoveryRetryPolicy, resourceThrottleRetryPolicy, sessionReadRetryPolicy, defaultRetryPolicy, callback) {
var that = this;
log.info("[%s] [%s] [%s]", requestOptions.method, requestOptions.host, requestOptions.path);
log.debug("[apply] Request Options: %o", requestOptions);
var httpsRequest = createRequestObjectFunc(connectionPolicy, requestOptions, function (err, response, headers) {
if (err) {
// Non-success status codes/errors
log.info("Received error: %o", err);
var retryPolicy = null;
headers = headers || {};
// Forbidden errors mean that we might be connecting to the wrong endpoint (failover/etc.), so refresh the endpoints
// System errors mean that we can't reach the endpoint, so refresh the endpoints (unless the failure happened while refreshing the endpoints)
if ((err.code === StatusCodes.Forbidden && err.substatus === SubStatusCodes.WriteForbidden)
|| (requestOptions.path !== "" && typeof err.code === "string" && (err.code === StatusCodes.ConnectionRefused))) {
retryPolicy = endpointDiscoveryRetryPolicy;
} else if (err.code === StatusCodes.TooManyRequests) {
retryPolicy = resourceThrottleRetryPolicy;
} else if (err.code === StatusCodes.NotFound && err.substatus === SubStatusCodes.ReadSessionNotAvailable) {
retryPolicy = sessionReadRetryPolicy;
} else {
retryPolicy = defaultRetryPolicy;
}
retryPolicy.shouldRetry(err, function (shouldRetry, newUrl) {
if (!shouldRetry) {
headers[Constants.ThrottleRetryCount] = resourceThrottleRetryPolicy.currentRetryAttemptCount;
headers[Constants.ThrottleRetryWaitTimeInMs] = resourceThrottleRetryPolicy.cummulativeWaitTimeinMilliseconds;
log.error("[apply] Cannot retry, %o", err);
return callback(err, response, headers);
} else {
log.info("Retrying in %d ms", retryPolicy.retryAfterInMilliseconds);
setTimeout(function () {
if (typeof newUrl !== 'undefined')
requestOptions = that.modifyRequestOptions(requestOptions, newUrl);
that.apply(body, createRequestObjectFunc, connectionPolicy, requestOptions, endpointDiscoveryRetryPolicy, resourceThrottleRetryPolicy, sessionReadRetryPolicy, defaultRetryPolicy, callback);
}, retryPolicy.retryAfterInMilliseconds);
return;
}
});
return
}
headers[Constants.ThrottleRetryCount] = resourceThrottleRetryPolicy.currentRetryAttemptCount;
headers[Constants.ThrottleRetryWaitTimeInMs] = resourceThrottleRetryPolicy.cummulativeWaitTimeinMilliseconds;
return callback(err, response, headers);
});
if (httpsRequest) {
if (body["stream"] !== null) {
body["stream"].pipe(httpsRequest);
} else if (body["buffer"] !== null) {
httpsRequest.write(body["buffer"]);
httpsRequest.end();
} else {
httpsRequest.end();
}
}
},
modifyRequestOptions: function (oldRequestOptions, newUrl) {
var properties = Object.keys(newUrl);
for (var index in properties) {
if (properties[index] !== "path")
oldRequestOptions[properties[index]] = newUrl[properties[index]];
}
return oldRequestOptions;
}
}
//SCRIPT END
if (typeof exports !== "undefined") {
module.exports = RetryUtility;
}