Skip to content

Conversation

@roomote
Copy link
Contributor

@roomote roomote bot commented Aug 18, 2025

Summary

This PR fixes the race condition in getStorageBasePath() that was causing intermittent errors when multiple instances tried to write to the same .write_test file simultaneously.

Problem

When multiple instances of the extension run concurrently, they all attempt to create and delete the same .write_test file to verify write permissions on the custom storage path. This causes a race condition where one instance might delete the file while another is trying to write to it, resulting in the error:

Custom storage path "/Users/jimweller/.roo/" is unusable, will use default path

Solution

The fix adds a unique suffix to the .write_test file name using:

  • Process ID (process.pid)
  • Current timestamp (Date.now())
  • Random string (7 characters)

This ensures each concurrent call uses a unique test file, preventing conflicts.

Changes

  1. Modified src/utils/storage.ts:

    • Changed test file from .write_test to .write_test_${uniqueSuffix}
    • Added comprehensive comments explaining the race condition fix
  2. Added src/utils/__tests__/storage.spec.ts:

    • Created comprehensive test suite for getStorageBasePath()
    • Added specific test for concurrent calls to verify the race condition is fixed
    • Tests verify that each concurrent call uses a unique test file

Testing

  • ✅ All new tests pass
  • ✅ Verified concurrent calls use unique test files
  • ✅ Linting and type checks pass

Fixes #7173


Important

Fixes race condition in getStorageBasePath() by using unique test file names, with tests added in storage.spec.ts.

  • Behavior:
    • Fixes race condition in getStorageBasePath() in storage.ts by appending a unique suffix to .write_test file names.
    • Unique suffix includes process ID, timestamp, and random string.
  • Testing:
    • Adds storage.spec.ts with tests for getStorageBasePath().
    • Tests cover default path usage, custom path writability, and concurrent calls.
    • Verifies unique test file names in concurrent scenarios.
  • Misc:
    • Adds comments in storage.ts explaining the race condition fix.

This description was created by Ellipsis for 0eb5b3b. You can customize this summary. It will automatically update as commits are pushed.

…t file names

- Add unique suffix to .write_test file using process ID, timestamp, and random string
- Prevents concurrent calls from conflicting on the same test file
- Add comprehensive test coverage for storage.ts including concurrent call testing

Fixes #7173
@roomote roomote bot requested review from cte, jr and mrubens as code owners August 18, 2025 07:58
@dosubot dosubot bot added size:L This PR changes 100-499 lines, ignoring generated files. bug Something isn't working labels Aug 18, 2025
Copy link
Contributor Author

@roomote roomote bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewing my own code is like debugging in a mirror - everything looks backwards but the bugs are still mine.

const uniqueSuffix = `${process.pid}_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`
const testFile = path.join(customStoragePath, `.write_test_${uniqueSuffix}`)
await fs.writeFile(testFile, "test")
await fs.rm(testFile)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we consider wrapping the cleanup in a try-finally block? If fs.rm(testFile) fails, the test file will remain on disk. While the unique naming prevents conflicts, orphaned test files could accumulate over time:


// Test if path is writable
const testFile = path.join(customStoragePath, ".write_test")
// Use unique suffix to prevent race conditions when multiple instances run simultaneously
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment could be more specific about what "race conditions" means here. Perhaps: "Use unique suffix to prevent file conflicts when multiple VSCode instances or concurrent operations access the same custom storage path simultaneously"?

expect(mockVscode.window.showErrorMessage).toHaveBeenCalled()

consoleSpy.mockRestore()
})
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it worth adding a test case for when fs.rm() fails after successful write? This would verify the function still returns the custom path despite cleanup failure, which seems to be the intended behavior.

consoleSpy.mockRestore()
})

it("should handle concurrent calls without race conditions", async () => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense to group the concurrent/race condition tests into a nested describe block like describe("concurrent access handling") for better test organization?

@hannesrudolph hannesrudolph added the Issue/PR - Triage New issue. Needs quick review to confirm validity and assign labels. label Aug 18, 2025
@jimweller
Copy link

I actually like PR # 7164 better. It's more elegant and less intrusive.

#3788
#7173

@daniel-lxs
Copy link
Member

Closing in favor of #7164

@daniel-lxs daniel-lxs closed this Aug 19, 2025
@github-project-automation github-project-automation bot moved this from New to Done in Roo Code Roadmap Aug 19, 2025
@github-project-automation github-project-automation bot moved this from Triage to Done in Roo Code Roadmap Aug 19, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working Issue/PR - Triage New issue. Needs quick review to confirm validity and assign labels. size:L This PR changes 100-499 lines, ignoring generated files.

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

getStorageBasePath(), race condition

5 participants