Skip to content
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

Eventhub Stress Test Onboarding #28320

Merged
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
31ef454
stress test initial setup
m-redding Mar 28, 2022
2c97223
initial setup of publish test after refactoring/app insights integration
m-redding Apr 6, 2022
7e67bda
added basic buffered test
m-redding Apr 11, 2022
bcf27f8
deployment infrastructure update
m-redding Apr 12, 2022
32d1a79
updated buffered producer test
m-redding Apr 15, 2022
99f6135
buffered producer test cleanup
m-redding Apr 15, 2022
d68b9a8
buffered producer test
m-redding Apr 15, 2022
3bb3cfc
buffered producer test
m-redding Apr 15, 2022
72de30e
Adding more scenarios and switching to event producer test
m-redding Apr 21, 2022
8e3ee3f
restructuring metrics
m-redding Apr 21, 2022
9c24f75
more metrics updates
m-redding Apr 21, 2022
7f63000
Merge branch 'Azure:main' into eventhub-stress-tests
m-redding Apr 21, 2022
2ee7f56
README updates
m-redding Apr 21, 2022
52bfa86
Merge branch 'eventhub-stress-tests' of https://github.com/m-redding/…
m-redding Apr 21, 2022
78cc71b
Update sdk/eventhub/Azure.Messaging.EventHubs/stress/README.md
m-redding Apr 22, 2022
a798c8f
Update sdk/eventhub/Azure.Messaging.EventHubs/stress/src/Infrastructu…
m-redding Apr 22, 2022
25ec412
initial refactoring
m-redding Apr 25, 2022
f16edd8
deploy yaml file updates
m-redding Apr 25, 2022
8c6ac6d
readme updates
m-redding Apr 26, 2022
dbb7258
Delete stress-test-resources.json
m-redding Apr 26, 2022
1013982
dockerfile updates
m-redding Apr 26, 2022
ae204ce
Delete stress-test-resources.json
m-redding Apr 26, 2022
bf4230b
comment and main updates
m-redding Apr 28, 2022
925888e
Update sdk/eventhub/Azure.Messaging.EventHubs/stress/README.md
m-redding Apr 28, 2022
a86d0a6
Update sdk/eventhub/Azure.Messaging.EventHubs/stress/src/Infrastructu…
m-redding Apr 29, 2022
518aa6d
Update sdk/eventhub/Azure.Messaging.EventHubs/stress/src/Configuratio…
m-redding Apr 29, 2022
eafe6a5
Update sdk/eventhub/Azure.Messaging.EventHubs/stress/src/Configuratio…
m-redding Apr 29, 2022
da89c76
feedback updates
m-redding Apr 29, 2022
c5cd920
Added Azure Event Listener
m-redding Apr 29, 2022
3dab67c
buffered producer options
m-redding Apr 29, 2022
02ce4ee
typo
m-redding Apr 29, 2022
2161001
Event Hubs Stress Test Onboarding
m-redding Apr 29, 2022
ca66f38
Event Hubs Stress Test Onboarding
m-redding Apr 29, 2022
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
1 change: 1 addition & 0 deletions eng/Packages.Data.props
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@
<PackageReference Update="FluentAssertions" Version="5.10.3" />
<PackageReference Update="FsCheck.Xunit" Version="2.14.0" />
<PackageReference Update="Microsoft.Azure.ApplicationInsights.Query" Version="1.0.0" />
<PackageReference Update="Microsoft.ApplicationInsights" Version="2.20.0" />
m-redding marked this conversation as resolved.
Show resolved Hide resolved
<PackageReference Update="Microsoft.AspNetCore" Version="2.2.0" />
<PackageReference Update="Microsoft.AspNetCore.Mvc.Testing" Version="2.2.0" />
<PackageReference Update="Microsoft.AspNetCore.Server.Kestrel" Version="2.1.3" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
m-redding marked this conversation as resolved.
Show resolved Hide resolved
<PropertyGroup>
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
<HasSharedItems>true</HasSharedItems>
</PropertyGroup>

