Skip to content

Commit 9313194

Browse files
committed
fix: limit single indexer success rate
1 parent 7d29bba commit 9313194

File tree

5 files changed

+84
-19
lines changed

5 files changed

+84
-19
lines changed

Cargo.lock

Lines changed: 4 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

candidate-selection/src/criteria/performance.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,9 @@ impl Performance {
8282
// This results in decay pulling success rate upward.
8383
successful_responses += 1.0;
8484
let failed_responses: f64 = self.latency_failure.map(|f| f.response_count).sum();
85-
Normalized::new(successful_responses / (successful_responses + failed_responses).max(1.0))
86-
.unwrap()
85+
let success_rate = successful_responses / (successful_responses + failed_responses);
86+
// Limit an individual indexer's success rate to 99%.
87+
Normalized::new(success_rate.min(0.99)).unwrap()
8788
}
8889

8990
fn latency_success(&self) -> NotNan<f64> {

indexer-selection/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ edition = "2021"
66
[dependencies]
77
candidate-selection = { path = "../candidate-selection" }
88
custom_debug = "0.6.1"
9-
thegraph-core = "0.4.0"
9+
thegraph-core = "0.3.2"
1010
url = "2.5.0"
1111

1212
[dev-dependencies]

indexer-selection/src/lib.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
1-
use std::collections::hash_map::DefaultHasher;
2-
use std::f64::consts::E;
3-
use std::fmt::Display;
4-
use std::hash::{Hash as _, Hasher as _};
5-
6-
use custom_debug::CustomDebug;
7-
use thegraph_core::types::alloy_primitives::Address;
8-
use thegraph_core::types::DeploymentId;
9-
use url::Url;
10-
111
use candidate_selection::criteria::performance::expected_value_probabilities;
122
pub use candidate_selection::criteria::performance::{ExpectedPerformance, Performance};
133
pub use candidate_selection::{ArrayVec, Normalized};
4+
use custom_debug::CustomDebug;
5+
use std::{
6+
collections::hash_map::DefaultHasher,
7+
f64::consts::E,
8+
fmt::Display,
9+
hash::{Hash as _, Hasher as _},
10+
};
11+
use thegraph_core::types::{alloy_primitives::Address, DeploymentId};
12+
use url::Url;
1413

1514
#[cfg(test)]
1615
mod test;
@@ -23,6 +22,7 @@ pub struct Candidate {
2322
pub url: Url,
2423
pub perf: ExpectedPerformance,
2524
pub fee: Normalized,
25+
/// seconds behind chain head
2626
pub seconds_behind: u32,
2727
pub slashable_grt: u64,
2828
/// subgraph versions behind latest deployment

indexer-selection/src/test.rs

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ fn sensitivity_seconds_behind() {
116116
.into(),
117117
url: "https://example.com".parse().unwrap(),
118118
perf: ExpectedPerformance {
119-
success_rate: Normalized::ONE,
119+
success_rate: Normalized::new(0.99).unwrap(),
120120
latency_success_ms: 0,
121121
latency_failure_ms: 0,
122122
},
@@ -164,3 +164,68 @@ fn sensitivity_seconds_behind() {
164164
"select candidate closer to chain head",
165165
);
166166
}
167+
168+
#[test]
169+
fn multi_selection_preference() {
170+
let candidates = [
171+
Candidate {
172+
indexer: hex!("0000000000000000000000000000000000000000").into(),
173+
deployment: hex!("0000000000000000000000000000000000000000000000000000000000000000")
174+
.into(),
175+
url: "https://example.com/".parse().unwrap(),
176+
perf: ExpectedPerformance {
177+
success_rate: Normalized::new(0.99).unwrap(),
178+
latency_success_ms: 93,
179+
latency_failure_ms: 0,
180+
},
181+
fee: Normalized::ZERO,
182+
seconds_behind: 0,
183+
slashable_grt: 9445169,
184+
versions_behind: 0,
185+
zero_allocation: false,
186+
},
187+
Candidate {
188+
indexer: hex!("0000000000000000000000000000000000000001").into(),
189+
deployment: hex!("0000000000000000000000000000000000000000000000000000000000000000")
190+
.into(),
191+
url: "https://example.com/".parse().unwrap(),
192+
perf: ExpectedPerformance {
193+
success_rate: Normalized::new(0.99).unwrap(),
194+
latency_success_ms: 0,
195+
latency_failure_ms: 0,
196+
},
197+
fee: Normalized::ZERO,
198+
seconds_behind: 0,
199+
slashable_grt: 1330801,
200+
versions_behind: 0,
201+
zero_allocation: false,
202+
},
203+
Candidate {
204+
indexer: hex!("0000000000000000000000000000000000000002").into(),
205+
deployment: hex!("0000000000000000000000000000000000000000000000000000000000000000")
206+
.into(),
207+
url: "https://example.com/".parse().unwrap(),
208+
perf: ExpectedPerformance {
209+
success_rate: Normalized::new(0.99).unwrap(),
210+
latency_success_ms: 224,
211+
latency_failure_ms: 0,
212+
},
213+
fee: Normalized::ZERO,
214+
seconds_behind: 0,
215+
slashable_grt: 2675210,
216+
versions_behind: 0,
217+
zero_allocation: false,
218+
},
219+
];
220+
221+
for c in &candidates {
222+
println!("{} {:?}", c.indexer, c.score());
223+
}
224+
let combined_score =
225+
Candidate::score_many::<3>(&candidates.iter().collect::<ArrayVec<&Candidate, 3>>());
226+
assert!(candidates.iter().all(|c| c.score() < combined_score));
227+
228+
let selected: ArrayVec<&Candidate, 3> = crate::select(&candidates);
229+
println!("{:#?}", selected);
230+
assert_eq!(3, selected.len(), "all indexers selected");
231+
}

0 commit comments

Comments
 (0)