Repository navigation
fix: don't load a partially written dataset file from the cache - #749
TanishGudise wants to merge 2 commits into
Conversation
create_dataset_files wrote <parse_func_hash>.arrow in place. When the build stopped partway (Ctrl-C, or an exception such as an unreadable response line), ArrowWriter closed the file cleanly, so it held the rows flushed so far (in batches of 1,000) and still loaded. The next run then returned that partial dataset as the cached result. Write to a temporary file (named with the process id) and move it into place only when the build finishes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Strix Security ReviewWarning This pull request has 1 commit after the last Strix review ( No security issues found. Review summaryReviewed the atomic dataset-write change in Updated for Reviewed by Strix |
EntelligenceAI PR SummaryDataset creation now writes Arrow output to a process-specific temporary file and atomically moves it into the cache only after the build completes successfully, preventing interrupted or failed builds from being reused as valid cached datasets. Temporary files are cleaned up on write or rename failures, and new tests cover interruptions, malformed responses, successful rebuilds, and failed final moves. flowchart TD
classDef newBehavior fill:#dcfce7,stroke:#16a34a,color:#14532d;
A["Cached responses"] --> B["Write dataset to process-specific temporary Arrow file"]
B --> C["Parse responses and build dataset"]
C --> D["Atomic replace temporary file with final cache file"]
D --> E["Load completed dataset from cache"]
C --> F["Build fails or is interrupted"]
F --> G["Remove temporary file; leave no final dataset"]
D --> H["Rename fails"]
H --> G
class B,C,D,E,F,G,H newBehavior;
Review Scorecard
Safe to merge with a low blast radius; code quality rated Excellent (5/5): the review found nothing to flag. Key Findings:
Evaluated against
|
WalkthroughThis PR adds atomic persistence for generated Arrow datasets, ensuring interrupted or failed writes cannot leave reusable partial cache files. It updates dataset creation to finalize files safely and reload the committed path, with comprehensive tests covering parsing failures, corrupt responses, cache rebuilds, rename failures, cleanup, and error propagation. ChangesFiles
Added the Files
Added pytest coverage for interrupted parsing, corrupt response lines, successful dataset rebuilding from cached responses, failures during atomic renames, cleanup of temporary and partial files, rejection of incomplete cache hits, dataset reloadability, and error propagation. |
If os.replace raised (for example a permission error, or a directory already at the dataset path), the completed temporary file was left in the cache directory. Run the move inside the try and remove the temporary file in finally, so it is cleaned up after any failure. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Closes #745
create_dataset_fileswrote<parse_func_hash>.arrowin place. When the build stopped partway (Ctrl-C, or an exception such as an unreadable line in a responses file),ArrowWriterclosed the file cleanly, so it held the rows flushed so far (in batches of 1,000) and still loaded. The next run returned that partial dataset as the cached result.This writes the dataset to a temporary file,
<parse_func_hash>.arrow.<pid>.tmp(the process id keeps two processes running the same job from touching each other's file), and moves it into place withos.replaceonly after the build finishes. If the build fails or is interrupted, or the final move fails, the temporary file is removed and the exception is re-raised. The existing error paths (all requests failed,require_all_responses, invalid rows) behave as before; they now remove the temporary file instead of the final one.Tests (
tests/unittests/test_create_dataset_files.py)KeyboardInterruptfromparse_func, and an unreadable response line) leaves no dataset file and no temporary file, soattempt_loading_cached_datasetreturnsNone. On main both cases load a 1,000-row partial dataset and fail.<hash>.arrowand leaves no temporary file behind (passes on main and with the fix).test_failed_move_into_place_leaves_no_temporary_file, added after review; fails on 1c8f5e9).Each part of the fix removed on its own makes a test fail: no cleanup of the temporary file, moving it into place even when the build fails, and loading the temporary path at the end.
Manual check: end to end against a local OpenAI-compatible stub (2,500 rows, interrupted at row 2,200 of the build), the next identical run returned 2,000 rows on main and 2,500 with this change, with 0 new requests in both.
This doesn't repair a partial
<hash>.arrowleft by an earlier version; deleting that file makes the next run rebuild it from the cached responses.CodeExecutionBackend.create_dataset_files(code_executor/code_execution_backend/base_backend.py) writes and reloads its.arrowfile the same way; I left it out to keep this PR small and can add it here or in a follow-up.Without network access, the full test suite has no new failures compared with main: the offline failures are tests that need the network, plus
test_simple_code_execution_local, which also fails locally on main.I also checked this against the open #730: it merges without conflicts, and with both changes applied the tests from both PRs pass. #730's change on its own doesn't make the two new failing tests here pass.
CI note: CI on main is currently red for reasons unrelated to this PR:
test_basic_reasoningandtest_batch_reasoningfail because litellm 1.83.7 doesn't mapclaude-3-7-sonnet-20250219, and total coverage is 78%, under--cov-fail-under=80(run 37956501158 on 31abdb7). Once CI runs here (first-time contributor runs wait for approval), those failures will show on this PR too until main is fixed.🤖 Generated with Claude Code