Skip to content

Commit 937e05a

Browse files
authored
Merge pull request BrentOzarULTD#3921 from BrentOzarULTD/3903_sp_BlitzFirst_connections
sp_BlitzFirst: add Top 5 server/login/app breakdown to High Number of Connections
2 parents 2c5f1b9 + dc10aef commit 937e05a

1 file changed

Lines changed: 159 additions & 9 deletions

File tree

sp_BlitzFirst.sql

Lines changed: 159 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2664,15 +2664,165 @@ If one of them is a lead blocker, consider killing that query.'' AS HowToStopit,
26642664

26652665
IF @max_worker_threads > 0
26662666
BEGIN
2667-
INSERT INTO #BlitzFirstResults (CheckID, Priority, FindingsGroup, Finding, URL, Details)
2668-
SELECT 49 AS CheckID,
2669-
210 AS Priority,
2670-
'Potential Upcoming Problems' AS FindingGroup,
2671-
'High Number of Connections' AS Finding,
2672-
'https://www.brentozar.com/archive/2014/05/connections-slow-sql-server-threadpool/' AS URL,
2673-
'There are ' + CAST(SUM(1) AS VARCHAR(20)) + ' open connections, which would lead to ' + @LineFeed + 'worker thread exhaustion and THREADPOOL waits' + @LineFeed + 'if they all ran queries at the same time.' AS Details
2674-
FROM sys.dm_exec_connections c
2675-
HAVING SUM(1) > @max_worker_threads;
2667+
/* Count connections first so we only build the Top 5 breakdowns when the
2668+
alert actually fires. See issue #3903 for the feature request. */
2669+
DECLARE @TotalConnections INT;
2670+
SET @TotalConnections = (SELECT COUNT(*) FROM sys.dm_exec_connections);
2671+
2672+
IF @TotalConnections > @max_worker_threads
2673+
BEGIN
2674+
/* Load connection + session attributes into a table variable once so we can
2675+
aggregate it three different ways (by host, login, and app) without
2676+
re-reading the DMVs. sys.dm_exec_sessions.last_request_end_time defaults
2677+
to 1900-01-01 for sessions that have never run a request - treat that
2678+
sentinel as NULL so it renders as "unknown" rather than "45000 days ago". */
2679+
DECLARE @ConnSessions TABLE
2680+
(
2681+
HostName NVARCHAR(128) NOT NULL,
2682+
LoginName NVARCHAR(128) NOT NULL,
2683+
AppName NVARCHAR(128) NOT NULL,
2684+
LastFinish DATETIME NULL
2685+
);
2686+
2687+
INSERT INTO @ConnSessions (HostName, LoginName, AppName, LastFinish)
2688+
SELECT
2689+
COALESCE(NULLIF(s.host_name, N''), N'(unknown host)'),
2690+
COALESCE(NULLIF(s.login_name, N''), N'(unknown login)'),
2691+
COALESCE(NULLIF(s.program_name, N''), N'(unknown app)'),
2692+
NULLIF(s.last_request_end_time, CONVERT(DATETIME, '1900-01-01'))
2693+
FROM sys.dm_exec_connections c
2694+
LEFT JOIN sys.dm_exec_sessions s ON s.session_id = c.session_id;
2695+
2696+
DECLARE @TopServers NVARCHAR(MAX),
2697+
@TopLogins NVARCHAR(MAX),
2698+
@TopApps NVARCHAR(MAX);
2699+
2700+
SELECT @TopServers = STUFF((
2701+
SELECT @LineFeed
2702+
+ g.ConnectionGroup + ' - ' + CAST(g.ConnectionCount AS VARCHAR(20)) + ' connections'
2703+
+ ', most recent query finished '
2704+
+ CASE
2705+
WHEN g.MostRecentSec IS NULL THEN 'unknown'
2706+
WHEN g.MostRecentSec < 60 THEN CAST(g.MostRecentSec AS VARCHAR(10)) + ' seconds ago'
2707+
WHEN g.MostRecentSec < 3600 THEN CAST(g.MostRecentSec / 60 AS VARCHAR(10)) + ' minutes ago'
2708+
WHEN g.MostRecentSec < 86400 THEN CAST(g.MostRecentSec / 3600 AS VARCHAR(10)) + ' hours '
2709+
+ CAST((g.MostRecentSec % 3600) / 60 AS VARCHAR(10)) + ' minutes ago'
2710+
ELSE CAST(g.MostRecentSec / 86400 AS VARCHAR(10)) + ' days '
2711+
+ CAST((g.MostRecentSec % 86400) / 3600 AS VARCHAR(10)) + ' hours ago'
2712+
END
2713+
+ ', oldest query finished '
2714+
+ CASE
2715+
WHEN g.OldestSec IS NULL THEN 'unknown'
2716+
WHEN g.OldestSec < 60 THEN CAST(g.OldestSec AS VARCHAR(10)) + ' seconds ago'
2717+
WHEN g.OldestSec < 3600 THEN CAST(g.OldestSec / 60 AS VARCHAR(10)) + ' minutes ago'
2718+
WHEN g.OldestSec < 86400 THEN CAST(g.OldestSec / 3600 AS VARCHAR(10)) + ' hours '
2719+
+ CAST((g.OldestSec % 3600) / 60 AS VARCHAR(10)) + ' minutes ago'
2720+
ELSE CAST(g.OldestSec / 86400 AS VARCHAR(10)) + ' days '
2721+
+ CAST((g.OldestSec % 86400) / 3600 AS VARCHAR(10)) + ' hours ago'
2722+
END
2723+
FROM (
2724+
SELECT TOP (5)
2725+
ConnectionGroup = HostName,
2726+
ConnectionCount = COUNT(*),
2727+
MostRecentSec = DATEDIFF(SECOND, MAX(LastFinish), GETDATE()),
2728+
OldestSec = DATEDIFF(SECOND, MIN(LastFinish), GETDATE())
2729+
FROM @ConnSessions
2730+
GROUP BY HostName
2731+
ORDER BY COUNT(*) DESC, HostName
2732+
) g
2733+
ORDER BY g.ConnectionCount DESC, g.ConnectionGroup
2734+
FOR XML PATH(''), TYPE
2735+
).value(N'.[1]', N'NVARCHAR(MAX)'), 1, LEN(@LineFeed), N'');
2736+
2737+
SELECT @TopLogins = STUFF((
2738+
SELECT @LineFeed
2739+
+ g.ConnectionGroup + ' - ' + CAST(g.ConnectionCount AS VARCHAR(20)) + ' connections'
2740+
+ ', most recent query finished '
2741+
+ CASE
2742+
WHEN g.MostRecentSec IS NULL THEN 'unknown'
2743+
WHEN g.MostRecentSec < 60 THEN CAST(g.MostRecentSec AS VARCHAR(10)) + ' seconds ago'
2744+
WHEN g.MostRecentSec < 3600 THEN CAST(g.MostRecentSec / 60 AS VARCHAR(10)) + ' minutes ago'
2745+
WHEN g.MostRecentSec < 86400 THEN CAST(g.MostRecentSec / 3600 AS VARCHAR(10)) + ' hours '
2746+
+ CAST((g.MostRecentSec % 3600) / 60 AS VARCHAR(10)) + ' minutes ago'
2747+
ELSE CAST(g.MostRecentSec / 86400 AS VARCHAR(10)) + ' days '
2748+
+ CAST((g.MostRecentSec % 86400) / 3600 AS VARCHAR(10)) + ' hours ago'
2749+
END
2750+
+ ', oldest query finished '
2751+
+ CASE
2752+
WHEN g.OldestSec IS NULL THEN 'unknown'
2753+
WHEN g.OldestSec < 60 THEN CAST(g.OldestSec AS VARCHAR(10)) + ' seconds ago'
2754+
WHEN g.OldestSec < 3600 THEN CAST(g.OldestSec / 60 AS VARCHAR(10)) + ' minutes ago'
2755+
WHEN g.OldestSec < 86400 THEN CAST(g.OldestSec / 3600 AS VARCHAR(10)) + ' hours '
2756+
+ CAST((g.OldestSec % 3600) / 60 AS VARCHAR(10)) + ' minutes ago'
2757+
ELSE CAST(g.OldestSec / 86400 AS VARCHAR(10)) + ' days '
2758+
+ CAST((g.OldestSec % 86400) / 3600 AS VARCHAR(10)) + ' hours ago'
2759+
END
2760+
FROM (
2761+
SELECT TOP (5)
2762+
ConnectionGroup = LoginName,
2763+
ConnectionCount = COUNT(*),
2764+
MostRecentSec = DATEDIFF(SECOND, MAX(LastFinish), GETDATE()),
2765+
OldestSec = DATEDIFF(SECOND, MIN(LastFinish), GETDATE())
2766+
FROM @ConnSessions
2767+
GROUP BY LoginName
2768+
ORDER BY COUNT(*) DESC, LoginName
2769+
) g
2770+
ORDER BY g.ConnectionCount DESC, g.ConnectionGroup
2771+
FOR XML PATH(''), TYPE
2772+
).value(N'.[1]', N'NVARCHAR(MAX)'), 1, LEN(@LineFeed), N'');
2773+
2774+
SELECT @TopApps = STUFF((
2775+
SELECT @LineFeed
2776+
+ g.ConnectionGroup + ' - ' + CAST(g.ConnectionCount AS VARCHAR(20)) + ' connections'
2777+
+ ', most recent query finished '
2778+
+ CASE
2779+
WHEN g.MostRecentSec IS NULL THEN 'unknown'
2780+
WHEN g.MostRecentSec < 60 THEN CAST(g.MostRecentSec AS VARCHAR(10)) + ' seconds ago'
2781+
WHEN g.MostRecentSec < 3600 THEN CAST(g.MostRecentSec / 60 AS VARCHAR(10)) + ' minutes ago'
2782+
WHEN g.MostRecentSec < 86400 THEN CAST(g.MostRecentSec / 3600 AS VARCHAR(10)) + ' hours '
2783+
+ CAST((g.MostRecentSec % 3600) / 60 AS VARCHAR(10)) + ' minutes ago'
2784+
ELSE CAST(g.MostRecentSec / 86400 AS VARCHAR(10)) + ' days '
2785+
+ CAST((g.MostRecentSec % 86400) / 3600 AS VARCHAR(10)) + ' hours ago'
2786+
END
2787+
+ ', oldest query finished '
2788+
+ CASE
2789+
WHEN g.OldestSec IS NULL THEN 'unknown'
2790+
WHEN g.OldestSec < 60 THEN CAST(g.OldestSec AS VARCHAR(10)) + ' seconds ago'
2791+
WHEN g.OldestSec < 3600 THEN CAST(g.OldestSec / 60 AS VARCHAR(10)) + ' minutes ago'
2792+
WHEN g.OldestSec < 86400 THEN CAST(g.OldestSec / 3600 AS VARCHAR(10)) + ' hours '
2793+
+ CAST((g.OldestSec % 3600) / 60 AS VARCHAR(10)) + ' minutes ago'
2794+
ELSE CAST(g.OldestSec / 86400 AS VARCHAR(10)) + ' days '
2795+
+ CAST((g.OldestSec % 86400) / 3600 AS VARCHAR(10)) + ' hours ago'
2796+
END
2797+
FROM (
2798+
SELECT TOP (5)
2799+
ConnectionGroup = AppName,
2800+
ConnectionCount = COUNT(*),
2801+
MostRecentSec = DATEDIFF(SECOND, MAX(LastFinish), GETDATE()),
2802+
OldestSec = DATEDIFF(SECOND, MIN(LastFinish), GETDATE())
2803+
FROM @ConnSessions
2804+
GROUP BY AppName
2805+
ORDER BY COUNT(*) DESC, AppName
2806+
) g
2807+
ORDER BY g.ConnectionCount DESC, g.ConnectionGroup
2808+
FOR XML PATH(''), TYPE
2809+
).value(N'.[1]', N'NVARCHAR(MAX)'), 1, LEN(@LineFeed), N'');
2810+
2811+
INSERT INTO #BlitzFirstResults (CheckID, Priority, FindingsGroup, Finding, URL, Details)
2812+
VALUES (
2813+
49,
2814+
210,
2815+
'Potential Upcoming Problems',
2816+
'High Number of Connections',
2817+
'https://www.brentozar.com/archive/2014/05/connections-slow-sql-server-threadpool/',
2818+
'There are ' + CAST(@TotalConnections AS VARCHAR(20)) + ' open connections, which would lead to ' + @LineFeed
2819+
+ 'worker thread exhaustion and THREADPOOL waits' + @LineFeed
2820+
+ 'if they all ran queries at the same time.'
2821+
+ @LineFeed + @LineFeed + 'Top 5 Servers:' + @LineFeed + ISNULL(@TopServers, '(none)')
2822+
+ @LineFeed + @LineFeed + 'Top 5 Logins:' + @LineFeed + ISNULL(@TopLogins, '(none)')
2823+
+ @LineFeed + @LineFeed + 'Top 5 Apps:' + @LineFeed + ISNULL(@TopApps, '(none)')
2824+
);
2825+
END
26762826
END
26772827
END
26782828

0 commit comments

Comments
 (0)