diff --git a/node/src/bin/manager.rs b/node/src/bin/manager.rs index a2743a4bf71..05585105555 100644 --- a/node/src/bin/manager.rs +++ b/node/src/bin/manager.rs @@ -349,6 +349,13 @@ pub enum StatsCommand { /// The name of a table to fully count table: Option, }, + /// Perform a SQL ANALYZE in a Entity table + Analyze { + /// The id of the deployment + id: String, + /// The name of the Entity to ANALYZE + entity: String, + }, } impl From for config::Opt { @@ -695,6 +702,11 @@ async fn main() { commands::stats::account_like(ctx.pools(), clear, table) } Show { nsp, table } => commands::stats::show(ctx.pools(), nsp, table), + Analyze { id, entity } => { + let store = ctx.store(); + let subgraph_store = store.subgraph_store(); + commands::stats::analyze(subgraph_store, id, entity).await + } } } }; diff --git a/node/src/manager/commands/stats.rs b/node/src/manager/commands/stats.rs index 66eb4f02acd..6fefcd21335 100644 --- a/node/src/manager/commands/stats.rs +++ b/node/src/manager/commands/stats.rs @@ -1,4 +1,5 @@ use std::collections::HashMap; +use std::sync::Arc; use diesel::r2d2::ConnectionManager; use diesel::r2d2::PooledConnection; @@ -6,12 +7,15 @@ use diesel::sql_query; use diesel::sql_types::{Integer, Text}; use diesel::PgConnection; use diesel::RunQueryDsl; +use graph::components::store::EntityType; use graph::prelude::anyhow; use graph::prelude::anyhow::bail; +use graph::prelude::DeploymentHash; use graph_store_postgres::command_support::catalog::Site; use graph_store_postgres::command_support::{catalog as store_catalog, SqlName}; use graph_store_postgres::connection_pool::ConnectionPool; use graph_store_postgres::Shard; +use graph_store_postgres::SubgraphStore; use graph_store_postgres::PRIMARY_SHARD; fn parse_table_name(table: &str) -> Result<(&str, SqlName), anyhow::Error> { @@ -151,3 +155,22 @@ pub fn show( Ok(()) } + +pub async fn analyze( + store: Arc, + hash: String, + entity_name: String, +) -> Result<(), anyhow::Error> { + println!("Running ANALYZE for {entity_name} entity"); + let entity_type = EntityType::new(entity_name); + let deployment_hash = DeploymentHash::new(hash).map_err(|malformed_hash| { + anyhow!( + "Subgraph hash must be a valid IPFS hash: {}", + malformed_hash + ) + })?; + store + .analyze(&deployment_hash, entity_type) + .await + .map_err(|e| anyhow!(e)) +} diff --git a/store/postgres/src/deployment_store.rs b/store/postgres/src/deployment_store.rs index 52180aeb805..51648c8a589 100644 --- a/store/postgres/src/deployment_store.rs +++ b/store/postgres/src/deployment_store.rs @@ -674,6 +674,24 @@ impl DeploymentStore { }) .await } + + /// Runs the SQL `ANALYZE` command in a table. + pub(crate) async fn analyze( + &self, + site: Arc, + entity_type: EntityType, + ) -> Result<(), StoreError> { + let store = self.clone(); + self.with_conn(move |conn, _| { + let layout = store.layout(conn, site)?; + let table = layout.table_for_entity(&entity_type)?; + let table_name = &table.qualified_name; + let sql = format!("analyze {table_name}"); + conn.execute(&sql)?; + Ok(()) + }) + .await + } } /// Methods that back the trait `graph::components::Store`, but have small diff --git a/store/postgres/src/subgraph_store.rs b/store/postgres/src/subgraph_store.rs index cd83645be04..c0950d9c430 100644 --- a/store/postgres/src/subgraph_store.rs +++ b/store/postgres/src/subgraph_store.rs @@ -16,7 +16,7 @@ use graph::{ components::{ server::index_node::VersionInfo, store::{ - self, DeploymentLocator, EnsLookup as EnsLookupTrait, + self, DeploymentLocator, EnsLookup as EnsLookupTrait, EntityType, WritableStore as WritableStoreTrait, }, }, @@ -947,6 +947,15 @@ impl SubgraphStoreInner { ) .await; } + + pub async fn analyze( + &self, + id: &DeploymentHash, + entity_type: EntityType, + ) -> Result<(), StoreError> { + let (store, site) = self.store(&id)?; + store.analyze(site, entity_type).await + } } struct EnsLookup {