Skip to content

Commit 22280ce

Browse files
authored
Merge pull request #469 from Azure/chgagnon/triggerbindingsmerge
Trigger bindings -> main
2 parents f06252e + 7bcae01 commit 22280ce

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+4180
-66
lines changed

.editorconfig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ dotnet_analyzer_diagnostic.severity = error
1010
# Namespace does not match folder structure - Ideally this should be enabled but it seems to have issues with root level files so disabling for now
1111
dotnet_diagnostic.IDE0130.severity = none
1212

13+
# CA1805: Do not initialize unnecessarily - It's better to be explicit when initializing vars to ensure correct value is used
14+
dotnet_diagnostic.CA1805.severity = none
15+
1316
# Documentation related errors, remove once they are fixed
1417
dotnet_diagnostic.CS1591.severity = none
1518
dotnet_diagnostic.CS1573.severity = none

README.md

Lines changed: 180 additions & 18 deletions
Large diffs are not rendered by default.

performance/.editorconfig

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# See https://github.com/dotnet/roslyn-analyzers/blob/main/.editorconfig for an example on different settings and how they're used
2+
3+
[*.cs]
4+
5+
# Disabled
6+
dotnet_diagnostic.CA1309.severity = silent # Use ordinal StringComparison - this isn't important for tests and just adds clutter
7+
dotnet_diagnostic.CA1305.severity = silent # Specify IFormatProvider - this isn't important for tests and just adds clutter
8+
dotnet_diagnostic.CA1707.severity = silent # Identifiers should not contain underscores - this helps make test names more readable
9+
dotnet_diagnostic.CA2201.severity = silent # Do not raise reserved exception types - tests can throw whatever they want
10+
dotnet_diagnostic.CA1051.severity = silent # Do not declare visible instance fields - doesn't matter for tests

