Skip to content

Commit b0728ca

Browse files
committed
graphql, tests: Ensure logs graphql queries work on failed subgraphs
- include _logs in the set of special fields that bypass indexing error shortcutting when subgraph failed - add integration test to ensure _log queries return logs after subgraph failed
1 parent 80602b4 commit b0728ca

2 files changed

Lines changed: 89 additions & 3 deletions

File tree

graphql/src/store/resolver.rs

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ use graph::derive::CheapClone;
1313
use graph::prelude::alloy::primitives::B256;
1414
use graph::prelude::*;
1515
use graph::schema::{
16-
ast as sast, INTROSPECTION_SCHEMA_FIELD_NAME, INTROSPECTION_TYPE_FIELD_NAME, META_FIELD_NAME,
17-
META_FIELD_TYPE,
16+
ast as sast, INTROSPECTION_SCHEMA_FIELD_NAME, INTROSPECTION_TYPE_FIELD_NAME, LOGS_FIELD_NAME,
17+
META_FIELD_NAME, META_FIELD_TYPE,
1818
};
1919
use graph::schema::{ErrorPolicy, BLOCK_FIELD_TYPE};
2020

@@ -354,6 +354,23 @@ impl Resolver for StoreResolver {
354354
return Ok(());
355355
}
356356

357+
// Check if the query only contains debugging fields (_meta, _logs).
358+
// If so, don't add indexing errors - these queries are specifically for debugging
359+
// failed subgraphs and should work without errors.
360+
// Introspection queries (__schema, __type) still get the indexing_error to inform
361+
// users the subgraph has issues, but they return data.
362+
let only_debugging_fields = result
363+
.data()
364+
.map(|data| {
365+
data.iter()
366+
.all(|(key, _)| key == META_FIELD_NAME || key == LOGS_FIELD_NAME)
367+
})
368+
.unwrap_or(false);
369+
370+
if only_debugging_fields {
371+
return Ok(());
372+
}
373+
357374
// Add the "indexing_error" to the response.
358375
assert!(result.errors_mut().is_empty());
359376
*result.errors_mut() = vec![QueryError::IndexingError];
@@ -365,9 +382,10 @@ impl Resolver for StoreResolver {
365382
ErrorPolicy::Deny => {
366383
let mut data = result.take_data();
367384

368-
// Only keep the _meta, __schema and __type fields from the data
385+
// Only keep the _meta, _logs, __schema and __type fields from the data
369386
let meta_fields = data.as_mut().and_then(|d| {
370387
let meta_field = d.remove(META_FIELD_NAME);
388+
let logs_field = d.remove(LOGS_FIELD_NAME);
371389
let schema_field = d.remove(INTROSPECTION_SCHEMA_FIELD_NAME);
372390
let type_field = d.remove(INTROSPECTION_TYPE_FIELD_NAME);
373391

@@ -377,6 +395,9 @@ impl Resolver for StoreResolver {
377395
if let Some(meta_field) = meta_field {
378396
meta_fields.push((Word::from(META_FIELD_NAME), meta_field));
379397
}
398+
if let Some(logs_field) = logs_field {
399+
meta_fields.push((Word::from(LOGS_FIELD_NAME), logs_field));
400+
}
380401
if let Some(schema_field) = schema_field {
381402
meta_fields
382403
.push((Word::from(INTROSPECTION_SCHEMA_FIELD_NAME), schema_field));

tests/tests/integration_tests.rs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1136,6 +1136,71 @@ async fn test_poi_for_failed_subgraph(ctx: TestContext) -> anyhow::Result<()> {
11361136
let resp = Subgraph::query_with_vars(FETCH_POI, vars).await?;
11371137
assert_eq!(None, resp.get("errors"));
11381138
assert!(resp["data"]["proofOfIndexing"].is_string());
1139+
1140+
// Test that _logs query works on failed subgraphs (critical for debugging!)
1141+
// Wait a moment for logs to be written
1142+
sleep(Duration::from_secs(2)).await;
1143+
1144+
let query = r#"{
1145+
_logs(first: 100) {
1146+
id
1147+
timestamp
1148+
level
1149+
text
1150+
}
1151+
}"#
1152+
.to_string();
1153+
1154+
let resp = subgraph.query(&query).await?;
1155+
1156+
// Should not have GraphQL errors when querying logs on failed subgraph
1157+
assert!(
1158+
resp.get("errors").is_none(),
1159+
"Expected no errors when querying _logs on failed subgraph, got: {:?}",
1160+
resp.get("errors")
1161+
);
1162+
1163+
let logs = resp["data"]["_logs"]
1164+
.as_array()
1165+
.context("Expected _logs to be an array")?;
1166+
1167+
// The critical assertion: _logs query works on failed subgraphs
1168+
// This enables debugging even when the subgraph has crashed
1169+
println!(
1170+
"Successfully queried _logs on failed subgraph, found {} log entries",
1171+
logs.len()
1172+
);
1173+
1174+
// Print a sample of logs to see what's available (for documentation/debugging)
1175+
if !logs.is_empty() {
1176+
println!("Sample logs from failed subgraph:");
1177+
for (i, log) in logs.iter().take(5).enumerate() {
1178+
println!(
1179+
" Log {}: level={:?}, text={:?}",
1180+
i + 1,
1181+
log["level"].as_str(),
1182+
log["text"].as_str()
1183+
);
1184+
}
1185+
}
1186+
1187+
// Verify we can also filter by level on failed subgraphs
1188+
let query = r#"{
1189+
_logs(level: ERROR, first: 100) {
1190+
level
1191+
text
1192+
}
1193+
}"#
1194+
.to_string();
1195+
1196+
let resp = subgraph.query(&query).await?;
1197+
assert!(
1198+
resp.get("errors").is_none(),
1199+
"Expected no errors when filtering _logs by level on failed subgraph"
1200+
);
1201+
1202+
println!("✓ _logs query works on failed subgraphs - critical for debugging!");
1203+
11391204
Ok(())
11401205
}
11411206

0 commit comments

Comments
 (0)