Skip to content

Commit 85c58fd

Browse files
[Perf Tests] Event Hubs perf tests - track 1 and track 2 (Azure#13056)
### Changes in the PR - Track 2 tests are part of the test folder, which would be compiled along with the regular tests and would require changes if the API is updated - Track 1 tests - a separate npm project, takes dependence on the perf package, doesn't get compiled along with the regular tests ### To run track 2 perf tests 1. Build the event-hubs package `rush build -t event-hubs`. 2. Navigate to `event-hubs` folder `cd sdk\eventhub\event-hubs\`. 3. Create an event-hubs namespace and populate the .env file at `eventhub\event-hubs` folder with `EVENTHUB_CONNECTION_STRING`, `EVENTHUB_NAME` and `CONSUMER_GROUP_NAME` variables. 4. Run the tests as follows from the `event-hubs` folder. - batch send - `npm run perf-test:node -- SendTest --warmup 2 --duration 7 --parallel 2` - receive(Standalone test - doesn't use the framework) - `tsc -p . --module "commonjs" && node dist-esm\test\perf\track-2\receive.spec.js` ### To run track 1 perf tests 1. Navigate to `test-utils\perfstress` folder `cd sdk\test-utils\perfstress\` 2. Build the package `rush update && rush build -t test-utils-perfstress` 3. Pack the perf package `rushx pack` 4. Navigate to `event-hubs\perf\track-1` folder `cd sdk\eventhub\event-hubs\perf\track-1`. 5. Install the perf package `npm i ..\..\..\..\..\test-utils\perfstress\azure-test-utils-perfstress-1.0.0.tgz` 6. Run `npm install` to get `event-hubs V2`. 7. Create an event-hubs namespace and populate the .env file at `eventhub\event-hubs` folder with `EVENTHUB_CONNECTION_STRING`, `EVENTHUB_NAME` and `CONSUMER_GROUP_NAME` variables. 8. Run the tests as follows from the `event-hubs` folder. - batch send - `npm run perf-test:node -- SendTest --warmup 2 --duration 7 --parallel 2` - receive(Standalone test - doesn't use the framework) - `ts-node receive.spec.ts`
1 parent 8569b84 commit 85c58fd

File tree

14 files changed

+507
-2
lines changed

14 files changed

+507
-2
lines changed

eng/.docsettings.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ omitted_paths:
2020
- sdk/servicebus/service-bus/test/perf-js-libs/service-bus-*
2121
- sdk/servicebus/service-bus/test/perf/**
2222
- sdk/servicebus/service-bus/test/stress/*
23+
- sdk/eventhub/event-hubs/test/perf/track-1/*
24+
- sdk/eventhub/event-hubs/test/perf/track-2/*
2325
- sdk/schemaregistry/README.md
2426
- sdk/storage/*/test/README.md
2527
- sdk/storage/storage-internal-avro/*

sdk/eventhub/event-hubs/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
"lint:fix": "eslint package.json api-extractor.json src test --ext .ts --fix --fix-type [problem,suggestion]",
6464
"lint": "eslint package.json api-extractor.json src test --ext .ts -f html -o event-hubs-lintReport.html || exit 0",
6565
"pack": "npm pack 2>&1",
66+
"perf-test:node": "tsc -p . --module \"commonjs\" && node dist-esm/test/perf/track-2/index.spec.js",
6667
"prebuild": "npm run clean",
6768
"test:browser": "npm run build:test && npm run unit-test:browser && npm run integration-test:browser",
6869
"test:node": "npm run build:test && npm run unit-test:node && npm run integration-test:node",
@@ -107,6 +108,7 @@
107108
"@azure/dev-tool": "^1.0.0",
108109
"@azure/eslint-plugin-azure-sdk": "^3.0.0",
109110
"@azure/identity": "^1.1.0",
111+
"@azure/test-utils-perfstress": "^1.0.0",
110112
"@microsoft/api-extractor": "7.7.11",
111113
"@rollup/plugin-commonjs": "11.0.2",
112114
"@rollup/plugin-inject": "^4.0.0",
@@ -149,6 +151,7 @@
149151
"karma-sourcemap-loader": "^0.3.8",
150152
"mocha": "^7.1.1",
151153
"mocha-junit-reporter": "^1.18.0",
154+
"moment": "^2.24.0",
152155
"nyc": "^14.0.0",
153156
"prettier": "^1.16.4",
154157
"puppeteer": "^3.3.0",

sdk/eventhub/event-hubs/src/eventHubSender.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ import { defaultDataTransformer } from "./dataTransformer";
4040
*/
4141
export class EventHubSender extends LinkEntity {
4242
/**
43-
* @property senderLock The unqiue lock name per connection that is used to acquire the
43+
* @property senderLock The unique lock name per connection that is used to acquire the
4444
* lock for establishing a sender link by an entity on that connection.
4545
* @readonly
4646
*/
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
### Guide
2+
3+
1. Navigate to `test-utils\perfstress` folder `cd sdk\test-utils\perfstress\`
4+
2. Build the package `rush update && rush build -t test-utils-perfstress`
5+
3. Pack the perf package `rushx pack`. This step would create a `azure-test-utils-perfstress-1.0.0.tgz` file at `test-utils\perfstress` folder.
6+
4. Navigate to `event-hubs\perf\track-1` folder `cd sdk\eventhub\event-hubs\perf\track-1`.
7+
5. Run `npm install` to get `event-hubs V2` and the perf package at `test-utils\perfstress` folder.
8+
6. Create an event-hubs namespace and populate the .env file at `eventhub\event-hubs` folder with `EVENTHUB_CONNECTION_STRING`, `EVENTHUB_NAME` and `CONSUMER_GROUP_NAME` variables.
9+
7. Run the tests as follows from the `event-hubs` folder.
10+
- batch send
11+
- `npm run perf-test:node -- SendTest --warmup 2 --duration 7 --parallel 2`
12+
- receive(Standalone test - doesn't use the framework)
13+
- `ts-node receive.spec.ts`
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT license.
3+
4+
import { PerfStressProgram, selectPerfStressTest } from "@azure/test-utils-perfstress";
5+
import { SendTest } from "./send.spec";
6+
7+
console.log("=== Starting the perfStress test ===");
8+
9+
const perfStressProgram = new PerfStressProgram(selectPerfStressTest([SendTest]));
10+
11+
perfStressProgram.run();
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"name": "track-1",
3+
"version": "1.0.0",
4+
"description": "",
5+
"main": "index.js",
6+
"keywords": [],
7+
"author": "",
8+
"license": "ISC",
9+
"dependencies": {
10+
"@azure/event-hubs": "^2.1.4",
11+
"@azure/test-utils-perfstress": "file:../../../../../test-utils/perfstress/azure-test-utils-perfstress-1.0.0.tgz"
12+
},
13+
"scripts": {
14+
"perf-test:node": "ts-node index.spec.ts"
15+
}
16+
}
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
/*
2+
# Overview
3+
Measures the maximum throughput of `receiver.receive()` in package `@azure/event-hubs`.
4+
5+
# Instructions
6+
1. Create an Event Hubs namespace with `Tier=Standard` and `Throughput Units=20`.
7+
2. Create an Event Hub inside the namespace.
8+
3. Set env vars `EVENTHUB_CONNECTION_STRING`, `EVENTHUB_NAME` and `CONSUMER_GROUP_NAME` at the .env file at `eventhub\event-hubs` folder.
9+
4. This test presumes that there are no messages in the event hub.
10+
5. `ts-node receive.ts [eventBodySize] [numberOfEvents]`
11+
6. Example: `ts-node receive.ts 1024 1000000`
12+
*/
13+
14+
import { EventHubClient, EventPosition, EventData } from "@azure/event-hubs";
15+
import { getEnvVar } from "@azure/test-utils-perfstress";
16+
import moment from "moment";
17+
import { delay } from "@azure/core-amqp";
18+
19+
// Load the .env file if it exists
20+
import * as dotenv from "dotenv";
21+
dotenv.config({ path: "../../../.env" });
22+
23+
const _start = moment();
24+
25+
let _messages = 0;
26+
27+
const connectionString = getEnvVar("EVENTHUB_CONNECTION_STRING");
28+
const eventHubName = getEnvVar("EVENTHUB_NAME");
29+
const consumerGroup = getEnvVar("CONSUMER_GROUP_NAME");
30+
31+
async function main(): Promise<void> {
32+
const eventBodySize = process.argv.length > 2 ? parseInt(process.argv[2]) : 1024;
33+
const numberOfEvents = process.argv.length > 3 ? parseInt(process.argv[3]) : 500;
34+
35+
const client = EventHubClient.createFromConnectionString(connectionString, eventHubName);
36+
const partitionIds = await client.getPartitionIds();
37+
await client.close();
38+
39+
log(`Total messages: ${numberOfEvents}`);
40+
41+
await sendBatch(numberOfEvents, partitionIds, eventBodySize);
42+
const writeResultsPromise = WriteResults(numberOfEvents);
43+
44+
await RunTest(connectionString, eventHubName, numberOfEvents, partitionIds);
45+
await writeResultsPromise;
46+
}
47+
48+
async function sendBatch(numberOfEvents: number, partitionIds: string[], eventBodySize: number) {
49+
const _payload = Buffer.alloc(eventBodySize);
50+
const producer = EventHubClient.createFromConnectionString(connectionString, eventHubName);
51+
const numberOfPartitions = partitionIds.length;
52+
const numberOfEventsPerPartition = Math.ceil(numberOfEvents / numberOfPartitions);
53+
54+
for (let partitionId of partitionIds) {
55+
let numberOfEventsSent = 0;
56+
while (numberOfEventsSent <= numberOfEventsPerPartition) {
57+
await producer.send({ body: _payload }, partitionId);
58+
numberOfEventsSent++;
59+
}
60+
}
61+
await producer.close();
62+
}
63+
64+
async function RunTest(
65+
connectionString: string,
66+
eventHubName: string,
67+
messages: number,
68+
partitionIds: string[]
69+
): Promise<void> {
70+
const consumerClient = EventHubClient.createFromConnectionString(connectionString, eventHubName);
71+
72+
const onMessageHandler = async (_: EventData) => {
73+
_messages++;
74+
if (_messages === messages) {
75+
await consumerClient.close();
76+
}
77+
};
78+
const onErrorHandler = async (err: Error) => {
79+
console.log(`Error on partition : ${err}`);
80+
};
81+
82+
for (let partitionId of partitionIds) {
83+
consumerClient.receive(partitionId, onMessageHandler, onErrorHandler, {
84+
eventPosition: EventPosition.fromStart(),
85+
consumerGroup
86+
});
87+
}
88+
}
89+
90+
async function WriteResults(messages: number): Promise<void> {
91+
let lastMessages = 0;
92+
let lastElapsed = 0;
93+
let maxMessages = 0;
94+
let maxElapsed = Number.MAX_SAFE_INTEGER;
95+
96+
do {
97+
await delay(1000);
98+
99+
const receivedMessages = _messages;
100+
const currentMessages = receivedMessages - lastMessages;
101+
lastMessages = receivedMessages;
102+
103+
const elapsed = moment().diff(_start);
104+
const currentElapsed = elapsed - lastElapsed;
105+
lastElapsed = elapsed;
106+
107+
if (currentMessages / currentElapsed > maxMessages / maxElapsed) {
108+
maxMessages = currentMessages;
109+
maxElapsed = currentElapsed;
110+
}
111+
112+
WriteResult(
113+
receivedMessages,
114+
elapsed,
115+
currentMessages,
116+
currentElapsed,
117+
maxMessages,
118+
maxElapsed
119+
);
120+
} while (_messages < messages);
121+
}
122+
123+
function WriteResult(
124+
totalMessages: number,
125+
totalElapsed: number,
126+
currentMessages: number,
127+
currentElapsed: number,
128+
maxMessages: number,
129+
maxElapsed: number
130+
): void {
131+
const memoryUsage = process.memoryUsage();
132+
log(
133+
`\tTot Msg\t${totalMessages}` +
134+
`\tCur MPS\t${Math.round((currentMessages * 1000) / currentElapsed)}` +
135+
`\tAvg MPS\t${Math.round((totalMessages * 1000) / totalElapsed)}` +
136+
`\tMax MPS\t${Math.round((maxMessages * 1000) / maxElapsed)}` +
137+
`\tRSS\t${memoryUsage.rss}` +
138+
`\tHeapUsed\t${memoryUsage.heapUsed}`
139+
);
140+
}
141+
142+
function log(message: string): void {
143+
console.log(`[${moment().format("hh:mm:ss.SSS")}] ${message}`);
144+
}
145+
146+
main().catch((err) => {
147+
log(`Error occurred: ${err}`);
148+
});
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT license.
3+
4+
import {
5+
getEnvVar,
6+
PerfStressOptionDictionary,
7+
PerfStressTest
8+
} from "@azure/test-utils-perfstress";
9+
import { EventHubClient, EventData } from "@azure/event-hubs";
10+
11+
// Expects the .env file at the same level as the "test" folder
12+
import * as dotenv from "dotenv";
13+
dotenv.config({ path: "../../../.env" });
14+
15+
const connectionString = getEnvVar("EVENTHUB_CONNECTION_STRING");
16+
const eventHubName = getEnvVar("EVENTHUB_NAME");
17+
18+
interface SendTestOptions {
19+
eventBodySize: number;
20+
numberOfEvents: number;
21+
}
22+
23+
const client = EventHubClient.createFromConnectionString(connectionString, eventHubName);
24+
export class SendTest extends PerfStressTest<SendTestOptions> {
25+
producer: EventHubClient;
26+
eventBatch: EventData[];
27+
public options: PerfStressOptionDictionary<SendTestOptions> = {
28+
eventBodySize: {
29+
required: true,
30+
description: "Size in bytes",
31+
shortName: "sz",
32+
longName: "size",
33+
defaultValue: 1024
34+
},
35+
numberOfEvents: {
36+
required: true,
37+
description: "Number of events per send",
38+
shortName: "num",
39+
longName: "numberOfEvents",
40+
defaultValue: 10
41+
}
42+
};
43+
44+
constructor() {
45+
super();
46+
this.producer = client;
47+
const event = {
48+
body: Buffer.alloc(this.parsedOptions.eventBodySize.value!)
49+
};
50+
this.eventBatch = new Array(this.parsedOptions.numberOfEvents.value!).fill(event);
51+
}
52+
53+
public async globalCleanup() {
54+
await this.producer.close();
55+
}
56+
57+
async runAsync(): Promise<void> {
58+
await this.producer.sendBatch(this.eventBatch);
59+
}
60+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"compilerOptions": {
3+
"module": "commonjs",
4+
"esModuleInterop": true
5+
}
6+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
### Guide
2+
3+
1. Build the event-hubs package `rush build -t event-hubs`.
4+
2. Navigate to `event-hubs` folder `cd sdk\eventhub\event-hubs\`.
5+
3. Create an event-hubs namespace and populate the .env file at `eventhub\event-hubs` folder with `EVENTHUB_CONNECTION_STRING`, `EVENTHUB_NAME` and `CONSUMER_GROUP_NAME` variables.
6+
4. Run the tests as follows from the `event-hubs` folder.
7+
- batch send
8+
- `npm run perf-test:node -- SendTest --warmup 2 --duration 7 --parallel 2`
9+
- receive(Standalone test - doesn't use the framework)
10+
- `tsc -p . --module "commonjs" && node dist-esm\test\perf\track-2\receive.spec.js`

0 commit comments

Comments
 (0)