<PropertyGroup>
<Import_RootNamespace>Azure.Messaging.EventHubs.Tests</Import_RootNamespace>
</PropertyGroup>

<ItemGroup>
<Compile Include="$(MSBuildThisFileDirectory)Testing\EventGenerator.cs" Link="SharedSource\Stress\%(Filename)%(Extension)" />
</ItemGroup>
</Project>
6 changes: 6 additions & 0 deletions sdk/eventhub/Azure.Messaging.EventHubs/stress/Chart.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
dependencies:
- name: stress-test-addons
repository: https://stresstestcharts.blob.core.windows.net/helm/
version: 0.1.13
digest: sha256:007ec9983233e0f8c8ad8aa7f1df5f57b1b4c127d223d59d233b3c5366bd5173
generated: "2022-04-12T11:20:37.5250017-07:00"
15 changes: 15 additions & 0 deletions sdk/eventhub/Azure.Messaging.EventHubs/stress/Chart.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
apiVersion: v2
name: eventhub-net-stress-test
version: 0.1.1
description: Stress tests for Event Hubs for .NET

dependencies:
- name: stress-test-addons
version: 0.1.13
repository: https://stresstestcharts.blob.core.windows.net/helm/
m-redding marked this conversation as resolved.
Show resolved Hide resolved

annotations:
stressTest: 'true'
namespace: 'net'
dockerbuilddir: '../../../..'
dockerfile: './Dockerfile'
m-redding marked this conversation as resolved.
Show resolved Hide resolved
28 changes: 28 additions & 0 deletions sdk/eventhub/Azure.Messaging.EventHubs/stress/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# right version of dotnet?
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build-env

# Copy in engineering system needed to build
COPY /sdk/eventhub/Azure.Messaging.EventHubs/ /app/sdk/eventhub/Azure.Messaging.EventHubs/
m-redding marked this conversation as resolved.
Show resolved Hide resolved
COPY /eng /app/eng
COPY /tools /app/tools
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do these also need to be ./ instead of /?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They're referencing the root, and aren't relative to the current directory, so I believe they're correct as written.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@benbp: Any thoughts on templatizing some of this? The inclusions here will be needed for any .NET library to build due to the engsys dependencies.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jsquire I'm definitely in favor of common stress components, either global or language level. For this dockerfile, my initial thought would be to create an intermediate builder image, so you then you can do something like FROM azsdkengsys.azurecr.io/stress/dotnet/base AS build-env?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Development could be somewhat annoying if you're iterating on both, so we may want to provide a way to build multiple dockerfiles in sequence if it starts getting iterated on heavily.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really like the idea of the intermediate image.

