Skip to content

Commit

Permalink
Merge branch 'feature/async-pagination-queries' into feature/avoid-lo…
Browse files Browse the repository at this point in the history
…ng-heavy-tasks
  • Loading branch information
xgreenx authored Oct 14, 2024
2 parents c61436e + 35c7184 commit d8c8c5e
Show file tree
Hide file tree
Showing 23 changed files with 369 additions and 92 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]

### Added
- [2350](https://github.com/FuelLabs/fuel-core/pull/2350): Added a new CLI flag `graphql-number-of-threads` to limit the number of threads used by the GraphQL service. The default value is `2`, `0` enables the old behavior.
- [2335](https://github.com/FuelLabs/fuel-core/pull/2335): Added CLI arguments for configuring GraphQL query costs.

### Fixed
- [2345](https://github.com/FuelLabs/fuel-core/pull/2345): In PoA increase priority of block creation timer trigger compare to txpool event management
Expand All @@ -20,6 +22,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
#### Breaking
- [2341](https://github.com/FuelLabs/fuel-core/pull/2341): The maximum number of processed coins from the `coins_to_spend` query is limited to `max_inputs`.

### Added
- [2347](https://github.com/FuelLabs/fuel-core/pull/2347): Add GraphQL complexity histogram

## [Version 0.39.0]

### Added
Expand Down
24 changes: 24 additions & 0 deletions bin/fuel-core/src/cli/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ use fuel_core::{
},
fuel_core_graphql_api::{
worker_service::DaCompressionConfig,
Costs,
ServiceConfig as GraphQLConfig,
},
producer::Config as ProducerConfig,
Expand Down Expand Up @@ -500,6 +501,29 @@ impl Command {
request_body_bytes_limit: graphql.graphql_request_body_bytes_limit,
api_request_timeout: graphql.api_request_timeout.into(),
query_log_threshold_time: graphql.query_log_threshold_time.into(),
costs: Costs {
balance_query: graphql.costs.balance_query,
coins_to_spend: graphql.costs.coins_to_spend,
get_peers: graphql.costs.get_peers,
estimate_predicates: graphql.costs.estimate_predicates,
dry_run: graphql.costs.dry_run,
submit: graphql.costs.submit,
submit_and_await: graphql.costs.submit_and_await,
status_change: graphql.costs.status_change,
storage_read: graphql.costs.storage_read,
tx_get: graphql.costs.tx_get,
tx_status_read: graphql.costs.tx_status_read,
tx_raw_payload: graphql.costs.tx_raw_payload,
block_header: graphql.costs.block_header,
block_transactions: graphql.costs.block_transactions,
block_transactions_ids: graphql.costs.block_transactions_ids,
storage_iterator: graphql.costs.storage_iterator,
bytecode_read: graphql.costs.bytecode_read,
state_transition_bytecode_read: graphql
.costs
.state_transition_bytecode_read,
da_compressed_block_read: graphql.costs.da_compressed_block_read,
},
},
combined_db_config,
snapshot_reader,
Expand Down
160 changes: 160 additions & 0 deletions bin/fuel-core/src/cli/run/graphql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
use std::net;

use fuel_core::fuel_core_graphql_api::DEFAULT_QUERY_COSTS;

#[derive(Debug, Clone, clap::Args)]
pub struct GraphQLArgs {
/// The IP address to bind the GraphQL service to.
Expand Down Expand Up @@ -63,4 +65,162 @@ pub struct GraphQLArgs {
/// Timeout before drop the request.
#[clap(long = "api-request-timeout", default_value = "30s", env)]
pub api_request_timeout: humantime::Duration,

#[clap(flatten)]
pub costs: QueryCosts,
}

/// Costs for individual graphql queries.
#[derive(Debug, Clone, clap::Args)]
pub struct QueryCosts {
/// Query costs for getting balances.
#[clap(
long = "query-cost-balance-query",
default_value = DEFAULT_QUERY_COSTS.balance_query.to_string(),
env
)]
pub balance_query: usize,

/// Query costs for getting coins to spend.
#[clap(
long = "query-cost-coins-to-spend",
default_value = DEFAULT_QUERY_COSTS.coins_to_spend.to_string(),
env)]
pub coins_to_spend: usize,

/// Query costs for getting peers.
#[clap(
long = "query-cost-get-peers",
default_value = DEFAULT_QUERY_COSTS.get_peers.to_string(),
env
)]
pub get_peers: usize,

/// Query costs for estimating predicates.
#[clap(
long = "query-cost-estimate-predicates",
default_value = DEFAULT_QUERY_COSTS.estimate_predicates.to_string(),
env
)]
pub estimate_predicates: usize,

