Skip to content

Commit

Permalink
Add Query Store Top Queries
Browse files Browse the repository at this point in the history
Add Query Store top queries tab.  Using messaging system to get data directly from query store on monitored instances.
  • Loading branch information
DavidWiseman committed Jul 5, 2024
1 parent 4c097bf commit c52d25e
Show file tree
Hide file tree
Showing 27 changed files with 2,172 additions and 192 deletions.
34 changes: 34 additions & 0 deletions DBADash/CollectionConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -356,5 +356,39 @@ public DBADashConnection GetDestination(string hash)
{
return DestinationConnection.Hash == hash ? DestinationConnection : SecondaryDestinationConnections.FirstOrDefault(c => c.Hash == hash);
}

public DBADashSource GetSourceConnection(string connectionID)
{
var src = SourceConnections.FirstOrDefault(s => string.Equals(s.ConnectionID, connectionID, StringComparison.InvariantCultureIgnoreCase));
if (src != null) // We have a match on ConnectionID
{
return src;
}
else if (connectionID.Contains('|') && ScanForAzureDBs) // We don't have a match but ConnectionID looks like an AzureDB connection.
{
// Try to find the master connection for this AzureDB
var masterInstanceName = connectionID.Split('|')[0] + "|master";
var masterSrc = SourceConnections.FirstOrDefault(s => string.Equals(s.ConnectionID, masterInstanceName, StringComparison.InvariantCultureIgnoreCase));
if (masterSrc != null)
{
// Master connection found. Create a copy with the correct database name
src = masterSrc.DeepCopy();
src.ConnectionID = null;
var builder = new SqlConnectionStringBuilder(masterSrc.SourceConnection.ConnectionString)
{
InitialCatalog = connectionID.Split('|')[1]
};
src.SourceConnection.ConnectionString = builder.ToString();
var collector = new DBCollector(src, ServiceName);
src.ConnectionID = collector.ConnectionID;
// Double check that the generated ConnectionID matches the one we're looking for & return the connection
if (string.Equals(src.ConnectionID, connectionID, StringComparison.InvariantCultureIgnoreCase))
{
return src;
}
}
}
throw new ArgumentException($"Unable to find instance with ConnectionID {connectionID}");
}
}
}
2 changes: 2 additions & 0 deletions DBADash/DBADash.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
<None Remove="SQL\SQLIdentityColumns.sql" />
<None Remove="SQL\SQLJobs.sql" />
<None Remove="SQL\SQLJobSteps.sql" />
<None Remove="SQL\SQLQueryStoreTopQueries.sql" />
<None Remove="SQL\SQLRunningJobs.sql" />
<None Remove="SQL\SQLTableSize.sql" />
</ItemGroup>
Expand Down Expand Up @@ -69,6 +70,7 @@
<EmbeddedResource Include="SQL\SQLOSInfo.sql" />
<EmbeddedResource Include="SQL\SQLOSLoadedModules.sql" />
<EmbeddedResource Include="SQL\SQLPerformanceCounters.sql" />
<EmbeddedResource Include="SQL\SQLQueryStoreTopQueries.sql" />
<EmbeddedResource Include="SQL\SQLRemoveEventSessions.sql" />
<EmbeddedResource Include="SQL\SQLRemoveEventSessionsAzure.sql" />
<EmbeddedResource Include="SQL\SQLRunningJobs.sql" />
Expand Down
36 changes: 2 additions & 34 deletions DBADash/Messaging/CollectionMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public override async Task<DataSet> Process(CollectionConfig cfg, Guid handle)
handle,
CollectionTypes,
ConnectionID);
var src = GetSourceConnection(ConnectionID, cfg);
var src = cfg.GetSourceConnection(ConnectionID);

var (standardCollections, customCollections) = ParseCollectionTypes(src, cfg);

Expand Down Expand Up @@ -150,38 +150,6 @@ public override async Task<DataSet> Process(CollectionConfig cfg, Guid handle)
return (standardCollections, customCollections);
}

