Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
29858fe
starting the migration guide
richardpark-msft Dec 4, 2019
45065e3
Update event hubs migration guide.md
richardpark-msft Dec 5, 2019
a1c6ebd
Change wording to 'receiving'
richardpark-msft Dec 6, 2019
e7b1def
AAD line
richardpark-msft Dec 6, 2019
7ca4971
* Checked against code - casing for AAD was incorrect (should have be…
richardpark-msft Dec 6, 2019
c1855a0
Merge branch 'richardpark-eh-migration-guide' of https://github.com/r…
richardpark-msft Dec 6, 2019
5d7dceb
Applying Ramya's changes
richardpark-msft Dec 6, 2019
686dc6a
Took part of a description from our REST documentation and tailored i…
richardpark-msft Dec 6, 2019
e6e89bd
Merge branch 'richardpark-eh-migration-guide' of https://github.com/r…
richardpark-msft Dec 6, 2019
8edaf44
Updating migration guide sample for sending.
richardpark-msft Dec 6, 2019
52bce99
Add in a description of EventProcessorHost
richardpark-msft Dec 6, 2019
79fdac1
Removed the 'as well'
richardpark-msft Dec 6, 2019
39f6a44
Adding example for EventProcessorHost => EventHubConsumerClient migra…
richardpark-msft Dec 10, 2019
339ba8c
* Don't use $Default anywhere if we can avoid it. Guide the user choo…
richardpark-msft Dec 10, 2019
7fe7dea
There were two packages, not one.
richardpark-msft Dec 11, 2019
20492ab
Document the multiple types of message receiving we can handle.
richardpark-msft Dec 11, 2019
78138d4
We only construct clients, not empires.
richardpark-msft Dec 11, 2019
8992aee
It's a function. Be proud of it and call it theoretically in the table.
richardpark-msft Dec 11, 2019
58bdcb1
Parens.
richardpark-msft Dec 11, 2019
07fb17c
Removed!
richardpark-msft Dec 11, 2019
b6286da
Updated with in-person feedback
Dec 11, 2019
de2de90
Remove backtick from the toc links
Dec 11, 2019
55e8480
send example was incorrect (should have been sendBatch)
Dec 11, 2019
de034b9
The user is in control of the batch size...
richardpark-msft Dec 11, 2019
93e8259
Merge branch 'richardpark-eh-migration-guide' of https://github.com/r…
Dec 11, 2019
8e74f24
Let's assume they're already full events and not just data.
richardpark-msft Dec 11, 2019
bb92f68
Tenatitively fixing sample
Dec 11, 2019
9b37716
Merge branch 'richardpark-eh-migration-guide' of https://github.com/r…
Dec 11, 2019
2c25e0e
Merge remote-tracking branch 'upstream/master' into richardpark-eh-mi…
Dec 11, 2019
96d7a5b
Advertise the goodness that was EPH and that is now part of the Event…
richardpark-msft Dec 11, 2019
0db9b43
* Make sample a bit more interesting by allowing them to specify the …
richardpark-msft Dec 11, 2019
e9de8a0
EventHubs V2 implies something else...
richardpark-msft Dec 11, 2019
91755dc
Same deal here - EventHubs V5 implies something different.
richardpark-msft Dec 11, 2019
433ddaf
A little more context goes a long way
richardpark-msft Dec 11, 2019
6ee889c
Update the "new users should go to readme.md" blurb.
richardpark-msft Dec 11, 2019
038e662
createFromConnectionString is really a constructor. Put it in the rig…
richardpark-msft Dec 11, 2019
eec6e97
Love it.
richardpark-msft Dec 11, 2019
d59f209
Merge branch 'richardpark-eh-migration-guide' of https://github.com/r…
richardpark-msft Dec 11, 2019
9d95885
Sync sample change with readme change
richardpark-msft Dec 11, 2019
7574c08
Updated to mention the change is a combination of calling subscribe()…
richardpark-msft Dec 11, 2019
5b97049
Adding in EventPosition as well since those methods have also been re…
richardpark-msft Dec 11, 2019
3d5ea48
Make mention of the package since everything is now unified.
richardpark-msft Dec 11, 2019
d170dc2
Provide examples on what those different sources of errors are.
richardpark-msft Dec 11, 2019
121adff
Updated to not mention SubscriptionEventHandlers, which is really jus…
richardpark-msft Dec 11, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
216 changes: 216 additions & 0 deletions sdk/eventhub/event-hubs/migrationguide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
# Migration Guide (EventHubs v2 to v5)

This document is intended for users that are familiar with V2 of the JavaScript SDK for Event Hubs library (`@azure/event-hubs@2.x.x` & `@azure/event-processor-host@2.x.x`) and wish
to migrate their application to V5 of the same library.

For users new to the JavaScript SDK for Event Hubs, please see the [readme file for the @azure/event-hubs package](https://github.com/Azure/azure-sdk-for-js/blob/master/sdk/eventhub/event-hubs/README.md).

## General changes

In the interest of simplifying the API surface we've made two distinct
clients, rather than having a single `EventHubClient`:
* [EventHubProducerClient](https://docs.microsoft.com/en-us/javascript/api/@azure/event-hubs/eventhubproducerclient?view=azure-node-preview)
for sending messages.
* [EventHubConsumerClient](https://docs.microsoft.com/en-us/javascript/api/@azure/event-hubs/eventhubconsumerclient?view=azure-node-preview)
for receiving messages.

We've also merged the functionality from `EventProcessorHost` in the `@azure/event-processor-host` package into
`EventHubConsumerClient` in the `@azure/event-hubs` package, allowing `EventHubConsumerClient` to be the single
point of entry for receiving of any type (from single partition, all partitions, or with load balancing and checkpointing features) within Event Hubs.


### Client constructors

| In v2 | Equivalent in v5 | Sample |
|------------------------------------------------|------------------------------------------------------------------|--------|
| `EventHubClient.createFromConnectionString()` | `new EventHubProducerClient()` or `new EventHubConsumerClient()` | [receiveEvents](https://github.com/Azure/azure-sdk-for-js/blob/master/sdk/eventhub/event-hubs/samples/receiveEvents.ts), [sendEvents](https://github.com/Azure/azure-sdk-for-js/blob/master/sdk/eventhub/event-hubs/samples/sendEvents.ts) |
| `EventHubClient.createFromAadTokenCredentials()` | `new EventHubProducerClient()` or `new EventHubConsumerClient()` | [usingAadAuth](https://github.com/Azure/azure-sdk-for-js/blob/master/sdk/eventhub/event-hubs/samples/usingAadAuth.ts)
| `EventProcessorHost.createFromConnectionString()` | `new EventHubConsumerClient(..., checkpointStore)` | [receiveEventsUsingCheckpointStore](https://github.com/Azure/azure-sdk-for-js/blob/master/sdk/eventhub/event-hubs/samples/receiveEventsUsingCheckpointStore.ts) |

### Receiving events

| In v2 | Equivalent in v5 | Sample |
|------------------------------------------------|------------------------------------------------------------------|--------|
| `EventHubClient.receive()` and `EventHubClient.receiveBatch()` | `EventHubConsumerClient.subscribe()` | [receiveEvents](https://github.com/Azure/azure-sdk-for-js/blob/master/sdk/eventhub/event-hubs/samples/receiveEvents.ts) |
Comment thread
richardpark-msft marked this conversation as resolved.

### Sending events

| In v2 | Equivalent in v5 | Sample |
|------------------------------------------------|------------------------------------------------------------------|--------|
| `EventHubClient.send()` | `EventHubConsumerClient.sendBatch()` | [sendEvents](https://github.com/Azure/azure-sdk-for-js/blob/master/sdk/eventhub/event-hubs/samples/sendEvents.ts) |

### Minor renames

| In v2 | Equivalent in v5 |
|------------------------------------------------|-----------------------------|
| `EventPosition.fromStart()` | `EventPosition.earliest()` |
| `EventPosition.fromEnd()` | `EventPosition.latest()` |

## Migration samples

* [Receiving events](#migrating-code-from-eventhubclient-to-eventhubconsumerclient-for-receiving-events)
* [Receiving events with checkpointing](#migrating-code-from-eventprocessorhost-to-eventhubconsumerclient-for-receiving-events)
* [Sending events](#migrating-code-from-eventhubclient-to-eventhubproducerclient-for-sending-events)

### Migrating code from `EventHubClient` to `EventHubConsumerClient` for receiving events

In V2, event handlers were passed as positional arguments to `receive`.

In V5, event handlers are passed as part of a `SubscriptionEventHandlers` shaped object.

For example, this code which receives from a partition in V2:

```typescript
const client = EventHubClient.createFromConnectionString(connectionString);
const rcvHandler = client.receive(partitionId, onMessageHandler, onErrorHandler, {
eventPosition: EventPosition.fromStart(),
consumerGroup: consumerGroupName
});
await rcvHandler.stop();
```

Becomes this in V5:

```typescript
const eventHubConsumerClient = new EventHubConsumerClient(consumerGroupName, connectionString);

const subscription = eventHubConsumerClient.subscribe(
partitionId, {
processInitialize: (initContext) => {
initContext.setStartingPosition(EventPosition.fromStart());
},
processEvents: onMessageHandler,
processError: onErrorHandler
});

await subscription.close();
```

See [`receiveEvents.ts`](https://github.com/Azure/azure-sdk-for-js/blob/master/sdk/eventhub/event-hubs/samples/receiveEvents.ts)
for a sample program demonstrating this.

### Migrating code from `EventHubClient` to `EventHubProducerClient` for sending events

In V2, there were multiple options on how to send data.

In V5, this has been consolidated into a more efficient `sendBatch` method.
Batching merges information from multiple messages into a single send, reducing
the amount of network communication needed vs sending messages one at a time.

So in V2:
```typescript
const eventsToSend = [
// events go here
];

const client = EventHubClient.createFromConnectionString(connectionString);

// Would fail if the total size of events exceed the max size supported by the library.
await client.sendBatch(eventsToSend, partitionId);
```

In V5:
```typescript
const producer = new EventHubProducerClient(connectionString);

const eventsToSend = [
// events go here
];

let batch = await producer.createBatch();
let i = 0;

while (i < eventsToSend.length) {
// messages can fail to be added to the batch if they exceed the maximum size configured for
// the EventHub.
const isAdded = batch.tryAdd(eventsToSend[i]);

if (isAdded) {
console.log(`Added event number ${i} to the batch`);
++i;
continue;
}

if (batch.count === 0) {
// If we can't add it and the batch is empty that means the message we're trying to send
// is too large, even when it would be the _only_ message in the batch.
//
// At this point you'll need to decide if you're okay with skipping this message entirely
// or find some way to shrink it.
console.log(`Message was too large and can't be sent until it's made smaller. Skipping...`);
++i;
continue;
}

// otherwise this just signals a good spot to send our batch
console.log(`Batch is full - sending ${batch.count} messages as a single batch.`);
await producer.sendBatch(batch);

// and create a new one to house the next set of messages
batch = await producer.createBatch();
}

// send any remaining messages, if any.
if (batch.count > 0) {
console.log(`Sending remaining ${batch.count} messages as a single batch.`)
await producer.sendBatch(batch);
}
```

### Migrating code from `EventProcessorHost` to `EventHubConsumerClient` for receiving events

In V2, `EventProcessorHost` allowed you to balance the load between multiple instances of
your program when receiving events.

In V5, `EventHubConsumerClient` allows you to do the same with the `subscribe()` method if you
pass a `CheckpointStore` to the constructor.

So in V2:
```typescript
const eph = EventProcessorHost.createFromConnectionString(
EventProcessorHost.createHostName(ephName),
storageConnectionString,
storageContainerName,
ehConnectionString,
{
eventHubPath: eventHubName,
onEphError: (error: any) => {
console.log("[%s] Error: %O", ephName, error);
}
}
);

const onMessage: OnReceivedMessage = async (context: PartitionContext, event: EventData) => { }

const onError: OnReceivedError = (error: any) => {
console.log("[%s] Received Error: %O", ephName, error);
};

await eph.start(onMessage, onError);
```

And in V5:
```typescript
import { EventHubConsumerClient, CheckpointStore } from "@azure/event-hubs";
import { ContainerClient } from "@azure/storage-blob";
import { BlobCheckpointStore } from "@azure/eventhubs-checkpointstore-blob";

const containerClient = new ContainerClient(storageConnectionString, storageContainerName);
const checkpointStore : CheckpointStore = new BlobCheckpointStore(containerClient);
const eventHubConsumerClient = new EventHubConsumerClient(consumerGroupName, ehConnectionString, eventHubName);

const subscription = eventHubConsumerClient.subscribe(
partitionId, {
// In V5 we deliver messages in batches, rather than a single message
// at a time. You can control the batch size via the options passed to the client.
processEvents: (messages, context) => {},

// Prior to V5 errors were handled by separate callbacks depending
// on where they were thrown i.e when managing different partitions vs receiving from each partition.
//
// In V5 you only need a single error handler for all of those cases.
processError: onErrorHandler
});

await subscription.close();
```
67 changes: 47 additions & 20 deletions sdk/eventhub/event-hubs/samples/sendEvents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,39 +36,66 @@ export async function main(): Promise<void> {
// If you would like more control you can pass either a `partitionKey` or a `partitionId`
// into the createBatch() `options` parameter which will allow you full control over the
// destination.
let batch = await producer.createBatch();
const batchOptions = {
// The maxSizeInBytes lets you manually control the size of the batch.
// if this is not set we will get the maximum batch size from Event Hubs.
//
// For this sample you can change the batch size to see how different parts
// of the sample handle batching. In production we recommend using the default
// and not specifying a maximum size.
//
// maxSizeInBytes: 200
};

let batch = await producer.createBatch(batchOptions);

let numEventsSent = 0;

// add events to our batch
for (const event of eventsToSend) {
let i = 0;

while (i < eventsToSend.length) {
// messages can fail to be added to the batch if they exceed the maximum size configured for
// the EventHub.
const isAdded = batch.tryAdd({ body: event });
const isAdded = batch.tryAdd({ body: eventsToSend[i] });

if (isAdded) {
console.log(`Added eventsToSend[${i}] to the batch`);
++i;
continue;
}

if (!isAdded) {
if (batch.count === 0) {
// if we can't add it and the batch is empty that means the message we're trying to send
// is too large, even when it would be the _only_ message in the batch.
//
// To fix this you'll need to potentially split the message up across multiple batches or
// skip it. In this example, we'll skip the message.
console.log(`Message was too large and can't be sent until it's made smaller. Skipping...`);
continue;
}

// otherwise this just signals a good spot to send our batch
console.log(`Batch is full - sending ${batch.count} messages as a single batch.`)
await producer.sendBatch(batch);

// and create a new one to house the next set of messages
batch = await producer.createBatch();
if (batch.count === 0) {
// If we can't add it and the batch is empty that means the message we're trying to send
// is too large, even when it would be the _only_ message in the batch.
//
// At this point you'll need to decide if you're okay with skipping this message entirely
// or find some way to shrink it.
console.log(`Message was too large and can't be sent until it's made smaller. Skipping...`);
++i;
continue;
}

// otherwise this just signals a good spot to send our batch
console.log(`Batch is full - sending ${batch.count} messages as a single batch.`);
await producer.sendBatch(batch);
numEventsSent += batch.count;

// and create a new one to house the next set of messages
batch = await producer.createBatch(batchOptions);
}

// send any remaining messages, if any.
if (batch.count > 0) {
console.log(`Sending remaining ${batch.count} messages as a single batch.`)
await producer.sendBatch(batch);
numEventsSent += batch.count;
}

console.log(`Sent ${numEventsSent} events`);

if (numEventsSent !== eventsToSend.length) {
throw new Error(`Not all messages were sent (${numEventsSent}/${eventsToSend.length})`);
}
} catch (err) {
console.log("Error when creating & sending a batch of events: ", err);
Expand Down