Skip to content

Commit 4ff59df

Browse files
authored
add graph-indexed header (#5710)
This adds a `graph-indexed` header to query responses. The header value contains the block hash, number, and timestamp for the most recently processed block in the subgraph. This avoids the need to rewrite all queries to include `_meta { block { hash number timestamp } }` in either the indexer-service or gateway. Related: edgeandnode/gateway#900, graphprotocol/indexer-rs#494
1 parent 1e7732c commit 4ff59df

File tree

4 files changed

+47
-9
lines changed

4 files changed

+47
-9
lines changed

graph/src/data/query/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,5 @@ mod trace;
77
pub use self::cache_status::CacheStatus;
88
pub use self::error::{QueryError, QueryExecutionError};
99
pub use self::query::{Query, QueryTarget, QueryVariables};
10-
pub use self::result::{QueryResult, QueryResults};
10+
pub use self::result::{LatestBlockInfo, QueryResult, QueryResults};
1111
pub use self::trace::Trace;

graph/src/data/query/result.rs

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::cheap_clone::CheapClone;
44
use crate::components::server::query::ServerResponse;
55
use crate::data::value::Object;
66
use crate::derive::CacheWeight;
7-
use crate::prelude::{r, CacheWeight, DeploymentHash};
7+
use crate::prelude::{r, BlockHash, BlockNumber, CacheWeight, DeploymentHash};
88
use http_body_util::Full;
99
use hyper::header::{
1010
ACCESS_CONTROL_ALLOW_HEADERS, ACCESS_CONTROL_ALLOW_METHODS, ACCESS_CONTROL_ALLOW_ORIGIN,
@@ -48,20 +48,37 @@ where
4848
ser.end()
4949
}
5050

51+
fn serialize_block_hash<S>(data: &BlockHash, serializer: S) -> Result<S::Ok, S::Error>
52+
where
53+
S: Serializer,
54+
{
55+
serializer.serialize_str(&data.to_string())
56+
}
57+
5158
pub type Data = Object;
5259

5360
#[derive(Debug)]
5461
/// A collection of query results that is serialized as a single result.
5562
pub struct QueryResults {
5663
results: Vec<Arc<QueryResult>>,
5764
pub trace: Trace,
65+
pub indexed_block: Option<LatestBlockInfo>,
66+
}
67+
68+
#[derive(Debug, Serialize)]
69+
pub struct LatestBlockInfo {
70+
#[serde(serialize_with = "serialize_block_hash")]
71+
pub hash: BlockHash,
72+
pub number: BlockNumber,
73+
pub timestamp: Option<u64>,
5874
}
5975

6076
impl QueryResults {
61-
pub fn empty(trace: Trace) -> Self {
77+
pub fn empty(trace: Trace, indexed_block: Option<LatestBlockInfo>) -> Self {
6278
QueryResults {
6379
results: Vec::new(),
6480
trace,
81+
indexed_block,
6582
}
6683
}
6784

@@ -155,6 +172,7 @@ impl From<Data> for QueryResults {
155172
QueryResults {
156173
results: vec![Arc::new(x.into())],
157174
trace: Trace::None,
175+
indexed_block: None,
158176
}
159177
}
160178
}
@@ -164,6 +182,7 @@ impl From<QueryResult> for QueryResults {
164182
QueryResults {
165183
results: vec![Arc::new(x)],
166184
trace: Trace::None,
185+
indexed_block: None,
167186
}
168187
}
169188
}
@@ -173,6 +192,7 @@ impl From<Arc<QueryResult>> for QueryResults {
173192
QueryResults {
174193
results: vec![x],
175194
trace: Trace::None,
195+
indexed_block: None,
176196
}
177197
}
178198
}
@@ -182,6 +202,7 @@ impl From<QueryExecutionError> for QueryResults {
182202
QueryResults {
183203
results: vec![Arc::new(x.into())],
184204
trace: Trace::None,
205+
indexed_block: None,
185206
}
186207
}
187208
}
@@ -191,6 +212,7 @@ impl From<Vec<QueryExecutionError>> for QueryResults {
191212
QueryResults {
192213
results: vec![Arc::new(x.into())],
193214
trace: Trace::None,
215+
indexed_block: None,
194216
}
195217
}
196218
}
@@ -205,14 +227,16 @@ impl QueryResults {
205227
pub fn as_http_response(&self) -> ServerResponse {
206228
let json = serde_json::to_string(&self).unwrap();
207229
let attestable = self.results.iter().all(|r| r.is_attestable());
230+
let indexed_block = serde_json::to_string(&self.indexed_block).unwrap();
208231
Response::builder()
209232
.status(200)
210233
.header(ACCESS_CONTROL_ALLOW_ORIGIN, "*")
211234
.header(CONTENT_TYPE, "application/json")
212235
.header(ACCESS_CONTROL_ALLOW_HEADERS, "Content-Type, User-Agent")
213236
.header(ACCESS_CONTROL_ALLOW_METHODS, "GET, OPTIONS, POST")
214237
.header(CONTENT_TYPE, "application/json")
215-
.header("Graph-Attestable", attestable.to_string())
238+
.header("graph-attestable", attestable.to_string())
239+
.header("graph-indexed", indexed_block)
216240
.body(Full::from(json))
217241
.unwrap()
218242
}
@@ -386,8 +410,7 @@ fn multiple_data_items() {
386410
let obj1 = make_obj("key1", "value1");
387411
let obj2 = make_obj("key2", "value2");
388412

389-
let trace = Trace::None;
390-
let mut res = QueryResults::empty(trace);
413+
let mut res = QueryResults::empty(Trace::None, None);
391414
res.append(obj1, CacheStatus::default());
392415
res.append(obj2, CacheStatus::default());
393416

graphql/src/runner.rs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use graph::{
1717
};
1818
use graph::{data::graphql::load_manager::LoadManager, prelude::QueryStoreManager};
1919
use graph::{
20-
data::query::{QueryResults, QueryTarget},
20+
data::query::{LatestBlockInfo, QueryResults, QueryTarget},
2121
prelude::QueryStore,
2222
};
2323

@@ -117,6 +117,20 @@ where
117117
let network = Some(store.network_name().to_string());
118118
let schema = store.api_schema()?;
119119

120+
let latest_block = match store.block_ptr().await.ok().flatten() {
121+
Some(block) => Some(LatestBlockInfo {
122+
timestamp: store
123+
.block_number_with_timestamp_and_parent_hash(&block.hash)
124+
.await
125+
.ok()
126+
.flatten()
127+
.and_then(|(_, t, _)| t),
128+
hash: block.hash,
129+
number: block.number,
130+
}),
131+
None => None,
132+
};
133+
120134
// Test only, see c435c25decbc4ad7bbbadf8e0ced0ff2
121135
#[cfg(debug_assertions)]
122136
let state = INITIAL_DEPLOYMENT_STATE_FOR_TESTS
@@ -148,7 +162,8 @@ where
148162
let by_block_constraint =
149163
StoreResolver::locate_blocks(store.as_ref(), &state, &query).await?;
150164
let mut max_block = 0;
151-
let mut result: QueryResults = QueryResults::empty(query.root_trace(do_trace));
165+
let mut result: QueryResults =
166+
QueryResults::empty(query.root_trace(do_trace), latest_block);
152167
let mut query_res_futures: Vec<_> = vec![];
153168
let setup_elapsed = execute_start.elapsed();
154169

store/test-store/src/store.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -526,7 +526,7 @@ async fn execute_subgraph_query_internal(
526526
100,
527527
graphql_metrics(),
528528
));
529-
let mut result = QueryResults::empty(query.root_trace(trace));
529+
let mut result = QueryResults::empty(query.root_trace(trace), None);
530530
let deployment = query.schema.id().clone();
531531
let store = STORE
532532
.clone()

0 commit comments

Comments
 (0)