Skip to content

upstream-sync: atomic writes for checkpoint and dev-cache files#31

Merged
Liohtml merged 2 commits into
masterfrom
upstream-sync/atomic-checkpoint-cache-writes
Jun 24, 2026
Merged

upstream-sync: atomic writes for checkpoint and dev-cache files#31
Liohtml merged 2 commits into
masterfrom
upstream-sync/atomic-checkpoint-cache-writes

Conversation

@Liohtml

@Liohtml Liohtml commented Jun 9, 2026

Copy link
Copy Markdown
Owner

Upstream reference

Ported from D4Vinci/Scrapling v0.4.9 – PR #344
"fix(spiders): use os.replace for atomic checkpoint/cache writes on Windows"
(merged 2026-06-07)

Problem

Both ResponseCache::put and CheckpointManager::save used std::fs::write() directly on the target path. std::fs::write truncates the file then fills it, so a process kill (SIGKILL, power loss, OOM) between those two operations leaves a zero-byte or half-written file. On the next run this silently fails to parse, losing the checkpoint or corrupting the dev cache.

Fix

Write the serialized data to a sibling .tmp file, then call std::fs::rename to atomically replace the target. On POSIX, rename(2) is a single syscall — the target either contains the old content or the new content, never a partial write.

# Before (non-atomic):
std::fs::write(&file_path, &data)?;

# After (atomic):
let tmp_path = file_path.with_extension("tmp");   // or .json.tmp
std::fs::write(&tmp_path, &data)?;
std::fs::rename(&tmp_path, &file_path)

This is the direct Rust equivalent of Python's os.replace() used in the upstream fix.

Files changed

File Change
src/spiders/cache.rs ResponseCache::put — atomic write via tmp+rename
src/spiders/checkpoint.rs CheckpointManager::save — atomic write via tmp+rename

Testing

cargo checkcargo test ✅ — all 175 tests pass, no regressions.


Generated by Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Enhanced data persistence mechanisms for cache and checkpoint storage to prevent corruption and data loss during unexpected application interruptions.

Write to a sibling .tmp file then rename so a process kill between
truncation and completion cannot leave a corrupt or zero-length file.
std::fs::rename is atomic on POSIX (single syscall), matching the
behaviour of Python os.replace used in upstream Scrapling v0.4.9
(PR #344).

https://claude.ai/code/session_01WyJXoeiQoH8fRW4DBx7HQq
@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@Liohtml, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 57 minutes and 45 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 98a5afd5-69d0-479c-868b-3c5ff79833f1

📥 Commits

Reviewing files that changed from the base of the PR and between 423fcbe and b88a005.

📒 Files selected for processing (3)
  • Cargo.toml
  • src/spiders/cache.rs
  • src/spiders/checkpoint.rs
📝 Walkthrough

Walkthrough

This PR adds atomic write patterns to two persistence operations: response cache and checkpoint management. Both now write to temporary files and rename them to their final destinations, preventing partially written or corrupted files if a process crashes mid-write.

Changes

Atomic write safety for cache and checkpoint

Layer / File(s) Summary
Response cache atomic write
src/spiders/cache.rs
ResponseCache::put now writes JSON to a temporary .tmp file and then renames it to the final cache path instead of writing directly to the destination, ensuring atomicity.
Checkpoint atomic write
src/spiders/checkpoint.rs
CheckpointManager::save now writes JSON to checkpoint.json.tmp and then renames it to the final checkpoint.json path with comments indicating crash-safety intent, replacing the prior direct write.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Possibly related PRs

  • Liohtml/RUSTScrapling#22: Changes CrawlerEngine to drain in-flight tasks before checkpoint persistence, complementing this PR's atomic checkpoint write safety.

Poem

A rabbit with files writes with care,
Tmp files vanish in the air—
Rename them whole, never halfway,
No crashes truncate the data today! 🐇✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: atomic writes for both checkpoint and dev-cache files across the two modified files (cache.rs and checkpoint.rs).
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch upstream-sync/atomic-checkpoint-cache-writes

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Address Repo Guardian review (#43) on the atomic-write change:
- std::fs::rename does not atomically replace an existing file on Windows
  (MoveFileW fails with ERROR_ALREADY_EXISTS), so the cache/checkpoint
  overwrite path would break on windows-latest. Use
  tempfile::NamedTempFile::new_in(dir) + persist(target), which maps to
  MoveFileExW(REPLACE_EXISTING) on Windows and rename(2) on POSIX.
- NamedTempFile auto-removes the temp file on drop, so a failed persist
  no longer leaves orphaned .tmp files, and the random suffix avoids
  collisions between concurrent writers of the same target.
- Promote tempfile from dev-dependencies to dependencies.
- Add roundtrip + overwrite + cleanup tests for both ResponseCache and
  CheckpointManager (review Issue 4).

Closes #43

https://claude.ai/code/session_012RmdaovmNWZVAim4XxCWwn
@Liohtml Liohtml merged commit 6882f5e into master Jun 24, 2026
6 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.

2 participants