Bug Fix - SQL Server HADR queries for SQL Versions (#8833)

This commit is contained in:
Avinash Nigam 2021-02-26 10:59:29 -08:00 committed by GitHub
parent accf91305f
commit b362ee4665
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 203 additions and 79 deletions

View File

@ -169,6 +169,96 @@ func TestSqlServer_MultipleInit(t *testing.T) {
assert.Equal(t, s2.isInitialized, true)
}
func TestSqlServer_AGQueriesApplicableForDatabaseTypeSQLServer(t *testing.T) {
// This test case checks where Availability Group (AG / HADR) queries return an output when included for processing for DatabaseType = SQLServer
// And they should not be processed when DatabaseType = AzureSQLDB
// Please change the connection string to connect to relevant database when executing the test case
t.Skip("Skipping as unable to open tcp connection with host '127.0.0.1:1433")
testServer := "Server=127.0.0.1;Port=1433;Database=testdb1;User Id=SA;Password=ABCabc01;app name=telegraf;log=1"
s := &SQLServer{
Servers: []string{testServer},
DatabaseType: "SQLServer",
IncludeQuery: []string{"SQLServerAvailabilityReplicaStates", "SQLServerDatabaseReplicaStates"},
}
s2 := &SQLServer{
Servers: []string{testServer},
DatabaseType: "AzureSQLDB",
IncludeQuery: []string{"SQLServerAvailabilityReplicaStates", "SQLServerDatabaseReplicaStates"},
}
var acc, acc2 testutil.Accumulator
err := s.Gather(&acc)
require.NoError(t, err)
assert.Equal(t, s.isInitialized, true)
assert.Equal(t, s2.isInitialized, false)
err = s2.Gather(&acc2)
require.NoError(t, err)
assert.Equal(t, s.isInitialized, true)
assert.Equal(t, s2.isInitialized, true)
// acc includes size metrics, and excludes memory metrics
assert.True(t, acc.HasMeasurement("sqlserver_hadr_replica_states"))
assert.True(t, acc.HasMeasurement("sqlserver_hadr_dbreplica_states"))
// acc2 includes memory metrics, and excludes size metrics
assert.False(t, acc2.HasMeasurement("sqlserver_hadr_replica_states"))
assert.False(t, acc2.HasMeasurement("sqlserver_hadr_dbreplica_states"))
}
func TestSqlServer_AGQueryFieldsOutputBasedOnSQLServerVersion(t *testing.T) {
// This test case checks where Availability Group (AG / HADR) queries return specific fields supported by corresponding SQL Server version database being connected to.
// Please change the connection strings to connect to relevant database when executing the test case
t.Skip("Skipping as unable to open tcp connection with host '127.0.0.1:1433")
testServer2019 := "Server=127.0.0.10;Port=1433;Database=testdb2019;User Id=SA;Password=ABCabc01;app name=telegraf;log=1"
testServer2012 := "Server=127.0.0.20;Port=1433;Database=testdb2012;User Id=SA;Password=ABCabc01;app name=telegraf;log=1"
s2019 := &SQLServer{
Servers: []string{testServer2019},
DatabaseType: "SQLServer",
IncludeQuery: []string{"SQLServerAvailabilityReplicaStates", "SQLServerDatabaseReplicaStates"},
}
s2012 := &SQLServer{
Servers: []string{testServer2012},
DatabaseType: "SQLServer",
IncludeQuery: []string{"SQLServerAvailabilityReplicaStates", "SQLServerDatabaseReplicaStates"},
}
var acc2019, acc2012 testutil.Accumulator
err := s2019.Gather(&acc2019)
require.NoError(t, err)
assert.Equal(t, s2019.isInitialized, true)
assert.Equal(t, s2012.isInitialized, false)
err = s2012.Gather(&acc2012)
require.NoError(t, err)
assert.Equal(t, s2019.isInitialized, true)
assert.Equal(t, s2012.isInitialized, true)
// acc2019 includes new HADR query fields
assert.True(t, acc2019.HasField("sqlserver_hadr_replica_states", "basic_features"))
assert.True(t, acc2019.HasField("sqlserver_hadr_replica_states", "is_distributed"))
assert.True(t, acc2019.HasField("sqlserver_hadr_replica_states", "seeding_mode"))
assert.True(t, acc2019.HasTag("sqlserver_hadr_replica_states", "seeding_mode_desc"))
assert.True(t, acc2019.HasField("sqlserver_hadr_dbreplica_states", "is_primary_replica"))
assert.True(t, acc2019.HasField("sqlserver_hadr_dbreplica_states", "secondary_lag_seconds"))
// acc2012 does not include new HADR query fields
assert.False(t, acc2012.HasField("sqlserver_hadr_replica_states", "basic_features"))
assert.False(t, acc2012.HasField("sqlserver_hadr_replica_states", "is_distributed"))
assert.False(t, acc2012.HasField("sqlserver_hadr_replica_states", "seeding_mode"))
assert.False(t, acc2012.HasTag("sqlserver_hadr_replica_states", "seeding_mode_desc"))
assert.False(t, acc2012.HasField("sqlserver_hadr_dbreplica_states", "is_primary_replica"))
assert.False(t, acc2012.HasField("sqlserver_hadr_dbreplica_states", "secondary_lag_seconds"))
}
const mockPerformanceMetrics = `measurement;servername;type;Point In Time Recovery;Available physical memory (bytes);Average pending disk IO;Average runnable tasks;Average tasks;Buffer pool rate (bytes/sec);Connection memory per connection (bytes);Memory grant pending;Page File Usage (%);Page lookup per batch request;Page split per batch request;Readahead per page read;Signal wait (%);Sql compilation per batch request;Sql recompilation per batch request;Total target memory ratio
Performance metrics;WIN8-DEV;Performance metrics;0;6353158144;0;0;7;2773;415061;0;25;229371;130;10;18;188;52;14`

View File

@ -9,11 +9,11 @@ import (
// Variable @MajorMinorVersion:
// - 1000 --> SQL Server 2008
// - 1050 --> SQL Server 2008 R2
// - 1011 --> SQL Server 2012
// - 1012 --> SQL Server 2014
// - 1013 --> SQL Server 2016
// - 1014 --> SQL Server 2017
// - 1015 --> SQL Server 2019
// - 1100 --> SQL Server 2012
// - 1200 --> SQL Server 2014
// - 1300 --> SQL Server 2016
// - 1400 --> SQL Server 2017
// - 1500 --> SQL Server 2019
// Thanks Bob Ward (http://aka.ms/bobwardms)
// and the folks at Stack Overflow (https://github.com/opserver/Opserver/blob/9c89c7e9936b58ad237b30e6f4cc6cd59c406889/Opserver.Core/Data/SQL/SQLInstance.Memory.cs)
@ -1169,6 +1169,8 @@ FROM (
) AS z
`
// Collects availability replica state information from `sys.dm_hadr_availability_replica_states` for a High Availability / Disaster Recovery (HADR) setup
// Certain fields are only supported on SQL Server 2016 and newer version, identified by check MajorMinorVersion >= 1300
const sqlServerAvailabilityReplicaStates string = `
IF SERVERPROPERTY('EngineEdition') NOT IN (2,3,4) BEGIN /*NOT IN Standard,Enterpris,Express*/
DECLARE @ErrorMessage AS nvarchar(500) = 'Telegraf - Connection string Server:'+ @@ServerName + ',Database:' + DB_NAME() +' is not a SQL Server Standard,Enterprise or Express. Check the database_type parameter in the telegraf configuration.';
@ -1176,50 +1178,65 @@ IF SERVERPROPERTY('EngineEdition') NOT IN (2,3,4) BEGIN /*NOT IN Standard,Enterp
RETURN
END
IF SERVERPROPERTY('IsHadrEnabled') = 1 BEGIN
SELECT
'sqlserver_hadr_replica_states' AS [measurement],
REPLACE(@@SERVERNAME,'\',':') AS [sql_instance],
convert(nvarchar(36), hars.replica_id) as replica_id,
ar.replica_server_name,
convert(nvarchar(36), hars.group_id) as group_id,
ag.name AS group_name,
ag.basic_features,
ag.is_distributed,
hags.synchronization_health_desc AS ag_synchronization_health_desc,
ar.replica_metadata_id,
ar.availability_mode,
ar.availability_mode_desc,
ar.failover_mode,
ar.failover_mode_desc,
ar.session_timeout,
ar.primary_role_allow_connections,
ar.primary_role_allow_connections_desc,
ar.secondary_role_allow_connections,
ar.secondary_role_allow_connections_desc,
ar.seeding_mode,
ar.seeding_mode_desc,
hars.is_local,
hars.role,
hars.role_desc,
hars.operational_state,
hars.operational_state_desc,
hars.connected_state,
hars.connected_state_desc,
hars.recovery_health,
hars.recovery_health_desc,
hars.synchronization_health AS replica_synchronization_health,
hars.synchronization_health_desc AS replica_synchronization_health_desc,
hars.last_connect_error_number,
hars.last_connect_error_description,
hars.last_connect_error_timestamp
from sys.dm_hadr_availability_replica_states AS hars
inner join sys.availability_replicas AS ar on hars.replica_id = ar.replica_id
inner join sys.availability_groups AS ag on ar.group_id = ag.group_id
inner join sys.dm_hadr_availability_group_states AS hags ON hags.group_id = ag.group_id
DECLARE
@SqlStatement AS nvarchar(max)
,@MajorMinorVersion AS int = CAST(PARSENAME(CAST(SERVERPROPERTY('ProductVersion') AS nvarchar),4) AS int)*100 + CAST(PARSENAME(CAST(SERVERPROPERTY('ProductVersion') AS nvarchar),3) AS int)
,@Columns AS nvarchar(MAX) = ''
IF @MajorMinorVersion >= 1300 BEGIN
SET @Columns += N'
,ag.basic_features
,ag.is_distributed
,ar.seeding_mode
,ar.seeding_mode_desc'
END
SET @SqlStatement = N'
IF SERVERPROPERTY(''IsHadrEnabled'') = 1 BEGIN
SELECT
''sqlserver_hadr_replica_states'' AS [measurement]
,REPLACE(@@SERVERNAME, ''\'', '':'') AS [sql_instance]
,convert(nvarchar(36), hars.replica_id) as replica_id
,ar.replica_server_name
,convert(nvarchar(36), hars.group_id) as group_id
,ag.name AS group_name
,hags.synchronization_health_desc AS ag_synchronization_health_desc
,ar.replica_metadata_id
,ar.availability_mode
,ar.availability_mode_desc
,ar.failover_mode
,ar.failover_mode_desc
,ar.session_timeout
,ar.primary_role_allow_connections
,ar.primary_role_allow_connections_desc
,ar.secondary_role_allow_connections
,ar.secondary_role_allow_connections_desc
,hars.is_local
,hars.role
,hars.role_desc
,hars.operational_state
,hars.operational_state_desc
,hars.connected_state
,hars.connected_state_desc
,hars.recovery_health
,hars.recovery_health_desc
,hars.synchronization_health AS replica_synchronization_health
,hars.synchronization_health_desc AS replica_synchronization_health_desc
,hars.last_connect_error_number
,hars.last_connect_error_description
,hars.last_connect_error_timestamp'
+ @Columns + N'
FROM sys.dm_hadr_availability_replica_states AS hars
INNER JOIN sys.availability_replicas AS ar on hars.replica_id = ar.replica_id
INNER JOIN sys.availability_groups AS ag on ar.group_id = ag.group_id
INNER JOIN sys.dm_hadr_availability_group_states AS hags ON hags.group_id = ag.group_id
END'
EXEC sp_executesql @SqlStatement
`
// Collects database replica state information from `sys.dm_hadr_database_replica_states` for a High Availability / Disaster Recovery (HADR) setup
// Certain fields are only supported on SQL Server 2016 and newer version, or SQL Server 2014 and newer, identified by check MajorMinorVersion >= 1300 or MajorMinorVersion >= 1200
const sqlServerDatabaseReplicaStates string = `
IF SERVERPROPERTY('EngineEdition') NOT IN (2,3,4) BEGIN /*NOT IN Standard,Enterpris,Express*/
DECLARE @ErrorMessage AS nvarchar(500) = 'Telegraf - Connection string Server:'+ @@ServerName + ',Database:' + DB_NAME() +' is not a SQL Server Standard,Enterprise or Express. Check the database_type parameter in the telegraf configuration.';
@ -1227,38 +1244,55 @@ IF SERVERPROPERTY('EngineEdition') NOT IN (2,3,4) BEGIN /*NOT IN Standard,Enterp
RETURN
END
IF SERVERPROPERTY('IsHadrEnabled') = 1 BEGIN
SELECT
'sqlserver_hadr_dbreplica_states' AS [measurement],
REPLACE(@@SERVERNAME,'\',':') AS [sql_instance],
database_id,
db_name(database_id) as database_name,
convert(nvarchar(36), drs.replica_id) as replica_id,
ar.replica_server_name,
convert(nvarchar(36), drs.group_database_id) as group_database_id,
is_primary_replica,
synchronization_state,
synchronization_state_desc,
is_commit_participant,
synchronization_health,
synchronization_health_desc,
database_state,
database_state_desc,
is_suspended,
suspend_reason,
suspend_reason_desc,
last_sent_time,
last_received_time,
last_hardened_time,
last_redone_time,
log_send_queue_size,
log_send_rate,
redo_queue_size,
redo_rate,
filestream_send_rate,
last_commit_time,
secondary_lag_seconds
from sys.dm_hadr_database_replica_states AS drs
inner join sys.availability_replicas AS ar on drs.replica_id = ar.replica_id
DECLARE
@SqlStatement AS nvarchar(max)
,@MajorMinorVersion AS int = CAST(PARSENAME(CAST(SERVERPROPERTY('ProductVersion') AS nvarchar),4) AS int)*100 + CAST(PARSENAME(CAST(SERVERPROPERTY('ProductVersion') AS nvarchar),3) AS int)
,@Columns AS nvarchar(MAX) = ''
IF @MajorMinorVersion >= 1200 BEGIN
SET @Columns += N'
,is_primary_replica'
END
IF @MajorMinorVersion >= 1300 BEGIN
SET @Columns += N'
,secondary_lag_seconds'
END
SET @SqlStatement = N'
IF SERVERPROPERTY(''IsHadrEnabled'') = 1 BEGIN
SELECT
''sqlserver_hadr_dbreplica_states'' AS [measurement]
,REPLACE(@@SERVERNAME,''\'','':'') AS [sql_instance]
,database_id
,db_name(database_id) as database_name
,convert(nvarchar(36), drs.replica_id) as replica_id
,ar.replica_server_name
,convert(nvarchar(36), drs.group_database_id) as group_database_id
,synchronization_state
,synchronization_state_desc
,is_commit_participant
,synchronization_health
,synchronization_health_desc
,database_state
,database_state_desc
,is_suspended
,suspend_reason
,suspend_reason_desc
,last_sent_time
,last_received_time
,last_hardened_time
,last_redone_time
,log_send_queue_size
,log_send_rate
,redo_queue_size
,redo_rate
,filestream_send_rate
,last_commit_time'
+ @Columns + N'
FROM sys.dm_hadr_database_replica_states AS drs
INNER JOIN sys.availability_replicas AS ar on drs.replica_id = ar.replica_id
END'
EXEC sp_executesql @SqlStatement
`