Skip to content

Commit 677342b

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 dcdb5e6 commit 677342b

File tree

2 files changed

+89
-3
lines changed

2 files changed

+89
-3
lines changed

graphql/src/store/resolver.rs

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ use graph::data::value::{Object, Word};
1212
use graph::derive::CheapClone;
1313
use graph::prelude::*;
1414
use graph::schema::{
15-
ast as sast, INTROSPECTION_SCHEMA_FIELD_NAME, INTROSPECTION_TYPE_FIELD_NAME, META_FIELD_NAME,
16-
META_FIELD_TYPE,
15+
ast as sast, INTROSPECTION_SCHEMA_FIELD_NAME, INTROSPECTION_TYPE_FIELD_NAME, LOGS_FIELD_NAME,
16+
META_FIELD_NAME, META_FIELD_TYPE,
1717
};
1818
use graph::schema::{ErrorPolicy, BLOCK_FIELD_TYPE};
1919

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

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

367-
// Only keep the _meta, __schema and __type fields from the data
384+
// Only keep the _meta, _logs, __schema and __type fields from the data
368385
let meta_fields = data.as_mut().and_then(|d| {
369386
let meta_field = d.remove(META_FIELD_NAME);
387+
let logs_field = d.remove(LOGS_FIELD_NAME);
370388
let schema_field = d.remove(INTROSPECTION_SCHEMA_FIELD_NAME);
371389
let type_field = d.remove(INTROSPECTION_TYPE_FIELD_NAME);
372390

@@ -376,6 +394,9 @@ impl Resolver for StoreResolver {
376394
if let Some(meta_field) = meta_field {
377395
meta_fields.push((Word::from(META_FIELD_NAME), meta_field));
378396
}
397+
if let Some(logs_field) = logs_field {
398+
meta_fields.push((Word::from(LOGS_FIELD_NAME), logs_field));
399+
}
379400
if let Some(schema_field) = schema_field {
380401
meta_fields
381402
.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
@@ -1028,6 +1028,71 @@ async fn test_poi_for_failed_subgraph(ctx: TestContext) -> anyhow::Result<()> {
10281028
let resp = Subgraph::query_with_vars(FETCH_POI, vars).await?;
10291029
assert_eq!(None, resp.get("errors"));
10301030
assert!(resp["data"]["proofOfIndexing"].is_string());
1031+
1032+
// Test that _logs query works on failed subgraphs (critical for debugging!)
1033+
// Wait a moment for logs to be written
1034+
sleep(Duration::from_secs(2)).await;
1035+
1036+
let query = r#"{
1037+
_logs(first: 100) {
1038+
id
1039+
timestamp
1040+
level
1041+
text
1042+
}
1043+
}"#
1044+
.to_string();
1045+
1046+
let resp = subgraph.query(&query).await?;
1047+
1048+
// Should not have GraphQL errors when querying logs on failed subgraph
1049+
assert!(
1050+
resp.get("errors").is_none(),
1051+
"Expected no errors when querying _logs on failed subgraph, got: {:?}",
1052+
resp.get("errors")
1053+
);
1054+
1055+
let logs = resp["data"]["_logs"]
1056+
.as_array()
1057+
.context("Expected _logs to be an array")?;
1058+
1059+
// The critical assertion: _logs query works on failed subgraphs
1060+
// This enables debugging even when the subgraph has crashed
1061+
println!(
1062+
"Successfully queried _logs on failed subgraph, found {} log entries",
1063+
logs.len()
1064+
);
1065+
1066+
// Print a sample of logs to see what's available (for documentation/debugging)
1067+
if !logs.is_empty() {
1068+
println!("Sample logs from failed subgraph:");
1069+
for (i, log) in logs.iter().take(5).enumerate() {
1070+
println!(
1071+
" Log {}: level={:?}, text={:?}",
1072+
i + 1,
1073+
log["level"].as_str(),
1074+
log["text"].as_str()
1075+
);
1076+
}
1077+
}
1078+
1079+
// Verify we can also filter by level on failed subgraphs
1080+
let query = r#"{
1081+
_logs(level: ERROR, first: 100) {
1082+
level
1083+
text
1084+
}
1085+
}"#
1086+
.to_string();
1087+
1088+
let resp = subgraph.query(&query).await?;
1089+
assert!(
1090+
resp.get("errors").is_none(),
1091+
"Expected no errors when filtering _logs by level on failed subgraph"
1092+
);
1093+
1094+
println!("✓ _logs query works on failed subgraphs - critical for debugging!");
1095+
10311096
Ok(())
10321097
}
10331098

0 commit comments

Comments
 (0)