Skip to content

fix(core): serialize file tool operations and make writes atomic (#29078) - #29499

Merged
DavidAPierce merged 12 commits into
google-gemini:mainfrom
elberthc-byte:fix-29078-concurrency-atomic-writes
Sep 30, 2026
Merged

DavidAPierce merged 12 commits into
google-gemini:mainfrom
elberthc-byte:fix-29078-concurrency-atomic-writes

Conversation

@elberthc-byte

Copy link
Copy Markdown
Contributor

Summary

Fixes concurrent file operations race conditions across tool executions (notably parallel sub-agents) in packages/core. When multiple tool invocations execute simultaneously against the same file path, uncoordinated read-modify-write sequences cause silent lost updates, inaccurate diff calculations, and shadow Git snapshot collisions (.git/index.lock).

This PR:

  1. Introduces an in-process, per-path async mutex (withPathLock) supporting immediate cancellation while queued.
  2. Normalizes lock keys to canonical real paths with parent directory resolution fallback when target files do not exist yet.
  3. Serializes read-modify-write critical sections in EditTool and WriteFileTool by resolved path.
  4. Implements atomic file writes in StandardFileSystemService.writeTextFile using sibling temporary files with permission preservation and Windows EBUSY/EPERM/EACCES rename retries.
  5. Serializes shadow Git repository snapshot creations per resolved project root to prevent .git/index.lock collisions.

Details

  • In-process Path Mutex (withPathLock): Coordinates file tool actions inside the Node.js process using normalized absolute path keys with resolveToRealPath and parent directory resolution fallback. Waiters in the lock queue observe AbortSignal and reject immediately if aborted, while handing off the lock chain safely to downstream waiters.
  • EditTool & WriteFileTool Serialization: Wraps calculateEdit $\to$ writeTextFile and file existence verification in withPathLock. Prevents two parallel operations from reading an identical base version and clobbering each other's edits.
  • Atomic Writes in StandardFileSystemService: Writes content to a unique sibling .tmp file (${realPath}.${randomUUID()}.tmp) and atomically renames (fs.rename) it to the target path. Preserves existing file permissions (0600), handles symlink target resolution, cleans up temp files on error, and retries on Windows transient lock/sharing violation errors (EBUSY, EPERM, EACCES).
  • Git Service Snapshot Serialization: Wraps createFileSnapshot in withPathLock('git-snapshot:' + realProjectRoot) to prevent concurrent staging (git add .) and commit operations from conflicting or locking .git/index.lock.
  • Zero New Linter Suppressions: Adheres strictly to project coding standards with zero new eslint-disable rules.

Related Issues

Closes #29078

How to Validate

1. Automated Vitest Tests

Run the dedicated concurrency and core test suites:

npx vitest run \
  packages/core/src/utils/pathMutex.test.ts \
  packages/core/src/services/fileSystemService.test.ts \
  packages/core/src/services/fileSystemService.atomic.test.ts \
  packages/core/src/services/gitService.test.ts \
  packages/core/src/tools/edit.test.ts \
  packages/core/src/tools/write-file.test.ts

Expected: All 6 test files pass (162 tests passed).

2. Manual Verification

  • Scenario 1 (Non-atomic writes): A concurrent reader in a tight loop reading a file during a 10MB write observed 0 partial or truncated reads. The file transitioned atomically between the old and new states.
  • Scenario 2 (Lost updates): Two parallel operations performing read-modify-write appending to the same file were serialized by withPathLock. Both operations persisted cleanly with zero lost updates.
  • Scenario 3 (Checkpoint race): Five concurrent calls to GitService.createFileSnapshot in the same shadow repository executed concurrently without .git/index.lock collisions or corrupted snapshot history.
  • Sub-Agent Concurrency: Two sub-agents appending unique lines to test.txt preserved both lines on disk with no lost updates.

Pre-Merge Checklist

  • Updated relevant documentation and README (if needed)
  • Added/updated tests (if needed)
  • Noted breaking changes (if any)
  • Validated on required platforms/methods:
    • MacOS
      • npm run
      • npx
      • Docker
      • Podman
      • Seatbelt
    • Windows
      • npm run
      • npx
      • Docker
    • Linux
      • npm run
      • npx
      • Docker

…gle-gemini#29078)

Parallel tool executions (such as concurrent sub-agents) scheduling file
modifications on the same path can interleave read-modify-write operations,
resulting in lost updates, inaccurate diff calculations, and corrupted shadow
Git snapshots.

- Introduce an in-process per-path async mutex (withPathLock) supporting immediate cancellation while queued
- Normalize lock keys to real paths with parent directory resolution fallback
- Serialize read-modify-write critical sections in EditTool and WriteFileTool per resolved path
- Atomically write files via sibling temp files with permission preservation and Windows EBUSY/EPERM/EACCES retries
- Serialize shadow Git repository snapshot creation per resolved project root to prevent .git/index.lock collisions
- Add comprehensive regression and unit tests for concurrent edits, atomic writes, and snapshot serialization
@elberthc-byte
elberthc-byte requested review from a team as code owners September 25, 2026 07:44
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses critical race conditions in file system operations and Git snapshotting within the packages/core module. By introducing an in-process path-based mutex and implementing atomic file write patterns, the changes ensure that concurrent tool executions—such as those triggered by parallel sub-agents—do not result in lost updates or corrupted file states. These improvements enhance the reliability of file system interactions and Git repository management.

Highlights

  • In-process Path Mutex: Introduced an async per-path mutex (withPathLock) to serialize file operations, preventing race conditions during concurrent read-modify-write sequences.
  • Atomic File Writes: Implemented atomic file writes in StandardFileSystemService by writing to temporary sibling files and performing an atomic rename, including retries for transient Windows filesystem errors.
  • Git Snapshot Serialization: Wrapped Git snapshot creation in a path lock to prevent concurrent staging and commit operations from causing .git/index.lock collisions.
  • Tool Serialization: Updated EditTool and WriteFileTool to utilize the new path mutex, ensuring that parallel tool invocations do not clobber each other's changes.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩

@github-actions github-actions Bot added the size/l A large sized PR label Sep 25, 2026
@github-actions

github-actions Bot commented Sep 25, 2026 •

Copy link
Copy Markdown

📊 PR Size: size/L

  • Lines changed: 856
  • Additions: +831
  • Deletions: -25
  • Files changed: 11

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces an in-process, per-path mutex utility (withPathLock) to serialize concurrent file system operations, preventing race conditions and data loss when multiple tools or agents attempt to write to or edit the same file concurrently. It updates StandardFileSystemService to write files atomically using sibling temporary files and rename operations (with retry logic for Windows lock errors), and integrates the path lock mechanism into GitService (for snapshot creation), EditTool, and WriteFileTool. Comprehensive unit and integration tests have been added to verify atomicity, concurrency, and abort handling. I have no additional feedback to provide as the implementation is robust and well-tested.

@gemini-cli gemini-cli Bot added priority/p1 Important and should be addressed in the near term. area/core Issues related to User Interface, OS Support, Core Functionality labels Sep 25, 2026
@elberthc-byte

Copy link
Copy Markdown
Contributor Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces an in-process, per-path mutex (withPathLock) to serialize read-modify-write operations on the same file, preventing concurrent edits or writes from interleaving and causing data loss. This lock is integrated into the file system service, git snapshotting, edit tool, and write-file tool. Feedback on the changes highlights two potential sources of test flakiness in CI environments: one in fileSystemService.atomic.test.ts where a temporary ENOENT during file replacement could cause assertion failures, and another in pathMutex.test.ts where real-time timing assertions (Date.now()) should be replaced with robust event-order checks.

Comment thread packages/core/src/services/fileSystemService.atomic.test.ts Outdated
Comment thread packages/core/src/utils/pathMutex.test.ts Outdated
- Exclude ENOENT (-1) sizes during transient file replacement in StandardFileSystemService atomicity test
- Replace real-time elapsed measurement in pathMutex test with deterministic event sequence assertion
@elberthc-byte

Copy link
Copy Markdown
Contributor Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces an in-process, per-path mutex (withPathLock) to serialize concurrent file operations and prevent race conditions or lost updates. This locking mechanism is integrated into EditToolInvocation, WriteFileToolInvocation, and GitService.createFileSnapshot. Additionally, StandardFileSystemService.writeTextFile has been updated to perform atomic writes by writing to a temporary sibling file and renaming it into place, while preserving file permissions and retrying on transient Windows lock errors. Comprehensive unit and atomic tests have been added to verify these changes. As there are no review comments, I have no additional feedback to provide.

@elberthc-byte

Copy link
Copy Markdown
Contributor Author

/run-test

@elberthc-byte

Copy link
Copy Markdown
Contributor Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces an in-process, per-path mutex utility (withPathLock) to serialize concurrent file system operations (edits, writes, and git snapshots) against the same file path, preventing race conditions and lost updates. It also refactors StandardFileSystemService.writeTextFile to write atomically by writing to a sibling temporary file and renaming it into place, while preserving permissions and retrying on transient Windows lock errors. Comprehensive unit and integration tests have been added to verify these concurrency and atomicity improvements. There are no review comments provided, so I have no feedback to provide on the review itself.

@github-actions

github-actions Bot commented Sep 25, 2026 •

Copy link
Copy Markdown

✅ 70 tests passed successfully on gemini-3-flash-preview.

🧠 Model Steering Guidance

This PR modifies files that affect the model's behavior (prompts, tools, or instructions).

  • ⚠️ Consider adding Evals: No behavioral evaluations (evals/*.eval.ts) were added or updated in this PR. Consider adding a test case to verify the new behavior and prevent regressions.

This is an automated guidance message triggered by steering logic signatures.

Normalize target file paths with path.resolve and dynamic regex escaping in
fileSystemService.test.ts, and skip disk-level POSIX permission preservation
on Windows in fileSystemService.atomic.test.ts.
auto-merge was automatically disabled September 30, 2026 06:50

Head branch was pushed to by a user without write access

@elberthc-byte

Copy link
Copy Markdown
Contributor Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces an in-process, per-path mutex utility (withPathLock) to serialize concurrent file operations and prevent race conditions. It updates StandardFileSystemService to write files atomically using sibling temporary files and rename operations, preserving permissions and handling Windows-specific lock errors. Additionally, it integrates the path lock into GitService snapshot creation, EditToolInvocation, and WriteFileToolInvocation to safely serialize concurrent reads and writes targeting the same files. Comprehensive unit and integration tests have been added to verify these concurrency controls. I have no further feedback to provide as no review comments were submitted.

@DavidAPierce
DavidAPierce added this pull request to the merge queue Sep 30, 2026
Merged via the queue into google-gemini:main with commit c6bccb7 Sep 30, 2026
35 checks passed

This branch was successfully deployed

1 active deployment
eval-gate — f17e3e90 Deployed Sep 30, 2026 by elberthc-byte via Evaluate Steering & Regressions #2120
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/core Issues related to User Interface, OS Support, Core Functionality priority/p1 Important and should be addressed in the near term. size/l A large sized PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug(tools): concurrent file writes suffer lost-update race (no atomic write or per-path locking)

2 participants