Skip to content

Performance: make delete system faster with concurrent deletions #4

@codingstark-dev

Description

@codingstark-dev

Summary

The current deleteModules() function in src/scanner.ts:415-451 processes deletions sequentially — it spawns a new rm -rf process for each directory one at a time. For users with hundreds of node_modules directories, this is a bottleneck.

Current Implementation

// src/scanner.ts:424-442
for (const mod of modules) {
  try {
    const proc = Bun.spawn({
      cmd: ["rm", "-rf", mod.path],
      stdout: "ignore",
      stderr: "ignore",
    });
    const ok = await proc.exited === 0;
    // ...
  }
}

Each deletion waits for the previous one to finish before starting the next. With 265 node_modules directories, the overhead of spawning 265 separate processes sequentially adds up.

Proposed Improvements

1. Parallel deletions with concurrency limit

Use a semaphore (like the existing Semaphore class in the same file) to run multiple deletions in parallel:

const semaphore = new Semaphore(8); // configurable concurrency
await Promise.allSettled(modules.map(async (mod) => {
  await semaphore.acquire();
  try {
    // delete mod.path
  } finally {
    semaphore.release();
  }
}));

2. Use native fs.rm instead of spawning rm -rf

Spawning a shell process per directory has significant overhead. Using fs.promises.rm() (or Bun native) avoids process creation:

import { rm } from "node:fs/promises";
await rm(mod.path, { recursive: true, force: true });

This is cross-platform (fixes Windows too) and avoids process spawn overhead.

3. Batch deletions

For very large deletion sets, consider batching progress updates to reduce UI re-renders.

Expected Impact

  • Sequential rm -rf: ~265 process spawns, one at a time
  • Concurrent fs.rm: No process overhead, parallel I/O, dramatically faster
  • Rough estimate: 2-10x speedup depending on filesystem and number of directories

Acceptance Criteria

  • Deletions run concurrently with a configurable concurrency limit
  • Replace rm -rf spawn with fs.promises.rm() (cross-platform bonus)
  • Progress feedback during deletion (shows current/deleted count)
  • Error handling per-directory (one failure does not stop others)
  • Benchmark: deletion time for 100+ directories is measurably faster
  • All existing functionality preserved (dry-run, interactive delete, delete-all)

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions