Skip to content

Commit

Permalink
fix(indexer-selection): shift required block range by 1 minute (#442)
Browse files Browse the repository at this point in the history
Users typically abused the old behavior by setting block constraints at
the most recent block from some RPC provider and now the gateway cannot
usually ensure that indexers have the required minimum block since
indexer statuses are polled every ~30 seconds and the block was often
produced within 10 seconds of the query hitting the gateway.

Even though the use-case is fragile, we still want to support it before
graphprotocol/indexer#791 is implemented.
  • Loading branch information
Theodus authored Dec 1, 2023
1 parent 419909a commit e7c8dd6
Show file tree
Hide file tree
Showing 3 changed files with 19 additions and 11 deletions.
22 changes: 15 additions & 7 deletions indexer-selection/src/indexing.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::ops::RangeInclusive;
use std::time::{Duration, Instant};

use prelude::GRT;
Expand Down Expand Up @@ -48,16 +49,23 @@ pub struct BlockStatus {
}

impl BlockStatus {
pub fn meets_requirements(&self, requirements: &BlockRequirements) -> bool {
let (min, max) = match requirements.range {
Some(range) => range,
None => return true,
};
pub fn meets_requirements(&self, requirements: &BlockRequirements, block_rate_hz: f64) -> bool {
if self.behind_reported_block {
return false;
}
let min_block = self.min_block.unwrap_or(0);
(min_block <= min) && (max <= self.reported_number)

// Allow selecting indexers if their reported block is "close enough" to the required range. This is to allow an
// admittedly fragile use-case where client queries contain a constraint based on the most recent block from
// some RPC provider. Indexers closer to chain head and with higher success rate will be favored all else being
// equal.
let offset = (block_rate_hz * 60.0) as u64;

let (min, max) = match requirements.range {
Some((start, end)) => (start.saturating_sub(offset), end.saturating_sub(offset)),
None => return true,
};
let reported_range = RangeInclusive::new(self.min_block.unwrap_or(0), self.reported_number);
reported_range.contains(&min) && reported_range.contains(&max)
}
}

Expand Down
2 changes: 1 addition & 1 deletion indexer-selection/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ impl State {
.ok_or(IndexerError::NoStatus)?;

let block_status = state.status.block.as_ref().ok_or(IndexerError::NoStatus)?;
if !block_status.meets_requirements(&params.requirements) {
if !block_status.meets_requirements(&params.requirements, params.block_rate_hz) {
return Err(IndexerError::MissingRequiredBlock.into());
}

Expand Down
6 changes: 3 additions & 3 deletions indexer-selection/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ fn utiliy_params(
budget,
requirements,
latest_block,
block_rate_hz: 0.1,
block_rate_hz: 60.0_f64.recip(),
}
}

Expand Down Expand Up @@ -199,8 +199,8 @@ impl Topology {
expected_errors.0.entry(err).or_default().insert(indexer);
};

if matches!(required_block, Some(n) if n > status.block.as_ref().unwrap().reported_number)
{
let allowed_block = status.block.as_ref().unwrap().reported_number;
if matches!(required_block, Some(n) if n.saturating_sub(1) > allowed_block) {
set_err(IndexerError::MissingRequiredBlock);
} else if status.block.is_none() {
set_err(IndexerError::NoStatus);
Expand Down

0 comments on commit e7c8dd6

Please sign in to comment.