From 361d1fb172a40c4d7a030d5010f4a942b037ccfb Mon Sep 17 00:00:00 2001 From: mrferris Date: Tue, 13 Feb 2024 22:01:12 -0500 Subject: [PATCH] feat: Adds 4444s filtering, statistics, and visualizations --- entity/src/audit_stats.rs | 12 ++ glados-audit/src/stats.rs | 104 ++++++++++++++---- glados-core/src/stats.rs | 32 +++++- glados-web/assets/js/stats_history.js | 12 +- glados-web/templates/audit_dashboard.html | 22 +++- migration/src/lib.rs | 4 +- .../m20240213_190221_add_premerge_stats.rs | 60 ++++++++++ 7 files changed, 218 insertions(+), 28 deletions(-) create mode 100644 migration/src/m20240213_190221_add_premerge_stats.rs diff --git a/entity/src/audit_stats.rs b/entity/src/audit_stats.rs index dc0bd0f7..25c7cd33 100644 --- a/entity/src/audit_stats.rs +++ b/entity/src/audit_stats.rs @@ -16,6 +16,7 @@ pub struct Model { pub success_rate_latest: f32, pub success_rate_random: f32, pub success_rate_oldest: f32, + pub success_rate_premerge: f32, pub success_rate_all_headers: f32, pub success_rate_all_bodies: f32, pub success_rate_all_receipts: f32, @@ -25,6 +26,9 @@ pub struct Model { pub success_rate_random_headers: f32, pub success_rate_random_bodies: f32, pub success_rate_random_receipts: f32, + pub success_rate_premerge_headers: f32, + pub success_rate_premerge_bodies: f32, + pub success_rate_premerge_receipts: f32, } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] @@ -40,6 +44,7 @@ pub async fn create( success_rate_latest: f32, success_rate_random: f32, success_rate_oldest: f32, + success_rate_premerge: f32, success_rate_all_headers: f32, success_rate_all_bodies: f32, success_rate_all_receipts: f32, @@ -49,6 +54,9 @@ pub async fn create( success_rate_random_headers: f32, success_rate_random_bodies: f32, success_rate_random_receipts: f32, + success_rate_premerge_headers: f32, + success_rate_premerge_bodies: f32, + success_rate_premerge_receipts: f32, conn: &DatabaseConnection, ) -> Result { let audit_stats = ActiveModel { @@ -59,6 +67,7 @@ pub async fn create( success_rate_latest: Set(success_rate_latest), success_rate_random: Set(success_rate_random), success_rate_oldest: Set(success_rate_oldest), + success_rate_premerge: Set(success_rate_premerge), success_rate_all_headers: Set(success_rate_all_headers), success_rate_all_bodies: Set(success_rate_all_bodies), success_rate_all_receipts: Set(success_rate_all_receipts), @@ -68,6 +77,9 @@ pub async fn create( success_rate_random_headers: Set(success_rate_random_headers), success_rate_random_bodies: Set(success_rate_random_bodies), success_rate_random_receipts: Set(success_rate_random_receipts), + success_rate_premerge_headers: Set(success_rate_premerge_headers), + success_rate_premerge_bodies: Set(success_rate_premerge_bodies), + success_rate_premerge_receipts: Set(success_rate_premerge_receipts), }; Ok(audit_stats.insert(conn).await?) } diff --git a/glados-audit/src/stats.rs b/glados-audit/src/stats.rs index 740e50e6..18b98058 100644 --- a/glados-audit/src/stats.rs +++ b/glados-audit/src/stats.rs @@ -1,12 +1,12 @@ use chrono::Utc; use entity::audit_stats; use glados_core::stats::{ - filter_audits, get_audit_stats, AuditFilters, ContentTypeFilter, Period, StrategyFilter, - SuccessFilter, + filter_audits, get_audit_stats, AuditFilters, ChainSegmentFilter, ContentTypeFilter, Period, + StrategyFilter, SuccessFilter, }; use sea_orm::{DatabaseConnection, DbErr}; use tokio::time::{interval, Duration}; -use tracing::{debug, error, info}; +use tracing::{debug, error}; /// Loops indefinitely, periodically recording audit stats to the database. pub async fn periodically_record_stats(period: Duration, conn: DatabaseConnection) -> ! { @@ -14,11 +14,10 @@ pub async fn periodically_record_stats(period: Duration, conn: DatabaseConnectio let mut interval = interval(period); loop { + interval.tick().await; record_current_stats(&conn).await.unwrap_or_else(|e| { error!("failed to record audit stats: {e}"); }); - info!("Successfully recorded audit stats"); - interval.tick().await; } } @@ -31,6 +30,7 @@ async fn record_current_stats(conn: &DatabaseConnection) -> Result<(), DbErr> { latest, random, oldest, + premerge, all_headers, all_bodies, all_receipts, @@ -40,12 +40,16 @@ async fn record_current_stats(conn: &DatabaseConnection) -> Result<(), DbErr> { random_headers, random_bodies, random_receipts, + premerge_headers, + premerge_bodies, + premerge_receipts, ) = tokio::join!( get_audit_stats( filter_audits(AuditFilters { strategy: StrategyFilter::All, content_type: ContentTypeFilter::All, - success: SuccessFilter::All + success: SuccessFilter::All, + chain_segment: ChainSegmentFilter::All, }), Period::Hour, conn @@ -54,7 +58,8 @@ async fn record_current_stats(conn: &DatabaseConnection) -> Result<(), DbErr> { filter_audits(AuditFilters { strategy: StrategyFilter::Latest, content_type: ContentTypeFilter::All, - success: SuccessFilter::All + success: SuccessFilter::All, + chain_segment: ChainSegmentFilter::All, }), Period::Hour, conn @@ -63,7 +68,8 @@ async fn record_current_stats(conn: &DatabaseConnection) -> Result<(), DbErr> { filter_audits(AuditFilters { strategy: StrategyFilter::Random, content_type: ContentTypeFilter::All, - success: SuccessFilter::All + success: SuccessFilter::All, + chain_segment: ChainSegmentFilter::All, }), Period::Hour, conn @@ -72,7 +78,18 @@ async fn record_current_stats(conn: &DatabaseConnection) -> Result<(), DbErr> { filter_audits(AuditFilters { strategy: StrategyFilter::Oldest, content_type: ContentTypeFilter::All, - success: SuccessFilter::All + success: SuccessFilter::All, + chain_segment: ChainSegmentFilter::All, + }), + Period::Hour, + conn + ), + get_audit_stats( + filter_audits(AuditFilters { + strategy: StrategyFilter::All, + content_type: ContentTypeFilter::All, + success: SuccessFilter::All, + chain_segment: ChainSegmentFilter::PreMerge, }), Period::Hour, conn @@ -81,7 +98,8 @@ async fn record_current_stats(conn: &DatabaseConnection) -> Result<(), DbErr> { filter_audits(AuditFilters { strategy: StrategyFilter::All, content_type: ContentTypeFilter::Headers, - success: SuccessFilter::All + success: SuccessFilter::All, + chain_segment: ChainSegmentFilter::All, }), Period::Hour, conn @@ -90,7 +108,8 @@ async fn record_current_stats(conn: &DatabaseConnection) -> Result<(), DbErr> { filter_audits(AuditFilters { strategy: StrategyFilter::All, content_type: ContentTypeFilter::Bodies, - success: SuccessFilter::All + success: SuccessFilter::All, + chain_segment: ChainSegmentFilter::All, }), Period::Hour, conn @@ -99,7 +118,8 @@ async fn record_current_stats(conn: &DatabaseConnection) -> Result<(), DbErr> { filter_audits(AuditFilters { strategy: StrategyFilter::All, content_type: ContentTypeFilter::Receipts, - success: SuccessFilter::All + success: SuccessFilter::All, + chain_segment: ChainSegmentFilter::All, }), Period::Hour, conn @@ -108,7 +128,8 @@ async fn record_current_stats(conn: &DatabaseConnection) -> Result<(), DbErr> { filter_audits(AuditFilters { strategy: StrategyFilter::Latest, content_type: ContentTypeFilter::Headers, - success: SuccessFilter::All + success: SuccessFilter::All, + chain_segment: ChainSegmentFilter::All, }), Period::Hour, conn @@ -117,7 +138,8 @@ async fn record_current_stats(conn: &DatabaseConnection) -> Result<(), DbErr> { filter_audits(AuditFilters { strategy: StrategyFilter::Latest, content_type: ContentTypeFilter::Bodies, - success: SuccessFilter::All + success: SuccessFilter::All, + chain_segment: ChainSegmentFilter::All, }), Period::Hour, conn @@ -126,7 +148,8 @@ async fn record_current_stats(conn: &DatabaseConnection) -> Result<(), DbErr> { filter_audits(AuditFilters { strategy: StrategyFilter::Latest, content_type: ContentTypeFilter::Receipts, - success: SuccessFilter::All + success: SuccessFilter::All, + chain_segment: ChainSegmentFilter::All, }), Period::Hour, conn @@ -135,7 +158,8 @@ async fn record_current_stats(conn: &DatabaseConnection) -> Result<(), DbErr> { filter_audits(AuditFilters { strategy: StrategyFilter::Random, content_type: ContentTypeFilter::Headers, - success: SuccessFilter::All + success: SuccessFilter::All, + chain_segment: ChainSegmentFilter::All, }), Period::Hour, conn @@ -144,20 +168,52 @@ async fn record_current_stats(conn: &DatabaseConnection) -> Result<(), DbErr> { filter_audits(AuditFilters { strategy: StrategyFilter::Random, content_type: ContentTypeFilter::Bodies, - success: SuccessFilter::All + success: SuccessFilter::All, + chain_segment: ChainSegmentFilter::All, }), Period::Hour, conn ), get_audit_stats( filter_audits(AuditFilters { - strategy: StrategyFilter::Random, + strategy: StrategyFilter::All, content_type: ContentTypeFilter::Receipts, - success: SuccessFilter::All + success: SuccessFilter::All, + chain_segment: ChainSegmentFilter::All, }), Period::Hour, conn - ) + ), + get_audit_stats( + filter_audits(AuditFilters { + strategy: StrategyFilter::All, + content_type: ContentTypeFilter::Headers, + success: SuccessFilter::All, + chain_segment: ChainSegmentFilter::PreMerge, + }), + Period::Hour, + conn + ), + get_audit_stats( + filter_audits(AuditFilters { + strategy: StrategyFilter::All, + content_type: ContentTypeFilter::Bodies, + success: SuccessFilter::All, + chain_segment: ChainSegmentFilter::PreMerge, + }), + Period::Hour, + conn + ), + get_audit_stats( + filter_audits(AuditFilters { + strategy: StrategyFilter::All, + content_type: ContentTypeFilter::Receipts, + success: SuccessFilter::All, + chain_segment: ChainSegmentFilter::PreMerge, + }), + Period::Hour, + conn + ), ); // Handle errors and get success rates. @@ -165,6 +221,7 @@ async fn record_current_stats(conn: &DatabaseConnection) -> Result<(), DbErr> { let success_rate_latest = latest?.pass_percent; let success_rate_random = random?.pass_percent; let success_rate_oldest = oldest?.pass_percent; + let success_rate_premerge = premerge?.pass_percent; let success_rate_all_headers = all_headers?.pass_percent; let success_rate_all_bodies = all_bodies?.pass_percent; let success_rate_all_receipts = all_receipts?.pass_percent; @@ -174,6 +231,9 @@ async fn record_current_stats(conn: &DatabaseConnection) -> Result<(), DbErr> { let success_rate_random_headers = random_headers?.pass_percent; let success_rate_random_bodies = random_bodies?.pass_percent; let success_rate_random_receipts = random_receipts?.pass_percent; + let success_rate_premerge_headers = premerge_headers?.pass_percent; + let success_rate_premerge_bodies = premerge_bodies?.pass_percent; + let success_rate_premerge_receipts = premerge_receipts?.pass_percent; // Record the values. match audit_stats::create( @@ -183,6 +243,7 @@ async fn record_current_stats(conn: &DatabaseConnection) -> Result<(), DbErr> { success_rate_latest, success_rate_random, success_rate_oldest, + success_rate_premerge, success_rate_all_headers, success_rate_all_bodies, success_rate_all_receipts, @@ -192,6 +253,9 @@ async fn record_current_stats(conn: &DatabaseConnection) -> Result<(), DbErr> { success_rate_random_headers, success_rate_random_bodies, success_rate_random_receipts, + success_rate_premerge_headers, + success_rate_premerge_bodies, + success_rate_premerge_receipts, conn, ) .await diff --git a/glados-core/src/stats.rs b/glados-core/src/stats.rs index 0d3021fa..c0daab98 100644 --- a/glados-core/src/stats.rs +++ b/glados-core/src/stats.rs @@ -5,7 +5,9 @@ use chrono::{DateTime, Duration, Utc}; use entity::{ content::{self, SubProtocol}, content_audit::{self, AuditResult, SelectionStrategy}, + execution_metadata, }; +use sea_orm::sea_query::Alias; use sea_orm::{ sea_query::{Expr, IntoCondition}, ColumnTrait, DatabaseConnection, DbErr, EntityTrait, JoinType, PaginatorTrait, QueryFilter, @@ -43,7 +45,7 @@ pub fn filter_audits(filters: AuditFilters) -> Select { } }; // Content type filters - match filters.content_type { + let audits = match filters.content_type { ContentTypeFilter::All => audits, ContentTypeFilter::Headers => audits.join( JoinType::InnerJoin, @@ -84,6 +86,27 @@ pub fn filter_audits(filters: AuditFilters) -> Select { .into_condition() }), ), + }; + + match filters.chain_segment { + ChainSegmentFilter::PreMerge => { + // Alias content table for this join because the content table was already joined above. + let content_alias = Alias::new("content_table"); + let aliased_content_metadata_relation = { + let mut rel = content::Relation::ExecutionMetadata.def(); + rel.from_tbl = rel.from_tbl.alias(content_alias.clone()); + rel + }; + audits + .join_as( + JoinType::InnerJoin, + content_audit::Relation::Content.def(), + content_alias, + ) + .join(JoinType::InnerJoin, aliased_content_metadata_relation) + .filter(execution_metadata::Column::BlockNumber.lt(15_537_392)) + } + ChainSegmentFilter::All => audits, } } @@ -192,6 +215,7 @@ pub struct AuditFilters { pub strategy: StrategyFilter, pub content_type: ContentTypeFilter, pub success: SuccessFilter, + pub chain_segment: ChainSegmentFilter, } #[derive(Deserialize)] @@ -216,3 +240,9 @@ pub enum ContentTypeFilter { Bodies, Receipts, } + +#[derive(Deserialize)] +pub enum ChainSegmentFilter { + All, + PreMerge, +} diff --git a/glados-web/assets/js/stats_history.js b/glados-web/assets/js/stats_history.js index 8dbc503e..49186162 100644 --- a/glados-web/assets/js/stats_history.js +++ b/glados-web/assets/js/stats_history.js @@ -99,7 +99,7 @@ function createMultiLineChart(height, width, dataSets) { // Select all '.legend' group elements and click on all but the first three svg.selectAll(".legend") - .each(function(d, i) { + .each(function (d, i) { if (i >= 3) { // Skip the first three dispatchClick(this); } @@ -113,7 +113,11 @@ function createMultiLineChart(height, width, dataSets) { .attr("fill", (d, i) => colors[i % colors.length]); // Add text to the legend. - const labels = ["All", "Latest", "Random", "Oldest", "All Headers", "All Bodies", "All Receipts", "Latest Headers", "Latest Bodies", "Latest Receipts", "Random Headers", "Random Bodies", "Random Receipts"]; + const labels = ["All", "Latest", "Random", "Oldest", "4444s", + "All Headers", "All Bodies", "All Receipts", + "Latest Headers", "Latest Bodies", "Latest Receipts", + "Random Headers", "Random Bodies", "Random Receipts", + "4444s Headers", "4444s Bodies", "4444s Receipts"]; legend.append("text") .attr("x", -24) .attr("y", 9) @@ -131,6 +135,7 @@ function convertDataForChart(data) { 'success_rate_latest', 'success_rate_random', 'success_rate_oldest', + 'success_rate_premerge', 'success_rate_all_headers', 'success_rate_all_bodies', 'success_rate_all_receipts', @@ -140,6 +145,9 @@ function convertDataForChart(data) { 'success_rate_random_headers', 'success_rate_random_bodies', 'success_rate_random_receipts', + 'success_rate_premerge_headers', + 'success_rate_premerge_bodies', + 'success_rate_premerge_receipts' ]; return successRateKeys.map(key => diff --git a/glados-web/templates/audit_dashboard.html b/glados-web/templates/audit_dashboard.html index 97a86f5a..23f32e05 100644 --- a/glados-web/templates/audit_dashboard.html +++ b/glados-web/templates/audit_dashboard.html @@ -38,10 +38,17 @@

Audit Dashboard

+ +
+ + +
-
+