UNPKG

@azure/cosmos

Version:
138 lines 6.27 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.PartitionKeyRangeFailoverInfo = void 0; const tslib_1 = require("tslib"); // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. const index_js_1 = require("./index.js"); const semaphore_1 = tslib_1.__importDefault(require("semaphore")); /** * @hidden * This class manages the failover information for partition key ranges in Cosmos DB. * It tracks the current endpoint, failed endpoints, and the number of consecutive read/write request failures. */ class PartitionKeyRangeFailoverInfo { failedEndPoints = []; currentEndPoint; consecutiveReadRequestFailureCount = 0; consecutiveWriteRequestFailureCount = 0; firstRequestFailureTime = Date.now(); lastRequestFailureTime = Date.now(); failureCountSemaphore; tryMoveNextLocationSemaphore; /** * @internal */ constructor(currentEndpoint) { this.currentEndPoint = currentEndpoint; this.failureCountSemaphore = (0, semaphore_1.default)(1); this.tryMoveNextLocationSemaphore = (0, semaphore_1.default)(1); } /** * Checks if the circuit breaker can trigger a partition failover based on the failure counts. * Returns true if the number of consecutive failures exceeds the defined thresholds for read or write requests. */ async canCircuitBreakerTriggerPartitionFailOver(isReadOnlyRequest) { const { consecutiveReadRequestFailureCount, consecutiveWriteRequestFailureCount } = await this.snapshotConsecutiveRequestFailureCount(); return isReadOnlyRequest ? consecutiveReadRequestFailureCount > index_js_1.Constants.ReadRequestFailureCountThreshold : consecutiveWriteRequestFailureCount > index_js_1.Constants.WriteRequestFailureCountThreshold; } /** * Increments the failure counts for read or write requests and updates the timestamps. * If the time since the last failure exceeds the reset window, it resets the failure counts. */ async incrementRequestFailureCounts(isReadOnlyRequest, currentTimeInMilliseconds) { return new Promise((resolve, reject) => { this.failureCountSemaphore.take(async () => { try { const { lastRequestFailureTime } = await this.snapshotPartitionFailoverTimestamps(); if (currentTimeInMilliseconds - lastRequestFailureTime > index_js_1.Constants.ConsecutiveFailureCountResetIntervalInMS) { this.consecutiveReadRequestFailureCount = 0; this.consecutiveWriteRequestFailureCount = 0; } if (isReadOnlyRequest) { this.consecutiveReadRequestFailureCount++; } else { this.consecutiveWriteRequestFailureCount++; } this.lastRequestFailureTime = currentTimeInMilliseconds; return resolve(); } catch (error) { reject(error); } finally { // Release the semaphore lock this.failureCountSemaphore.leave(); } }); }); } /** * Returns a snapshot of the first and last request failure timestamps. * This method is used to retrieve the current state of failure timestamps without modifying them. */ async snapshotPartitionFailoverTimestamps() { return { firstRequestFailureTime: this.firstRequestFailureTime, lastRequestFailureTime: this.lastRequestFailureTime, }; } /** * Attempts to move to the next available location for the partition key range. * If the current endpoint is the same as the failed endpoint, it tries to find a new endpoint * from the provided list of endpoints. If a new endpoint is found, it updates the current endpoint * and returns true. If no new endpoint is found, it returns false. */ async tryMoveNextLocation(endPoints, failedEndPoint, diagnosticNode, partitionKeyRangeId) { if (failedEndPoint !== this.currentEndPoint) { return true; } return new Promise((resolve, reject) => { this.tryMoveNextLocationSemaphore.take(() => { try { for (const endpoint of endPoints) { if (this.currentEndPoint === endpoint) { continue; } if (this.failedEndPoints.includes(endpoint)) { continue; } this.failedEndPoints.push(failedEndPoint); this.currentEndPoint = endpoint; return resolve(true); } diagnosticNode.addData({ partitionKeyRangeFailoverInfo: `PartitionKeyRangeId: ${partitionKeyRangeId}, failedLocations: ${this.failedEndPoints}, newLocation: ${this.currentEndPoint}`, }); return resolve(false); } catch (err) { reject(err); } finally { this.tryMoveNextLocationSemaphore.leave(); } }); }); } /** Returns the current endpoint being used for partition key range operations.*/ getCurrentEndPoint() { return this.currentEndPoint; } /** * Returns a snapshot of the current consecutive request failure counts for read and write requests. * This method is used to retrieve the current state of failure counts without modifying them. */ async snapshotConsecutiveRequestFailureCount() { return { consecutiveReadRequestFailureCount: this.consecutiveReadRequestFailureCount, consecutiveWriteRequestFailureCount: this.consecutiveWriteRequestFailureCount, }; } } exports.PartitionKeyRangeFailoverInfo = PartitionKeyRangeFailoverInfo; //# sourceMappingURL=PartitionKeyRangeFailoverInfo.js.map