Skip to content

Commit 4c0728d

Browse files
authored
[Turbopack] use file.read() instead of file.track() in webpack loaders (#69659)
### What? `track()` invalidates based on watcher events and might have false invalidation when events occur but files do not actually change. This also happens when restoring from persistent caching where all files are considered as changed. `read()` invalidates only when the file content actually changed. Since webpack loaders are usually pretty expensive we want to use `read()` instead of `track()` here.
1 parent 7fc8076 commit 4c0728d

File tree

2 files changed

+103
-43
lines changed

2 files changed

+103
-43
lines changed

turbopack/crates/turbo-tasks-fs/src/lib.rs

+102-42
Original file line numberDiff line numberDiff line change
@@ -311,45 +311,14 @@ impl DiskFileSystem {
311311

312312
Ok(Self::cell(instance))
313313
}
314-
}
315-
316-
impl Debug for DiskFileSystem {
317-
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
318-
write!(f, "name: {}, root: {}", self.name, self.root)
319-
}
320-
}
321314

322-
#[turbo_tasks::value_impl]
323-
impl FileSystem for DiskFileSystem {
324315
#[turbo_tasks::function(fs)]
325-
async fn read(&self, fs_path: Vc<FileSystemPath>) -> Result<Vc<FileContent>> {
326-
let full_path = self.to_sys_path(fs_path).await?;
327-
self.register_invalidator(&full_path)?;
328-
329-
let _lock = self.lock_path(&full_path).await;
330-
let content = match retry_future(|| File::from_path(full_path.clone()))
331-
.instrument(tracing::info_span!(
332-
"read file",
333-
path = display(full_path.display())
334-
))
335-
.await
336-
{
337-
Ok(file) => FileContent::new(file),
338-
Err(e) if e.kind() == ErrorKind::NotFound || e.kind() == ErrorKind::InvalidFilename => {
339-
FileContent::NotFound
340-
}
341-
Err(e) => {
342-
bail!(anyhow!(e).context(format!("reading file {}", full_path.display())))
343-
}
344-
};
345-
Ok(content.cell())
346-
}
347-
348-
#[turbo_tasks::function(fs)]
349-
async fn read_dir(&self, fs_path: Vc<FileSystemPath>) -> Result<Vc<DirectoryContent>> {
316+
async fn read_dir_internal(
317+
&self,
318+
fs_path: Vc<FileSystemPath>,
319+
) -> Result<Vc<InternalDirectoryContent>> {
350320
let full_path = self.to_sys_path(fs_path).await?;
351321
self.register_dir_invalidator(&full_path)?;
352-
let fs_path = fs_path.await?;
353322

354323
// we use the sync std function here as it's a lot faster (600%) in
355324
// node-file-trace
@@ -366,7 +335,7 @@ impl FileSystem for DiskFileSystem {
366335
|| e.kind() == ErrorKind::NotADirectory
367336
|| e.kind() == ErrorKind::InvalidFilename =>
368337
{
369-
return Ok(DirectoryContent::not_found());
338+
return Ok(InternalDirectoryContent::not_found());
370339
}
371340
Err(e) => {
372341
bail!(anyhow!(e).context(format!("reading dir {}", full_path.display())))
@@ -386,13 +355,13 @@ impl FileSystem for DiskFileSystem {
386355
let file_name: RcStr = path.file_name()?.to_str()?.into();
387356
let path_to_root = sys_to_unix(path.strip_prefix(&self.root).ok()?.to_str()?);
388357

389-
let fs_path = FileSystemPath::new_normalized(fs_path.fs, path_to_root.into());
358+
let path = path_to_root.into();
390359

391360
let entry = match e.file_type() {
392-
Ok(t) if t.is_file() => DirectoryEntry::File(fs_path),
393-
Ok(t) if t.is_dir() => DirectoryEntry::Directory(fs_path),
394-
Ok(t) if t.is_symlink() => DirectoryEntry::Symlink(fs_path),
395-
Ok(_) => DirectoryEntry::Other(fs_path),
361+
Ok(t) if t.is_file() => InternalDirectoryEntry::File(path),
362+
Ok(t) if t.is_dir() => InternalDirectoryEntry::Directory(path),
363+
Ok(t) if t.is_symlink() => InternalDirectoryEntry::Symlink(path),
364+
Ok(_) => InternalDirectoryEntry::Other(path),
396365
Err(err) => return Some(Err(err.into())),
397366
};
398367

@@ -401,7 +370,72 @@ impl FileSystem for DiskFileSystem {
401370
.collect::<Result<_>>()
402371
.with_context(|| format!("reading directory item in {}", full_path.display()))?;
403372

404-
Ok(DirectoryContent::new(entries))
373+
Ok(InternalDirectoryContent::new(entries))
374+
}
375+
}
376+
377+
impl Debug for DiskFileSystem {
378+
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
379+
write!(f, "name: {}, root: {}", self.name, self.root)
380+
}
381+
}
382+
383+
#[turbo_tasks::value_impl]
384+
impl FileSystem for DiskFileSystem {
385+
#[turbo_tasks::function(fs)]
386+
async fn read(&self, fs_path: Vc<FileSystemPath>) -> Result<Vc<FileContent>> {
387+
let full_path = self.to_sys_path(fs_path).await?;
388+
self.register_invalidator(&full_path)?;
389+
390+
let _lock = self.lock_path(&full_path).await;
391+
let content = match retry_future(|| File::from_path(full_path.clone()))
392+
.instrument(tracing::info_span!(
393+
"read file",
394+
path = display(full_path.display())
395+
))
396+
.await
397+
{
398+
Ok(file) => FileContent::new(file),
399+
Err(e) if e.kind() == ErrorKind::NotFound || e.kind() == ErrorKind::InvalidFilename => {
400+
FileContent::NotFound
401+
}
402+
Err(e) => {
403+
bail!(anyhow!(e).context(format!("reading file {}", full_path.display())))
404+
}
405+
};
406+
Ok(content.cell())
407+
}
408+
409+
#[turbo_tasks::function]
410+
async fn read_dir(self: Vc<Self>, fs_path: Vc<FileSystemPath>) -> Result<Vc<DirectoryContent>> {
411+
match &*self.read_dir_internal(fs_path).await? {
412+
InternalDirectoryContent::NotFound => Ok(DirectoryContent::not_found()),
413+
InternalDirectoryContent::Entries(entries) => {
414+
let fs = fs_path.await?.fs;
415+
let entries = entries
416+
.iter()
417+
.map(|(name, entry)| {
418+
let entry = match entry {
419+
InternalDirectoryEntry::File(path) => DirectoryEntry::File(
420+
FileSystemPath::new_normalized(fs, path.clone()),
421+
),
422+
InternalDirectoryEntry::Directory(path) => DirectoryEntry::Directory(
423+
FileSystemPath::new_normalized(fs, path.clone()),
424+
),
425+
InternalDirectoryEntry::Symlink(path) => DirectoryEntry::Symlink(
426+
FileSystemPath::new_normalized(fs, path.clone()),
427+
),
428+
InternalDirectoryEntry::Other(path) => DirectoryEntry::Other(
429+
FileSystemPath::new_normalized(fs, path.clone()),
430+
),
431+
InternalDirectoryEntry::Error => DirectoryEntry::Error,
432+
};
433+
(name.clone(), entry)
434+
})
435+
.collect();
436+
Ok(DirectoryContent::new(entries))
437+
}
438+
}
405439
}
406440

407441
#[turbo_tasks::function(fs)]
@@ -1849,6 +1883,15 @@ pub enum FileLinesContent {
18491883
NotFound,
18501884
}
18511885

1886+
#[derive(Hash, Clone, Debug, PartialEq, Eq, TraceRawVcs, Serialize, Deserialize)]
1887+
pub enum InternalDirectoryEntry {
1888+
File(RcStr),
1889+
Directory(RcStr),
1890+
Symlink(RcStr),
1891+
Other(RcStr),
1892+
Error,
1893+
}
1894+
18521895
#[derive(Hash, Clone, Copy, Debug, PartialEq, Eq, TraceRawVcs, Serialize, Deserialize)]
18531896
pub enum DirectoryEntry {
18541897
File(Vc<FileSystemPath>),
@@ -1916,6 +1959,23 @@ impl From<&DirectoryEntry> for FileSystemEntryType {
19161959
}
19171960
}
19181961

1962+
#[turbo_tasks::value]
1963+
#[derive(Debug)]
1964+
pub enum InternalDirectoryContent {
1965+
Entries(Vec<(RcStr, InternalDirectoryEntry)>),
1966+
NotFound,
1967+
}
1968+
1969+
impl InternalDirectoryContent {
1970+
pub fn new(entries: Vec<(RcStr, InternalDirectoryEntry)>) -> Vc<Self> {
1971+
Self::cell(InternalDirectoryContent::Entries(entries))
1972+
}
1973+
1974+
pub fn not_found() -> Vc<Self> {
1975+
Self::cell(InternalDirectoryContent::NotFound)
1976+
}
1977+
}
1978+
19191979
#[turbo_tasks::value]
19201980
#[derive(Debug)]
19211981
pub enum DirectoryContent {

turbopack/crates/turbopack-node/src/transforms/webpack.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -716,7 +716,7 @@ async fn dir_dependency_shallow(glob: Vc<ReadGlobResult>) -> Result<Vc<Completio
716716
// Reading all files to add itself as dependency
717717
match *item {
718718
DirectoryEntry::File(file) => {
719-
file.track().await?;
719+
file.read().await?;
720720
}
721721
DirectoryEntry::Directory(dir) => {
722722
dir_dependency(dir.read_glob(Glob::new("**".into()), false)).await?;

0 commit comments

Comments
 (0)