UNPKG

@azure/event-hubs

Version:
176 lines (175 loc) • 8.27 kB
var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; 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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var loadBalancingStrategy_exports = {}; __export(loadBalancingStrategy_exports, { listAvailablePartitions: () => listAvailablePartitions }); module.exports = __toCommonJS(loadBalancingStrategy_exports); var import_logger = require("../logger.js"); function getActivePartitionOwnerships(partitionOwnershipMap, expirationIntervalInMs) { const activePartitionOwnershipMap = /* @__PURE__ */ new Map(); partitionOwnershipMap.forEach((partitionOwnership, partitionId) => { if (typeof partitionOwnership.lastModifiedTimeInMs === "undefined" || partitionOwnership.lastModifiedTimeInMs === null) { return; } const timeSincePartitionClaimed = Date.now() - partitionOwnership.lastModifiedTimeInMs; if (timeSincePartitionClaimed < expirationIntervalInMs && partitionOwnership.ownerId) { activePartitionOwnershipMap.set(partitionId, partitionOwnership); } }); return activePartitionOwnershipMap; } function calculateBalancedLoadCounts(ownerToOwnershipMap, partitionIds) { const minPartitionsPerOwner = Math.floor(partitionIds.length / ownerToOwnershipMap.size); const requiredNumberOfOwnersWithExtraPartition = partitionIds.length % ownerToOwnershipMap.size; return { minPartitionsPerOwner, requiredNumberOfOwnersWithExtraPartition }; } function getEventProcessorCounts(minPartitionsPerOwner, ownerToOwnershipMap) { const counts = { haveRequiredPartitions: 0, haveAdditionalPartition: 0, haveTooManyPartitions: 0 }; for (const ownershipList of ownerToOwnershipMap.values()) { const numberOfPartitions = ownershipList.length; if (numberOfPartitions === minPartitionsPerOwner) { counts.haveRequiredPartitions++; } else if (numberOfPartitions === minPartitionsPerOwner + 1) { counts.haveAdditionalPartition++; } else if (numberOfPartitions > minPartitionsPerOwner + 1) { counts.haveTooManyPartitions++; } } return counts; } function isLoadBalanced(requiredNumberOfOwnersWithExtraPartition, totalExpectedEventProcessors, { haveAdditionalPartition, haveRequiredPartitions }) { return haveAdditionalPartition === requiredNumberOfOwnersWithExtraPartition && haveRequiredPartitions + haveAdditionalPartition === totalExpectedEventProcessors; } function getNumberOfPartitionsToClaim(minRequiredPartitionCount, requiredNumberOfOwnersWithExtraPartition, numPartitionsOwnedByUs, { haveAdditionalPartition, haveTooManyPartitions }) { let actualRequiredPartitionCount = minRequiredPartitionCount; if (requiredNumberOfOwnersWithExtraPartition > 0 && // Eventually the `haveTooManyPartitions` will decay into `haveAdditionalPartition` // EventProcessors as partitions are balanced to consumers that aren't at par. // We can consider them to be `haveAdditionalPartition` EventProcessors for our purposes. haveAdditionalPartition + haveTooManyPartitions < requiredNumberOfOwnersWithExtraPartition) { actualRequiredPartitionCount = minRequiredPartitionCount + 1; } return actualRequiredPartitionCount - numPartitionsOwnedByUs; } function findPartitionsToSteal(numberOfPartitionsToClaim, minPartitionsPerOwner, requiredNumberOfOwnersWithExtraPartition, ourOwnerId, ownerToOwnershipMap) { const partitionsToSteal = []; const listOfPartitionOwnerships = []; ownerToOwnershipMap.forEach((partitionOwnerships, ownerId) => { if (ownerId === ourOwnerId || partitionOwnerships.length <= minPartitionsPerOwner) return; listOfPartitionOwnerships.push(partitionOwnerships); }); listOfPartitionOwnerships.sort((a, b) => { if (a.length > b.length) return -1; if (a.length < b.length) return 1; return 0; }); let ownersEncounteredWithExtraPartitions = 0; let currentPartitionOwnershipList = listOfPartitionOwnerships.shift(); while (numberOfPartitionsToClaim > 0 && currentPartitionOwnershipList) { let ownersExpectedPartitionCount = minPartitionsPerOwner; if (ownersEncounteredWithExtraPartitions < requiredNumberOfOwnersWithExtraPartition) { ownersExpectedPartitionCount++; } ownersEncounteredWithExtraPartitions++; let numberAvailableToSteal = currentPartitionOwnershipList.length - ownersExpectedPartitionCount; while (Math.min(numberOfPartitionsToClaim, numberAvailableToSteal)) { const indexToClaim = Math.floor(Math.random() * currentPartitionOwnershipList.length); partitionsToSteal.push(currentPartitionOwnershipList.splice(indexToClaim, 1)[0].partitionId); numberOfPartitionsToClaim--; numberAvailableToSteal--; } currentPartitionOwnershipList = listOfPartitionOwnerships.shift(); } return partitionsToSteal; } function listAvailablePartitions(ownerId, claimedPartitionOwnershipMap, partitionIds, expirationIntervalInMs) { if (!partitionIds.length) { return []; } const activePartitionOwnershipMap = getActivePartitionOwnerships( claimedPartitionOwnershipMap, expirationIntervalInMs ); import_logger.logger.verbose( `[${ownerId}] Number of active ownership records: ${activePartitionOwnershipMap.size}.` ); if (activePartitionOwnershipMap.size === 0) { return partitionIds; } const ownerToOwnershipMap = /* @__PURE__ */ new Map(); for (const activeOwnership of activePartitionOwnershipMap.values()) { const partitionOwnershipList = ownerToOwnershipMap.get(activeOwnership.ownerId) || []; partitionOwnershipList.push(activeOwnership); ownerToOwnershipMap.set(activeOwnership.ownerId, partitionOwnershipList); } if (!ownerToOwnershipMap.has(ownerId)) { ownerToOwnershipMap.set(ownerId, []); } import_logger.logger.info(`[${ownerId}] Number of active event processors: ${ownerToOwnershipMap.size}.`); const { minPartitionsPerOwner, requiredNumberOfOwnersWithExtraPartition } = calculateBalancedLoadCounts(ownerToOwnershipMap, partitionIds); import_logger.logger.verbose( `[${ownerId}] Expected minimum number of partitions per event processor: ${minPartitionsPerOwner},expected number of event processors with additional partition: ${requiredNumberOfOwnersWithExtraPartition}.` ); const eventProcessorCounts = getEventProcessorCounts(minPartitionsPerOwner, ownerToOwnershipMap); if (isLoadBalanced( requiredNumberOfOwnersWithExtraPartition, ownerToOwnershipMap.size, eventProcessorCounts )) { return []; } let numberOfPartitionsToClaim = getNumberOfPartitionsToClaim( minPartitionsPerOwner, requiredNumberOfOwnersWithExtraPartition, ownerToOwnershipMap.get(ownerId).length, eventProcessorCounts ); if (numberOfPartitionsToClaim <= 0) { return []; } const partitionsToClaim = []; const unclaimedPartitionIds = partitionIds.filter((id) => !activePartitionOwnershipMap.has(id)); while (Math.min(numberOfPartitionsToClaim, unclaimedPartitionIds.length)) { const indexToClaim = Math.floor(Math.random() * unclaimedPartitionIds.length); partitionsToClaim.push(unclaimedPartitionIds.splice(indexToClaim, 1)[0]); numberOfPartitionsToClaim--; } if (numberOfPartitionsToClaim === 0) { return partitionsToClaim; } const partitionsToSteal = findPartitionsToSteal( numberOfPartitionsToClaim, minPartitionsPerOwner, requiredNumberOfOwnersWithExtraPartition, ownerId, ownerToOwnershipMap ); return partitionsToClaim.concat(partitionsToSteal); } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { listAvailablePartitions }); //# sourceMappingURL=loadBalancingStrategy.js.map