-
Notifications
You must be signed in to change notification settings - Fork 1.4k
[EPH] Call methods on PartitionProcessor while processing single partition #4467
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 7 commits
bc9906a
e93041b
9c93fdd
bc8b013
3224994
e8c0ece
711c871
8b83f64
fe5c3e7
37baa5c
84c8bcd
315bbd1
157a01a
7acc79f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| import { | ||
| EventHubClient, | ||
| EventData, | ||
| EventPosition, | ||
| delay, | ||
| EventProcessor, | ||
| PartitionContext | ||
| } from "@azure/event-hubs"; | ||
|
|
||
| class EventProcessorHost { | ||
| async processEvents(events: EventData[]) { | ||
| for (const event of events) { | ||
| console.log("Received event", event.body); | ||
| } | ||
| } | ||
|
|
||
| async processError(error: Error) { | ||
| console.log(`Encountered an error: ${error.message}`); | ||
| } | ||
|
|
||
| async initialize() { | ||
| console.log(`Started processing`); | ||
| } | ||
|
|
||
| async close() { | ||
| console.log(`Stopped processing`); | ||
| } | ||
| } | ||
|
|
||
| // Define connection string and related Event Hubs entity name here | ||
| const connectionString = ""; | ||
| const eventHubName = ""; | ||
|
|
||
| async function main() { | ||
| const client = new EventHubClient(connectionString, eventHubName); | ||
|
|
||
| const eventProcessorFactory = (context: PartitionContext) => { | ||
| return new EventProcessorHost(); | ||
| }; | ||
|
|
||
| const eph = new EventProcessor( | ||
| "$Default", | ||
| client, | ||
| eventProcessorFactory, | ||
| "partitionManager" as any, | ||
| { | ||
| initialEventPosition: EventPosition.earliest(), | ||
| maxBatchSize: 10, | ||
| maxWaitTime: 20 | ||
|
ShivangiReja marked this conversation as resolved.
Outdated
|
||
| } | ||
| ); | ||
| await eph.start(); | ||
| // after 2 seconds, stop processing | ||
| await delay(2000); | ||
|
|
||
| await eph.stop(); | ||
| await client.close(); | ||
| } | ||
|
|
||
| main().catch((err) => { | ||
| console.log("Error occurred: ", err); | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -53,3 +53,8 @@ export const client = debugModule("azure:event-hubs:client"); | |
| * log statements for iothub client | ||
| */ | ||
| export const iotClient = debugModule("azure:event-hubs:iothubClient"); | ||
| /** | ||
| * @ignore | ||
| * log statements for partitionManager | ||
| */ | ||
| export const partitionPump = debugModule("azure:event-hubs:partitionPump"); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,73 @@ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
| // Licensed under the MIT License. | ||
|
|
||
| import * as log from "./log"; | ||
| import { EventProcessorOptions, PartitionProcessor } from "./eventProcessor"; | ||
| import { PartitionContext } from "./partitionContext"; | ||
| import { EventHubClient } from "./eventHubClient"; | ||
| import { EventPosition } from "./eventPosition"; | ||
| import { EventHubConsumer } from "./receiver"; | ||
|
|
||
| export class PartitionPump { | ||
| private _partitionContext: PartitionContext; | ||
| private _eventHubClient: EventHubClient; | ||
| private _partitionProcessor: PartitionProcessor; | ||
| private _processorOptions: EventProcessorOptions; | ||
| private _receiver: EventHubConsumer | undefined; | ||
| private _isReceiving: boolean = false; | ||
|
|
||
| constructor( | ||
| eventHubClient: EventHubClient, | ||
| partitionContext: PartitionContext, | ||
| partitionProcessor: PartitionProcessor, | ||
| options?: EventProcessorOptions | ||
| ) { | ||
| if (!options) options = {}; | ||
| this._eventHubClient = eventHubClient; | ||
| this._partitionContext = partitionContext; | ||
| this._partitionProcessor = partitionProcessor; | ||
| this._processorOptions = options; | ||
| } | ||
|
|
||
| async start(partitionId: string): Promise<void> { | ||
| await this._partitionProcessor.initialize!(); | ||
|
ShivangiReja marked this conversation as resolved.
Outdated
|
||
| await this._receiveEvents(partitionId); | ||
|
ShivangiReja marked this conversation as resolved.
Outdated
|
||
| log.partitionPump("Successfully started the receiver."); | ||
| } | ||
|
|
||
| private async _receiveEvents(partitionId: string): Promise<void> { | ||
| this._isReceiving = true; | ||
| this._receiver = await this._eventHubClient.createConsumer( | ||
|
ShivangiReja marked this conversation as resolved.
Outdated
|
||
| this._partitionContext.consumerGroupName, | ||
| partitionId, | ||
| this._processorOptions.initialEventPosition || EventPosition.earliest() | ||
| ); | ||
| try { | ||
| while (this._isReceiving) { | ||
| const receivedEvents = await this._receiver.receiveBatch( | ||
| this._processorOptions.maxBatchSize || 1, | ||
| this._processorOptions.maxWaitTime | ||
| ); | ||
| await this._partitionProcessor.processEvents(receivedEvents); | ||
| } | ||
| } catch (err) { | ||
| this._isReceiving = false; | ||
| this._receiver.close(); | ||
|
ShivangiReja marked this conversation as resolved.
Outdated
|
||
| await this._partitionProcessor.processError(err); | ||
|
ShivangiReja marked this conversation as resolved.
Outdated
|
||
| log.partitionPump("An error occurred while receiving events.", err); | ||
| } | ||
| } | ||
|
|
||
| async stop(): Promise<void> { | ||
|
ShivangiReja marked this conversation as resolved.
Outdated
|
||
| this._isReceiving = false; | ||
| try { | ||
| if (this._receiver) { | ||
| this._receiver.close(); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is your intent to kick off the If so, we should also add a One thing I think we'll have to spend more time on is guaranteeing we close the underlying AMQP link. I think right now if we were to quickly call
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah..I'll test that scenario.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If each
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This is the generic issue, I think we should handle this scenario inside close(). For now I'll pass the abortSignal to the receiveBatch call, and call abort when stop is called. |
||
| } | ||
| await this._partitionProcessor.close!("Stopped processing"); | ||
|
ShivangiReja marked this conversation as resolved.
Outdated
|
||
| } catch (err) { | ||
| log.partitionPump("An error occurred while closing the receiver.", err); | ||
|
ShivangiReja marked this conversation as resolved.
Outdated
|
||
| throw err; | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,100 @@ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
|
ShivangiReja marked this conversation as resolved.
|
||
| // Licensed under the MIT License. | ||
|
|
||
| import chai from "chai"; | ||
| const should = chai.should(); | ||
| import chaiAsPromised from "chai-as-promised"; | ||
| chai.use(chaiAsPromised); | ||
| import debugModule from "debug"; | ||
| const debug = debugModule("azure:event-hubs:partitionPump"); | ||
| import { | ||
| EventPosition, | ||
| EventHubClient, | ||
| EventData, | ||
| EventProcessor, | ||
| PartitionContext, | ||
| delay | ||
| } from "../src"; | ||
| import { EnvVarKeys, getEnvVars } from "./utils/testUtils"; | ||
| const env = getEnvVars(); | ||
|
|
||
| describe("Event processor Host", function(): void { | ||
|
ShivangiReja marked this conversation as resolved.
Outdated
|
||
| const service = { | ||
| connectionString: env[EnvVarKeys.EVENTHUB_CONNECTION_STRING], | ||
| path: env[EnvVarKeys.EVENTHUB_NAME] | ||
| }; | ||
| const client: EventHubClient = new EventHubClient(service.connectionString, service.path); | ||
| before("validate environment", async function(): Promise<void> { | ||
| should.exist( | ||
| env[EnvVarKeys.EVENTHUB_CONNECTION_STRING], | ||
| "define EVENTHUB_CONNECTION_STRING in your environment before running integration tests." | ||
| ); | ||
| should.exist( | ||
| env[EnvVarKeys.EVENTHUB_NAME], | ||
| "define EVENTHUB_NAME in your environment before running integration tests." | ||
| ); | ||
| }); | ||
|
|
||
| after("close the connection", async function(): Promise<void> { | ||
| await client.close(); | ||
| }); | ||
|
|
||
| describe("Partition processor", function(): void { | ||
| const receivedEvents: EventData[] = []; | ||
| let isinitializeCalled = false; | ||
|
ShivangiReja marked this conversation as resolved.
Outdated
|
||
| let isCloseCalled = false; | ||
| class TestEventProcessor { | ||
| async initialize() { | ||
| isinitializeCalled = true; | ||
| debug(`Started processing`); | ||
| } | ||
| async processEvents(events: EventData[]) { | ||
| for (const event of events) { | ||
| receivedEvents.push(event); | ||
| debug("Received event", event.body); | ||
| } | ||
| } | ||
|
|
||
| async processError(error: Error) { | ||
| debug(`Encountered an error: ${error.message}`); | ||
| } | ||
|
|
||
| async close() { | ||
| isCloseCalled = true; | ||
| debug(`Stopped processing`); | ||
| } | ||
| } | ||
| const eventProcessorFactory = (context: PartitionContext) => { | ||
| return new TestEventProcessor(); | ||
| }; | ||
|
|
||
| it("should call methods on a PartitionProcessor ", async function(): Promise<void> { | ||
| const partitionInfo = await client.getPartitionProperties("0"); | ||
| const eph = new EventProcessor( | ||
| "$Default", | ||
| client, | ||
| eventProcessorFactory, | ||
| "partitionManager" as any, | ||
| { | ||
| initialEventPosition: EventPosition.fromSequenceNumber( | ||
| partitionInfo.lastEnqueuedSequenceNumber | ||
| ), | ||
| maxBatchSize: 1, | ||
| maxWaitTime: 5 | ||
| } | ||
| ); | ||
| const producer = client.createProducer({ partitionId: "0" }); | ||
| await producer.send({ body: "Hello world!!!" }); | ||
|
|
||
| await eph.start(); | ||
| // after 10 seconds, stop processing | ||
| await delay(1000); | ||
|
ShivangiReja marked this conversation as resolved.
Outdated
|
||
| await eph.stop(); | ||
| await producer.close(); | ||
| isinitializeCalled.should.equal(true); | ||
| receivedEvents.length.should.equal(1); | ||
| receivedEvents[0].body.should.equal("Hello world!!!"); | ||
| isCloseCalled.should.equal(true); | ||
| }); | ||
| }); | ||
| }).timeout(90000); | ||
Uh oh!
There was an error while loading. Please reload this page.