Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
a1a045e
Don't publish TSA results on PRs (#327)
Charles-Gagnon Aug 30, 2022
d026ecd
Add more debug logging (#328)
lucyzhang929 Aug 31, 2022
365efce
Add benchmark project for performance tests (#294)
lucyzhang929 Aug 31, 2022
76575f2
Fix localization/serialization issues (#329)
Charles-Gagnon Aug 31, 2022
337fb64
Add policheck and var for uploading to TSA (#332)
Charles-Gagnon Sep 1, 2022
c5a746f
Fix local build errors (#334)
Charles-Gagnon Sep 2, 2022
a916d35
Fix log error not printing out primary key name (#335)
Charles-Gagnon Sep 2, 2022
b680ca8
Don't run Roslyn analyzers on PRs (#336)
Charles-Gagnon Sep 6, 2022
f912d2c
Do not process rows if lease acquisition fails (#339)
JatinSanghvi Sep 9, 2022
3a92c76
Fix README link (#342)
Charles-Gagnon Sep 9, 2022
4da4c38
Escape special characters in Readme file (#346)
JatinSanghvi Sep 13, 2022
a27b080
Wait for error inside `StartFunctionsHostAndWaitForError` method (#349)
JatinSanghvi Sep 14, 2022
f23083b
Add test for multiple triggered functions (#343)
JatinSanghvi Sep 14, 2022
f42d273
Add retry when attempting to release leases (#340)
AmeyaRele Sep 14, 2022
dc40a5f
sql version telemetry data from input bindings (#337)
MaddyDev Sep 14, 2022
1dad068
Refactor locking logic in class `SqlTableChangeMonitor` (#357)
JatinSanghvi Sep 16, 2022
f4185a1
Merge commit '365efceb0fcb5dcb5f01957e6fd870070f628a0f' into triggerb…
Charles-Gagnon Sep 19, 2022
930f190
cherry-pick c5a746f7e70de3ed050289d3ab2651a39667abda
Charles-Gagnon Sep 2, 2022
3410e23
Merge commit '76575f29e964f3487684b84e279444279296f5c2' into chgagnon…
Charles-Gagnon Sep 19, 2022
4df33f9
Merge pull request #359 from Azure/chgagnon/mergeFromMain1
Charles-Gagnon Sep 19, 2022
d36422f
Add comments to explain LastSyncVersion selection (#358)
AmeyaRele Sep 20, 2022
942736c
Fix
Charles-Gagnon Sep 20, 2022
89f7981
Move test scripts
Charles-Gagnon Sep 20, 2022
a42b0fb
Merge branch 'main' into chgagnon/mergeFromMain3
Charles-Gagnon Sep 20, 2022
8270663
Merge pull request #360 from Azure/chgagnon/mergeFromMain2
Charles-Gagnon Sep 20, 2022
a7f19af
Merge pull request #361 from Azure/chgagnon/mergeFromMain3
Charles-Gagnon Sep 20, 2022
56d8289
Constructor cleanup (#362)
Charles-Gagnon Sep 20, 2022
2717881
Add configuration for batch size/polling interval (#364)
Charles-Gagnon Sep 22, 2022
0f5976e
Merge branch 'triggerbindings' into chgagnon/syncLogicApps
Charles-Gagnon Sep 22, 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
3 changes: 3 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ dotnet_analyzer_diagnostic.severity = error
# 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
dotnet_diagnostic.IDE0130.severity = none

# CA1805: Do not initialize unnecessarily - It's better to be explicit when initializing vars to ensure correct value is used
dotnet_diagnostic.CA1805.severity = none

# Documentation related errors, remove once they are fixed
dotnet_diagnostic.CS1591.severity = none
dotnet_diagnostic.CS1573.severity = none
Expand Down
1 change: 1 addition & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@
<PackageVersion Include="Microsoft.Azure.WebJobs.Script.ExtensionsMetadataGenerator" Version="1.2.3" />
<PackageVersion Include="Newtonsoft.Json" Version="11.0.2" />
<PackageVersion Include="Microsoft.AspNetCore.Http" Version="2.1.22" />
<PackageVersion Include="BenchmarkDotNet" Version="0.13.1" />
</ItemGroup>
</Project>
40 changes: 29 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ Azure SQL bindings for Azure Functions are supported for:
- [Python functions](#python-functions)
- [Input Binding Tutorial](#input-binding-tutorial-2)
- [Output Binding Tutorial](#output-binding-tutorial-2)
- [Configuration](#configuration)
- [Trigger Binding Configuration](#trigger-binding-configuration)
- [Sql_Trigger_BatchSize](#sql_trigger_batchsize)
- [Sql_Trigger_PollingIntervalMs](#sql_trigger_pollingintervalms)
- [More Samples](#more-samples)
- [Input Binding](#input-binding)
- [Query String](#query-string)
Expand All @@ -48,7 +52,7 @@ Azure SQL bindings for Azure Functions are supported for:
- [Stored Procedure](#stored-procedure)
- [IAsyncEnumerable](#iasyncenumerable)
- [Output Binding](#output-binding)
- [ICollector<T>/IAsyncCollector<T>](#icollectortiasynccollectort)
- [ICollector&lt;T&gt;/IAsyncCollector&lt;T&gt;](#icollectortiasynccollectort)
- [Array](#array)
- [Single Row](#single-row)
- [Primary Keys and Identity Columns](#primary-keys-and-identity-columns)
Expand Down Expand Up @@ -240,7 +244,7 @@ Note: This tutorial requires that a SQL database is setup as shown in [Create a
- Open your app that you created in [Create a Function App](#create-a-function-app) in VSCode
- Press 'F1' and search for 'Azure Functions: Create Function'
- Choose HttpTrigger -> (Provide a function name) -> Company.namespace -> anonymous
- In the file that opens, replace the 'public static async Task< IActionResult > Run' block with the below code.
- In the file that opens, replace the `public static async Task<IActionResult> Run` block with the below code.

```csharp
public static async Task<IActionResult> Run(
Expand Down Expand Up @@ -288,7 +292,7 @@ Note: This tutorial requires that a SQL database is setup as shown in [Create a
- Open your app in VSCode
- Press 'F1' and search for 'Azure Functions: Create Function'
- Choose HttpTrigger -> (Provide a function name) -> Company.namespace is fine -> anonymous
- In the file that opens, replace the 'public static async Task<IActionResult> Run' block with the below code
- In the file that opens, replace the `public static async Task<IActionResult> Run` block with the below code

```csharp
public static IActionResult Run(
Expand Down Expand Up @@ -377,7 +381,7 @@ Note: This tutorial requires that a SQL database is setup as shown in [Create a
- Open your app that you created in [Create a Function App](#create-a-function-app) in VSCode
- Press 'F1' and search for 'Azure Functions: Create Function'
- Choose HttpTrigger -> (Provide a function name) -> anonymous
- In the file that opens (index.js), replace the 'module.exports = async function (context, req)' block with the below code.
- In the file that opens (`index.js`), replace the `module.exports = async function (context, req)` block with the below code.

```javascript
module.exports = async function (context, req, employee) {
Expand Down Expand Up @@ -417,7 +421,7 @@ Note: This tutorial requires that a SQL database is setup as shown in [Create a
- Open your app in VSCode
- Press 'F1' and search for 'Azure Functions: Create Function'
- Choose HttpTrigger -> (Provide a function name) -> anonymous
- In the file that opens (index.js), replace the 'module.exports = async function (context, req)' block with the below code.
- In the file that opens (`index.js`), replace the `module.exports = async function (context, req)` block with the below code.

```javascript
module.exports = async function (context, req) {
Expand Down Expand Up @@ -472,7 +476,7 @@ Note: This tutorial requires that a SQL database is setup as shown in [Create a
- Open your app that you created in [Create a Function App](#create-a-function-app) in VSCode
- Press 'F1' and search for 'Azure Functions: Create Function'
- Choose HttpTrigger -> (Provide a function name) -> anonymous
- In the file that opens (__init__.py), replace the 'def main(req: func.HttpRequest) -> func.HttpResponse:' block with the below code.
- In the file that opens (`__init__.py`), replace the `def main(req: func.HttpRequest) -> func.HttpResponse:` block with the below code.

```python
def main(req: func.HttpRequest, employee: func.SqlRowList) -> func.HttpResponse:
Expand Down Expand Up @@ -515,7 +519,7 @@ Note: This tutorial requires that a SQL database is setup as shown in [Create a
- Open your app in VSCode
- Press 'F1' and search for 'Azure Functions: Create Function'
- Choose HttpTrigger -> (Provide a function name) -> anonymous
- In the file that opens (__init__.py), replace the 'def main(req: func.HttpRequest) -> func.HttpResponse:' block with the below code.
- In the file that opens (`__init__.py`), replace the `def main(req: func.HttpRequest) -> func.HttpResponse:` block with the below code.

```python
def main(req: func.HttpRequest, employee: func.Out[func.SqlRow]) -> func.HttpResponse:
Expand Down Expand Up @@ -554,6 +558,20 @@ Note: This tutorial requires that a SQL database is setup as shown in [Create a
- Hit 'F5' to run your code. Click the link to upsert the output array values in your SQL table. Your upserted values should launch in the browser.
- Congratulations! You have successfully created your first SQL output binding! Checkout [Output Binding](#Output-Binding) for more information on how to use it and explore on your own!

## Configuration

This section goes over some of the configuration values you can use to customize the SQL bindings. See [How to Use Azure Function App Settings](https://learn.microsoft.com/azure/azure-functions/functions-how-to-use-azure-function-app-settings) to learn more.

### Trigger Binding Configuration

#### Sql_Trigger_BatchSize

This controls the number of changes processed at once before being sent to the triggered function.

#### Sql_Trigger_PollingIntervalMs

This controls the delay in milliseconds between processing each batch of changes.

## More Samples

### Input Binding
Expand All @@ -567,8 +585,8 @@ The input binding takes four [arguments](https://github.com/Azure/azure-function

The following are valid binding types for the result of the query/stored procedure execution:

- **IEnumerable<T>**: Each element is a row of the result represented by `T`, where `T` is a user-defined POCO, or Plain Old C# Object. `T` should follow the structure of a row in the queried table. See the [Query String](#query-string) section for an example of what `T` should look like.
- **IAsyncEnumerable<T>**: Each element is again a row of the result represented by `T`, but the rows are retrieved "lazily". A row of the result is only retrieved when `MoveNextAsync` is called on the enumerator. This is useful in the case that the query can return a very large amount of rows.
- **IEnumerable&lt;T&gt;**: Each element is a row of the result represented by `T`, where `T` is a user-defined POCO, or Plain Old C# Object. `T` should follow the structure of a row in the queried table. See the [Query String](#query-string) section for an example of what `T` should look like.
- **IAsyncEnumerable&lt;T&gt;**: Each element is again a row of the result represented by `T`, but the rows are retrieved "lazily". A row of the result is only retrieved when `MoveNextAsync` is called on the enumerator. This is useful in the case that the query can return a very large amount of rows.
- **String**: A JSON string representation of the rows of the result (an example is provided [here](https://github.com/Azure/azure-functions-sql-extension/blob/main/samples/samples-csharp/InputBindingSamples/GetProductsString.cs)).
- **SqlCommand**: The SqlCommand is populated with the appropriate query and parameters, but the associated connection is not opened. It is the responsiblity of the user to execute the command and read in the results. This is useful in the case that the user wants more control over how the results are read in. An example is provided [here](https://github.com/Azure/azure-functions-sql-extension/blob/main/samples/samples-csharp/InputBindingSamples/GetProductsSqlCommand.cs).

Expand Down Expand Up @@ -708,13 +726,13 @@ The output binding takes two [arguments](https://github.com/Azure/azure-function

The following are valid binding types for the rows to be upserted into the table:

- **ICollector<T>/IAsyncCollector<T>**: Each element is a row represented by `T`, where `T` is a user-defined POCO, or Plain Old C# Object. `T` should follow the structure of a row in the queried table. See the [Query String](#query-string) for an example of what `T` should look like.
- **ICollector&lt;T&gt;/IAsyncCollector&lt;T&gt;**: Each element is a row represented by `T`, where `T` is a user-defined POCO, or Plain Old C# Object. `T` should follow the structure of a row in the queried table. See the [Query String](#query-string) for an example of what `T` should look like.
- **T**: Used when just one row is to be upserted into the table.
- **T[]**: Each element is again a row of the result represented by `T`. This output binding type requires manual instantiation of the array in the function.

The repo contains examples of each of these binding types [here](https://github.com/Azure/azure-functions-sql-extension/tree/main/samples/samples-csharp/OutputBindingSamples). A few examples are also included below.

#### ICollector<T>/IAsyncCollector<T>
#### ICollector&lt;T&gt;/IAsyncCollector&lt;T&gt;

When using an `ICollector`, it is not necessary to instantiate it. The function can add rows to the `ICollector` directly, and its contents are automatically upserted once the function exits.

Expand Down
9 changes: 9 additions & 0 deletions WebJobs.Extensions.Sql.sln
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
.editorconfig = .editorconfig
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Azure.WebJobs.Extensions.Sql.Performance", "performance\Microsoft.Azure.WebJobs.Extensions.Sql.Performance.csproj", "{1A5148B7-F877-4813-852C-F94D6EF795E8}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "performance", "performance", "{F0F3562F-9176-4461-98E4-13D38D3DD056}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -38,6 +42,10 @@ Global
{A5B55530-71C8-4A9A-92AD-1D33A5E80FC1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A5B55530-71C8-4A9A-92AD-1D33A5E80FC1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A5B55530-71C8-4A9A-92AD-1D33A5E80FC1}.Release|Any CPU.Build.0 = Release|Any CPU
{1A5148B7-F877-4813-852C-F94D6EF795E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1A5148B7-F877-4813-852C-F94D6EF795E8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1A5148B7-F877-4813-852C-F94D6EF795E8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1A5148B7-F877-4813-852C-F94D6EF795E8}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -46,6 +54,7 @@ Global
{B2E0EFB9-BEDB-48F7-BD5A-0E1010333475} = {3691FB44-971D-43FD-9B2F-916B8CE689DB}
{4453B407-2CA3-4011-BDE5-247FCE5C1974} = {21CDC01C-247B-46B0-A8F5-9D35686C628B}
{A5B55530-71C8-4A9A-92AD-1D33A5E80FC1} = {F7E99EB5-47D3-4B50-A6AA-D8D5508A121A}
{1A5148B7-F877-4813-852C-F94D6EF795E8} = {F0F3562F-9176-4461-98E4-13D38D3DD056}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {49902AA5-150F-4567-B562-4AA8549B2CF4}
Expand Down
5 changes: 3 additions & 2 deletions builds/TSAConfig.gdntsa
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"codebaseName": "Sql Bindings",
"notificationAliases": [
"sqltools@service.microsoft.com"
"sqlbindings@microsoft.com"
],
"codebaseAdmins": [
"REDMOND\\chlafren",
Expand All @@ -14,6 +14,7 @@
"tools": [
"BinSkim",
"RoslynAnalyzers",
"CredScan"
"CredScan",
"Policheck"
]
}
19 changes: 17 additions & 2 deletions builds/azure-pipelines/template-steps-build-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,14 @@ steps:
inputs:
useGlobalJson: true

# Run Policheck early to avoid scanning dependency folders
- task: securedevelopmentteam.vss-secure-development-tools.build-task-policheck.PoliCheck@2
displayName: 'Run PoliCheck'
inputs:
targetType: F
result: PoliCheck.xml
condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'))

- script: npm install -g azure-functions-core-tools
displayName: 'Install Azure Functions Core Tools'

Expand Down Expand Up @@ -84,12 +92,13 @@ steps:
arguments: 'analyze $(Build.SourcesDirectory)\src\bin\${{ parameters.configuration }}\* --recurse --verbose'
condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'))

# Don't run for PRs since this currently breaks on runs from forks. We run this daily ourselves anyways.
- task: securedevelopmentteam.vss-secure-development-tools.build-task-roslynanalyzers.RoslynAnalyzers@3
inputs:
userProvideBuildInfo: 'autoMsBuildInfo'
env:
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'))
condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'), ne(variables['Build.Reason'], 'PullRequest'))

- task: securedevelopmentteam.vss-secure-development-tools.build-task-credscan.CredScan@2
inputs:
Expand Down Expand Up @@ -135,7 +144,7 @@ steps:
inputs:
GdnPublishTsaOnboard: true
GdnPublishTsaConfigFile: '$(Build.SourcesDirectory)\builds\TSAConfig.gdntsa'
condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'))
condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'), eq(variables['TSA_UPLOAD'], 'true'))

# 5.0 isn't supported on Mac yet
- task: UseDotNet@2
Expand Down Expand Up @@ -253,3 +262,9 @@ steps:
displayName: 'Component Detection'
inputs:
failOnAlert: true

- task: securedevelopmentteam.vss-secure-development-tools.build-task-postanalysis.PostAnalysis@2
displayName: 'Post Analysis'
inputs:
GdnBreakPolicyMinSev: Error
condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT'))
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<!-- Need to set root namespace to empty for IDE0130 to work properly - otherwise it errors out on top-level namespaces for some reason -->
<RootNamespace></RootNamespace>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="BenchmarkDotNet" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\test\Microsoft.Azure.WebJobs.Extensions.Sql.Tests.csproj" />
<ProjectReference Include="..\samples\samples-csharp\Microsoft.Azure.WebJobs.Extensions.Sql.Samples.csproj" />
</ItemGroup>

<Target Name="CopySamples" AfterTargets="Build">
<ItemGroup>
<_CSharpCopyItems Include="..\samples\samples-csharp\bin\$(Configuration)\$(TargetFramework)\**\*.*" />
</ItemGroup>
<Copy SourceFiles="@(_CSharpCopyItems)" DestinationFolder="$(OutDir)\SqlExtensionSamples\CSharp\%(RecursiveDir)" />
<Message Text="Copied C# Samples output to $(OutDir)\SqlExtensionSamples\CSharp" Importance="high" />
</Target>

<!-- Copy C# Samples output to BenchmarkDotNet project bin (https://github.com/dotnet/BenchmarkDotNet/issues/946) -->
<ItemGroup>
<None Include="$(OutDir)\**\*">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<Link>%(RecursiveDir)\%(Filename)%(Extension)</Link>
<Visible>True</Visible>
</None>
</ItemGroup>

</Project>
16 changes: 16 additions & 0 deletions performance/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Running Performance Tests

## Pre-requisites
The performance tests are based on the IntegrationTestBase class. Follow the instructions to set up the pre-requisites for integration tests [here](../test/README.md#running-integration-tests).

## Run
The performance tests use BenchmarkDotNet to benchmark performance for input and output bindings.

Run the tests from the terminal.
```
cd performance
dotnet run -c Release
```

## Results
The test results will be generated in the BenchmarkDotNet.Artifacts folder.
16 changes: 16 additions & 0 deletions performance/SqlBindingBenchmarks.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

using BenchmarkDotNet.Running;

namespace Microsoft.Azure.WebJobs.Extensions.Sql.Performance
{
public class SqlBindingPerformance
{
public static void Main()
{
BenchmarkRunner.Run<SqlInputBindingPerformance>();
BenchmarkRunner.Run<SqlOutputBindingPerformance>();
}
}
}
41 changes: 41 additions & 0 deletions performance/SqlInputBindingPerformance.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs.Extensions.Sql.Samples.Common;
using Microsoft.Azure.WebJobs.Extensions.Sql.Samples.InputBindingSamples;
using Microsoft.Azure.WebJobs.Extensions.Sql.Tests.Common;
using Microsoft.Azure.WebJobs.Extensions.Sql.Tests.Integration;
using BenchmarkDotNet.Attributes;

namespace Microsoft.Azure.WebJobs.Extensions.Sql.Performance
{
public class SqlInputBindingPerformance : IntegrationTestBase
{
[GlobalSetup]
public void GlobalSetup()
{
this.StartFunctionHost(nameof(GetProductsTopN), SupportedLanguages.CSharp);
Product[] products = GetProductsWithSameCost(10000, 100);
this.InsertProducts(products);
}

[Benchmark]
[Arguments("1")]
[Arguments("10")]
[Arguments("100")]
[Arguments("1000")]
[Arguments("10000")]
public async Task<HttpResponseMessage> GetProductsTest(string count)
{
return await this.SendInputRequest("getproductstopn", count);
}

[GlobalCleanup]
public void GlobalCleanup()
{
this.Dispose();
}
}
}
Loading