Skip to content

Commit ddd73e1

Browse files
Merge pull request #222 from JocaPC/master
Automatic tuning FLGP demo
2 parents 64be802 + 4e592c6 commit ddd73e1

24 files changed

+782
-0
lines changed
118 KB
Loading
116 KB
Loading
259 KB
Loading
266 KB
Loading
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
*.xproj.user
2+
.vs/*
3+
.vscode/*
4+
bin/*
5+
obj/*
6+
*.sln
7+
*.log
8+
Properties/PublishProfiles/*
9+
*.development.json
10+
*.lock.json
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
using Belgrade.SqlClient;
2+
using Microsoft.AspNetCore.Mvc;
3+
using System;
4+
using System.Threading.Tasks;
5+
6+
namespace FlgpWwiDemo.Controllers
7+
{
8+
[Route("api/[controller]")]
9+
public class DemoController : Controller
10+
{
11+
IQueryMapper queryMapper = null;
12+
13+
public DemoController(IQueryMapper queryMapper)
14+
{
15+
this.queryMapper = queryMapper;
16+
}
17+
18+
// GET api/demo
19+
[HttpGet]
20+
[Produces("application/json")]
21+
public async Task<string> Get()
22+
{
23+
decimal result = 0;
24+
string status = "OK";
25+
long start = DateTimeOffset.Now.ToUnixTimeMilliseconds();
26+
long end = 0;
27+
await this.queryMapper
28+
.OnError(ex=> status = ex.Message)
29+
.ExecuteReader("EXEC dbo.report 7", reader => {
30+
result = reader.GetDecimal(0);
31+
end = DateTimeOffset.Now.ToUnixTimeMilliseconds();
32+
});
33+
return "{\"x\":\"" + DateTime.Now.ToUniversalTime().ToString() + "\",\"y\":" + (end-start) + ",\"start\":" + start + ",\"end\":" + end + ",\"result\":" + result +",\"status\":\"" + status + "\"}";
34+
}
35+
36+
37+
// GET api/demo/init
38+
[HttpGet("init")]
39+
public async Task Init()
40+
{
41+
await this.queryMapper.ExecuteReader("EXEC dbo.[initialize]", _ => { });
42+
}
43+
44+
// GET api/demo/regression
45+
[HttpGet("regression")]
46+
public async Task Regression()
47+
{
48+
await this.queryMapper.ExecuteReader("EXEC dbo.regression", _ => { });
49+
}
50+
51+
// GET api/demo/on
52+
[HttpGet("on")]
53+
public async Task On()
54+
{
55+
await this.queryMapper.ExecuteReader("EXEC dbo.auto_tuning_on", _ => { });
56+
}
57+
58+
59+
// GET api/demo/off
60+
[HttpGet("off")]
61+
public async Task Off()
62+
{
63+
await this.queryMapper.ExecuteReader("EXEC dbo.auto_tuning_off", _ => { });
64+
}
65+
}
66+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3+
<PropertyGroup>
4+
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
5+
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
6+
</PropertyGroup>
7+
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
8+
<PropertyGroup Label="Globals">
9+
<ProjectGuid>ca0088d0-bcb5-4485-89ab-860e23bcf9b7</ProjectGuid>
10+
<RootNamespace>FlgpWwiDemo</RootNamespace>
11+
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
12+
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
13+
<TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
14+
</PropertyGroup>
15+
<PropertyGroup>
16+
<SchemaVersion>2.0</SchemaVersion>
17+
</PropertyGroup>
18+
<Import Project="$(VSToolsPath)\DotNet.Web\Microsoft.DotNet.Web.targets" Condition="'$(VSToolsPath)' != ''" />
19+
</Project>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Linq;
5+
using System.Threading.Tasks;
6+
using Microsoft.AspNetCore.Hosting;
7+
using Microsoft.AspNetCore.Builder;
8+
9+
namespace FlgpWwiDemo
10+
{
11+
public class Program
12+
{
13+
public static void Main(string[] args)
14+
{
15+
var host = new WebHostBuilder()
16+
.UseKestrel()
17+
.UseContentRoot(Directory.GetCurrentDirectory())
18+
.UseIISIntegration()
19+
.UseStartup<Startup>()
20+
.Build();
21+
22+
host.Run();
23+
}
24+
}
25+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"iisSettings": {
3+
"windowsAuthentication": false,
4+
"anonymousAuthentication": true,
5+
"iisExpress": {
6+
"applicationUrl": "http://localhost:57485/",
7+
"sslPort": 0
8+
}
9+
},
10+
"profiles": {
11+
"IIS Express": {
12+
"commandName": "IISExpress",
13+
"launchBrowser": true,
14+
"launchUrl": "index.html",
15+
"environmentVariables": {
16+
"ASPNETCORE_ENVIRONMENT": "Development"
17+
}
18+
},
19+
"FlgpWwiDemo": {
20+
"commandName": "Project",
21+
"launchBrowser": true,
22+
"launchUrl": "http://localhost:5000/index.html",
23+
"environmentVariables": {
24+
"ASPNETCORE_ENVIRONMENT": "Development"
25+
}
26+
}
27+
}
28+
}
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
# Forcing last good plan
2+
This code sample demonstrates how [Automatic tuning in SQL Server 2017 CTP2.0+](https://docs.microsoft.com/sql/relational-databases/automatic-tuning/automatic-tuning) can identify and automatically fix performance problems in your workload.
3+
4+
### Contents
5+
6+
[About this sample](#about-this-sample)<br/>
7+
[Before you begin](#before-you-begin)<br/>
8+
[Run this sample](#run-this-sample)<br/>
9+
[Sample details](#sample-details)<br/>
10+
[Disclaimers](#disclaimers)<br/>
11+
[Related links](#related-links)<br/>
12+
13+
<a name=about-this-sample></a>
14+
15+
## About this sample
16+
1. **Applies to:** SQL Server 2017 (or higher) Enterprise / Developer / Evaluation Edition
17+
2. **Key features:**
18+
- Automatic tuning / forcing last good plan
19+
- Query Store
20+
3. **Workload:** Single analytic query executed on [WideWorldImporters](../../../databases/wide-world-importers) database
21+
4. **Programming Language:** T-SQL, .NET C#, JavaScript
22+
5. **Author:** Jovan Popovic [jovanpop-msft]
23+
24+
There are two ways to demonstrate the feature using this sample:
25+
- Level 300 demo: T-SQL code that simulates workload using T-SQL commands and shows results using dynamic management views and Query Store UI in SQL Server Management Studio.
26+
- Level 100-200 demo: ASP.NET application that simulates workload using AJAX requests sent to web server and shows results in the web page. Optionally use Query Store UI in SQL Server Management Studio to show performance regressions.
27+
28+
<a name=before-you-begin></a>
29+
30+
## Before you begin
31+
32+
To run this sample, you need the following prerequisites.
33+
34+
**Software prerequisites:**
35+
36+
1. SQL Server 2017 CTP2.0 (or higher)
37+
2. ASP.NET Core 1.0.1 installed (only if you want to use ASP.NET sample). Optionally Visual Studio Code or Visual Studio 2015 U3 (or higher)
38+
39+
<a name=run-this-sample></a>
40+
41+
## Run this sample
42+
43+
### Setup code
44+
1. Download [two T-SQL script files in sql-scripts](sql-scripts) folder if you want to use just T-SQL sample. Optionally you can clone this repository using [Git for Windows](http://www.git-scm.com/), or download the zip file.
45+
2. Download the [WideWorldImporters](../../../databases/wide-world-importers) database and restore it on your server.
46+
3. Execute setup.sql script on your [WideWorldImporters](../../../databases/wide-world-importers) database that will add necessary stored procedures and indexes.
47+
48+
### Configure ASP.NET sample (Only for ASP.NET Sample)
49+
1. Clone this repository using [Git for Windows](http://www.git-scm.com/), or download the zip file, if you have not done it.
50+
2. Open appsettings.json file in the root of the folder and change server, database, username, and password in the connection string.
51+
3. From the project root folder open command prompt and run `dotnet update`, `dotnet build`, and `dotnet run`. These commands will update NuGet packages, build project, and run web app. As an alternative,
52+
open project using Visual Studio 2015 U3, or Visual Studio Code, compile and run sample.
53+
54+
<a name=sample-details></a>
55+
## Sample Details
56+
57+
This sample demonstrates how SQL Server 2017 analyzes workload, keep track about the last good
58+
plan that successfully executed the query in the past, and reverts regressed plan if it is worse that the last known good plan.
59+
The following query is used to demonstrate plan regression and correction:
60+
61+
```
62+
select avg([UnitPrice]*[Quantity])
63+
from Sales.OrderLines
64+
where PackageTypeID = @packagetypeid
65+
```
66+
67+
This query is executed against [WideWorldImporters](../../../databases/wide-world-importers) database. The query can be executed using SQL plan that has **"Hash aggregate"** operator, which provides results quickly. However, sometime Query Optimizer might choose a plan with **"Stream aggregate"** operator that will cause the performance regression. This sample shows how Automatic tuning feature detects this kind of regression and automatically fix the problem.
68+
69+
### T-SQL Sample
70+
Open **demo-full.sql** and follow the comments in the code. Here is the short explanation of the scenario:
71+
72+
#### Part I - regression detection
73+
- Execute query `EXEC dbo.report 7` 30-300 times. SQL Database will collect statistics about the query. Number of queries that should be executed might vary depending on performance of your server (30 would be enough, but you might need to increase this number).
74+
- Execute query `EXEC dbo.regression` to cause the regression.
75+
- Execute query `EXEC dbo.report 7` 20 times and verify that the execution is slower.
76+
- Query `sys.dm_db_tuning_recommendations` and verify that regression is detected and that
77+
the correction script is in the view. Since some information are formatted as JSON docuemnts, you can use the following query to extract relevant information:
78+
79+
```
80+
SELECT planForceDetails.query_id, reason, score,
81+
JSON_VALUE(details, '$.implementationDetails.script') script,
82+
planForceDetails.[new plan_id], planForceDetails.[recommended plan_id]
83+
FROM sys.dm_db_tuning_recommendations
84+
CROSS APPLY OPENJSON (Details, '$.planForceDetails')
85+
WITH ( [query_id] int '$.queryId',
86+
[new plan_id] int '$.regressedPlanId',
87+
[recommended plan_id] int '$.forcedPlanId'
88+
) as planForceDetails;
89+
```
90+
91+
- Open Query Store UI in SSMS (e.g. "Top Resource Consuming Queries") and find the query. Verify that there are two plans - one faster with **Hash Aggregate** and another slower with **Stream Aggregate**, similar to the following figures:
92+
93+
![Last good plan](../../../../media/features/automatic-tuning/flgp-query-store-ui-last-good-plan.png "Last good plan")
94+
Fig. 1. Optimal plan with "Hash Aggregate".
95+
96+
![Regressed plan](../../../../media/features/automatic-tuning/flgp-query-store-ui-regressed-plan.png "Regressed plan")
97+
Fig. 2. Regressed plan with "Stream Aggregate".
98+
99+
- Take the correction script from the `sys.dm_db_tuning_recommendations` view and force the recommended plan.
100+
- Execute query `EXEC dbo.report 7` 20 times and verify that the execution is faster. Open Query Store UI in SSMS (e.g. "Top Resource Consuming Queries"), find the query, verify that the plan is forced and that the regression is fixed.
101+
102+
#### Part II - Automatic tuning
103+
- Reset the database state by executing `EXEC dbo.initialize` procedure, and enable automatic tuning on database.
104+
- Execute query `EXEC dbo.report 7` 30-300 times.
105+
- Execute query `EXEC dbo.regression` to cause the regression.
106+
- Execute query `EXEC dbo.report 7` 20 times and verify that the execution is slower.
107+
- Query `sys.dm_db_tuning_recommendations` and verify that regression is detected and that
108+
recommendation is in **Verifying** state.
109+
- Execute query `EXEC dbo.report 7` 30-50 times and verify that the execution is faster.
110+
- Open Query Store UI in SSMS (e.g. "Top Resource Consuming Queries") and find the query. Verify that there are two plans - one with **Hash Aggregate** and another with **Stream Aggregate**. Better plan should be forced, and you should see that the forced plan has better performance than regressed plan.
111+
112+
### ASP.NET Core Sample
113+
114+
This code sample contains a simple web page that periodically sends HTTP requests to the Web server. Web server executes T-SQL query against the database, returns query result, and calculates query elapsed time in each iteration.
115+
Web page collects response from the web server, calculates expected throughput based on the
116+
last 10 T-SQL request durations, and displays how many requests per second can be executed.
117+
118+
Open index.html page that will show number of requests per second that can be executed. Turn-on
119+
**Automatic tuning** using the **ON** button. You should see something like to following page:
120+
121+
![Web app](../../../../media/features/automatic-tuning/flgp-web-ui.png "Demo web app")
122+
Fig. 3. Number of requests per seconds.
123+
124+
> Number of requests per second might be different in your environment so, gauge needle might
125+
> be on left or right side. You can change the scale of the gauge if you manually edit index.html
126+
> file and change value 150 in the line 96: `var gauge = new GraphVizGauge("svg", { to: 150 });`
127+
> You can refresh the page after this change and the gauge will be re-scaled.
128+
129+
You can press **Regression** button to cause SQL plan choice regression in database layer. On the web page will be shown decreased number of requests per seconds that can be served.
130+
131+
![Web app](../../../../media/features/automatic-tuning/flgp-web-ui-regression.png "Demo web app")
132+
Fig. 4. Number of requests per seconds after regression.
133+
134+
After some time, you will notice that the regression will be automatically corrected. Pressing the **Regression** button again will not cause any regression.
135+
136+
<a name=disclaimers></a>
137+
138+
## Disclaimers
139+
The code included in this sample is not intended to be a set of best practices on how to build scalable enterprise grade applications. This is beyond the scope of this sample.
140+
141+
<a name=related-links></a>
142+
143+
## Related Links
144+
145+
- [Automatic tuning in SQL Server 2017 CTP2.0+](https://docs.microsoft.com/sql/relational-databases/automatic-tuning/automatic-tuning)
146+
- [sys.dm_db_tuning_recommendations view (Transact-SQL)](https://docs.microsoft.com/sql/relational-databases/system-dynamic-management-views/sys-dm-db-tuning-recommendations-transact-sql)
147+
- [Monitoring Performance By Using the Query Store](https://docs.microsoft.com/en-us/sql/relational-databases/performance/monitoring-performance-by-using-the-query-store)
148+
149+

0 commit comments

Comments
 (0)