UNPKG

@azure/cosmos

Version:
153 lines (152 loc) 6.64 kB
var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var PartitionKeyRangeFailoverInfo_exports = {}; __export(PartitionKeyRangeFailoverInfo_exports, { PartitionKeyRangeFailoverInfo: () => PartitionKeyRangeFailoverInfo }); module.exports = __toCommonJS(PartitionKeyRangeFailoverInfo_exports); var import_index = require("./index.js"); var import_semaphore = __toESM(require("semaphore")); 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, import_semaphore.default)(1); this.tryMoveNextLocationSemaphore = (0, import_semaphore.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 > import_index.Constants.ReadRequestFailureCountThreshold : consecutiveWriteRequestFailureCount > import_index.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 > import_index.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 { 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 }; } } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { PartitionKeyRangeFailoverInfo });