@azure/event-hubs
Version:
Azure Event Hubs SDK for JS.
176 lines (175 loc) • 8.27 kB
JavaScript
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