performance/SqlBindingBenchmarks.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ public static void Main()
1111
{
1212
BenchmarkRunner.Run<SqlInputBindingPerformance>();
1313
BenchmarkRunner.Run<SqlOutputBindingPerformance>();
14+
BenchmarkRunner.Run<SqlTriggerBindingPerformance>();
15+
BenchmarkRunner.Run<SqlTriggerBindingPerformance_BatchOverride>();
16+
BenchmarkRunner.Run<SqlTriggerBindingPerformance_PollingIntervalOverride>();
17+
BenchmarkRunner.Run<SqlTriggerPerformance_Overrides>();
18+
BenchmarkRunner.Run<SqlTriggerBindingPerformance_Parallelization>();
1419
}
1520
}
1621
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
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+
9+
namespace Microsoft.Azure.WebJobs.Extensions.Sql.Performance
10+
{
11+
[MemoryDiagnoser]
12+
public class SqlTriggerBindingPerformance : SqlTriggerBindingPerformanceTestBase
13+
{
14+
[GlobalSetup]
15+
public void GlobalSetup()
16+
{
17+
this.SetChangeTrackingForTable("Products", true);
18+
this.StartFunctionHost(nameof(ProductsTrigger), SupportedLanguages.CSharp);
19+
}
20+
21+
[Benchmark]
22+
[Arguments(1)]
23+
[Arguments(10)]
24+
[Arguments(100)]
25+
[Arguments(1000)]
26+
public async Task ProductsTriggerTest(int count)
27+
{
28+
await this.WaitForProductChanges(
29+
1,
30+
count,
31+
SqlChangeOperation.Insert,
32+
() => { this.InsertProducts(1, count); return Task.CompletedTask; },
33+
id => $"Product {id}",
34+
id => id * 100,
35+
this.GetBatchProcessingTimeout(1, count));
36+
}
37+
}
38+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
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 Microsoft.Azure.WebJobs.Extensions.Sql.Tests.Integration;
5+
using BenchmarkDotNet.Attributes;
6+
7+
namespace Microsoft.Azure.WebJobs.Extensions.Sql.Performance
8+
{
9+
public class SqlTriggerBindingPerformanceTestBase : SqlTriggerBindingIntegrationTests
10+
{
11+
[IterationCleanup]
12+
public void IterationCleanup()
13+
{
14+
// Delete all rows in Products table after each iteration so we start fresh each time
15+
this.ExecuteNonQuery("TRUNCATE TABLE Products");
16+
// Clear the leases table, otherwise we may end up getting blocked by leases from a previous run
17+
this.ExecuteNonQuery(@"DECLARE @cmd varchar(100)
18+
DECLARE cmds CURSOR FOR
19+
SELECT 'TRUNCATE TABLE az_func.' + Name + ''
20+
FROM sys.tables
21+
WHERE Name LIKE 'Leases_%'
22+
23+
OPEN cmds
24+
WHILE 1 = 1
25+
BEGIN
26+
FETCH cmds INTO @cmd
27+
IF @@fetch_status != 0 BREAK
28+
EXEC(@cmd)
29+
END
30+
CLOSE cmds;
31+
DEALLOCATE cmds");
32+
}
33+
34+
[GlobalCleanup]
35+
public void GlobalCleanup()
36+
{
37+
this.Dispose();
38+
}
39+
}
40+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
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+
9+
namespace Microsoft.Azure.WebJobs.Extensions.Sql.Performance
10+
{
11+
[MemoryDiagnoser]
12+
public class SqlTriggerBindingPerformance_Parallelization : SqlTriggerBindingPerformanceTestBase
13+
{
14+
[Params(2, 5)]
15+
public int HostCount;
16+
17+
[GlobalSetup]
18+
public void GlobalSetup()
19+
{
20+
this.SetChangeTrackingForTable("Products", true);
21+
for (int i = 0; i < this.HostCount; ++i)
22+
{
23+
this.StartFunctionHost(nameof(ProductsTrigger), SupportedLanguages.CSharp);
24+
}
25+
}
26+
27+
[Benchmark]
28+
public async Task MultiHost()
29+
{
30+
int firstId = 1;
31+
int lastId = 90;
32+
await this.WaitForProductChanges(
33+
firstId,
34+
lastId,
35+
SqlChangeOperation.Insert,
36+
() => { this.InsertProducts(firstId, lastId); return Task.CompletedTask; },
37+
id => $"Product {id}",
38+
id => id * 100,
39+
this.GetBatchProcessingTimeout(firstId, lastId));
40+
41+
firstId = 1;
42+
lastId = 60;
43+
// All table columns (not just the columns that were updated) would be returned for update operation.
44+
await this.WaitForProductChanges(
45+
firstId,
46+
lastId,
47+
SqlChangeOperation.Update,
48+
() => { this.UpdateProducts(firstId, lastId); return Task.CompletedTask; },
49+
id => $"Updated Product {id}",
50+
id => id * 100,
51+
this.GetBatchProcessingTimeout(firstId, lastId));
52+
53+
firstId = 31;
54+
lastId = 90;
55+
// The properties corresponding to non-primary key columns would be set to the C# type's default values
56+
// (null and 0) for delete operation.
57+
await this.WaitForProductChanges(
58+
firstId,
59+
lastId,
60+
SqlChangeOperation.Delete,
61+
() => { this.DeleteProducts(firstId, lastId); return Task.CompletedTask; },
62+
_ => null,
63+
_ => 0,
64+
this.GetBatchProcessingTimeout(firstId, lastId));
65+
}
66+
}
67+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
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.Collections.Generic;
9+
10+
namespace Microsoft.Azure.WebJobs.Extensions.Sql.Performance
11+
{
12+
[MemoryDiagnoser]
13+
public class SqlTriggerBindingPerformance_BatchOverride : SqlTriggerBindingPerformanceTestBase
14+
{
15+
16+
[Params(100, 1000)]
17+
public int BatchSize;
18+
19+
[GlobalSetup]
20+
public void GlobalSetup()
21+
{
22+
this.SetChangeTrackingForTable("Products", true);
23+
this.StartFunctionHost(
24+
nameof(ProductsTrigger),
25+
SupportedLanguages.CSharp,
26+
environmentVariables: new Dictionary<string, string>() {
27+
{ "Sql_Trigger_BatchSize", this.BatchSize.ToString() }
28+
});
29+
}
30+
31+
[Benchmark]
32+
[Arguments(0.1)]
33+
[Arguments(0.5)]
34+
[Arguments(1)]
35+
[Arguments(5)]
36+
public async Task Run(double numBatches)
37+
{
38+
int count = (int)(numBatches * this.BatchSize);
39+
await this.WaitForProductChanges(
40+
1,
41+
count,
42+
SqlChangeOperation.Insert,
43+
() => { this.InsertProducts(1, count); return Task.CompletedTask; },
44+
id => $"Product {id}",
45+
id => id * 100,
46+
this.GetBatchProcessingTimeout(1, count, batchSize: this.BatchSize));
47+
}
48+
}
49+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
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.Collections.Generic;
9+
10+
namespace Microsoft.Azure.WebJobs.Extensions.Sql.Performance
11+
{
12+
[MemoryDiagnoser]
13+
public class SqlTriggerPerformance_Overrides : SqlTriggerBindingPerformanceTestBase
14+
{
15+
[Params(1, 10, 100, 500)]
16+
public int PollingIntervalMs;
17+
18+
[Params(500, 1000, 2000)]
19+
public int BatchSize;
20+
21+
[GlobalSetup]
22+
public void GlobalSetup()
23+
{
24+
this.SetChangeTrackingForTable("Products", true);
25+
this.StartFunctionHost(
26+
nameof(ProductsTrigger),
27+
SupportedLanguages.CSharp,
28+
environmentVariables: new Dictionary<string, string>() {
29+
{ "Sql_Trigger_BatchSize", this.BatchSize.ToString() },
30+
{ "Sql_Trigger_PollingIntervalMs", this.PollingIntervalMs.ToString() }
31+
});
32+
}
33+
34+
[Benchmark]
35+
[Arguments(0.1)]
36+
[Arguments(0.5)]
37+
[Arguments(1)]
38+
[Arguments(5)]
39+
[Arguments(10)]
40+
public async Task Run(double numBatches)
41+
{
42+
int count = (int)(numBatches * this.BatchSize);
43+
await this.WaitForProductChanges(
44+
1,
45+
count,
46+
SqlChangeOperation.Insert,
47+
() => { this.InsertProducts(1, count); return Task.CompletedTask; },
48+
id => $"Product {id}",
49+
id => id * 100,
50+
this.GetBatchProcessingTimeout(1, count, batchSize: this.BatchSize));
51+
}
52+
}
53+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
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.Collections.Generic;
9+
10+
namespace Microsoft.Azure.WebJobs.Extensions.Sql.Performance
11+
{
12+
[MemoryDiagnoser]
13+
public class SqlTriggerBindingPerformance_PollingIntervalOverride : SqlTriggerBindingPerformanceTestBase
14+
{
15+
[Params(1, 10, 100, 500, 2000)]
16+
public int PollingIntervalMs;
17+
18+
[GlobalSetup]
19+
public void GlobalSetup()
20+
{
21+
this.SetChangeTrackingForTable("Products", true);
22+
this.StartFunctionHost(
23+
nameof(ProductsTrigger),
24+
SupportedLanguages.CSharp,
25+
environmentVariables: new Dictionary<string, string>() {
26+
{ "Sql_Trigger_PollingIntervalMs", this.PollingIntervalMs.ToString() }
27+
});
28+
}
29+
30+
[Benchmark]
31+
public async Task Run()
32+
{
33+
int count = SqlTableChangeMonitor<object>.DefaultBatchSize * 2;
34+
await this.WaitForProductChanges(
35+
1,
36+
count,
37+
SqlChangeOperation.Insert,
38+
() => { this.InsertProducts(1, count); return Task.CompletedTask; },
39+
id => $"Product {id}",
40+
id => id * 100,
41+
this.GetBatchProcessingTimeout(1, count, pollingIntervalMs: this.PollingIntervalMs));
42+
}
43+
}
44+
}

0 commit comments

Comments
 (0)