Skip to content

Commit f3e108a

Browse files
stuhoodwisechengyi
authored andcommitted
Render cyclic path in node error to avoid --print-exception-stacktrace (#8422)
### Problem See #5739: currently, rendering a cycle in the graph requires enabling `--print-exception-stacktrace`. But `--print-exception-stacktrace` is annoying for end users. ### Solution Use the cyclic path that @illicitonion added in #7642 to render an error directly. And in a separate commit, remove `EntryKey`, which was only used for "recording" cycles in the `Graph`... from an era where we actually looked at the complete dump of the runtime graph, I think? ### Result `--print-exception-stacktrace` is no longer necessary to see the "path" involved in a cycle. Fixes #5739.
1 parent c103db3 commit f3e108a

File tree

6 files changed

+136
-151
lines changed

6 files changed

+136
-151
lines changed

src/rust/engine/graph/src/entry.rs

+67-97
Original file line numberDiff line numberDiff line change
@@ -146,26 +146,6 @@ impl<N: Node> EntryState<N> {
146146
}
147147
}
148148

149-
///
150-
/// Because there are guaranteed to be more edges than nodes in Graphs, we mark cyclic
151-
/// dependencies via a wrapper around the Node (rather than adding a byte to every
152-
/// valid edge).
153-
///
154-
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
155-
pub(crate) enum EntryKey<N: Node> {
156-
Valid(N),
157-
Cyclic(N),
158-
}
159-
160-
impl<N: Node> EntryKey<N> {
161-
pub(crate) fn content(&self) -> &N {
162-
match self {
163-
&EntryKey::Valid(ref v) => v,
164-
&EntryKey::Cyclic(ref v) => v,
165-
}
166-
}
167-
}
168-
169149
///
170150
/// An Entry and its adjacencies.
171151
///
@@ -174,7 +154,7 @@ pub struct Entry<N: Node> {
174154
// TODO: This is a clone of the Node, which is also kept in the `nodes` map. It would be
175155
// nice to avoid keeping two copies of each Node, but tracking references between the two
176156
// maps is painful.
177-
node: EntryKey<N>,
157+
node: N,
178158

179159
state: Arc<Mutex<EntryState<N>>>,
180160
}
@@ -185,15 +165,15 @@ impl<N: Node> Entry<N> {
185165
/// the EntryId of an Entry until after it is stored in the Graph, and we need the EntryId
186166
/// in order to run the Entry.
187167
///
188-
pub(crate) fn new(node: EntryKey<N>) -> Entry<N> {
168+
pub(crate) fn new(node: N) -> Entry<N> {
189169
Entry {
190170
node: node,
191171
state: Arc::new(Mutex::new(EntryState::initial())),
192172
}
193173
}
194174

195175
pub fn node(&self) -> &N {
196-
self.node.content()
176+
&self.node
197177
}
198178

199179
///
@@ -216,7 +196,7 @@ impl<N: Node> Entry<N> {
216196
///
217197
pub(crate) fn run<C>(
218198
context_factory: &C,
219-
entry_key: &EntryKey<N>,
199+
node: &N,
220200
entry_id: EntryId,
221201
run_token: RunToken,
222202
generation: Generation,
@@ -228,77 +208,67 @@ impl<N: Node> Entry<N> {
228208
{
229209
// Increment the RunToken to uniquely identify this work.
230210
let run_token = run_token.next();
231-
match entry_key {
232-
&EntryKey::Valid(ref n) => {
233-
let context = context_factory.clone_for(entry_id);
234-
let node = n.clone();
235-
236-
context_factory.spawn(future::lazy(move || {
237-
// If we have previous result generations, compare them to all current dependency
238-
// generations (which, if they are dirty, will cause recursive cleaning). If they
239-
// match, we can consider the previous result value to be clean for reuse.
240-
let was_clean = if let Some(previous_dep_generations) = previous_dep_generations {
241-
let context2 = context.clone();
242-
context
243-
.graph()
244-
.dep_generations(entry_id, &context)
245-
.then(move |generation_res| match generation_res {
246-
Ok(ref dep_generations) if dep_generations == &previous_dep_generations => {
247-
// Dependencies have not changed: Node is clean.
248-
Ok(true)
249-
}
250-
_ => {
251-
// If dependency generations mismatched or failed to fetch, clear its
252-
// dependencies and indicate that it should re-run.
253-
context2.graph().clear_deps(entry_id, run_token);
254-
Ok(false)
255-
}
256-
})
257-
.to_boxed()
258-
} else {
259-
future::ok(false).to_boxed()
260-
};
261-
262-
// If the Node was clean, complete it. Otherwise, re-run.
263-
was_clean.and_then(move |was_clean| {
264-
if was_clean {
265-
// No dependencies have changed: we can complete the Node without changing its
266-
// previous_result or generation.
267-
context
268-
.graph()
269-
.complete(&context, entry_id, run_token, None);
270-
future::ok(()).to_boxed()
271-
} else {
272-
// The Node needs to (re-)run!
273-
let context2 = context.clone();
274-
node
275-
.run(context)
276-
.then(move |res| {
277-
context2
278-
.graph()
279-
.complete(&context2, entry_id, run_token, Some(res));
280-
Ok(())
281-
})
282-
.to_boxed()
211+
let context = context_factory.clone_for(entry_id);
212+
let node = node.clone();
213+
214+
context_factory.spawn(future::lazy(move || {
215+
// If we have previous result generations, compare them to all current dependency
216+
// generations (which, if they are dirty, will cause recursive cleaning). If they
217+
// match, we can consider the previous result value to be clean for reuse.
218+
let was_clean = if let Some(previous_dep_generations) = previous_dep_generations {
219+
let context2 = context.clone();
220+
context
221+
.graph()
222+
.dep_generations(entry_id, &context)
223+
.then(move |generation_res| match generation_res {
224+
Ok(ref dep_generations) if dep_generations == &previous_dep_generations => {
225+
// Dependencies have not changed: Node is clean.
226+
Ok(true)
227+
}
228+
_ => {
229+
// If dependency generations mismatched or failed to fetch, clear its
230+
// dependencies and indicate that it should re-run.
231+
context2.graph().clear_deps(entry_id, run_token);
232+
Ok(false)
283233
}
284234
})
285-
}));
235+
.to_boxed()
236+
} else {
237+
future::ok(false).to_boxed()
238+
};
286239

287-
EntryState::Running {
288-
waiters: Vec::new(),
289-
start_time: Instant::now(),
290-
run_token,
291-
generation,
292-
previous_result,
293-
dirty: false,
240+
// If the Node was clean, complete it. Otherwise, re-run.
241+
was_clean.and_then(move |was_clean| {
242+
if was_clean {
243+
// No dependencies have changed: we can complete the Node without changing its
244+
// previous_result or generation.
245+
context
246+
.graph()
247+
.complete(&context, entry_id, run_token, None);
248+
future::ok(()).to_boxed()
249+
} else {
250+
// The Node needs to (re-)run!
251+
let context2 = context.clone();
252+
node
253+
.run(context)
254+
.then(move |res| {
255+
context2
256+
.graph()
257+
.complete(&context2, entry_id, run_token, Some(res));
258+
Ok(())
259+
})
260+
.to_boxed()
294261
}
295-
}
296-
&EntryKey::Cyclic(_) => EntryState::Completed {
297-
result: EntryResult::Clean(Err(N::Error::cyclic())),
298-
dep_generations: Vec::new(),
299-
run_token,
300-
generation,
301-
},
262+
})
263+
}));
264+
265+
EntryState::Running {
266+
waiters: Vec::new(),
267+
start_time: Instant::now(),
268+
run_token,
269+
generation,
270+
previous_result,
271+
dirty: false,
302272
}
303273
}
304274

@@ -339,7 +309,7 @@ impl<N: Node> Entry<N> {
339309
ref result,
340310
generation,
341311
..
342-
} if self.node.content().cacheable() && !result.is_dirty() => {
312+
} if self.node.cacheable() && !result.is_dirty() => {
343313
return future::result(result.as_ref().clone())
344314
.map(move |res| (res, generation))
345315
.to_boxed();
@@ -374,10 +344,10 @@ impl<N: Node> Entry<N> {
374344
"Re-starting node {:?}. It was: previous_result={:?}, cacheable={}",
375345
self.node,
376346
result,
377-
self.node.content().cacheable()
347+
self.node.cacheable()
378348
);
379349
assert!(
380-
result.is_dirty() || !self.node.content().cacheable(),
350+
result.is_dirty() || !self.node.cacheable(),
381351
"A clean Node should not reach this point: {:?}",
382352
result
383353
);
@@ -394,12 +364,12 @@ impl<N: Node> Entry<N> {
394364
entry_id,
395365
run_token,
396366
generation,
397-
if self.node.content().cacheable() {
367+
if self.node.cacheable() {
398368
Some(dep_generations)
399369
} else {
400370
None
401371
},
402-
if self.node.content().cacheable() {
372+
if self.node.cacheable() {
403373
Some(result)
404374
} else {
405375
None
@@ -684,6 +654,6 @@ impl<N: Node> Entry<N> {
684654
Some(Err(ref x)) => format!("{:?}", x),
685655
None => "<None>".to_string(),
686656
};
687-
format!("{} == {}", self.node.content(), state).replace("\"", "\\\"")
657+
format!("{} == {}", self.node, state).replace("\"", "\\\"")
688658
}
689659
}

0 commit comments

Comments
 (0)