/// Query costs for dry running a set of transactions.
#[clap(
long = "query-cost-dry-run",
default_value = DEFAULT_QUERY_COSTS.dry_run.to_string(),
env
)]
pub dry_run: usize,

/// Query costs for submitting a transaction.
#[clap(
long = "query-cost-submit",
default_value = DEFAULT_QUERY_COSTS.submit.to_string(),
env
)]
pub submit: usize,

/// Query costs for submitting and awaiting a transaction.
#[clap(
long = "query-cost-submit-and-await",
default_value = DEFAULT_QUERY_COSTS.submit_and_await.to_string(),
env
)]
pub submit_and_await: usize,

/// Query costs for the status change query.
#[clap(
long = "query-cost-status-change",
default_value = DEFAULT_QUERY_COSTS.status_change.to_string(),
env
)]
pub status_change: usize,

/// Query costs for reading from storage.
#[clap(
long = "query-cost-storage-read",
default_value = DEFAULT_QUERY_COSTS.storage_read.to_string(),
env
)]
pub storage_read: usize,

/// Query costs for getting a transaction.
#[clap(
long = "query-cost-tx-get",
default_value = DEFAULT_QUERY_COSTS.tx_get.to_string(),
env
)]
pub tx_get: usize,

/// Query costs for reading tx status.
#[clap(
long = "query-cost-tx-status-read",
default_value = DEFAULT_QUERY_COSTS.tx_status_read.to_string(),
env
)]
pub tx_status_read: usize,

/// Query costs for getting the raw tx payload.
#[clap(
long = "query-cost-tx-raw-payload",
default_value = DEFAULT_QUERY_COSTS.tx_raw_payload.to_string(),
env
)]
pub tx_raw_payload: usize,

/// Query costs for block header.
#[clap(
long = "query-cost-block-header",
default_value = DEFAULT_QUERY_COSTS.block_header.to_string(),
env
)]
pub block_header: usize,

/// Query costs for block transactions.
#[clap(
long = "query-cost-block-transactions",
default_value = DEFAULT_QUERY_COSTS.block_transactions.to_string(),
env
)]
pub block_transactions: usize,

/// Query costs for block transactions ids.
#[clap(
long = "query-cost-block-transactions-ids",
default_value = DEFAULT_QUERY_COSTS.block_transactions_ids.to_string(),
env
)]
pub block_transactions_ids: usize,

/// Query costs for iterating over storage entries.
#[clap(
long = "query-cost-storage-iterator",
default_value = DEFAULT_QUERY_COSTS.storage_iterator.to_string(),
env
)]
pub storage_iterator: usize,

/// Query costs for reading bytecode.
#[clap(
long = "query-cost-bytecode-read",
default_value = DEFAULT_QUERY_COSTS.bytecode_read.to_string(),
env
)]
pub bytecode_read: usize,

/// Query costs for reading state transition bytecode.
#[clap(
long = "query-cost-state-transition-bytecode-read",
default_value = DEFAULT_QUERY_COSTS.state_transition_bytecode_read.to_string(),
env
)]
pub state_transition_bytecode_read: usize,

/// Query costs for reading a DA compressed block.
#[clap(
long = "query-cost-da-compressed-block-read",
default_value = DEFAULT_QUERY_COSTS.da_compressed_block_read.to_string(),
env
)]
pub da_compressed_block_read: usize,
}
55 changes: 41 additions & 14 deletions crates/fuel-core/src/graphql_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use fuel_core_storage::{
};
use std::{
net::SocketAddr,
sync::OnceLock,
time::Duration,
};

Expand All @@ -17,6 +18,17 @@ pub(crate) mod validation_extension;
pub(crate) mod view_extension;
pub mod worker_service;