COPY ./*.proj /app
COPY ./Directory.Build.props /app
COPY ./Directory.Build.targets /app
COPY ./NuGet.Config /app
COPY ./sdk/eventhub/Azure.Messaging.EventHubs.Shared /app/sdk/eventhub/Azure.Messaging.EventHubs.Shared
COPY ./sdk/core /app/sdk/core

# Run restore and build, copy the .dll files over so they can used in the running container
WORKDIR /app
RUN dotnet restore './sdk/eventhub/Azure.Messaging.EventHubs/stress/src/'
RUN dotnet build './sdk/eventhub/Azure.Messaging.EventHubs/stress/src/'
COPY /artifacts/bin/Azure.Messaging.EventHubs.Stress/Debug /app/bin/Debug
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We definitely want to change this over to a Release build so that we're testing the same configuration as we'd be releasing.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we consider coping all of the things that we're interested in into a common /dist directory so that we can just copy that on L24-26? That would localize the knowledge about where the build output lives into the step doing the build.


# Copy in the dll files to be ready to run
FROM build-env as publish
WORKDIR /app
COPY --from=build-env /app/artifacts/bin/Azure.Messaging.EventHubs.Stress/Debug /app/artifacts/bin/Azure.Messaging.EventHubs.Stress/Debug
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would we want this to be /app/bin/Debug as the source, since we're coping the output there on L19?

COPY --from=build-env /app/sdk/eventhub/Azure.Messaging.EventHubs.Shared /app/sdk/evethub/Azure.Messaging.EventHubs.Shared
COPY --from=build-env /app/sdk/eventhub/Azure.Messaging.EventHubs/stress /app/sdk/eventhub/Azure.Messaging.EventHubs/stress
jsquire marked this conversation as resolved.
Show resolved Hide resolved

WORKDIR /app/artifacts/bin/Azure.Messaging.EventHubs.Stress/Debug/net6.0
63 changes: 63 additions & 0 deletions sdk/eventhub/Azure.Messaging.EventHubs/stress/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Event Hub Stress Tests
m-redding marked this conversation as resolved.
Show resolved Hide resolved
m-redding marked this conversation as resolved.
Show resolved Hide resolved
This is a preliminary README. It demonstrates how to use the stress tests in the current state.

In order to run the stress tests locally, the necessary resource connections need to be input through the command line interface. Test runs can call any of the following tests:
- EventProducerTest : "EventProd"
- EventBufferedProducerTest : "EventBuffProd"
- BurstBufferedProducerTest : "BurstBuffProd"
- ConcurrentBufferedProducerTest : "ConcurBuffProd"

## Local Stress Test Runs
### Setting up resources and packages
```cmd
(env) ~/stress/src> dotnet clean
(env) ~/stress/src> dotnet build
```

### Running Tests
When tests are run locally, Azure resources need to be created prior to running the test. This can be done through the Azure CLI, an ARM template or bicep file, or the Azure Portal. The user is required to input the connection strings upon request on the command line when the test is being run. For more information about what resources are needed for each test, see the "Scenario Information" section below.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ckairen I think we should also support a "deploy resources only" type mode to make local testing easier with resources.


The recommended approach is to run tests one at a time when running locally.
To run any one test, run the following:
```cmd
(env) ~/stress/src> dotnet run -f netcoreapp3.1 BasicPublishReadTest local
```

## Deploy a stress test
In order to deploy stress tests to be run in kubernetes clusters, run:
```cmd
(env) ~/azure-sdk-for-net/eng/common/scripts/stress-testing/deploy-stress-tests.ps1 `
m-redding marked this conversation as resolved.
Show resolved Hide resolved
>> -Login `
>> -PushImages
```
This command requires Azure login credentials.

## Scenario Information
### Event Producer Test
This test requires an event hub namespace, an event hub, and an application insights resource. Note that an event hub may experience throttling if too few partitions are used. This test creates 2 producers and has 5 concurrent processes per producer sending batches of events. This is a long-running, consistent volume test.

### Event Buffered Producer Test
This test requires an event hub namespace, an event hub, and an application insights resource. High CPU usage may be experienced if too few partitions are used. This test creates 2 producers and continuously sends to each producer separately. This is a long-running, consistent volume test.

### Concurrent Buffered Producer Test
This test requires an event hub namespace, an event hub, and an application insights resource. High CPU usage may be experienced if too few partitions are used. This test creates 2 producers and has 5 concurrent processes per producer continuously sending events. This is a long-running, consistent volume test.

### Burst Buffered Producer Test
This test requires an event hub namespace, an event hub, and an application insights resource. Note that this test may have a high CPU usage. This test creates 2 producers and sends sets of events to each producer separately every 15 minutes. This is a long-running, variable volume test.

## Seeing Metrics and Logging in App Insights
All metrics and logging are sent to App Insights via the Instrumentation Key provided during the initialization of the test. A brief explanation of the metrics collection approach is described below.
- Any exceptions that occur are tracked by the telemetry client as exception telemetry. They can be accessed through the logs by filtering for exceptions, or through the application insights portal in the "Exceptions" blade. If an exception occured during send, the exception telemetry will include a "process" property containing "send"
- Successful enqueues and sends are tracked through Metrics. For the buffered producer, the total number of enqueues is tracked, and the actual sends include the ability to separate counts into partition Ids.
m-redding marked this conversation as resolved.
Show resolved Hide resolved

## Contributing

This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit https://cla.microsoft.com.

When you submit a pull request, a CLA-bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA.

This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [[email protected]](mailto:[email protected]) with any additional questions or comments.

Please see our [contributing guide](https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/eventhub/Azure.Messaging.EventHubs/CONTRIBUTING.md) for more information.

![Impressions](https://azure-sdk-impressions.azurewebsites.net/api/impressions/azure-sdk-for-net%2Fsdk%2Feventhub%2FAzure.Messaging.EventHubs%2stress%2FREADME.png)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This png fails to load on the readme.

Copy link
Member

@jsquire jsquire Apr 26, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Loads for me; it's one of our tracking pixels, so it will look like nothing. @benbp: Are you seeing it 404 or just nothing show up?

Copy link
Member

@benbp benbp Apr 26, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jsquire it looks like the problem is a missing F character in the url encoding: EventHubs %2 stress. I added a commit suggestion below.

image

It looks like this on the readme, that's how I caught it:

image

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ahhh.... I see it now. Yeah, that's on me. Thanks for the catch!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Chrome/Edge do a nice job of fixing the malformed encoding. Checking in the browser worked.

m-redding marked this conversation as resolved.
Show resolved Hide resolved
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Version>1.0.0</Version>
<OutputType>Exe</OutputType>
<LangVersion>latest</LangVersion>
<IsTestSupportProject>true</IsTestSupportProject>
<IsStressProject>false</IsStressProject>
Copy link
Member

@jsquire jsquire Apr 22, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@benbp: Now that the "other" perf/stress framework is just for performance, would it be possible to remove the engineering system behaviors keyed to IsStressProject? We don't want them here and it feels bad to have to say "this isn't a stress project" when it is.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was not familiar with these, but a cursory glance at the repo makes me think we can. @mikeharder?

</PropertyGroup>

<ItemGroup>
<PackageReference Include="System.Memory.Data" />
<PackageReference Include="Azure.Storage.Blobs" />
<PackageReference Include="Microsoft.ApplicationInsights" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\Azure.Messaging.EventHubs.csproj" />
</ItemGroup>

<!-- Import Azure.Core shared source -->
<ItemGroup>
<Compile Include="$(AzureCoreSharedSources)Argument.cs" LinkBase="SharedSource\Azure.Core" />
<Compile Include="$(AzureCoreSharedSources)TaskExtensions.cs" LinkBase="SharedSource\Azure.Core" />
</ItemGroup>

<!-- Import Event Hubs shared source -->
<Import Project="$(MSBuildThisFileDirectory)..\..\..\Azure.Messaging.EventHubs.Shared\src\Azure.Messaging.EventHubs.Shared.Stress.projitems" Label="Stress" />
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Azure.Messaging.EventHubs.Consumer;
using Azure.Messaging.EventHubs.Producer;
using Azure.Messaging.EventHubs.Tests;

namespace Azure.Messaging.EventHubs.Stress
{
internal class BufferedPublisher
{
private string connectionString;
private string eventHubName;
private Metrics metrics;
private ProducerConfiguration testConfiguration;

public BufferedPublisher(ProducerConfiguration testConfigurationIn, Metrics metricsIn)
{
connectionString = testConfigurationIn.EventHubsConnectionString;
eventHubName = testConfigurationIn.EventHub;
testConfiguration = testConfigurationIn;
metrics = metricsIn;
}

public async Task Start(CancellationToken cancellationToken)
{
var enqueueTasks = new List<Task>();

while (!cancellationToken.IsCancellationRequested)
{
using var backgroundCancellationSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);

try
{
var producer = new EventHubBufferedProducerClient(connectionString, eventHubName);
m-redding marked this conversation as resolved.
Show resolved Hide resolved
producer.SendEventBatchSucceededAsync += args =>
{
var numEvents = args.EventBatch.ToList().Count;
m-redding marked this conversation as resolved.
Show resolved Hide resolved
var eventProperties = new Dictionary<String, String>();
m-redding marked this conversation as resolved.
Show resolved Hide resolved

metrics.Client.GetMetric(metrics.SuccessfullySentFromQueue, args.PartitionId).TrackValue(numEvents);

return Task.CompletedTask;
};

producer.SendEventBatchFailedAsync += args =>
{
var numEvents = args.EventBatch.ToList().Count;
m-redding marked this conversation as resolved.
Show resolved Hide resolved

var eventProperties = new Dictionary<String, String>();
m-redding marked this conversation as resolved.
Show resolved Hide resolved

metrics.Client.GetMetric(metrics.EventsNotSentAfterEnqueue, args.PartitionId).TrackValue(numEvents);
metrics.Client.TrackException(args.Exception);

return Task.CompletedTask;
};

await using (producer.ConfigureAwait(false))
m-redding marked this conversation as resolved.
Show resolved Hide resolved
{
// Create a set of background tasks to handle all but one of the concurrent sends. The final
// send will be performed directly.

if (testConfiguration.ConcurrentSends > 1)
{
for (var index = 0; index < testConfiguration.ConcurrentSends - 1; ++index)
{
enqueueTasks.Add(Task.Run(async () =>
{
while (!cancellationToken.IsCancellationRequested)
{
await PerformSend(producer, cancellationToken).ConfigureAwait(false);

if ((testConfiguration.ProducerPublishingDelay.HasValue) && (testConfiguration.ProducerPublishingDelay.Value > TimeSpan.Zero))
{
await Task.Delay(testConfiguration.ProducerPublishingDelay.Value, backgroundCancellationSource.Token).ConfigureAwait(false);
}
}
}));
}
}

// Perform one of the sends in the foreground, which will allow easier detection of a
// processor-level issue.

while (!cancellationToken.IsCancellationRequested)
{
try
{
await PerformSend(producer, cancellationToken).ConfigureAwait(false);

if ((testConfiguration.ProducerPublishingDelay.HasValue) && (testConfiguration.ProducerPublishingDelay.Value > TimeSpan.Zero))
{
await Task.Delay(testConfiguration.ProducerPublishingDelay.Value, cancellationToken).ConfigureAwait(false);
}
}
catch (TaskCanceledException)
{
backgroundCancellationSource.Cancel();
await Task.WhenAll(enqueueTasks).ConfigureAwait(false);
}
}
}
}
catch (TaskCanceledException)
{
// No action needed.
}
catch (Exception ex) when
(ex is OutOfMemoryException
|| ex is StackOverflowException
|| ex is ThreadAbortException)
{
throw;
}
catch (Exception ex)
{
metrics.Client.TrackException(ex);
}
}
}

private async Task PerformSend(EventHubBufferedProducerClient producer,
m-redding marked this conversation as resolved.
Show resolved Hide resolved
CancellationToken cancellationToken)
{
var events = EventGenerator.CreateEvents(testConfiguration.EventEnqueueListSize);

try
{
await producer.EnqueueEventsAsync(events, cancellationToken).ConfigureAwait(false);

metrics.Client.GetMetric(metrics.EventsEnqueued).TrackValue(testConfiguration.EventEnqueueListSize);
}
catch (TaskCanceledException)
{
// Run is completed.
}
catch (Exception ex)
{
var eventProperties = new Dictionary<String, String>();
eventProperties.Add("Process", "Send");

metrics.Client.TrackException(ex, eventProperties);
}
}
}
}
Loading