Skip to content

Commit cd70065

Browse files
authored
Fixes performance problems due to TaskScopes (#55721)
### What? see vercel/turborepo#5992 ### Turobopack changes * vercel/turborepo#6009 <!-- OJ Kwon - ci(workflow): update test filter --> * vercel/turborepo#6026 <!-- Will Binns-Smith - Remove next-dev references and benchmarks --> * vercel/turborepo#6038 <!-- Tim Neutkens - Remove test-prod action --> * vercel/turborepo#6039 <!-- Tim Neutkens - Fix action dependency --> * ~vercel/turborepo#6036 <!-- Will Binns-Smith - Turbopack: add support for an asset prefix (and basePath in Next.js) --> * vercel/turborepo#5992 <!-- Tobias Koppers - refactor TaskScopes to use an aggregation tree --> Closes WEB-1622
1 parent b689f84 commit cd70065

File tree

15 files changed

+444
-313
lines changed

15 files changed

+444
-313
lines changed

Cargo.lock

Lines changed: 59 additions & 108 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,11 @@ swc_core = { version = "0.83.12", features = [
3939
testing = { version = "0.34.1" }
4040

4141
# Turbo crates
42-
turbopack-binding = { git = "https://github.com/vercel/turbo.git", tag = "turbopack-230922.3" }
42+
turbopack-binding = { git = "https://github.com/vercel/turbo.git", tag = "turbopack-230928.3" }
4343
# [TODO]: need to refactor embed_directory! macro usages, as well as resolving turbo_tasks::function, macros..
44-
turbo-tasks = { git = "https://github.com/vercel/turbo.git", tag = "turbopack-230922.3" }
44+
turbo-tasks = { git = "https://github.com/vercel/turbo.git", tag = "turbopack-230928.3" }
4545
# [TODO]: need to refactor embed_directory! macro usage in next-core
46-
turbo-tasks-fs = { git = "https://github.com/vercel/turbo.git", tag = "turbopack-230922.3" }
46+
turbo-tasks-fs = { git = "https://github.com/vercel/turbo.git", tag = "turbopack-230928.3" }
4747

4848
# General Deps
4949

@@ -121,7 +121,7 @@ shadow-rs = { version = "0.23.0", default-features = false, features = [
121121
] }
122122
syn = "1.0.107"
123123
tempfile = "3.3.0"
124-
thiserror = "1.0.38"
124+
thiserror = "1.0.48"
125125
tiny-gradient = "0.1.0"
126126
tokio = "1.25.0"
127127
tokio-util = { version = "0.7.7", features = ["io"] }

packages/next-swc/crates/napi/src/next_api/endpoint.rs

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,9 @@ pub async fn endpoint_write_to_disk(
7777
let (written, issues, diags) = turbo_tasks
7878
.run_once(async move {
7979
let write_to_disk = endpoint.write_to_disk();
80+
let written = write_to_disk.strongly_consistent().await?;
8081
let issues = get_issues(write_to_disk).await?;
8182
let diags = get_diagnostics(write_to_disk).await?;
82-
let written = write_to_disk.strongly_consistent().await?;
8383
Ok((written, issues, diags))
8484
})
8585
.await
@@ -104,17 +104,16 @@ pub fn endpoint_server_changed_subscribe(
104104
func,
105105
move || async move {
106106
let changed = endpoint.server_changed();
107-
let issues = get_issues(changed).await?;
108-
let diags = get_diagnostics(changed).await?;
107+
// We don't capture issues and diagonistics here since we don't want to be
108+
// notified when they change
109109
changed.strongly_consistent().await?;
110-
Ok((issues, diags))
110+
Ok(())
111111
},
112-
|ctx| {
113-
let (issues, diags) = ctx.value;
112+
|_| {
114113
Ok(vec![TurbopackResult {
115114
result: (),
116-
issues: issues.iter().map(|i| NapiIssue::from(&**i)).collect(),
117-
diagnostics: diags.iter().map(|d| NapiDiagnostic::from(d)).collect(),
115+
issues: vec![],
116+
diagnostics: vec![],
118117
}])
119118
},
120119
)
@@ -132,17 +131,16 @@ pub fn endpoint_client_changed_subscribe(
132131
func,
133132
move || async move {
134133
let changed = endpoint.client_changed();
135-
let issues = get_issues(changed).await?;
136-
let diags = get_diagnostics(changed).await?;
134+
// We don't capture issues and diagonistics here since we don't want to be
135+
// notified when they change
137136
changed.strongly_consistent().await?;
138-
Ok((issues, diags))
137+
Ok(())
139138
},
140-
|ctx| {
141-
let (issues, diags) = ctx.value;
139+
|_| {
142140
Ok(vec![TurbopackResult {
143141
result: (),
144-
issues: issues.iter().map(|i| NapiIssue::from(&**i)).collect(),
145-
diagnostics: diags.iter().map(|d| NapiDiagnostic::from(d)).collect(),
142+
issues: vec![],
143+
diagnostics: vec![],
146144
}])
147145
},
148146
)

packages/next-swc/crates/napi/src/next_api/project.rs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -313,11 +313,11 @@ pub fn project_entrypoints_subscribe(
313313
turbo_tasks.clone(),
314314
func,
315315
move || async move {
316-
let entrypoints = container.entrypoints();
317-
let issues = get_issues(entrypoints).await?;
318-
let diags = get_diagnostics(entrypoints).await?;
316+
let entrypoints_operation = container.entrypoints();
317+
let entrypoints = entrypoints_operation.strongly_consistent().await?;
319318

320-
let entrypoints = entrypoints.strongly_consistent().await?;
319+
let issues = get_issues(entrypoints_operation).await?;
320+
let diags = get_diagnostics(entrypoints_operation).await?;
321321

322322
Ok((entrypoints, issues, diags))
323323
},
@@ -383,10 +383,10 @@ pub fn project_hmr_events(
383383
let state = project
384384
.project()
385385
.hmr_version_state(identifier.clone(), session);
386-
let update = project.project().hmr_update(identifier, state);
387-
let issues = get_issues(update).await?;
388-
let diags = get_diagnostics(update).await?;
389-
let update = update.strongly_consistent().await?;
386+
let update_operation = project.project().hmr_update(identifier, state);
387+
let update = update_operation.strongly_consistent().await?;
388+
let issues = get_issues(update_operation).await?;
389+
let diags = get_diagnostics(update_operation).await?;
390390
match &*update {
391391
Update::None => {}
392392
Update::Total(TotalUpdate { to }) => {
@@ -451,11 +451,11 @@ pub fn project_hmr_identifiers_subscribe(
451451
turbo_tasks.clone(),
452452
func,
453453
move || async move {
454-
let hmr_identifiers = container.hmr_identifiers();
455-
let issues = get_issues(hmr_identifiers).await?;
456-
let diags = get_diagnostics(hmr_identifiers).await?;
454+
let hmr_identifiers_operation = container.hmr_identifiers();
455+
let hmr_identifiers = hmr_identifiers_operation.strongly_consistent().await?;
457456

458-
let hmr_identifiers = hmr_identifiers.strongly_consistent().await?;
457+
let issues = get_issues(hmr_identifiers_operation).await?;
458+
let diags = get_diagnostics(hmr_identifiers_operation).await?;
459459

460460
Ok((hmr_identifiers, issues, diags))
461461
},

packages/next-swc/crates/napi/src/next_api/utils.rs

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -69,30 +69,23 @@ impl Drop for RootTask {
6969

7070
#[napi]
7171
pub fn root_task_dispose(
72-
#[napi(ts_arg_type = "{ __napiType: \"RootTask\" }")] _root_task: External<RootTask>,
72+
#[napi(ts_arg_type = "{ __napiType: \"RootTask\" }")] mut root_task: External<RootTask>,
7373
) -> napi::Result<()> {
74-
// TODO(alexkirsz) Implement. Not panicking here to avoid crashing the process
75-
// when testing.
74+
if let Some(task) = root_task.task_id.take() {
75+
root_task.turbo_tasks.dispose_root_task(task);
76+
}
7677
Ok(())
7778
}
7879

7980
pub async fn get_issues<T: Send>(source: Vc<T>) -> Result<Vec<ReadRef<PlainIssue>>> {
80-
let issues = source
81-
.peek_issues_with_path()
82-
.await?
83-
.strongly_consistent()
84-
.await?;
81+
let issues = source.peek_issues_with_path().await?;
8582
issues.get_plain_issues().await
8683
}
8784

8885
/// Collect [turbopack::core::diagnostics::Diagnostic] from given source,
8986
/// returns [turbopack::core::diagnostics::PlainDiagnostic]
9087
pub async fn get_diagnostics<T: Send>(source: Vc<T>) -> Result<Vec<ReadRef<PlainDiagnostic>>> {
91-
let captured_diags = source
92-
.peek_diagnostics()
93-
.await?
94-
.strongly_consistent()
95-
.await?;
88+
let captured_diags = source.peek_diagnostics().await?;
9689

9790
captured_diags
9891
.diagnostics

packages/next-swc/crates/next-api/src/project.rs

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ use turbo_tasks::{
2222
debug::ValueDebugFormat,
2323
graph::{AdjacencyMap, GraphTraversal},
2424
trace::TraceRawVcs,
25-
Completion, Completions, IntoTraitRef, State, TaskInput, TransientInstance, TryFlatJoinIterExt,
26-
Value, ValueToString, Vc,
25+
Completion, Completions, IntoTraitRef, State, TaskInput, TraitRef, TransientInstance,
26+
TryFlatJoinIterExt, Value, ValueToString, Vc,
2727
};
2828
use turbopack_binding::{
2929
turbo::{
@@ -562,14 +562,33 @@ impl Project {
562562
}
563563
}
564564

565+
let pages_document_endpoint = TraitRef::cell(
566+
self.pages_project()
567+
.document_endpoint()
568+
.into_trait_ref()
569+
.await?,
570+
);
571+
let pages_app_endpoint =
572+
TraitRef::cell(self.pages_project().app_endpoint().into_trait_ref().await?);
573+
let pages_error_endpoint = TraitRef::cell(
574+
self.pages_project()
575+
.error_endpoint()
576+
.into_trait_ref()
577+
.await?,
578+
);
579+
565580
let middleware = find_context_file(
566581
self.project_path(),
567582
middleware_files(self.next_config().page_extensions()),
568583
);
569584
let middleware = if let FindContextFileResult::Found(fs_path, _) = *middleware.await? {
570585
let source = Vc::upcast(FileSource::new(fs_path));
571586
Some(Middleware {
572-
endpoint: Vc::upcast(self.middleware_endpoint(source)),
587+
endpoint: TraitRef::cell(
588+
Vc::upcast::<Box<dyn Endpoint>>(self.middleware_endpoint(source))
589+
.into_trait_ref()
590+
.await?,
591+
),
573592
})
574593
} else {
575594
None
@@ -578,9 +597,9 @@ impl Project {
578597
Ok(Entrypoints {
579598
routes,
580599
middleware,
581-
pages_document_endpoint: self.pages_project().document_endpoint(),
582-
pages_app_endpoint: self.pages_project().app_endpoint(),
583-
pages_error_endpoint: self.pages_project().error_endpoint(),
600+
pages_document_endpoint,
601+
pages_app_endpoint,
602+
pages_error_endpoint,
584603
}
585604
.cell())
586605
}

packages/next-swc/crates/next-api/src/versioned_content_map.rs

Lines changed: 45 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@ use std::collections::HashMap;
22

33
use anyhow::{bail, Result};
44
use next_core::emit_client_assets;
5-
use turbo_tasks::{State, TryFlatJoinIterExt, TryJoinIterExt, ValueDefault, ValueToString, Vc};
5+
use serde::{Deserialize, Serialize};
6+
use turbo_tasks::{
7+
debug::ValueDebugFormat, trace::TraceRawVcs, State, TryFlatJoinIterExt, TryJoinIterExt,
8+
ValueDefault, ValueToString, Vc,
9+
};
610
use turbopack_binding::{
711
turbo::tasks_fs::FileSystemPath,
812
turbopack::core::{
@@ -18,8 +22,17 @@ use turbopack_binding::{
1822
#[turbo_tasks::value(transparent)]
1923
pub struct OutputAssetsOperation(Vc<OutputAssets>);
2024

21-
type VersionedContentMapInner =
22-
HashMap<Vc<FileSystemPath>, (Vc<Box<dyn VersionedContent>>, Vc<OutputAssets>)>;
25+
#[derive(
26+
Clone, Copy, TraceRawVcs, PartialEq, Eq, ValueDebugFormat, Serialize, Deserialize, Debug,
27+
)]
28+
struct MapEntry {
29+
assets_operation: Vc<OutputAssets>,
30+
}
31+
32+
#[turbo_tasks::value(transparent)]
33+
struct OptionMapEntry(Option<MapEntry>);
34+
35+
type VersionedContentMapInner = HashMap<Vc<FileSystemPath>, MapEntry>;
2336

2437
#[turbo_tasks::value]
2538
pub struct VersionedContentMap {
@@ -55,12 +68,9 @@ impl VersionedContentMap {
5568
let entries: Vec<_> = assets
5669
.iter()
5770
.map(|&asset| async move {
58-
// NOTE(alexkirsz) `.versioned_content()` should not be resolved, to ensure that
59-
// it always points to the task that computes the versioned
60-
// content.
6171
Ok((
6272
asset.ident().path().resolve().await?,
63-
(asset.versioned_content(), assets_operation),
73+
MapEntry { assets_operation },
6474
))
6575
})
6676
.try_join()
@@ -73,14 +83,17 @@ impl VersionedContentMap {
7383
}
7484

7585
#[turbo_tasks::function]
76-
pub async fn get(&self, path: Vc<FileSystemPath>) -> Result<Vc<Box<dyn VersionedContent>>> {
86+
pub async fn get(
87+
self: Vc<Self>,
88+
path: Vc<FileSystemPath>,
89+
) -> Result<Vc<Box<dyn VersionedContent>>> {
7790
let (content, _) = self.get_internal(path).await?;
7891
Ok(content)
7992
}
8093

8194
#[turbo_tasks::function]
8295
pub async fn get_and_write(
83-
&self,
96+
self: Vc<Self>,
8497
path: Vc<FileSystemPath>,
8598
client_relative_path: Vc<FileSystemPath>,
8699
client_output_path: Vc<FileSystemPath>,
@@ -105,28 +118,34 @@ impl VersionedContentMap {
105118
.await?;
106119
Ok(Vc::cell(keys))
107120
}
121+
122+
#[turbo_tasks::function]
123+
async fn raw_get(&self, path: Vc<FileSystemPath>) -> Result<Vc<OptionMapEntry>> {
124+
let result = {
125+
let map = self.map.get();
126+
map.get(&path).copied()
127+
};
128+
Ok(Vc::cell(result))
129+
}
108130
}
109131

110132
impl VersionedContentMap {
111133
async fn get_internal(
112-
&self,
134+
self: Vc<Self>,
113135
path: Vc<FileSystemPath>,
114136
) -> Result<(Vc<Box<dyn VersionedContent>>, Vc<OutputAssets>)> {
115-
let result = {
116-
// NOTE(alexkirsz) This is to avoid Rust marking this method as !Send because a
117-
// StateRef to the map is captured across an await boundary below, even though
118-
// it does not look like it would.
119-
// I think this is a similar issue as https://fasterthanli.me/articles/a-rust-match-made-in-hell
120-
let map = self.map.get();
121-
map.get(&path).copied()
122-
};
123-
let Some((content, assets_operation)) = result else {
124-
let path = path.to_string().await?;
125-
bail!("could not find versioned content for path {}", path);
126-
};
127-
// NOTE(alexkirsz) This is necessary to mark the task as active again.
128-
Vc::connect(assets_operation);
129-
Vc::connect(content);
130-
Ok((content, assets_operation))
137+
let result = self.raw_get(path).await?;
138+
if let Some(MapEntry { assets_operation }) = *result {
139+
// NOTE(alexkirsz) This is necessary to mark the task as active again.
140+
Vc::connect(assets_operation);
141+
for asset in assets_operation.await?.iter() {
142+
if asset.ident().path().resolve().await? == path {
143+
let content = asset.versioned_content();
144+
return Ok((content, assets_operation));
145+
}
146+
}
147+
}
148+
let path = path.to_string().await?;
149+
bail!("could not find versioned content for path {}", path);
131150
}
132151
}

packages/next/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@
193193
"@types/ws": "8.2.0",
194194
"@vercel/ncc": "0.34.0",
195195
"@vercel/nft": "0.22.6",
196-
"@vercel/turbopack-ecmascript-runtime": "https://gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-ecmascript-runtime/js?turbopack-230922.2",
196+
"@vercel/turbopack-ecmascript-runtime": "https://gitpkg-fork.vercel.sh/vercel/turbo/crates/turbopack-ecmascript-runtime/js?turbopack-230928.3",
197197
"acorn": "8.5.0",
198198
"ajv": "8.11.0",
199199
"amphtml-validator": "1.0.35",

packages/next/src/client/dev/error-overlay/hot-dev-client.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,8 @@ function handleSuccess() {
122122
if (isHotUpdate) {
123123
tryApplyUpdates(onBeforeFastRefresh, onFastRefresh)
124124
}
125+
} else {
126+
onBuildOk()
125127
}
126128
}
127129

packages/next/src/client/dev/hot-middleware-client.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import connect from './error-overlay/hot-dev-client'
22
import { sendMessage } from './error-overlay/websocket'
33

4+
let reloading = false
5+
46
export default (mode: 'webpack' | 'turbopack') => {
57
const devClient = connect(mode)
68

79
devClient.subscribeToHmrEvent((obj: any) => {
10+
if (reloading) return
811
// if we're on an error/404 page, we can't reliably tell if the newly added/removed page
912
// matches the current path. In that case, assume any added/removed entries should trigger a reload of the current page
1013
const isOnErrorPage =
@@ -19,6 +22,7 @@ export default (mode: 'webpack' | 'turbopack') => {
1922
clientId: window.__nextDevClientId,
2023
})
2124
)
25+
reloading = true
2226
return window.location.reload()
2327
}
2428
case 'removedPage': {

0 commit comments

Comments
 (0)