Skip to content

Commit 968647a

Browse files
Add change rate perf test (#564)
* Add SqlTriggerBindingIntegrationTestBase * more * Add Trigger ChangeRate performance test * Comment + use helper function
1 parent dda1947 commit 968647a

File tree

3 files changed

+94
-1
lines changed

3 files changed

+94
-1
lines changed

performance/SqlBindingBenchmarks.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ public static void Main(string[] args)
4444
{
4545
BenchmarkRunner.Run<SqlTriggerBindingPerformance_Parallelization>();
4646
}
47+
if (runAll || args.Contains("trigger_changerate"))
48+
{
49+
BenchmarkRunner.Run<SqlTriggerBindingPerformance_ChangeRate>();
50+
}
4751
}
4852
}
4953
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License. See License.txt in the project root for license information.
3+
4+
using System.Threading.Tasks;
5+
using Microsoft.Azure.WebJobs.Extensions.Sql.Samples.TriggerBindingSamples;
6+
using Microsoft.Azure.WebJobs.Extensions.Sql.Tests.Common;
7+
using BenchmarkDotNet.Attributes;
8+
using System.Threading;
9+
using Microsoft.Azure.WebJobs.Extensions.Sql.Tests.Integration;
10+
using System;
11+
12+
namespace Microsoft.Azure.WebJobs.Extensions.Sql.Performance
13+
{
14+
[MemoryDiagnoser]
15+
public class SqlTriggerBindingPerformance_ChangeRate : IDisposable
16+
{
17+
private SqlTriggerBindingIntegrationTests TestObject;
18+
19+
[IterationSetup]
20+
public void IterationSetup()
21+
{
22+
// We start a new instance each iteration because this test constantly inserts items
23+
// which results in more items being inserted than the test looks for (intentionally).
24+
// If we used the same trigger function for all runs then we run into issues with leftover
25+
// items from previous runs being picked up by the new function - so to get around this
26+
// we have to take the overhead hit of starting the function host each iteration to ensure
27+
// a completely clean start for each iteration.
28+
this.TestObject = new SqlTriggerBindingIntegrationTests();
29+
this.TestObject.SetChangeTrackingForTable("Products", true);
30+
this.TestObject.StartFunctionHost(nameof(ProductsTrigger), SupportedLanguages.CSharp);
31+
}
32+
33+
[IterationCleanup]
34+
public void IterationCleanup()
35+
{
36+
this.TestObject.Dispose();
37+
}
38+
39+
/// <summary>
40+
/// Runs a test with a high constant change rate. Items are inserted constantly until the test
41+
/// detects that the specified number of items has been processed. Note this will NOT be all
42+
/// of the items sent - those are being inserted at a much higher rate than the function can deal
43+
/// with.
44+
/// </summary>
45+
/// <param name="count">The number of items to process before ending the test</param>
46+
[Benchmark]
47+
[Arguments(1000)]
48+
public async Task ChangeRate(int count)
49+
{
50+
var tokenSource = new CancellationTokenSource();
51+
await this.TestObject.WaitForProductChanges(
52+
1,
53+
count,
54+
SqlChangeOperation.Insert,
55+
() => { this.ChangesLoop(tokenSource.Token); return Task.CompletedTask; },
56+
id => $"Product {id}",
57+
id => id * 100,
58+
this.TestObject.GetBatchProcessingTimeout(1, count)); // Wait for up to 30 seconds
59+
tokenSource.Cancel();
60+
}
61+
62+
private void ChangesLoop(CancellationToken token)
63+
{
64+
// Start off worker to insert items but then immediately return since
65+
// we're only going to be watching for a subset of the inserted items
66+
_ = Task.Run(() =>
67+
{
68+
const int ChangesInBatch = 50;
69+
int startIndex = 1, endIndex = ChangesInBatch;
70+
while (!token.IsCancellationRequested)
71+
{
72+
this.TestObject.InsertProducts(startIndex, endIndex);
73+
startIndex += ChangesInBatch;
74+
endIndex += ChangesInBatch;
75+
// No specific reason for this delay except to avoid having this take
76+
// up too many cycles when running the tests. 1 item/ms is fast enough
77+
// for our purposes currently
78+
Thread.Sleep(50);
79+
}
80+
}, token);
81+
}
82+
83+
public void Dispose()
84+
{
85+
GC.SuppressFinalize(this);
86+
this.TestObject.Dispose();
87+
}
88+
}
89+
}

test/Integration/SqlTriggerBindingIntegrationTestBase.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ void OutputHandler(object sender, DataReceivedEventArgs e)
187187
/// <param name="batchSize">The batch size if different than the default batch size</param>
188188
/// <param name="pollingIntervalMs">The polling interval in ms if different than the default polling interval</param>
189189
/// <returns></returns>
190-
protected int GetBatchProcessingTimeout(int firstId, int lastId, int batchSize = SqlTableChangeMonitor<object>.DefaultBatchSize, int pollingIntervalMs = SqlTableChangeMonitor<object>.DefaultPollingIntervalMs)
190+
public int GetBatchProcessingTimeout(int firstId, int lastId, int batchSize = SqlTableChangeMonitor<object>.DefaultBatchSize, int pollingIntervalMs = SqlTableChangeMonitor<object>.DefaultPollingIntervalMs)
191191
{
192192
int changesToProcess = lastId - firstId + 1;
193193
int calculatedTimeout = (int)(Math.Ceiling((double)changesToProcess / batchSize // The number of batches to process

0 commit comments

Comments
 (0)