Skip to content

Commit d41c50b

Browse files
authored
Turbopack: Store task error as pointer to the source error (#89293)
Store task error as pointer to the source error <!-- Thanks for opening a PR! Your contribution is much appreciated. To make sure your PR is handled as smoothly as possible we request that you follow the checklist sections below. Choose the right checklist for the change(s) that you're making: ## For Contributors ### Improving Documentation - Run `pnpm prettier-fix` to fix formatting issues before opening the PR. - Read the Docs Contribution Guide to ensure your contribution follows the docs guidelines: https://nextjs.org/docs/community/contribution-guide ### Fixing a bug - Related issues linked using `fixes #number` - Tests added. See: https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs - Errors have a helpful link attached, see https://github.com/vercel/next.js/blob/canary/contributing.md ### Adding a feature - Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. (A discussion must be opened, see https://github.com/vercel/next.js/discussions/new?category=ideas) - Related issues/discussions are linked using `fixes #number` - e2e tests added (https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs) - Documentation added - Telemetry added. In case of a feature if it's used or not. - Errors have a helpful link attached, see https://github.com/vercel/next.js/blob/canary/contributing.md ## For Maintainers - Minimal description (aim for explaining to someone not on the team to understand the PR) - When linking to a Slack thread, you might want to share details of the conclusion - Link both the Linear (Fixes NEXT-xxx) and the GitHub issues - Add review comments if necessary to explain to the reviewer the logic behind a change ### What? ### Why? ### How? Closes NEXT- Fixes # -->
1 parent 5415c3f commit d41c50b

File tree

11 files changed

+377
-64
lines changed

11 files changed

+377
-64
lines changed

crates/next-napi-bindings/src/next_api/project.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ use tracing_subscriber::{Registry, layer::SubscriberExt, util::SubscriberInitExt
3737
use turbo_rcstr::{RcStr, rcstr};
3838
use turbo_tasks::{
3939
Effects, FxIndexSet, NonLocalValue, OperationValue, OperationVc, PrettyPrintError, ReadRef,
40-
ResolvedVc, TaskInput, TransientInstance, TryJoinIterExt, TurboTasksApi, UpdateInfo, Vc,
41-
get_effects,
40+
ResolvedVc, TaskInput, TransientInstance, TryJoinIterExt, TurboTasksApi, TurboTasksCallApi,
41+
UpdateInfo, Vc, get_effects,
4242
message_queue::{CompilationEvent, Severity},
4343
trace::TraceRawVcs,
4444
};

crates/next-napi-bindings/src/next_api/turbopack_ctx.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use owo_colors::OwoColorize;
1818
use serde::Serialize;
1919
use terminal_hyperlink::Hyperlink;
2020
use turbo_tasks::{
21-
PrettyPrintError, TurboTasks, TurboTasksApi,
21+
PrettyPrintError, TurboTasks, TurboTasksCallApi,
2222
backend::TurboTasksExecutionError,
2323
message_queue::{CompilationEvent, Severity},
2424
};

turbopack/crates/turbo-tasks-backend/src/backend/mod.rs

Lines changed: 98 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -28,18 +28,19 @@ use turbo_bincode::{TurboBincodeBuffer, new_turbo_bincode_decoder, new_turbo_bin
2828
use turbo_tasks::{
2929
CellId, FxDashMap, RawVc, ReadCellOptions, ReadCellTracking, ReadConsistency,
3030
ReadOutputOptions, ReadTracking, SharedReference, TRANSIENT_TASK_BIT, TaskExecutionReason,
31-
TaskId, TaskPriority, TraitTypeId, TurboTasksBackendApi, ValueTypeId,
31+
TaskId, TaskPriority, TraitTypeId, TurboTasksBackendApi, TurboTasksPanic, ValueTypeId,
3232
backend::{
3333
Backend, CachedTaskType, CellContent, TaskExecutionSpec, TransientTaskType,
34-
TurboTasksExecutionError, TypedCellContent, VerificationMode,
34+
TurboTaskContextError, TurboTaskLocalContextError, TurboTasksError,
35+
TurboTasksExecutionError, TurboTasksExecutionErrorMessage, TypedCellContent,
36+
VerificationMode,
3537
},
3638
event::{Event, EventDescription, EventListener},
3739
message_queue::TimingEvent,
3840
registry::get_value_type,
3941
scope::scope_and_block,
4042
task_statistics::TaskStatisticsApi,
4143
trace::TraceRawVcs,
42-
turbo_tasks,
4344
util::{IdFactoryWithReuse, good_chunk_size, into_chunks},
4445
};
4546

@@ -66,6 +67,7 @@ use crate::{
6667
ActivenessState, CellRef, CollectibleRef, CollectiblesRef, Dirtyness, InProgressCellState,
6768
InProgressState, InProgressStateInner, OutputValue, TransientTask,
6869
},
70+
error::TaskError,
6971
utils::{
7072
arc_or_owned::ArcOrOwned,
7173
chunked_vec::ChunkedVec,
@@ -387,6 +389,69 @@ impl<B: BackingStorage> TurboTasksBackendInner<B> {
387389
self.task_statistics
388390
.map(|stats| stats.increment_cache_miss(task_type.native_fn));
389391
}
392+
393+
/// Reconstructs a full [`TurboTasksExecutionError`] from the compact [`TaskError`] storage
394+
/// representation. For [`TaskError::TaskChain`], this looks up the source error from the last
395+
/// task's output and rebuilds the nested `TaskContext` wrappers with `TurboTasksCallApi`
396+
/// references for lazy name resolution.
397+
fn task_error_to_turbo_tasks_execution_error(
398+
&self,
399+
error: &TaskError,
400+
ctx: &mut impl ExecuteContext<'_>,
401+
) -> TurboTasksExecutionError {
402+
match error {
403+
TaskError::Panic(panic) => TurboTasksExecutionError::Panic(panic.clone()),
404+
TaskError::Error(item) => TurboTasksExecutionError::Error(Arc::new(TurboTasksError {
405+
message: item.message.clone(),
406+
source: item
407+
.source
408+
.as_ref()
409+
.map(|e| self.task_error_to_turbo_tasks_execution_error(e, ctx)),
410+
})),
411+
TaskError::LocalTaskContext(local_task_context) => {
412+
TurboTasksExecutionError::LocalTaskContext(Arc::new(TurboTaskLocalContextError {
413+
name: local_task_context.name.clone(),
414+
source: local_task_context
415+
.source
416+
.as_ref()
417+
.map(|e| self.task_error_to_turbo_tasks_execution_error(e, ctx)),
418+
}))
419+
}
420+
TaskError::TaskChain(chain) => {
421+
let task_id = chain.last().unwrap();
422+
let error = {
423+
let task = ctx.task(*task_id, TaskDataCategory::Meta);
424+
if let Some(OutputValue::Error(error)) = task.get_output() {
425+
Some(error.clone())
426+
} else {
427+
None
428+
}
429+
};
430+
let error = error.map_or_else(
431+
|| {
432+
// Eventual consistency will cause errors to no longer be available
433+
TurboTasksExecutionError::Panic(Arc::new(TurboTasksPanic {
434+
message: TurboTasksExecutionErrorMessage::PIISafe(Cow::Borrowed(
435+
"Error no longer available",
436+
)),
437+
location: None,
438+
}))
439+
},
440+
|e| self.task_error_to_turbo_tasks_execution_error(&e, ctx),
441+
);
442+
let mut current_error = error;
443+
for &task_id in chain.iter().rev() {
444+
current_error =
445+
TurboTasksExecutionError::TaskContext(Arc::new(TurboTaskContextError {
446+
task_id,
447+
source: Some(current_error),
448+
turbo_tasks: ctx.turbo_tasks(),
449+
}));
450+
}
451+
current_error
452+
}
453+
}
454+
}
390455
}
391456

392457
pub(crate) struct OperationGuard<'a, B: BackingStorage> {
@@ -715,11 +780,9 @@ impl<B: BackingStorage> TurboTasksBackendInner<B> {
715780
let result = match output {
716781
OutputValue::Cell(cell) => Ok(Ok(RawVc::TaskCell(cell.task, cell.cell))),
717782
OutputValue::Output(task) => Ok(Ok(RawVc::TaskOutput(*task))),
718-
OutputValue::Error(error) => Err(error
719-
.with_task_context(task.get_task_type(), Some(task_id))
720-
.into()),
783+
OutputValue::Error(error) => Err(error.clone()),
721784
};
722-
if let Some(mut reader_task) = reader_task
785+
if let Some(mut reader_task) = reader_task.take()
723786
&& options.tracking.should_track(result.is_err())
724787
&& (!task.immutable() || cfg!(feature = "verify_immutable"))
725788
{
@@ -759,9 +822,15 @@ impl<B: BackingStorage> TurboTasksBackendInner<B> {
759822
drop(reader_task);
760823

761824
queue.execute(&mut ctx);
825+
} else {
826+
drop(task);
762827
}
763828

764-
return result;
829+
return result.map_err(|error| {
830+
self.task_error_to_turbo_tasks_execution_error(&error, &mut ctx)
831+
.with_task_context(task_id, turbo_tasks.pin())
832+
.into()
833+
});
765834
}
766835
drop(reader_task);
767836

@@ -962,6 +1031,7 @@ impl<B: BackingStorage> TurboTasksBackendInner<B> {
9621031
&self,
9631032
parent_span: Option<tracing::Id>,
9641033
reason: &str,
1034+
turbo_tasks: &dyn TurboTasksBackendApi<TurboTasksBackend<B>>,
9651035
) -> Option<(Instant, bool)> {
9661036
let snapshot_span =
9671037
tracing::trace_span!(parent: parent_span.clone(), "snapshot", reason = reason)
@@ -1087,7 +1157,7 @@ impl<B: BackingStorage> TurboTasksBackendInner<B> {
10871157
if let Some(ref m) = meta {
10881158
task_cache_stats
10891159
.lock()
1090-
.entry(self.debug_get_task_name(task_id))
1160+
.entry(self.get_task_name(task_id, turbo_tasks))
10911161
.or_default()
10921162
.add_counts(m);
10931163
}
@@ -1107,7 +1177,7 @@ impl<B: BackingStorage> TurboTasksBackendInner<B> {
11071177
if inner.flags.meta_modified() {
11081178
task_cache_stats
11091179
.lock()
1110-
.entry(self.debug_get_task_name(task_id))
1180+
.entry(self.get_task_name(task_id, turbo_tasks))
11111181
.or_default()
11121182
.add_counts(&inner);
11131183
}
@@ -1143,7 +1213,7 @@ impl<B: BackingStorage> TurboTasksBackendInner<B> {
11431213
#[cfg(feature = "print_cache_item_size")]
11441214
task_cache_stats
11451215
.lock()
1146-
.entry(self.debug_get_task_name(task_id))
1216+
.entry(self.get_task_name(task_id, turbo_tasks))
11471217
.or_default()
11481218
.add_meta(&meta);
11491219
Some(meta)
@@ -1163,7 +1233,7 @@ impl<B: BackingStorage> TurboTasksBackendInner<B> {
11631233
#[cfg(feature = "print_cache_item_size")]
11641234
task_cache_stats
11651235
.lock()
1166-
.entry(self.debug_get_task_name(task_id))
1236+
.entry(self.get_task_name(task_id, turbo_tasks))
11671237
.or_default()
11681238
.add_data(&data);
11691239
Some(data)
@@ -1317,7 +1387,7 @@ impl<B: BackingStorage> TurboTasksBackendInner<B> {
13171387
let elapsed = start.elapsed();
13181388
// avoid spamming the event queue with information about fast operations
13191389
if elapsed > Duration::from_secs(10) {
1320-
turbo_tasks().send_compilation_event(Arc::new(TimingEvent::new(
1390+
turbo_tasks.send_compilation_event(Arc::new(TimingEvent::new(
13211391
"Finished writing to filesystem cache".to_string(),
13221392
elapsed,
13231393
)));
@@ -1366,7 +1436,7 @@ impl<B: BackingStorage> TurboTasksBackendInner<B> {
13661436
self.verify_aggregation_graph(turbo_tasks, false);
13671437
}
13681438
if self.should_persist() {
1369-
self.snapshot_and_persist(Span::current().into(), "stop");
1439+
self.snapshot_and_persist(Span::current().into(), "stop", turbo_tasks);
13701440
}
13711441
drop_contents(&self.task_cache);
13721442
self.storage.drop_contents();
@@ -1718,9 +1788,13 @@ impl<B: BackingStorage> TurboTasksBackendInner<B> {
17181788
}
17191789
}
17201790

1721-
#[allow(dead_code)]
1722-
fn debug_get_task_name(&self, task_id: TaskId) -> String {
1723-
let task = self.storage.access_mut(task_id);
1791+
fn get_task_name(
1792+
&self,
1793+
task_id: TaskId,
1794+
turbo_tasks: &dyn TurboTasksBackendApi<TurboTasksBackend<B>>,
1795+
) -> String {
1796+
let mut ctx = self.execute_context(turbo_tasks);
1797+
let task = ctx.task(task_id, TaskDataCategory::Data);
17241798
if let Some(value) = task.get_persistent_task_type() {
17251799
value.to_string()
17261800
} else if let Some(value) = task.get_transient_task_type() {
@@ -2180,11 +2254,11 @@ impl<B: BackingStorage> TurboTasksBackendInner<B> {
21802254
}
21812255
Err(err) => {
21822256
if let Some(OutputValue::Error(old_error)) = current_output
2183-
&& old_error == &err
2257+
&& **old_error == err
21842258
{
21852259
None
21862260
} else {
2187-
Some(OutputValue::Error(err))
2261+
Some(OutputValue::Error(Arc::new((&err).into())))
21882262
}
21892263
}
21902264
};
@@ -2715,7 +2789,7 @@ impl<B: BackingStorage> TurboTasksBackendInner<B> {
27152789
}
27162790

27172791
let this = self.clone();
2718-
let snapshot = this.snapshot_and_persist(None, reason);
2792+
let snapshot = this.snapshot_and_persist(None, reason, turbo_tasks);
27192793
if let Some((snapshot_start, new_data)) = snapshot {
27202794
last_snapshot = snapshot_start;
27212795
if !new_data {
@@ -3527,6 +3601,10 @@ impl<B: BackingStorage> Backend for TurboTasksBackend<B> {
35273601
fn is_tracking_dependencies(&self) -> bool {
35283602
self.0.options.dependency_tracking
35293603
}
3604+
3605+
fn get_task_name(&self, task: TaskId, turbo_tasks: &dyn TurboTasksBackendApi<Self>) -> String {
3606+
self.0.get_task_name(task, turbo_tasks)
3607+
}
35303608
}
35313609

35323610
enum DebugTraceTransientTask {

turbopack/crates/turbo-tasks-backend/src/backend/operation/mod.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use std::{
1616
use bincode::{Decode, Encode};
1717
use turbo_tasks::{
1818
CellId, FxIndexMap, TaskExecutionReason, TaskId, TaskPriority, TurboTasksBackendApi,
19-
TypedSharedReference, backend::CachedTaskType,
19+
TurboTasksCallApi, TypedSharedReference, backend::CachedTaskType,
2020
};
2121

2222
use crate::{
@@ -93,6 +93,7 @@ pub trait ExecuteContext<'e>: Sized {
9393
fn suspending_requested(&self) -> bool;
9494
fn should_track_dependencies(&self) -> bool;
9595
fn should_track_activeness(&self) -> bool;
96+
fn turbo_tasks(&self) -> Arc<dyn TurboTasksCallApi>;
9697
}
9798

9899
pub trait ChildExecuteContext<'e>: Send + Sized {
@@ -637,6 +638,10 @@ where
637638
fn should_track_activeness(&self) -> bool {
638639
self.backend.should_track_activeness()
639640
}
641+
642+
fn turbo_tasks(&self) -> Arc<dyn TurboTasksCallApi> {
643+
self.turbo_tasks.pin()
644+
}
640645
}
641646

642647
struct ChildExecuteContextImpl<'e, B: BackingStorage> {

turbopack/crates/turbo-tasks-backend/src/backend/storage_schema.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1010,7 +1010,7 @@ mod tests {
10101010
fn test_schema_size() {
10111011
assert_eq!(
10121012
size_of::<TaskStorage>(),
1013-
144,
1013+
136,
10141014
"TaskStorage size changed! If this is intentional, update this test."
10151015
);
10161016
assert_eq!(

turbopack/crates/turbo-tasks-backend/src/data.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::{
22
fmt::{self, Debug, Display},
33
pin::Pin,
4+
sync::Arc,
45
};
56

67
use anyhow::Result;
@@ -9,10 +10,12 @@ use parking_lot::Mutex;
910
use rustc_hash::FxHashSet;
1011
use turbo_tasks::{
1112
CellId, RawVc, TaskExecutionReason, TaskId, TaskPriority, TraitTypeId,
12-
backend::{TransientTaskRoot, TurboTasksExecutionError},
13+
backend::TransientTaskRoot,
1314
event::{Event, EventDescription, EventListener},
1415
};
1516

17+
use crate::error::TaskError;
18+
1619
// this traits are needed for the transient variants of `CachedDataItem`
1720
// transient variants are never cloned or compared
1821
macro_rules! transient_traits {
@@ -78,8 +81,9 @@ impl CollectiblesRef {
7881
pub enum OutputValue {
7982
Cell(CellRef),
8083
Output(TaskId),
81-
Error(TurboTasksExecutionError),
84+
Error(Arc<TaskError>),
8285
}
86+
8387
impl OutputValue {
8488
/// Returns true if this output value references a transient task.
8589
///

0 commit comments

Comments
 (0)