UNPKG

motorq-documentdb

Version:
141 lines (128 loc) 8.35 kB
/* 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. */ "use strict"; 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; }