UNPKG

@azure/cosmos

Version:
127 lines 6.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.TimeoutFailoverRetryPolicy = void 0; const statusCodes_js_1 = require("../common/statusCodes.js"); const index_js_1 = require("../common/index.js"); const constants_js_1 = require("../common/constants.js"); const TimeoutError_js_1 = require("../request/TimeoutError.js"); /** * This class TimeoutFailoverRetryPolicy handles retries for read operations * (including data plane,metadata, and query plan) in case of request timeouts * (TimeoutError) or service unavailability (503 status code) by performing failover * and retrying on other regions. * @hidden */ class TimeoutFailoverRetryPolicy { globalEndpointManager; headers; methodType; resourceType; operationType; enableEndPointDiscovery; globalPartitionEndpointManager; maxRetryAttemptCount = 120; maxServiceUnavailableRetryCount = 1; retryAfterInMs = 0; failoverRetryCount = 0; request; locationEndpoint; constructor(globalEndpointManager, headers, methodType, resourceType, operationType, enableEndPointDiscovery, globalPartitionEndpointManager) { this.globalEndpointManager = globalEndpointManager; this.headers = headers; this.methodType = methodType; this.resourceType = resourceType; this.operationType = operationType; this.enableEndPointDiscovery = enableEndPointDiscovery; this.globalPartitionEndpointManager = globalPartitionEndpointManager; } /** * Checks if a timeout request is valid for the timeout failover retry policy. * A valid request should be a data plane, metadata, or query plan request. * @returns */ isValidRequestForTimeoutError() { const isQuery = constants_js_1.Constants.HttpHeaders.IsQuery in this.headers; const isQueryPlan = constants_js_1.Constants.HttpHeaders.IsQueryPlan in this.headers; if (this.methodType === index_js_1.HTTPMethod.get || isQuery || isQueryPlan) { return true; } return false; } async shouldRetry(err, diagnosticNode, retryContext, locationEndpoint, requestContext) { if (!err) { return false; } if (!retryContext || !locationEndpoint) { return false; } if (!this.enableEndPointDiscovery) { return false; } // Mark the partition as unavailable. // Let the Retry logic decide if the request should be retried if (requestContext && this.globalPartitionEndpointManager) { await this.globalPartitionEndpointManager.tryPartitionLevelFailover(requestContext, diagnosticNode); } // Check if the error is a timeout error (TimeoutErrorCode) and if it is not a valid HTTP network timeout request if (err.code === TimeoutError_js_1.TimeoutErrorCode && !this.isValidRequestForTimeoutError()) { return false; } if (err.code === statusCodes_js_1.StatusCodes.ServiceUnavailable && this.failoverRetryCount >= this.maxServiceUnavailableRetryCount) { return false; } if (this.failoverRetryCount >= this.maxRetryAttemptCount) { return false; } const canUseMultipleWriteLocations = this.globalEndpointManager.canUseMultipleWriteLocations(this.resourceType, this.operationType); const readRequest = (0, index_js_1.isReadRequest)(this.operationType); if (!canUseMultipleWriteLocations && !readRequest && !this.globalPartitionEndpointManager?.isPartitionLevelAutomaticFailoverEnabled()) { // Write requests on single master cannot be retried if partition level failover is disabled. // This means there are no other regions available to serve the writes. return false; } this.failoverRetryCount++; // Setting the retryLocationIndex to the next available location for retry. // The retryLocationIndex is determined based on the failoverRetryCount, starting from zero. retryContext.retryLocationServerIndex = await this.findEndpointIndex(this.failoverRetryCount); diagnosticNode.addData({ successfulRetryPolicy: "timeout-failover" }); return true; } /** * Determines index of endpoint to be used for retry based upon failoverRetryCount and avalable locations * @param failoverRetryCount - count of failovers * @returns */ async findEndpointIndex(failoverRetryCount) { // count of preferred locations specified by user const preferredLocationsCount = this.globalEndpointManager.preferredLocationsCount; const readRequest = (0, index_js_1.isReadRequest)(this.operationType); let endpointIndex = 0; // If preferredLocationsCount is not zero, it indicates that the user has specified preferred locations. if (preferredLocationsCount !== 0) { // The endpointIndex is set based on the preferred location and the failover retry count. endpointIndex = failoverRetryCount % preferredLocationsCount; } else { // In the absence of preferred locations, the endpoint selection is based on the failover count and the number of available locations. if (readRequest) { const getReadEndpoints = await this.globalEndpointManager.getReadEndpoints(); if (getReadEndpoints && getReadEndpoints.length > 0) { endpointIndex = failoverRetryCount % getReadEndpoints.length; } } else { const getWriteEndpoints = await this.globalEndpointManager.getWriteEndpoints(); if (getWriteEndpoints && getWriteEndpoints.length > 0) { endpointIndex = failoverRetryCount % getWriteEndpoints.length; } } } return endpointIndex; } } exports.TimeoutFailoverRetryPolicy = TimeoutFailoverRetryPolicy; //# sourceMappingURL=timeoutFailoverRetryPolicy.js.map