UNPKG

@azure/event-hubs

Version:
305 lines (304 loc) • 11.2 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 eventDataBatch_exports = {}; __export(eventDataBatch_exports, { EventDataBatchImpl: () => EventDataBatchImpl, isEventDataBatch: () => isEventDataBatch }); module.exports = __toCommonJS(eventDataBatch_exports); var import_eventData = require("./eventData.js"); var import_rhea_promise = require("rhea-promise"); var import_core_util = require("@azure/core-util"); var import_instrumentEventData = require("./diagnostics/instrumentEventData.js"); var import_error = require("./util/error.js"); const smallMessageOverhead = 5; const largeMessageOverhead = 8; const smallMessageMaxBytes = 255; function isEventDataBatch(eventDataBatch) { return (0, import_core_util.isObjectWithProperties)(eventDataBatch, ["count", "sizeInBytes", "tryAdd"]) && typeof eventDataBatch.tryAdd === "function" && typeof eventDataBatch.count === "number" && typeof eventDataBatch.sizeInBytes === "number"; } class EventDataBatchImpl { /** * Describes the amqp connection context for the Client. */ _context; /** * The Id of the partition to which the batch is expected to be sent to. * Specifying this will throw an error if the batch was created using a `partitionKey`. */ _partitionId; /** * A value that is hashed to produce a partition assignment. * It guarantees that messages with the same partitionKey end up in the same partition. * Specifying this will throw an error if the batch was created using a `partitionId`. */ _partitionKey; /** * The maximum size allowed for the batch. */ _maxSizeInBytes; /** * Current size of the batch in bytes. */ _sizeInBytes; /** * Encoded amqp messages. */ _encodedMessages = []; /** * Raw rhea messages stored for idempotent mode to avoid decode+re-encode. */ _rawMessages = []; /** * Number of events in the batch. */ _count; /** * List of 'message' span contexts. */ _spanContexts = []; /** * The message annotations to apply on the batch envelope. * This will reflect the message annotations on the first event * that was added to the batch. * A common annotation is the partition key. */ _batchAnnotations; /** * Indicates that the batch should be treated as idempotent. */ _isIdempotent; /** * The sequence number assigned to the first event in the batch while * the batch is being sent to the service. */ _pendingStartingSequenceNumber; /** * The publishing sequence number assigned to the first event in the batch at the time * the batch was successfully published. * If the producer was not configured to apply sequence numbering or if the batch * has not yet been successfully published, the value will be `undefined`. */ _startingPublishSequenceNumber; /** * EventDataBatch should not be constructed using `new EventDataBatch()` * Use the `createBatch()` method on your `EventHubProducer` instead. * @internal */ constructor(context, maxSizeInBytes, isIdempotent, partitionKey, partitionId) { this._context = context; this._maxSizeInBytes = maxSizeInBytes; this._isIdempotent = isIdempotent; this._partitionKey = (0, import_core_util.isDefined)(partitionKey) ? String(partitionKey) : partitionKey; this._partitionId = (0, import_core_util.isDefined)(partitionId) ? String(partitionId) : partitionId; this._sizeInBytes = 0; this._count = 0; } /** * The maximum size of the batch, in bytes. * @readonly */ get maxSizeInBytes() { return this._maxSizeInBytes; } /** * The partitionKey set during `EventDataBatch` creation. This value is hashed to * produce a partition assignment when the producer is created without a `partitionId` * @readonly */ get partitionKey() { return this._partitionKey; } /** * The partitionId set during `EventDataBatch` creation. * If this value is set then partitionKey can not be set. * @readonly */ get partitionId() { return this._partitionId; } /** * Size of the `EventDataBatch` instance after the events added to it have been * encoded into a single AMQP message. * @readonly */ get sizeInBytes() { return this._sizeInBytes; } /** * Number of events in the `EventDataBatch` instance. * @readonly */ get count() { return this._count; } /** * The publishing sequence number assigned to the first event in the batch at the time * the batch was successfully published. * If the producer was not configured to apply sequence numbering or if the batch * has not yet been successfully published, the value will be `undefined`. */ get startingPublishedSequenceNumber() { return this._startingPublishSequenceNumber; } /** * Gets the "message" span contexts that were created when adding events to the batch. * @internal */ get _messageSpanContexts() { return this._spanContexts; } /** * Generates an AMQP message that contains the provided encoded events and annotations. * @param encodedEvents - The already encoded events to include in the AMQP batch. * @param annotations - The message annotations to set on the batch. * @param publishingProps - Idempotent publishing properties used to decorate the events in the batch while sending. */ _generateBatch(encodedEvents, annotations, publishingProps) { if (this._isIdempotent && publishingProps) { const decoratedEvents = this._decorateRheaMessagesWithPublishingProps( this._rawMessages, publishingProps ); encodedEvents = decoratedEvents.map(import_rhea_promise.message.encode); } const batchEnvelope = { body: import_rhea_promise.message.data_sections(encodedEvents) }; if (annotations) { batchEnvelope.message_annotations = annotations; } return import_rhea_promise.message.encode(batchEnvelope); } /** * Uses the publishingProps to add idempotent properties as message annotations to rhea messages. */ _decorateRheaMessagesWithPublishingProps(events, publishingProps) { if (!this._isIdempotent) { return events; } const { lastPublishedSequenceNumber = 0, ownerLevel, producerGroupId } = publishingProps; const startingSequenceNumber = lastPublishedSequenceNumber + 1; for (let i = 0; i < events.length; i++) { const event = events[i]; (0, import_eventData.populateIdempotentMessageAnnotations)(event, { isIdempotentPublishingEnabled: this._isIdempotent, ownerLevel, producerGroupId, publishSequenceNumber: startingSequenceNumber + i }); } this._pendingStartingSequenceNumber = startingSequenceNumber; return events; } /** * Annotates a rhea message with placeholder idempotent properties if the batch is idempotent. * This is necessary so that we can accurately calculate the size of the batch while adding events. * Placeholder values are used because real values won't be known until we attempt to send the batch. */ _decorateRheaMessageWithPlaceholderIdempotencyProps(event) { if (!this._isIdempotent) { return event; } if (!event.message_annotations) { event.message_annotations = {}; } (0, import_eventData.populateIdempotentMessageAnnotations)(event, { isIdempotentPublishingEnabled: this._isIdempotent, ownerLevel: 0, publishSequenceNumber: 0, producerGroupId: 0 }); return event; } /** * Generates the single AMQP message which is the result of encoding all the events * added into the `EventDataBatch` instance. * * This is not meant for the user to use directly. * * When the `EventDataBatch` instance is passed to the `send()` method on the `EventHubProducer`, * this single batched AMQP message is what gets sent over the wire to the service. * @readonly */ _generateMessage(publishingProps) { return this._generateBatch(this._encodedMessages, this._batchAnnotations, publishingProps); } /** * Sets startingPublishSequenceNumber to the pending publish sequence number. */ _commitPublish() { this._startingPublishSequenceNumber = this._pendingStartingSequenceNumber; } /** * Tries to add an event data to the batch if permitted by the batch's size limit. * **NOTE**: Always remember to check the return value of this method, before calling it again * for the next event. * * @param eventData - An individual event data object. * @returns A boolean value indicating if the event data has been added to the batch or not. */ tryAdd(eventData, options = {}) { (0, import_error.throwTypeErrorIfParameterMissing)(this._context.connectionId, "tryAdd", "eventData", eventData); if (!(0, import_eventData.isAmqpAnnotatedMessage)(eventData)) { (0, import_eventData.assertIsEventData)(eventData); } const { entityPath, host } = this._context.config; const { event: instrumentedEvent, spanContext } = (0, import_instrumentEventData.instrumentEventData)( eventData, options, entityPath, host ); const amqpMessage = (0, import_eventData.toRheaMessage)(instrumentedEvent, this._partitionKey); const originalAnnotations = amqpMessage.message_annotations && { ...amqpMessage.message_annotations }; this._decorateRheaMessageWithPlaceholderIdempotencyProps(amqpMessage); const encodedMessage = import_rhea_promise.message.encode(amqpMessage); let currentSize = this._sizeInBytes; if (this.count === 0) { if (originalAnnotations) { this._batchAnnotations = originalAnnotations; } currentSize += this._generateBatch([], this._batchAnnotations).length; } const messageSize = encodedMessage.length; const messageOverhead = messageSize <= smallMessageMaxBytes ? smallMessageOverhead : largeMessageOverhead; currentSize += messageSize + messageOverhead; if (currentSize > this._maxSizeInBytes) { return false; } this._encodedMessages.push(encodedMessage); if (this._isIdempotent) { this._rawMessages.push(amqpMessage); } if (spanContext) { this._spanContexts.push(spanContext); } this._sizeInBytes = currentSize; this._count++; return true; } } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { EventDataBatchImpl, isEventDataBatch }); //# sourceMappingURL=eventDataBatch.js.map