Skip to content

fix(api): publish HTTP cache entries without in-place writes - #263

Merged
williammartin merged 2 commits into
cli:trunkfrom
baiyuxi930826:fix/atomic-api-cache-store
Sep 11, 2026
Merged

williammartin merged 2 commits into
cli:trunkfrom
baiyuxi930826:fix/atomic-api-cache-store

Conversation

@baiyuxi930826

@baiyuxi930826 baiyuxi930826 commented Jul 19, 2026 •

Copy link
Copy Markdown

Fixes #252.

Description

go-gh caches API responses on disk so later requests can reuse them. Previously, saving a response truncated the existing cache file before writing its replacement. An in-process mutex cannot coordinate separate gh processes, so another process could read an incomplete response or overwrite the same file concurrently. A failed or interrupted write could also destroy a usable entry.

Write the response to a temporary file in the same directory, close it successfully, and then rename it into place. Failed response writes leave the existing entry untouched. On Windows, briefly retry file-sharing conflicts before giving up on caching; the caller still receives the API response.

How did you test this change?

Not tested manually. I exercised this library change only through automated tests; I did not build and run gh against this branch.

Key points

  • Same-directory rename provides atomic replacement on Unix. Go does not guarantee atomic rename on Windows or other non-Unix platforms; retries improve handling of Windows contention, not that guarantee.
  • Windows sharing violations and potentially transient access-denied errors use exponential backoff within a 100 ms retry budget. Other errors return immediately. There is no fallback to truncating or copying over the destination.
  • Keep the existing in-process mutex without adding cross-process locks or custom Windows file-opening semantics. Caching remains best-effort.
  • Deferred close and removal clean up temporary files on errors and body panics. Forced process termination can still leave an unpublished temporary file behind.
  • No file or directory fsync is added: this cache does not promise durability across power loss or operating-system crashes.

Notes for reviewers

Start with fileStorage.store in pkg/api/cache.go, then the rename helper and its Windows error classification in pkg/api/cache_rename*.go. The publication and rename tests describe the failure, concurrency, and retry scenarios.

Issue #252 supplies the cache-corruption context motivating the change and we also saw it in cli/cli#14394

Authorship and follow-up

Who wrote this:

  • A human wrote it.
  • An agent wrote it under close human direction.
  • An agent wrote it independently, and no human has guided the implementation beyond the initial prompt.

The original patch is by @baiyuxi930826. Copilot wrote the follow-up implementation and this description under human direction.

Who answers review comments:

  • @williammartin will read and reply directly.
  • An agent will draft replies and @username will read them before they are posted.
  • Nobody has explicitly committed to replying.

@baiyuxi930826
baiyuxi930826 requested a review from a team as a code owner July 19, 2026 04:30
@baiyuxi930826
baiyuxi930826 requested review from BagToad and removed request for a team July 19, 2026 04:30
@williammartin williammartin changed the title fix(api): make HTTP response cache writes atomic fix(api): publish HTTP cache entries without in-place writes Sep 9, 2026
@williammartin
williammartin force-pushed the fix/atomic-api-cache-store branch from 206befd to b0feba3 Compare September 9, 2026 15:16
Write cache entries to a temp file in the same directory and rename into
place so concurrent gh processes and mid-write crashes cannot leave a
partial or interleaved cache file.

Fixes cli#252

Copilot AI 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.

🟡 Changes recommended

The retained cache-directory override lost its only regression test.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Publishes HTTP cache entries via temporary files and renames to prevent partial or corrupted reads.

Changes:

  • Adds atomic-style cache publication with cleanup and body replay.
  • Retries transient Windows rename conflicts.
  • Adds concurrency, failure, and platform-specific tests.
File summaries
File Description
pkg/api/cache.go Stages and renames cache responses.
pkg/api/cache_test.go Adds cache publication tests.
pkg/api/cache_rename_windows.go Implements bounded Windows retries.
pkg/api/cache_rename_windows_test.go Tests Windows rename behavior.
pkg/api/cache_rename_other.go Uses direct rename elsewhere.
Review details
  • Files reviewed: 5/5 changed files
  • Comments generated: 1
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread pkg/api/cache_test.go
@williammartin
williammartin requested review from sergiou87 and removed request for BagToad and babakks September 9, 2026 15:35
@williammartin
williammartin force-pushed the fix/atomic-api-cache-store branch from 6e1b7f1 to 4e421f8 Compare September 11, 2026 15:20
@williammartin

Copy link
Copy Markdown
Member

Thanks @baiyuxi930826, I used your work as a base to make some improvements.

Copilot AI 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.

🟡 Changes recommended

Failed body reads can return a silently truncated response to callers.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 5/5 changed files
  • Comments generated: 1
  • Review effort level: Balanced

Comment thread pkg/api/cache.go
Keep Windows rename retries bounded and platform-specific, simplify response serialization and cleanup, and cover cache publication through public-client and Windows filesystem scenarios.

Co-authored-by: Copilot App <[email protected]>
Copilot-Session: 6e0a2bb3-4485-45bd-bcbc-f426f9edbb7c

Copilot AI 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.

🟢 Approval recommended

The implementation addresses cross-process cache corruption with focused cleanup, error handling, and platform-specific tests.

Review details
  • Files reviewed: 5/5 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

@williammartin
williammartin merged commit 01be116 into cli:trunk Sep 11, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

API cache is not write-atomic.

5 participants