public static DBADashSource GetSourceConnection(string connectionID, CollectionConfig cfg)
{
var src = cfg.SourceConnections.FirstOrDefault(s => string.Equals(s.ConnectionID, connectionID, StringComparison.InvariantCultureIgnoreCase));
if (src != null) // We have a match on ConnectionID
{
return src;
}
else if (connectionID.Contains('|') && cfg.ScanForAzureDBs) // We don't have a match but ConnectionID looks like an AzureDB connection.
{
// Try to find the master connection for this AzureDB
var masterInstanceName = connectionID.Split('|')[0] + "|master";
var masterSrc = cfg.SourceConnections.FirstOrDefault(s => string.Equals(s.ConnectionID, masterInstanceName, StringComparison.InvariantCultureIgnoreCase));
if (masterSrc != null)
{
// Master connection found. Create a copy with the correct database name
src = masterSrc.DeepCopy();
src.ConnectionID = null;
var builder = new SqlConnectionStringBuilder(masterSrc.SourceConnection.ConnectionString)
{
InitialCatalog = connectionID.Split('|')[1]
};
src.SourceConnection.ConnectionString = builder.ToString();
var collector = new DBCollector(src, cfg.ServiceName);
src.ConnectionID = collector.ConnectionID;
// Double check that the generated ConnectionID matches the one we're looking for & return the connection
if (string.Equals(src.ConnectionID, connectionID, StringComparison.InvariantCultureIgnoreCase))
{
return src;
}
}
}
throw new ArgumentException($"Unable to find instance with ConnectionID {connectionID}");
}

}
}
4 changes: 2 additions & 2 deletions DBADash/Messaging/MessageProcessing.cs
Original file line number Diff line number Diff line change
Expand Up @@ -212,10 +212,10 @@ await SendReplyMessage(handle,
else
{
// Message is for this agent. Process the message
await msg.Process(Config, handle);
var ds = await msg.Process(Config, handle);
await SendReplyMessage(handle,
(new ResponseMessage()
{ Type = ResponseMessage.ResponseTypes.Success, Message = "Completed" }).Serialize(),
{ Type = ResponseMessage.ResponseTypes.Success, Message = "Completed", Data = ds}).Serialize(),
dest.ConnectionString);
}
}
Expand Down
54 changes: 54 additions & 0 deletions DBADash/Messaging/PlanCollectionMessage.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using Microsoft.Data.SqlClient;
using Serilog;
using SerilogTimings;
using System;
using System.Data;
using System.Threading.Tasks;

namespace DBADash.Messaging
{
public class PlanCollectionMessage : MessageBase
{
public string ConnectionID { get; set; }
public string DatabaseName { get; set; }

public long PlanID { get; set; }

public override async Task<DataSet> Process(CollectionConfig cfg, Guid handle)
{
if (IsExpired)
{
throw new Exception("Message expired");
}

using var op = Operation.Begin(
"Get Plan ID {id} from {database} on {instance} triggered from message {handle}",
PlanID,
DatabaseName,
ConnectionID,
handle);
try
{
var src = cfg.GetSourceConnection(ConnectionID);
var builder = new SqlConnectionStringBuilder(src.SourceConnection.ConnectionString)
{
InitialCatalog = DatabaseName
};
await using var cn = new SqlConnection(builder.ConnectionString);
await cn.OpenAsync();
await using var cmd =
new SqlCommand("SELECT query_plan FROM sys.query_store_plan WHERE plan_id = @plan_id", cn);
cmd.Parameters.AddWithValue("@plan_id", PlanID);
using var da = new SqlDataAdapter(cmd);
var ds = new DataSet();
da.Fill(ds);
return ds;
}
catch (Exception ex)
{
Log.Error(ex, "Plan collection request from {handle} failed", handle);
throw;
}
}
}
}
Loading

0 comments on commit c52d25e

Please sign in to comment.