#[derive(Clone, Debug)]
pub struct Config {
pub config: ServiceConfig,
pub utxo_validation: bool,
pub debug: bool,
pub vm_backtrace: bool,
pub max_tx: usize,
pub max_txpool_dependency_chain_length: usize,
pub chain_name: String,
}

#[derive(Clone, Debug)]
pub struct ServiceConfig {
pub addr: SocketAddr,
Expand All @@ -32,8 +44,11 @@ pub struct ServiceConfig {
/// Time to wait after submitting a query before debug info will be logged about query.
pub query_log_threshold_time: Duration,
pub api_request_timeout: Duration,
/// Configurable cost parameters to limit graphql queries complexity
pub costs: Costs,
}

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Costs {
pub balance_query: usize,
pub coins_to_spend: usize,
Expand All @@ -56,16 +71,19 @@ pub struct Costs {
pub da_compressed_block_read: usize,
}

pub const QUERY_COSTS: Costs = Costs {
// balance_query: 4000,
#[cfg(feature = "test-helpers")]
impl Default for Costs {
fn default() -> Self {
DEFAULT_QUERY_COSTS
}
}

pub const DEFAULT_QUERY_COSTS: Costs = Costs {
balance_query: 40001,
coins_to_spend: 40001,
// get_peers: 2000,
get_peers: 40001,
// estimate_predicates: 3000,
estimate_predicates: 40001,
dry_run: 12000,
// submit: 5000,
submit: 40001,
submit_and_await: 40001,
status_change: 40001,
Expand All @@ -82,15 +100,24 @@ pub const QUERY_COSTS: Costs = Costs {
da_compressed_block_read: 4000,
};

#[derive(Clone, Debug)]
pub struct Config {
pub config: ServiceConfig,
pub utxo_validation: bool,
pub debug: bool,
pub vm_backtrace: bool,
pub max_tx: usize,
pub max_txpool_dependency_chain_length: usize,
pub chain_name: String,
pub fn query_costs() -> &'static Costs {
QUERY_COSTS.get().unwrap_or(&DEFAULT_QUERY_COSTS)
}

pub static QUERY_COSTS: OnceLock<Costs> = OnceLock::new();

fn initialize_query_costs(costs: Costs) -> anyhow::Result<()> {
#[cfg(feature = "test-helpers")]
if costs != DEFAULT_QUERY_COSTS {
// We don't support setting these values in test contexts, because
// it can lead to unexpected behavior if multiple tests try to
// initialize different values.
anyhow::bail!("cannot initialize queries with non-default costs in tests")
}

QUERY_COSTS.get_or_init(|| costs);

Ok(())
}

pub trait IntoApiResult<T> {
Expand Down
3 changes: 3 additions & 0 deletions crates/fuel-core/src/graphql_api/api_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use crate::{
view_extension::ViewExtension,
Config,
},
graphql_api,
schema::{
CoreSchema,
CoreSchemaBuilder,
Expand Down Expand Up @@ -232,6 +233,8 @@ where
OnChain::LatestView: OnChainDatabase,
OffChain::LatestView: OffChainDatabase,
{
graphql_api::initialize_query_costs(config.config.costs.clone())?;

let network_addr = config.config.addr;
let combined_read_database = ReadDatabase::new(
config.config.database_batch_size,
Expand Down
13 changes: 13 additions & 0 deletions crates/fuel-core/src/graphql_api/metrics_extension.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@ use async_graphql::{
NextParseQuery,
NextRequest,
NextResolve,
NextValidation,
ResolveInfo,
},
parser::types::ExecutableDocument,
Response,
ServerError,
ServerResult,
ValidationResult,
Value,
Variables,
};
Expand Down Expand Up @@ -118,4 +121,14 @@ impl Extension for MetricsExtInner {

res
}

async fn validation(
&self,
ctx: &ExtensionContext<'_>,
next: NextValidation<'_>,
) -> Result<ValidationResult, Vec<ServerError>> {
let result = next.run(ctx).await?;
graphql_metrics().graphql_complexity_observe(result.complexity as f64);
Ok(result)
}
}
Loading

0 comments on commit d8c8c5e

Please sign in to comment.