Skip to content

Commit 474adf2

Browse files
committed
19191 FIX mk-sql: skip inaccessible databases in SQL Server Always On Availability Groups
CMK-32435 SUP-27332 Change-Id: I52de1b5e3c79a0fca52cf8fa7becf1d80143c76f
1 parent 9ef2986 commit 474adf2

4 files changed

Lines changed: 77 additions & 15 deletions

File tree

.werks/19191.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
[//]: # (werk v3)
2+
# mk-sql: skip inaccessible databases in SQL Server Always On Availability Groups
3+
4+
key | value
5+
---------- | ---
6+
date | 2026-03-20T12:34:51+00:00
7+
version | 2.5.0
8+
class | fix
9+
edition | community
10+
component | checks
11+
level | 2
12+
compatible | yes
13+
14+
mk-sql previously queried all SQL Server databases indiscriminately, including secondary replicas in Always On Availability Groups (AG) that were not directly accessible. This caused spurious errors when monitoring SQL Server instances participating in an AG.
15+
16+
Now, mk-sql only queries databases that are actually accessible: standalone databases, primary replicas, and readable secondary replicas. Inaccessible AG secondaries are silently skipped — this is expected behavior, not an error.
17+
18+
This change improves monitoring reliability by eliminating false error messages. No configuration adjustments are necessary, as the update is fully automatic and transparent to users.

packages/mk-sql/src/ms_sql/instance.rs

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -427,7 +427,11 @@ impl SqlInstance {
427427
}
428428

429429
/// Gather databases based on sections content: only if any of sections is database based
430-
async fn gather_databases(&self, client: &mut UniClient, sections: &[Section]) -> Vec<String> {
430+
async fn gather_active_databases(
431+
&self,
432+
client: &mut UniClient,
433+
sections: &[Section],
434+
) -> Vec<String> {
431435
let database_based_sections = section::get_per_database_sections();
432436
let need = database_based_sections.iter().any(|s| {
433437
sections
@@ -437,7 +441,8 @@ impl SqlInstance {
437441
.contains(s)
438442
});
439443
if need {
440-
self.generate_databases(client).await
444+
self.generate_databases(client, sqls::Id::DatabaseNamesActive)
445+
.await
441446
} else {
442447
Vec::new()
443448
}
@@ -450,7 +455,7 @@ impl SqlInstance {
450455
sections: &[Section],
451456
) -> String {
452457
let mut data: Vec<String> = Vec::new();
453-
let databases = self.gather_databases(client, sections).await;
458+
let databases = self.gather_active_databases(client, sections).await;
454459
for section in sections.iter() {
455460
data.push(
456461
self.generate_section(client, endpoint, section, &databases)
@@ -767,7 +772,9 @@ impl SqlInstance {
767772
query: &str,
768773
sep: char,
769774
) -> String {
770-
let databases = self.generate_databases(client).await;
775+
let databases = self
776+
.generate_databases(client, sqls::Id::DatabaseNamesAll)
777+
.await;
771778

772779
let result = run_custom_query(client, query)
773780
.await
@@ -800,6 +807,10 @@ impl SqlInstance {
800807
query: &str,
801808
sep: char,
802809
) -> String {
810+
if databases.is_empty() {
811+
log::warn!("No active databases, skip section {}", section.name());
812+
return String::new();
813+
}
803814
let chunks = if databases.len() >= 64 {
804815
let max_chunk = databases.len().div_ceil(4usize);
805816
let min_chunk = 16usize;
@@ -968,8 +979,8 @@ impl SqlInstance {
968979
}
969980

970981
/// doesn't return error - the same behavior as plugin
971-
pub async fn generate_databases(&self, client: &mut UniClient) -> Vec<String> {
972-
let result = run_known_query(client, sqls::Id::DatabaseNames)
982+
pub async fn generate_databases(&self, client: &mut UniClient, which: sqls::Id) -> Vec<String> {
983+
let result = run_known_query(client, which)
973984
.await
974985
.and_then(validate_rows)
975986
.map(|rows| self.process_databases_rows(&rows));

packages/mk-sql/src/ms_sql/sqls.rs

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ pub enum Id {
2121
ClusterActiveNodes,
2222
ClusterNodes,
2323
IsClustered,
24-
DatabaseNames,
24+
DatabaseNamesAll,
25+
DatabaseNamesActive,
2526
Databases,
2627
Datafiles,
2728
Backup,
@@ -168,7 +169,26 @@ WHERE object_name NOT LIKE '%Deprecated%'
168169
CAST(blocking_session_id AS varchar) AS blocking_session_id
169170
FROM sys.dm_os_waiting_tasks";
170171

171-
pub const DATABASE_NAMES: &str = "SELECT name FROM sys.databases";
172+
pub const DATABASE_NAMES_ALL: &str = "SELECT name FROM sys.databases";
173+
174+
/// Skips secondary replica databases participating in availability groups
175+
/// sys.fn_hadr_is_primary_replica(d.name) returns
176+
/// 1 for primary replica
177+
/// 0 for secondary replica
178+
/// NULL for databases not participating in availability groups
179+
/// ONLY SUPPORTED since version 12 (SQL Server 2014)
180+
pub const DATABASE_NAMES_ACTIVE: &str = r#"IF CAST(PARSENAME(CAST(SERVERPROPERTY('ProductVersion') AS varchar(30)), 4) AS int) >= 12
181+
BEGIN
182+
SELECT d.name
183+
FROM sys.databases AS d
184+
WHERE sys.fn_hadr_is_primary_replica(d.name) IS NULL
185+
OR sys.fn_hadr_is_primary_replica(d.name) = 1;
186+
END
187+
ELSE
188+
BEGIN
189+
SELECT d.name
190+
FROM sys.databases AS d;
191+
END;"#;
172192

173193
/// Executes `sp_spaceused` for each database parsing output AS resuult set
174194
/// Requires NVARCHAR support
@@ -446,7 +466,8 @@ lazy_static::lazy_static! {
446466
(Id::ClusterActiveNodes, QueryMap::new(query::CLUSTER_ACTIVE_NODES, None)),
447467
(Id::ClusterNodes, QueryMap::new(query::CLUSTER_NODES_NORMAL, Some(query::CLUSTER_NODES_AZURE))),
448468
(Id::IsClustered, QueryMap::new(query::IS_CLUSTERED, None)),
449-
(Id::DatabaseNames, QueryMap::new(query::DATABASE_NAMES, None)),
469+
(Id::DatabaseNamesAll, QueryMap::new(query::DATABASE_NAMES_ALL, None)),
470+
(Id::DatabaseNamesActive, QueryMap::new(query::DATABASE_NAMES_ACTIVE, None)),
450471
(Id::Databases, QueryMap::new(query::DATABASES, None)),
451472
(Id::Datafiles, QueryMap::new(query::DATAFILES, None)),
452473
(Id::Backup, QueryMap::new(query::BACKUP, None)),

packages/mk-sql/tests/test_ms_sql.rs

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,9 @@ async fn validate_all(i: &SqlInstance, c: &mut UniClient, e: &Endpoint) {
381381
}
382382

383383
async fn validate_database_names(instance: &SqlInstance, client: &mut UniClient) {
384-
let databases = instance.generate_databases(client).await;
384+
let databases = instance
385+
.generate_databases(client, sqls::Id::DatabaseNamesAll)
386+
.await;
385387
let expected = expected_databases();
386388
// O^2, but good enough for testing
387389
assert!(expected.iter().all(|item| databases.contains(item)),);
@@ -449,7 +451,9 @@ async fn validate_table_spaces(
449451
client: &mut UniClient,
450452
endpoint: &Endpoint,
451453
) {
452-
let databases = instance.generate_databases(client).await;
454+
let databases = instance
455+
.generate_databases(client, sqls::Id::DatabaseNamesAll)
456+
.await;
453457
let expected = expected_databases();
454458

455459
let result = instance
@@ -511,7 +515,9 @@ async fn validate_transaction_logs(
511515
) {
512516
let expected: HashSet<String> = expected_databases();
513517

514-
let databases = instance.generate_databases(client).await;
518+
let databases = instance
519+
.generate_databases(client, sqls::Id::DatabaseNamesAll)
520+
.await;
515521
let result = instance
516522
.generate_transaction_logs_section(
517523
endpoint,
@@ -544,7 +550,9 @@ async fn validate_transaction_logs(
544550

545551
async fn validate_datafiles(instance: &SqlInstance, client: &mut UniClient, endpoint: &Endpoint) {
546552
let expected: HashSet<String> = expected_databases();
547-
let databases = instance.generate_databases(client).await;
553+
let databases = instance
554+
.generate_databases(client, sqls::Id::DatabaseNamesAll)
555+
.await;
548556

549557
let result = instance
550558
.generate_datafiles_section(
@@ -583,7 +591,9 @@ async fn validate_datafiles(instance: &SqlInstance, client: &mut UniClient, endp
583591
async fn validate_databases(instance: &SqlInstance, client: &mut UniClient) {
584592
let expected: HashSet<String> = expected_databases();
585593

586-
let databases = instance.generate_databases(client).await;
594+
let databases = instance
595+
.generate_databases(client, sqls::Id::DatabaseNamesAll)
596+
.await;
587597
let result = instance
588598
.generate_databases_section(
589599
client,
@@ -621,7 +631,9 @@ async fn validate_databases(instance: &SqlInstance, client: &mut UniClient) {
621631
async fn validate_databases_error(instance: &SqlInstance, client: &mut UniClient) {
622632
let expected: HashSet<String> = expected_databases();
623633

624-
let databases = instance.generate_databases(client).await;
634+
let databases = instance
635+
.generate_databases(client, sqls::Id::DatabaseNamesAll)
636+
.await;
625637
let result = instance
626638
.generate_databases_section(
627639
client,

0 commit comments

Comments
 (0)