-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add support for diagnose report (#2)
* feat: add initial diagnostic support Introduced a new `Diagnose` struct with a `run` method to execute various diagnostic checks. Currently, not all diagnostic checks are fully implemented. * refactor: extract extension check to a separate function * feat: add index cache hit rate check * refactor: shorten column name in table header * refactor: simplify diagnose module logic * refactor: update table rendering and add color to rows * feat(diagnose): add SSL connection check * refactor: remove strum dependency and simplify Check enum Removed the strum dependency from the project and adjusted the Check enum and related code to use standard Rust features. * feat: add unused indexes check with size parsing * feat: add null indexes check to diagnostics * feat(diagnose): add bloat detection check * feat: Add duplicate index detection to diagnose module * feat: Add detection of outlier queries by execution ratio * refactor(diagnose): simplify Diagnose struct * chore: add diagnose report to test suite * refactor: remove redundant CheckResult constructor Replaced the CheckResult::new() constructor with struct literal initialization. * refactor: simplify cache and SSL check logic Refactor to use concise if-let-else syntax. * refactor: restructure diagnose module and add recommendation Moved diagnose logic into a dedicated submodule with separate files for the run logic and recommendations. Implemented a new recommendation system for diagnosis checks and added structured report rendering. * feat: add new recommendations for various database checks * docs: add diagnose report section and example to README * feat(bin): add a new cli option for diagnose command * style: fix fmt formatting issues
- Loading branch information
Showing
10 changed files
with
594 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
pub mod recommendation; | ||
pub mod report; | ||
pub mod run; | ||
pub mod size_parser; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
use crate::diagnose::run::Check; | ||
use lazy_static::lazy_static; | ||
use std::collections::HashMap; | ||
|
||
lazy_static! { | ||
pub static ref Recommendations: HashMap<Check, (&'static str, Vec<&'static str>)> = { | ||
let mut m = HashMap::new(); | ||
m.insert( | ||
Check::TableCacheHit, | ||
( | ||
"Cache hit rate is too low", | ||
vec![ | ||
"Review database settings: Consider comparing the database settings with ones recommended by PGTune and tweak values to improve performance.", | ||
"Validate database specs: A low buffer cache hit ratio can be a sign that the Postgres instance is too small for the workload.", | ||
], | ||
), | ||
); | ||
m.insert( | ||
Check::IndexCacheHit, | ||
( | ||
"Cache hit rate is too low", | ||
vec![ | ||
"Review database settings: Consider comparing the database settings with ones recommended by PGTune and tweak values to improve performance.", | ||
"Validate database specs: A low buffer cache hit ratio can be a sign that the Postgres instance is too small for the workload.", | ||
], | ||
), | ||
); | ||
m.insert( | ||
Check::UnusedIndexes, | ||
( | ||
"Remove unused indexes", | ||
vec![ | ||
"Consider eliminating indexes that are unused, which can impact the performance.", | ||
"If the index is large, remember to use the CONCURRENTLY option when dropping it, to avoid exclusively blocking the whole related table." | ||
], | ||
), | ||
); | ||
m.insert( | ||
Check::NullIndexes, | ||
( | ||
"Optimize \"NULL\" indexes", | ||
vec![ | ||
"NULL values unnecessarily bloat the index size and slow down insert operations on a related table.", | ||
"Consider replacing the index with a partial one that excludes NULL values.", | ||
], | ||
), | ||
); | ||
m.insert( | ||
Check::DuplicateIndexes, | ||
( | ||
"Remove duplicate indexes", | ||
vec![ | ||
"Consider removing the duplicate indexes to improve performance.", | ||
"If the index is large, remember to use the CONCURRENTLY option when dropping it, to avoid exclusively blocking the whole related table." | ||
], | ||
), | ||
|
||
); | ||
m.insert( | ||
Check::SslUsed, | ||
( | ||
"SSL is not used", | ||
vec![ | ||
"Connecting to the database via an unencrypted connection is a critical security risk.", | ||
"Consider enabling SSL to encrypt the connection between the client and the server.", | ||
], | ||
), | ||
); | ||
m.insert( | ||
Check::Bloat, | ||
( | ||
"Get rid of unnecessary bloat", | ||
vec![ | ||
"Review AUTOVACUUM settings: If it is misconfigured, it might result in your table consisting of mostly dead rows that are blocking the disk space and slowing down queries.", | ||
], | ||
), | ||
); | ||
m.insert( | ||
Check::Outliers, | ||
( | ||
"Add missing indexes", | ||
vec![ | ||
"Spot the queries that are consuming a lot of your database resources and are potentially missing an index.", | ||
"Perform EXPLAIN ANALYZE and check if the query planner does Seq Scan on one of the tables.", | ||
], | ||
), | ||
); | ||
m | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
use crate::diagnose::recommendation; | ||
use crate::diagnose::run::CheckResult; | ||
use prettytable::{Cell, Row as TableRow, Table}; | ||
|
||
pub fn render_diagnose_report(items: Vec<CheckResult>) { | ||
let term_width = textwrap::termwidth(); | ||
let recommendation_width = term_width / 3; // need to adjust this to make the recommendation text wrap nicely | ||
|
||
let mut table = Table::new(); | ||
|
||
let mut header_cell = Cell::new("Diagnose Report").style_spec("bH3"); | ||
header_cell.align(prettytable::format::Alignment::CENTER); | ||
table.set_titles(TableRow::new(vec![header_cell])); | ||
|
||
table.add_row(row!["Check", "Message", "Recommendation"]); | ||
|
||
for item in items { | ||
let style = if item.ok { "Fg" } else { "Fr" }; | ||
|
||
let status_and_name = format!("[{}] - {}", if item.ok { "√" } else { "x" }, item.check); | ||
|
||
// get the recommendation for the check | ||
let recommendation = if item.ok { | ||
"None".to_string() | ||
} else { | ||
let (header, details) = recommendation::Recommendations.get(&item.check).unwrap(); | ||
// build the recommendation text by concatenating the header and details with bullet points | ||
format!( | ||
"{}:\n{}", | ||
header, | ||
details | ||
.iter() | ||
.map(|detail| format!("• {}", detail)) | ||
.collect::<Vec<String>>() | ||
.join("\n") | ||
) | ||
}; | ||
|
||
table.add_row(TableRow::new(vec![ | ||
Cell::new(status_and_name.as_str()).style_spec(style), | ||
Cell::new(item.message.as_str()).style_spec(style), | ||
Cell::new(textwrap::fill(&recommendation, recommendation_width).as_str()) | ||
.style_spec(style), | ||
])); | ||
} | ||
|
||
table.printstd(); | ||
} |
Oops, something went wrong.