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+ }
0 commit comments