Skip to content

Add FileSystem SyncFile API (#14739)#14739

Open
xingbowang wants to merge 1 commit into
facebook:mainfrom
xingbowang:export-D104918547
Open

Add FileSystem SyncFile API (#14739)#14739
xingbowang wants to merge 1 commit into
facebook:mainfrom
xingbowang:export-D104918547

Conversation

@xingbowang
Copy link
Copy Markdown
Contributor

@xingbowang xingbowang commented May 13, 2026

Summary:

Adds public path-level file sync APIs to RocksDB so callers can ask Env or FileSystem to sync a named file without hand-rolled reopen logic.

This diff:

  • Adds public Env::SyncFile and FileSystem::SyncFile APIs for syncing or fsyncing a file by name without requiring callers to reopen it directly.
  • Documents the SyncFile contract: filesystems may override it as a no-op when flush/close already provides durability, and RocksDB callers should use it instead of hand-rolled ReopenWritableFile()+Sync()/Fsync() so filesystems can reject post-close data-write reopen paths.
  • Implements the default Env and FileSystem behavior by reopening the file as writable, calling Sync or Fsync, closing the handle, returning the sync/fsync error ahead of a close error when both fail, and explicitly consuming the close status on the sync-failure path.
  • Wires SyncFile through the Env/FileSystem bridge and wrapper layers, including EnvWrapper, FileSystemWrapper, CompositeEnvWrapper, EncryptedFileSystem, RemapFileSystem, read-only file systems, mock file systems, and fault-injection wrappers.
  • Keeps fault-injection SyncFile overrides on the base default implementation so the operation still exercises the wrapper's ReopenWritableFile, wrapped-file Sync/Fsync, and Close hooks instead of bypassing them through target forwarding.
  • Updates external SST ingestion to call FileSystem::SyncFile instead of manually reopening an ingested file as writable, while preserving the NotSupported skip behavior and failure logging.
  • Adds release-note coverage for the new public API and unit coverage for default Env/FileSystem success, reopen failure, close failure, sync-versus-close failure precedence, and external SST sync behavior.

There was an earlier version of this API which got reverted (PR #13987) due to internal change broken. Re-apply the change. The internal issue will be resolved in next release.

Reviewed By: pdillinger

Differential Revision: D104918547

@meta-cla meta-cla Bot added the CLA Signed label May 13, 2026
@meta-codesync
Copy link
Copy Markdown

meta-codesync Bot commented May 13, 2026

@xingbowang has exported this pull request. If you are a Meta employee, you can view the originating Diff in D104918547.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 13, 2026

✅ clang-tidy: No findings on changed lines

Completed in 389.2s.

@xingbowang xingbowang force-pushed the export-D104918547 branch 3 times, most recently from 06ce8d5 to 55fcf14 Compare May 14, 2026 12:06
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 14, 2026

Codex Code Review - OBSOLETE

Superseded by a newer AI review. Expand to see the original review.

🟡 Codex Code Review

Auto-triggered after CI passed — reviewing commit 55fcf14


Codex review failed before producing findings.

WARNING: proceeding, even though we could not update PATH: Refusing to create helper binaries under temporary dir "/tmp" (codex_home: AbsolutePathBuf("/tmp/codex-home"))
error: the argument '--base <BRANCH>' cannot be used with '[PROMPT]'

Usage: codex exec review --commit <SHA> --base <BRANCH> --title <TITLE> --model <MODEL> --config <key=value> --dangerously-bypass-approvals-and-sandbox --output-last-message <FILE> [PROMPT]

For more information, try '--help'.

ℹ️ About this response

Generated by Codex CLI.
Review methodology: claude_md/code_review.md

Limitations:

  • Codex may miss context from files not in the diff
  • Large PRs may be truncated
  • Always apply human judgment to AI suggestions

Commands:

  • /codex-review [context] — Request a code review
  • /codex-query <question> — Ask about the PR or codebase

@xingbowang xingbowang force-pushed the export-D104918547 branch from 55fcf14 to 9533e1b Compare May 14, 2026 12:57
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 14, 2026

Claude Code Review - OBSOLETE

Superseded by a newer AI review. Expand to see the original review.

✅ Claude Code Review

Auto-triggered after CI passed — reviewing commit 55fcf14


Summary

Well-designed API addition that cleanly replaces the manual reopen+sync+close pattern. The implementation follows existing RocksDB conventions and preserves correct error semantics. Two missing instrumentation overrides are the primary concern.

High-severity findings (2):

  • [utilities/env_timed.h] TimedFileSystem missing SyncFile override — sync operations won't appear in performance timing metrics.
  • [utilities/counted_fs.h] CountedFileSystem missing SyncFile override — sync operations won't be counted in file system statistics.
Full review (click to expand)

Findings

🔴 HIGH

H1. Missing TimedFileSystem::SyncFile override — utilities/env_timed.h
  • Issue: TimedFileSystem wraps all FileSystem operations with performance timing instrumentation but does not override SyncFile. When SyncFile is called through a TimedFileSystem wrapper, the overall sync latency won't be captured as a single metric. Sub-operations (the ReopenWritableFile call within the default implementation) will be timed individually, but there's no unified env_sync_file_nanos metric.
  • Root cause: The PR adds SyncFile to FileSystem/FileSystemWrapper but doesn't add corresponding overrides to all instrumentation wrappers.
  • Suggested fix: Add a TimedFileSystem::SyncFile override that wraps the call with a perf timer, consistent with how LinkFile, RenameFile, etc. are timed.
H2. Missing CountedFileSystem::SyncFile override — utilities/counted_fs.h
  • Issue: CountedFileSystem counts all FileSystem operations (it has syncs, dsyncs, fsyncs counters) but does not override SyncFile. The SyncFile operation won't increment any counter. Note that CountedFileSystem DOES override ReopenWritableFile (line 106), so sub-operation counting may partially work through wrapped file objects, but the unified sync count is lost.
  • Root cause: Same as H1 — incomplete wrapper coverage for the new API.
  • Suggested fix: Add a CountedFileSystem::SyncFile override that increments the appropriate sync counter.

🟡 MEDIUM

M1. Close() error behavior change — env/file_system.cc:108-131, env/env.cc:804-821
  • Issue: The old pattern in ExternalSstFileIngestionJob::Prepare() used a unique_ptr<FSWritableFile> that called Close() via destructor, silently ignoring close errors. The new SyncFile default implementation explicitly calls Close() and returns close errors (when sync succeeds). This is a behavioral improvement that surfaces previously hidden errors, but it means callers may now see IOError from close where they previously saw OK.
  • Root cause: Intentional design — the new API correctly propagates close errors. The old pattern was silently swallowing them.
  • Suggested fix: No code fix needed. Document this in the release note as an improvement: "SyncFile now properly reports close errors that were previously silently ignored during SST file ingestion."
M2. Sync point semantic change in ingestion job — db/external_sst_file_ingestion_job.cc:168-185
  • Issue: The BeforeSyncIngestedFile/AfterSyncIngestedFile sync points moved from inside if (status.ok()) (only fired after successful ReopenWritableFile) to unconditional positions (always fire). The SyncFailure test at db/external_sst_file_basic_test.cc:1291 uses these to disable/re-enable the filesystem around sync. The new placement is functionally correct for this test because the sync points still bracket the sync operation, but the semantic change (always firing vs. conditionally firing) should be noted.
  • Root cause: Consolidation of the reopen+sync+close into a single SyncFile call eliminates the intermediate status check that previously gated the sync points.
  • Suggested fix: Verify all tests using these sync points pass. The current placement appears correct for existing test usage.

🟢 LOW / NIT

L1. EncryptedFileSystem optimization opportunity — include/rocksdb/env_encryption.h:346
  • Issue: EncryptedFileSystem does not override SyncFile, so it falls back to the default reopen+sync+close pattern. This works correctly but may create unnecessary encryption overhead when reopening the file. A custom override could avoid this.
  • Suggested fix: Consider adding an EncryptedFileSystem::SyncFile override in a follow-up PR that avoids the decrypt/encrypt cycle.
L2. IOOptions() default in ingestion call — db/external_sst_file_ingestion_job.cc
  • Issue: The SyncFile call uses IOOptions() (default constructor). The old code also used IOOptions() for the Sync/Fsync calls, so this is consistent. However, a richer IOOptions with rate limiting or priority could be beneficial for large ingestion workloads.
  • Suggested fix: No change needed now; this is a future enhancement opportunity.
L3. Release note could mention behavioral improvement — unreleased_history/public_api_changes/sync_file_api.md
  • Issue: The release note describes the new APIs but doesn't mention that SST file ingestion now properly reports close errors during sync. Users upgrading may want to know about this.
  • Suggested fix: Add a sentence: "External SST file ingestion now properly reports file close errors during sync that were previously silently ignored."

Cross-Component Analysis

Context SyncFile affected? Behavior correct? Action needed?
WritePreparedTxnDB No direct interaction N/A None
ReadOnly DB ReadOnlyFileSystem::SyncFile → FailReadOnly() Yes None
CompactionService SyncFile not serialized for remote compaction N/A None
BlobDB Uses standard FileSystem Yes None
EncryptedFileSystem Falls back to default reopen+sync+close Correct but suboptimal Optimization opportunity (L1)
FaultInjectionTestFS PR adds override delegating to FileSystem::SyncFile Yes — fault injection on sub-ops preserved None
RemapFileSystem PR adds override with path remapping Yes None
MockFileSystem PR adds no-op override returning OK Yes None
TimedFileSystem No override — sync timing lost No Fix required (H1)
CountedFileSystem No override — sync counting lost No Fix required (H2)

Dismissed findings from debate

  • TEST_SYNC_POINT_CALLBACK file handle leak (initially HIGH): Dismissed. When a test callback injects failure after successful ReopenWritableFile, the unique_ptr destructor still calls Close() per the FSWritableFile contract (file_system.h lines 1126-1127). No leak occurs.
  • Hard link race condition (initially HIGH): Dismissed. The external SST ingestion API contract already requires that the application does not modify the file after calling IngestExternalFile. This is a pre-existing constraint, not a new issue.
  • FaultInjectionTestFS missing override (initially MEDIUM): Dismissed. The PR DOES add SyncFile overrides to both FaultInjectionTestFS and FaultInjectionTestEnv, delegating to the base class to preserve fault injection through sub-operations.

Positive Observations

  • Clean TODO resolution: Directly addresses the TODO at db/external_sst_file_ingestion_job.cc and GitHub issue Avoid using ReopenWritableFile for sync file to disk #13741.
  • Excellent API documentation: The header comments thoroughly document thread safety, error semantics, default behavior, and override guidance.
  • Consistent API design: Follows the established pattern of LinkFile, RenameFile, etc. with proper parameter ordering and naming.
  • Comprehensive unit tests: 8 new tests covering sync/fsync selection, reopen error propagation, close error surfacing, and error precedence for both Env and FileSystem layers.
  • Proper wrapper coverage: EnvWrapper, FileSystemWrapper, CompositeEnv, LegacyFileSystemWrapper, ReadOnlyFileSystem, RemapFileSystem, MockFileSystem, and fault injection wrappers are all handled.
  • Error precedence is correct: Sync errors take priority over close errors, preserving critical durability information.

ℹ️ About this response

Generated by Claude Code.
Review methodology: claude_md/code_review.md

Limitations:

  • Claude may miss context from files not in the diff
  • Large PRs may be truncated
  • Always apply human judgment to AI suggestions

Commands:

  • /claude-review [context] — Request a code review
  • /claude-query <question> — Ask about the PR or codebase

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 14, 2026

Codex Code Review - OBSOLETE

Superseded by a newer AI review. Expand to see the original review.

🟡 Codex Code Review

Auto-triggered after CI passed — reviewing commit 9533e1b


Codex review failed before producing findings.

WARNING: proceeding, even though we could not update PATH: Refusing to create helper binaries under temporary dir "/tmp" (codex_home: AbsolutePathBuf("/tmp/codex-home"))
error: the argument '--base <BRANCH>' cannot be used with '[PROMPT]'

Usage: codex exec review --commit <SHA> --base <BRANCH> --title <TITLE> --model <MODEL> --config <key=value> --dangerously-bypass-approvals-and-sandbox --output-last-message <FILE> [PROMPT]

For more information, try '--help'.

ℹ️ About this response

Generated by Codex CLI.
Review methodology: claude_md/code_review.md

Limitations:

  • Codex may miss context from files not in the diff
  • Large PRs may be truncated
  • Always apply human judgment to AI suggestions

Commands:

  • /codex-review [context] — Request a code review
  • /codex-query <question> — Ask about the PR or codebase

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 14, 2026

Claude Code Review - OBSOLETE

Superseded by a newer AI review. Expand to see the original review.

✅ Claude Code Review

Auto-triggered after CI passed — reviewing commit 9533e1b


Summary

Well-scoped PR that extracts a common reopen+sync+close pattern into a clean FileSystem::SyncFile() / Env::SyncFile() API, addressing the TODO in issue #13741. The default implementations are correct, error precedence logic is sound, and the fault injection overrides are properly designed. The test suite is comprehensive for the new API itself.

High-severity findings (0):

No high-severity findings.

Full review (click to expand)

Findings

🔴 HIGH

None.

🟡 MEDIUM

M1. Missing SyncFile overrides in instrumentation FileSystem wrappers
  • Issue: CountedFileSystem (utilities/counted_fs.h:106), TimedFileSystem (utilities/env_timed.h), and FileSystemTracingWrapper (env/file_system_tracer.h) override ReopenWritableFile (at least CountedFileSystem does) for counting/timing/tracing, but do not override SyncFile. These wrappers inherit FileSystemWrapper::SyncFile(), which forwards to target_->SyncFile(), meaning the SyncFile operation will not be counted, timed, or traced. This is an instrumentation gap — not a correctness bug, but it means monitoring dashboards will undercount file operations.
  • Root cause: The PR updates all correctness-critical FileSystem subclasses but omits instrumentation wrappers.
  • Suggested fix: Add SyncFile overrides to CountedFileSystem, TimedFileSystem, and FileSystemTracingWrapper that record the metric/time/trace, then delegate to the target. This can be done in a follow-up PR if scope is a concern.
M2. Fsync error path not tested with close-error precedence
  • Issue: The test EnvSyncFileDefaultReturnsSyncErrorBeforeCloseError (and its FileSystem counterpart) only tests the use_fsync=false (Sync) path for error precedence. The SyncFileTestResult::kFsyncError enum value exists but is never used in any test. If the fsync path has a bug in error precedence, it would go undetected.
  • Root cause: Tests only exercise use_fsync=false for the error-precedence scenario.
  • Suggested fix: Add a parallel test case (or parameterize the existing one) that sets state.fsync_result = SyncFileTestResult::kFsyncError with state.close_result = SyncFileTestResult::kCloseError and calls with use_fsync=true.
M3. Missing test coverage for RemapFileSystem::SyncFile, ReadOnlyFileSystem::SyncFile, and MockFileSystem::SyncFile
  • Issue: The PR adds SyncFile overrides to these three FileSystem subclasses but provides no direct unit tests for them. ReadOnlyFileSystem::SyncFile returns FailReadOnly(), RemapFileSystem::SyncFile encodes the path, and MockFileSystem::SyncFile returns OK — none of these behaviors are tested.
  • Root cause: Tests focus on the default Env/FileSystem implementations.
  • Suggested fix: Add simple unit tests verifying: (a) ReadOnlyFileSystem::SyncFile returns a read-only error, (b) RemapFileSystem::SyncFile maps the path correctly, (c) MockFileSystem::SyncFile succeeds.
M4. EncryptedFileSystem does not override SyncFile — acceptable but should be documented
  • Issue: EncryptedFileSystem (env/env_encryption.cc:345) has a custom ReopenWritableFile override that sets up cipher streams. It does not override SyncFile, so calls go through FileSystemWrapper::SyncFile()target_->SyncFile(), which routes to the underlying (non-encrypted) FS. This bypasses the encryption layer's ReopenWritableFile.
  • Root cause: SyncFile is a new API and EncryptedFileSystem was not updated.
  • Analysis: This is actually correct behavior — SyncFile only flushes OS buffers to disk and does not read or write file content, so encryption context is irrelevant. The underlying FS's ReopenWritableFile + Sync + Close correctly syncs the encrypted bytes on disk. However, it should be explicitly documented (e.g., a comment in EncryptedFileSystem) explaining why no override is needed.
  • Suggested fix: Add a comment in EncryptedFileSystem (or in the PR description) noting that SyncFile intentionally delegates to the underlying FS because sync is a metadata/durability operation that doesn't need encryption context.

🟢 LOW / NIT

L1. API design: bool use_fsync parameter
  • Issue: Using a bool parameter for sync vs. fsync is a minor API smell. An enum (e.g., SyncType::kSync / SyncType::kFsync) would be more self-documenting.
  • Analysis: However, this is consistent with the existing DBOptions::use_fsync which is also a bool. Changing to an enum would introduce inconsistency with the existing option. This is a pre-existing pattern.
  • Suggested fix: No change needed for this PR. Could be addressed in a broader API cleanup.
L2. Documentation could mention NotSupported propagation
  • Issue: The doc comments on Env::SyncFile and FileSystem::SyncFile say "The default implementation reopens the file as writable, syncs or fsyncs it, then closes it." Consider also mentioning that NotSupported from ReopenWritableFile will propagate, since this is the key behavior that remote filesystem integrations rely on.
  • Suggested fix: Optional: add a note like "Returns NotSupported if the underlying filesystem does not support ReopenWritableFile."
L3. SyncIngestedFile helper is now only used in one place
  • Issue: After this PR, SyncIngestedFile() (db/external_sst_file_ingestion_job.cc:1569) is only called from the global seqno write path (line 1483). It's a small template helper. This is fine but could be simplified to an inline call if desired.
  • Suggested fix: No change needed. Keeping it as a helper is acceptable.
L4. Minor: #include "test_util/sync_point.h" added to production files
  • Issue: env/env.cc and env/file_system.cc now include test_util/sync_point.h for TEST_SYNC_POINT_CALLBACK. This is a pre-existing pattern used throughout RocksDB production code — sync points compile to no-ops in release builds.
  • Suggested fix: No change needed. This follows existing conventions.

Cross-Component Analysis

Context Executes SyncFile? Assumptions hold? Action needed?
ReadOnly DB No (ingestion blocked) N/A ReadOnlyFileSystem::SyncFile correctly returns FailReadOnly
WritePreparedTxnDB Yes (standard ingestion) Yes Safe
EncryptedFileSystem Delegates to underlying FS Yes (sync doesn't need encryption) Document intent
MockFileSystem No-op (returns OK) Yes Safe for tests
FaultInjectionTestFS Calls FileSystem::SyncFile base Yes — routes through its own ReopenWritableFile Correct design
Remote/distributed FS ReopenWritableFile returns NotSupported Yes — propagated correctly Safe
Secondary DB Possible via try_catch_up Yes Safe

FaultInjection call routing verification:

  • FaultInjectionTestFS::SyncFile() calls FileSystem::SyncFile() (base class, not FileSystemWrapper)
  • FileSystem::SyncFile() calls this->ReopenWritableFile() — polymorphic dispatch hits FaultInjectionTestFS::ReopenWritableFile()
  • This wraps the file in TestFSWritableFile, which has fault-injectable Sync(), Fsync(), and Close() hooks
  • If it instead called FileSystemWrapper::SyncFile(), it would forward to target_->SyncFile(), completely bypassing fault injection
  • Verdict: The design is correct and the comment in the code explains the rationale well

EncryptedFileSystem bypass verification:

  • EncryptedFileSystem inherits FileSystemWrapper::SyncFile() → forwards to target_->SyncFile()
  • The underlying FS's SyncFile default calls its own ReopenWritableFileSyncClose
  • This syncs the raw encrypted bytes on disk without needing encryption context
  • The encryption layer's ReopenWritableFile (which sets up cipher streams) is correctly NOT needed here
  • Verdict: Safe — sync is a durability operation, not a data transformation

Positive Observations

  1. Clean API design: The SyncFile API follows existing patterns (LinkFile, RenameFile) and integrates naturally into the FileSystem hierarchy.
  2. Comprehensive error handling: The sync-versus-close error precedence is correctly implemented in both Env::SyncFile and FileSystem::SyncFile, with Close always being called even when Sync fails.
  3. Thorough unit tests: 8 new tests covering success, reopen failure, close-error-after-sync, and sync-error-precedence for both Env and FileSystem layers, using well-designed test helper classes.
  4. Correct fault injection design: The FaultInjectionTestFS/Env overrides correctly call the base class method (not the wrapper) to ensure fault injection hooks are exercised. The comments explain the design rationale.
  5. Addresses tech debt: Resolves the TODO comment at issue Avoid using ReopenWritableFile for sync file to disk #13741 and simplifies the ingestion code.
  6. Test sync point migration: Existing tests (SyncFailure, SyncFileNotSupported) are correctly updated to use new sync point names.

ℹ️ About this response

Generated by Claude Code.
Review methodology: claude_md/code_review.md

Limitations:

  • Claude may miss context from files not in the diff
  • Large PRs may be truncated
  • Always apply human judgment to AI suggestions

Commands:

  • /claude-review [context] — Request a code review
  • /claude-query <question> — Ask about the PR or codebase

@meta-codesync meta-codesync Bot changed the title Add FileSystem SyncFile API Add FileSystem SyncFile API (#14739) May 14, 2026
xingbowang added a commit to xingbowang/rocksdb that referenced this pull request May 14, 2026
Summary:

- Adds `FileSystem::SyncFile` and `Env::SyncFile` so sync-only callers do not need to use `ReopenWritableFile` directly.
- Implements the default path by reopening the file, calling `Sync` or `Fsync`, closing it, and preserving sync-before-close error precedence.
- Adds direct default-path tests for success, reopen `NotSupported`, close failure, and sync-versus-close failure precedence for both `Env` and `FileSystem`; the helpers avoid clang-tidy NRVO diagnostics.
- Removes the no-op encrypted file-system override and documents why fault-injection overrides intentionally call the base default implementation rather than wrapper forwarding.
- Documents the new public API in the unreleased release notes; Sally and Warm Storage integration stay in child diffs.

Differential Revision: D104918547
@xingbowang xingbowang force-pushed the export-D104918547 branch from 9533e1b to 119aee4 Compare May 14, 2026 16:27
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 14, 2026

Codex Code Review - OBSOLETE

Superseded by a newer AI review. Expand to see the original review.

🟡 Codex Code Review

Auto-triggered after CI passed — reviewing commit 119aee4


Codex review failed before producing findings.

WARNING: proceeding, even though we could not update PATH: Refusing to create helper binaries under temporary dir "/tmp" (codex_home: AbsolutePathBuf("/tmp/codex-home"))
error: the argument '--base <BRANCH>' cannot be used with '[PROMPT]'

Usage: codex exec review --commit <SHA> --base <BRANCH> --title <TITLE> --model <MODEL> --config <key=value> --dangerously-bypass-approvals-and-sandbox --output-last-message <FILE> [PROMPT]

For more information, try '--help'.

ℹ️ About this response

Generated by Codex CLI.
Review methodology: claude_md/code_review.md

Limitations:

  • Codex may miss context from files not in the diff
  • Large PRs may be truncated
  • Always apply human judgment to AI suggestions

Commands:

  • /codex-review [context] — Request a code review
  • /codex-query <question> — Ask about the PR or codebase

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 14, 2026

Claude Code Review - OBSOLETE

Superseded by a newer AI review. Expand to see the original review.

✅ Claude Code Review

Auto-triggered after CI passed — reviewing commit 119aee4


Summary

Clean, well-designed API addition that extracts the reopen+sync+close pattern into a reusable FileSystem::SyncFile() / Env::SyncFile() virtual method. The core implementation is correct, the error handling is improved, and the fault injection routing is sound. Two medium-severity gaps in wrapper coverage need attention.

High-severity findings (0):

No high-severity findings.

Medium-severity findings (2):

  • [env/file_system_tracer.h, utilities/counted_fs.h] FileSystemTracingWrapper and CountedFileSystem override ReopenWritableFile but not SyncFile, causing sync operations to be completely invisible to tracing and operation counting.
  • [include/rocksdb/file_system.h:628, include/rocksdb/env.h:388] API documentation for SyncFile is minimal — missing thread safety notes and behavior when file doesn't exist.
Full review (click to expand)

Findings

🟡 MEDIUM

M1. Missing SyncFile overrides in FileSystemTracingWrapper and CountedFileSystemenv/file_system_tracer.h, utilities/counted_fs.h
  • Issue: Both FileSystemTracingWrapper (line 20) and CountedFileSystem (utilities/counted_fs.h:84) override ReopenWritableFile to add tracing/counting but do not override SyncFile. Since they inherit FileSystemWrapper::SyncFile, calls go directly to target_->SyncFile(), bypassing the wrapper entirely. The ReopenWritableFile call inside target_->SyncFile() also bypasses the wrapper because it runs on target_, not on the wrapper. This means the entire sync operation — including the reopen — is invisible to tracing and counting.
  • Root cause: New virtual method added to FileSystem without updating all instrumentation wrappers.
  • Suggested fix: Add SyncFile overrides to both wrappers following the same pattern as ReopenWritableFile:
    • FileSystemTracingWrapper::SyncFile — record an IOTraceRecord with timing
    • CountedFileSystem::SyncFile — increment an appropriate counter (e.g., counters_.syncs++)
M2. Minimal API documentation — include/rocksdb/file_system.h:628, include/rocksdb/env.h:388
  • Issue: The doc comments are a single sentence: "Syncs file data and metadata for fname. The default implementation reopens the file as writable, syncs or fsyncs it, then closes it." Missing: thread safety guarantees, behavior when file doesn't exist, when NotSupported may be returned, and relationship to ReopenWritableFile.
  • Root cause: Other methods like LinkFile also have minimal docs, so this follows existing patterns. However, SyncFile has more complex semantics (default implementation with side effects) that warrant more documentation.
  • Suggested fix: Add notes on: (1) thread safety (safe to call concurrently on different files), (2) NotSupported may be returned by file systems that don't support reopening, (3) custom implementations should maintain error precedence (sync errors over close errors).

🟢 LOW / NIT

L1. bool use_fsync parameter could be an enum for extensibility — include/rocksdb/file_system.h:631
  • Issue: The bool use_fsync parameter limits future extensibility (e.g., if data-only sync without metadata is desired). An enum like SyncType { kSync, kFsync } would be more extensible.
  • Root cause: Follows existing DBOptions::use_fsync which is also a bool.
  • Suggested fix: Acceptable as-is since it's consistent with existing conventions. Consider for a future API revision.
L2. Test code duplication between Env and FileSystem test layers — env/env_test.cc
  • Issue: The 8 new tests are split into 4 Env tests and 4 FileSystem tests with nearly identical logic. Table-driven tests or a shared parameterized helper could reduce duplication.
  • Root cause: The two layers (Env, FileSystem) have different type systems (Status/IOStatus, WritableFile/FSWritableFile), making sharing harder.
  • Suggested fix: Consider extracting a template or macro for the common test pattern, but this is low priority given the test clarity benefits of explicit separate tests.
L3. SyncFileTestState counters are not atomic — env/env_test.cc
  • Issue: SyncFileTestState uses plain int for counters (reopen_count, sync_count, etc.). These are fine for single-threaded tests but would be incorrect if tests ever ran concurrently.
  • Suggested fix: Acceptable as-is for the current single-threaded test usage.

Cross-Component Analysis

Context SyncFile behavior Correct?
PosixFileSystem Base default: reopen (O_APPEND, no truncation) + sync + close Yes
FaultInjectionTestFS Calls FileSystem::SyncFile base → virtual dispatch to own ReopenWritableFile → file tracking preserved Yes
FaultInjectionTestEnv Calls Env::SyncFile base → virtual dispatch to own ReopenWritableFile → file tracking preserved Yes
EncryptedFileSystem FileSystemWrapper::SyncFile forwards to underlying FS → no encryption needed for sync-only Yes
ReadOnlyFileSystem Returns FailReadOnly() Yes
RemapFileSystem Remaps path, then forwards to FileSystemWrapper::SyncFile Yes
MockFileSystem No-op, returns OK Yes
FileSystemTracingWrapper Forwards to target, bypasses tracing Gap (M1)
CountedFileSystem Forwards to target, bypasses counting Gap (M1)
CompositeEnv Bridges Env→FileSystem correctly Yes
LegacyFileSystemWrapper Bridges FileSystem→Env correctly Yes

Positive Observations

  • Improved error handling: The new implementation explicitly calls Close() and handles close errors, whereas the old code relied on the destructor (which silently ignores close failures). This is a genuine improvement in data integrity.
  • Clean API extraction: The reopen+sync+close pattern was duplicated inline. Extracting it into a virtual method enables easy overriding by custom file systems (e.g., remote FS can implement sync natively without reopen).
  • Correct fault injection routing: The FaultInjectionTestFS::SyncFile and FaultInjectionTestEnv::SyncFile implementations correctly call the base class SyncFile (not the wrapper forwarding), ensuring virtual dispatch routes through their own ReopenWritableFile overrides. This is well-documented in code comments.
  • Comprehensive test coverage: 8 new tests cover success paths, reopen failure, close failure, and error precedence for both Env and FileSystem layers.
  • Proper handling of NotSupported: The ExternalSstFileIngestionJob::Prepare caller correctly ignores NotSupported from SyncFile, maintaining compatibility with remote file systems.

ℹ️ About this response

Generated by Claude Code.
Review methodology: claude_md/code_review.md

Limitations:

  • Claude may miss context from files not in the diff
  • Large PRs may be truncated
  • Always apply human judgment to AI suggestions

Commands:

  • /claude-review [context] — Request a code review
  • /claude-query <question> — Ask about the PR or codebase

xingbowang added a commit to xingbowang/rocksdb that referenced this pull request May 14, 2026
Summary:
Pull Request resolved: facebook#14739

- Adds `FileSystem::SyncFile` and `Env::SyncFile` so sync-only callers do not need to use `ReopenWritableFile` directly.
- Implements the default path by reopening the file, calling `Sync` or `Fsync`, closing it, and preserving sync-before-close error precedence.
- Adds direct default-path tests for success, reopen `NotSupported`, close failure, and sync-versus-close failure precedence for both `Env` and `FileSystem`; the helpers avoid clang-tidy NRVO diagnostics.
- Removes the no-op encrypted file-system override and documents why fault-injection overrides intentionally call the base default implementation rather than wrapper forwarding.
- Documents the new public API in the unreleased release notes; Sally and Warm Storage integration stay in child diffs.

Differential Revision: D104918547
xingbowang added a commit to xingbowang/rocksdb that referenced this pull request May 14, 2026
Summary:

This diff:

- Adds public `Env::SyncFile` and `FileSystem::SyncFile` APIs for syncing or fsyncing a file by name without requiring callers to reopen it directly.
- Implements the default `Env` and `FileSystem` behavior by reopening the file as writable, calling `Sync` or `Fsync`, closing the handle, and returning the sync/fsync error ahead of a close error when both fail.
- Wires `SyncFile` through the Env/FileSystem bridge and wrapper layers, including `EnvWrapper`, `FileSystemWrapper`, `CompositeEnvWrapper`, `RemapFileSystem`, read-only file systems, mock file systems, and fault-injection wrappers.
- Keeps fault-injection `SyncFile` overrides on the base default implementation so the operation still exercises the wrapper's `ReopenWritableFile`, wrapped-file `Sync`/`Fsync`, and `Close` hooks instead of bypassing them through target forwarding.
- Updates external SST ingestion to call `FileSystem::SyncFile` instead of manually reopening an ingested file as writable, while preserving the `NotSupported` skip behavior and failure logging.
- Adds release-note coverage for the new public API and unit coverage for default `Env`/`FileSystem` success, reopen failure, close failure, sync-versus-close failure precedence, and external SST sync behavior.

Differential Revision: D104918547
@xingbowang xingbowang force-pushed the export-D104918547 branch from 119aee4 to a02c818 Compare May 14, 2026 17:55
xingbowang added a commit to xingbowang/rocksdb that referenced this pull request May 15, 2026
Summary:

This diff:

- Adds public `Env::SyncFile` and `FileSystem::SyncFile` APIs for syncing or fsyncing a file by name without requiring callers to reopen it directly.
- Implements the default `Env` and `FileSystem` behavior by reopening the file as writable, calling `Sync` or `Fsync`, closing the handle, and returning the sync/fsync error ahead of a close error when both fail.
- Wires `SyncFile` through the Env/FileSystem bridge and wrapper layers, including `EnvWrapper`, `FileSystemWrapper`, `CompositeEnvWrapper`, `RemapFileSystem`, read-only file systems, mock file systems, and fault-injection wrappers.
- Keeps fault-injection `SyncFile` overrides on the base default implementation so the operation still exercises the wrapper's `ReopenWritableFile`, wrapped-file `Sync`/`Fsync`, and `Close` hooks instead of bypassing them through target forwarding.
- Updates external SST ingestion to call `FileSystem::SyncFile` instead of manually reopening an ingested file as writable, while preserving the `NotSupported` skip behavior and failure logging.
- Adds release-note coverage for the new public API and unit coverage for default `Env`/`FileSystem` success, reopen failure, close failure, sync-versus-close failure precedence, and external SST sync behavior.

Differential Revision: D104918547
@xingbowang xingbowang force-pushed the export-D104918547 branch from a02c818 to bdedb71 Compare May 15, 2026 17:34
xingbowang added a commit to xingbowang/rocksdb that referenced this pull request May 15, 2026
Summary:
Pull Request resolved: facebook#14739

This diff:

- Adds public `Env::SyncFile` and `FileSystem::SyncFile` APIs for syncing or fsyncing a file by name without requiring callers to reopen it directly.
- Implements the default `Env` and `FileSystem` behavior by reopening the file as writable, calling `Sync` or `Fsync`, closing the handle, and returning the sync/fsync error ahead of a close error when both fail.
- Wires `SyncFile` through the Env/FileSystem bridge and wrapper layers, including `EnvWrapper`, `FileSystemWrapper`, `CompositeEnvWrapper`, `RemapFileSystem`, read-only file systems, mock file systems, and fault-injection wrappers.
- Keeps fault-injection `SyncFile` overrides on the base default implementation so the operation still exercises the wrapper's `ReopenWritableFile`, wrapped-file `Sync`/`Fsync`, and `Close` hooks instead of bypassing them through target forwarding.
- Updates external SST ingestion to call `FileSystem::SyncFile` instead of manually reopening an ingested file as writable, while preserving the `NotSupported` skip behavior and failure logging.
- Adds release-note coverage for the new public API and unit coverage for default `Env`/`FileSystem` success, reopen failure, close failure, sync-versus-close failure precedence, and external SST sync behavior.

Differential Revision: D104918547
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 15, 2026

Codex Code Review - OBSOLETE

Superseded by a newer AI review. Expand to see the original review.

🟡 Codex Code Review

Auto-triggered after CI passed — reviewing commit bdedb71


Codex review failed before producing findings.

WARNING: proceeding, even though we could not update PATH: Refusing to create helper binaries under temporary dir "/tmp" (codex_home: AbsolutePathBuf("/tmp/codex-home"))
error: the argument '--base <BRANCH>' cannot be used with '[PROMPT]'

Usage: codex exec review --commit <SHA> --base <BRANCH> --title <TITLE> --model <MODEL> --config <key=value> --dangerously-bypass-approvals-and-sandbox --output-last-message <FILE> [PROMPT]

For more information, try '--help'.

ℹ️ About this response

Generated by Codex CLI.
Review methodology: claude_md/code_review.md

Limitations:

  • Codex may miss context from files not in the diff
  • Large PRs may be truncated
  • Always apply human judgment to AI suggestions

Commands:

  • /codex-review [context] — Request a code review
  • /codex-query <question> — Ask about the PR or codebase

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 15, 2026

Claude Code Review - OBSOLETE

Superseded by a newer AI review. Expand to see the original review.

✅ Claude Code Review

Auto-triggered after CI passed — reviewing commit bdedb71


Summary

Clean, well-structured PR that adds FileSystem::SyncFile() and Env::SyncFile() public APIs, replacing the manual reopen+sync+close pattern in external SST ingestion. The implementation is correct, follows existing wrapper patterns, and includes solid unit test coverage for the default implementations.

High-severity findings (0):
No high-severity findings.

Full review (click to expand)

Findings

🔴 HIGH

No high-severity findings.

🟡 MEDIUM

M1. Behavioral change: Close() status now checked — env/file_system.cc:119
  • Issue: The old ingestion code relied on unique_ptr destructor for Close (ignoring close errors). The new FileSystem::SyncFile() default explicitly calls Close() and returns its error when sync succeeds but close fails. This could surface previously-hidden close errors in production.
  • Root cause: The default SyncFile implementation correctly prioritizes sync errors over close errors, but the explicit close-status check is new behavior for the ingestion call site.
  • Suggested fix: This is arguably an improvement (better error reporting). Document the behavioral change in the release note to call attention to it. The current release note only mentions the new API, not the changed ingestion behavior.
M2. TimedFileSystem and CountedFileSystem missing SyncFile overrides — utilities/env_timed.h, utilities/counted_fs.h
  • Issue: Neither TimedFileSystem nor CountedFileSystem overrides SyncFile(). Calls to SyncFile() will be delegated to the target filesystem (via FileSystemWrapper), so the SyncFile call itself won't appear in timing/counting metrics. The underlying ReopenWritableFile/Sync/Close operations WILL still be individually timed/counted.
  • Root cause: These wrappers explicitly enumerate which operations to instrument; new operations require explicit support.
  • Suggested fix: Consider adding SyncFile overrides in a follow-up. Not blocking since underlying operations are still tracked.
M3. Test coverage: fsync failure path not explicitly tested — env/env_test.cc
  • Issue: The tests cover sync failure (SyncFileTestResult::kSyncError) but the fsync-specific failure path (kFsyncError) is only defined in the enum, not exercised in a dedicated test. While the code paths are symmetric, explicit coverage would be valuable.
  • Suggested fix: Add a test case for fsync failure (similar to EnvSyncFileDefaultReturnsSyncErrorBeforeCloseError but with use_fsync=true and fsync_result = kFsyncError).
M4. Test coverage: No integration test for wrapper chain — env/env_test.cc
  • Issue: Tests validate the Env and FileSystem default implementations in isolation using custom test wrappers, but no test exercises SyncFile through the CompositeEnv bridge, LegacyFileSystemWrapper bridge, or EncryptedFileSystem chain.
  • Suggested fix: Consider adding at least one integration test that exercises SyncFile through a multi-layer wrapper stack.

🟢 LOW / NIT

L1. Release note could mention ingestion behavior change — unreleased_history/public_api_changes/sync_file_api.md
  • Issue: The release note describes the new API but doesn't mention that external SST ingestion now uses it, which changes close-error handling behavior.
  • Suggested fix: Add a sentence noting the ingestion change.
L2. API documentation could be more detailed — include/rocksdb/file_system.h:628
  • Issue: The doc comment says "Syncs file data and metadata for fname. The default implementation reopens the file as writable, syncs or fsyncs it, then closes it." This is adequate but could mention: (1) thread safety, (2) that sync errors take precedence over close errors, (3) guidance for FileSystem implementers on when to override.
  • Suggested fix: Expand the doc comment in a follow-up.
L3. bool use_fsync parameter vs enum — include/rocksdb/file_system.h:632
  • Issue: Using bool use_fsync is consistent with DBOptions::use_fsync and SyncIngestedFile template, but less extensible than an enum for future sync variants.
  • Suggested fix: No change needed — consistency with existing patterns is the right choice here.

Cross-Component Analysis

Context Status Notes
PosixFileSystem Safe Overrides ReopenWritableFile, default SyncFile works correctly
EncryptedFileSystem Safe Inherits FileSystemWrapper::SyncFile which delegates to target; target's default calls virtual ReopenWritableFile which EncryptedFileSystem overrides
FaultInjectionTestFS Safe Calls FileSystem::SyncFile base which uses virtual dispatch to this->ReopenWritableFile(), exercising fault injection hooks
ReadOnlyFileSystem Safe Overrides SyncFile to return FailReadOnly()
RemapFileSystem Safe Overrides SyncFile to remap path then delegate
MockFileSystem Safe Overrides SyncFile to return OK
TimedFileSystem Minor gap Delegates via FileSystemWrapper; SyncFile not individually timed
CountedFileSystem Minor gap Delegates via FileSystemWrapper; SyncFile not individually counted
CompositeEnv bridge Safe Correctly bridges Env::SyncFile to FileSystem::SyncFile

Positive Observations

  • Addresses longstanding TODO (issue Avoid using ReopenWritableFile for sync file to disk #13741) cleanly
  • Error precedence logic (sync error > close error) is correct and well-tested
  • Fault injection design is clever — calling base class default ensures wrapper hooks are exercised via virtual dispatch
  • Comprehensive unit tests covering success, reopen failure, close failure, and error precedence for both Env and FileSystem layers
  • Proper handling of NotSupported preserved at the ingestion call site
  • All wrapper layers properly wired (9 different FS/Env implementations updated)

ℹ️ About this response

Generated by Claude Code.
Review methodology: claude_md/code_review.md

Limitations:

  • Claude may miss context from files not in the diff
  • Large PRs may be truncated
  • Always apply human judgment to AI suggestions

Commands:

  • /claude-review [context] — Request a code review
  • /claude-query <question> — Ask about the PR or codebase

xingbowang added a commit to xingbowang/rocksdb that referenced this pull request May 15, 2026
Summary:
Adds public Env::SyncFile and FileSystem::SyncFile APIs and switches external SST ingestion to use the FileSystem-level sync hook.


This diff:

- Adds public `Env::SyncFile` and `FileSystem::SyncFile` APIs for syncing or fsyncing a file by name without requiring callers to reopen it directly.
- Implements the default `Env` and `FileSystem` behavior by reopening the file as writable, calling `Sync` or `Fsync`, closing the handle, and returning the sync/fsync error ahead of a close error when both fail.
- Wires `SyncFile` through the Env/FileSystem bridge and wrapper layers, including `EnvWrapper`, `FileSystemWrapper`, `CompositeEnvWrapper`, `RemapFileSystem`, read-only file systems, mock file systems, and fault-injection wrappers.
- Keeps fault-injection `SyncFile` overrides on the base default implementation so the operation still exercises the wrapper's `ReopenWritableFile`, wrapped-file `Sync`/`Fsync`, and `Close` hooks instead of bypassing them through target forwarding.
- Updates external SST ingestion to call `FileSystem::SyncFile` instead of manually reopening an ingested file as writable, while preserving the `NotSupported` skip behavior and failure logging.
- Adds release-note coverage for the new public API and unit coverage for default `Env`/`FileSystem` success, reopen failure, close failure, sync-versus-close failure precedence, and external SST sync behavior.

Differential Revision: D104918547
@xingbowang xingbowang force-pushed the export-D104918547 branch from bdedb71 to dead360 Compare May 15, 2026 22:37
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 15, 2026

Codex Code Review - OBSOLETE

Superseded by a newer AI review. Expand to see the original review.

🟡 Codex Code Review

Auto-triggered after CI passed — reviewing commit dead360


Codex review failed before producing findings.

WARNING: proceeding, even though we could not update PATH: Refusing to create helper binaries under temporary dir "/tmp" (codex_home: AbsolutePathBuf("/tmp/codex-home"))
error: the argument '--base <BRANCH>' cannot be used with '[PROMPT]'

Usage: codex exec review --commit <SHA> --base <BRANCH> --title <TITLE> --model <MODEL> --config <key=value> --dangerously-bypass-approvals-and-sandbox --output-last-message <FILE> [PROMPT]

For more information, try '--help'.

ℹ️ About this response

Generated by Codex CLI.
Review methodology: claude_md/code_review.md

Limitations:

  • Codex may miss context from files not in the diff
  • Large PRs may be truncated
  • Always apply human judgment to AI suggestions

Commands:

  • /codex-review [context] — Request a code review
  • /codex-query <question> — Ask about the PR or codebase

@meta-codesync meta-codesync Bot changed the title Add FileSystem SyncFile API (#14739) Add FileSystem SyncFile API May 15, 2026
@xingbowang xingbowang force-pushed the export-D104918547 branch from dead360 to 99e3a62 Compare May 15, 2026 23:27
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 15, 2026

Claude Code Review - OBSOLETE

Superseded by a newer AI review. Expand to see the original review.

✅ Claude Code Review

Auto-triggered after CI passed — reviewing commit dead360


Summary

PR #14739 adds FileSystem::SyncFile() and Env::SyncFile() public APIs for syncing a file by name, with a default implementation that reopens, syncs/fsyncs, and closes. External SST ingestion is migrated to use this API. The core implementation is correct — error handling, file handle lifecycle, and fault injection dispatch are all sound. However, several wrapper classes are missing SyncFile overrides, breaking instrumentation, and the API signature is inconsistent with peer metadata operations.

High-severity findings (0):

No high-severity findings.

Full review (click to expand)

Findings

🟡 MEDIUM

M1. Missing CountedFileSystem::SyncFile override — utilities/counted_fs.h
  • Issue: CountedFileSystem overrides ReopenWritableFile to count reopen operations, but does not override SyncFile. The inherited FileSystemWrapper::SyncFile forwards directly to target_->SyncFile(), which means CountedFileSystem::ReopenWritableFile is never called during a SyncFile operation. Sync operations during ingestion will not be counted.
  • Root cause: FileSystemWrapper::SyncFile forwards to the target, bypassing the wrapper's own ReopenWritableFile instrumentation. Unlike FaultInjectionTestFS (which deliberately calls the base class default to exercise its hooks), CountedFileSystem inherits the simple forwarding behavior.
  • Suggested fix: Add a SyncFile override to CountedFileSystem that increments a counter (e.g., counters_.syncs++) and then forwards to target()->SyncFile(...). Alternatively, call FileSystem::SyncFile(...) (base class default) to exercise this->ReopenWritableFile(), but the simple counting approach is more consistent with how DeleteFile and RenameFile are handled.
M2. Missing FileSystemTracingWrapper::SyncFile override — env/file_system_tracer.h
  • Issue: FileSystemTracingWrapper overrides ReopenWritableFile (with tracing), DeleteFile, RenameFile, and most other FS operations to emit trace events. It does not override SyncFile, so sync operations will not be traced.
  • Root cause: Same forwarding pattern as M1 — the inherited FileSystemWrapper::SyncFile bypasses the tracing wrapper.
  • Suggested fix: Add a SyncFile override that emits a trace event (following the pattern of other traced operations like DeleteFile at file_system_tracer.cc:129).
M3. Missing TimedFileSystem::SyncFile override — utilities/env_timed.h
  • Issue: TimedFileSystem instruments file system operations with timing. Without a SyncFile override, sync latency during ingestion won't be measured.
  • Root cause: Same forwarding bypass pattern.
  • Suggested fix: Add a SyncFile override following the existing timing pattern used for other operations.
M4. FileOptions parameter inconsistency in FileSystem::SyncFileinclude/rocksdb/file_system.h:628
  • Issue: The new FileSystem::SyncFile takes (fname, FileOptions, IOOptions, use_fsync, dbg). Other metadata-like operations (DeleteFile, RenameFile, LinkFile) take only (fname, IOOptions, dbg). File-opening operations (NewWritableFile, ReopenWritableFile) take FileOptions. Since SyncFile is semantically a metadata operation (it doesn't create or write data), the FileOptions parameter is inconsistent with peer APIs.
  • Root cause: The default implementation needs FileOptions for ReopenWritableFile, but custom implementations that sync by path (e.g., remote/distributed FS) don't need it.
  • Suggested fix: Consider removing FileOptions and having the default implementation construct one internally (as FileSystem::NewLogger does at file_system.cc:114). Alternatively, document why FileOptions is needed. This is a public API, so getting the signature right before release is important.

🟢 LOW / NIT

L1. bool use_fsync limits future extensibility — include/rocksdb/file_system.h:633
  • Issue: A boolean parameter for sync mode limits future extensibility. If fdatasync or other sync modes are needed, the API would need to change.
  • Suggested fix: Consider using an enum (enum class SyncMode { kSync, kFsync }) for clarity and extensibility. This also improves readability at callsites vs /*use_fsync=*/true.
L2. Partial migration: SyncIngestedFile still used at line 1483 — db/external_sst_file_ingestion_job.cc:1483
  • Issue: The PR only migrates the link/move sync path. The global seqno write path still uses SyncIngestedFile with a RandomRWFile.
  • Root cause: The second callsite uses RandomRWFile (not WritableFile), so the SyncFile API (which reopens as writable) isn't directly applicable. This is a different pattern.
  • Suggested fix: No action needed for this PR. The remaining usage is intentionally different.
L3. Minimal API documentation — include/rocksdb/env.h:388, include/rocksdb/file_system.h:628
  • Issue: The doc comments describe what the default implementation does but don't document: thread safety guarantees, behavior when the file doesn't exist, or the semantic difference between sync and fsync in this context.
  • Suggested fix: Expand doc comments to cover these cases.
L4. EncryptedFileSystem missing SyncFile override — include/rocksdb/env_encryption.h
  • Issue: EncryptedFileSystem does not override SyncFile. The inherited FileSystemWrapper::SyncFile forwards to the target, calling the target's (non-encrypted) ReopenWritableFile.
  • Root cause: This is actually correct behaviorSyncFile only flushes existing data to disk without reading or writing new content. The underlying raw file's sync operation is semantically correct regardless of encryption layer. No encryption context is needed for sync.
  • Suggested fix: None needed functionally. Consider adding a comment documenting this intent.

Cross-Component Analysis

Context Does code execute? Assumptions hold? Action needed?
ReadOnly DB YES — returns FailReadOnly() YES None — correctly blocked
EncryptedFileSystem YES — forwards to target YES — sync doesn't need encryption None
Remote/distributed FS YES — may return NotSupported YES — handled in ingestion None
FaultInjectionTestFS YES — calls base class default YES — virtual dispatch exercises hooks None
WritePreparedTxnDB NO — SyncFile is only in ingestion path N/A None
User-defined timestamps NO — SyncFile is only in ingestion path N/A None
Concurrent ingestion POSSIBLE — each job has unique file paths YES — no path conflicts None

Positive Observations

  • Correct fault injection design: FaultInjectionTestFS::SyncFile calls FileSystem::SyncFile (base class) instead of forwarding to target. This ensures virtual dispatch routes ReopenWritableFile through the fault injection wrapper, exercising all hooks. This is a thoughtful and deliberate design choice, well-documented in the code comment.
  • Proper error precedence: The sync-before-close error handling correctly prioritizes sync failures over close failures, matching the existing RocksDB convention.
  • Clean file handle lifecycle: Explicit Close() followed by unique_ptr destruction is safe — PosixWritableFile::~PosixWritableFile() checks fd_ >= 0 and the base destructors are no-ops.
  • Good test coverage of the default implementation: All four error scenarios (success, reopen failure, close failure, sync-vs-close precedence) are tested for both Env and FileSystem layers.
  • Addresses long-standing TODO: This resolves issue Avoid using ReopenWritableFile for sync file to disk #13741 and the TODO comment that has been in the codebase since the original ingestion implementation.

ℹ️ About this response

Generated by Claude Code.
Review methodology: claude_md/code_review.md

Limitations:

  • Claude may miss context from files not in the diff
  • Large PRs may be truncated
  • Always apply human judgment to AI suggestions

Commands:

  • /claude-review [context] — Request a code review
  • /claude-query <question> — Ask about the PR or codebase

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 16, 2026

Codex Code Review - OBSOLETE

Superseded by a newer AI review. Expand to see the original review.

🟡 Codex Code Review

Auto-triggered after CI passed — reviewing commit 99e3a62


Codex review failed before producing findings.

WARNING: proceeding, even though we could not update PATH: Refusing to create helper binaries under temporary dir "/tmp" (codex_home: AbsolutePathBuf("/tmp/codex-home"))
error: the argument '--base <BRANCH>' cannot be used with '[PROMPT]'

Usage: codex exec review --commit <SHA> --base <BRANCH> --title <TITLE> --model <MODEL> --config <key=value> --dangerously-bypass-approvals-and-sandbox --output-last-message <FILE> [PROMPT]

For more information, try '--help'.

ℹ️ About this response

Generated by Codex CLI.
Review methodology: claude_md/code_review.md

Limitations:

  • Codex may miss context from files not in the diff
  • Large PRs may be truncated
  • Always apply human judgment to AI suggestions

Commands:

  • /codex-review [context] — Request a code review
  • /codex-query <question> — Ask about the PR or codebase

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 16, 2026

Claude Code Review - OBSOLETE

Superseded by a newer AI review. Expand to see the original review.

✅ Claude Code Review

Auto-triggered after CI passed — reviewing commit 99e3a62


Summary

Well-designed API that addresses a real need (issue #13741). The implementation is correct, follows existing conventions, and has good test coverage. No high-severity findings.

No high-severity findings.

Full review (click to expand)

Findings

🔴 HIGH

None.

🟡 MEDIUM

M1. Close Error Propagation — Intentional Behavioral Change — env/file_system.cc:118
  • Issue: The old code in ExternalSstFileIngestionJob::Prepare relied on unique_ptr destructor to close the file after sync, silently swallowing close errors. The new FileSystem::SyncFile default implementation explicitly calls Close() and returns the close error when sync succeeds. This is a semantic change: previously silent close failures will now surface as errors.
  • Root cause: The old pattern used RAII cleanup (destructor) which cannot propagate errors. The new pattern does explicit close with error capture.
  • Suggested fix: This is actually an improvement — surfacing close errors is better than silently ignoring them. However, this behavioral change should be documented in the PR description and release note, as it could cause previously-passing ingestion operations to fail if the underlying filesystem returns close errors.
M2. CountedFileSystem Sync/Fsync Counters Bypassed — utilities/counted_fs.h
  • Issue: CountedFileSystem overrides ReopenWritableFile and tracks syncs/fsyncs counters via per-file wrappers (CountedWritableFile). Since CountedFileSystem inherits FileSystemWrapper::SyncFile (which delegates to target_->SyncFile), the target's SyncFile default will call the target's ReopenWritableFile directly — bypassing CountedFileSystem's wrapping. Sync/fsync operations performed via the new SyncFile API won't increment the counters.
  • Root cause: FileSystemWrapper::SyncFile delegates to target_->SyncFile, and the target's default uses target->ReopenWritableFile — skipping the CountedFileSystem wrapper layer.
  • Suggested fix: Add CountedFileSystem::SyncFile override that either (a) calls FileSystem::SyncFile (base default) to use its own ReopenWritableFile and counted file wrappers, or (b) manually increments sync/fsync counters before delegating. This mirrors how FaultInjectionTestFS handles it.
M3. Missing C API Binding — include/rocksdb/c.h
  • Issue: New public API Env::SyncFile and FileSystem::SyncFile are added to C++ headers but no corresponding C API functions are added. While not all C++ APIs have C bindings, this could be a gap for C API users.
  • Suggested fix: Consider adding rocksdb_env_sync_file to the C API in a follow-up, or document that this is intentionally C++-only.

🟢 LOW / NIT

L1. Sync Point Naming Change May Affect External Users — db/external_sst_file_ingestion_job.cc:172
  • Issue: Test sync point "ExternalSstFileIngestionJob::Prepare:Reopen" is renamed to "ExternalSstFileIngestionJob::CheckSyncReturnCode". External users or downstream forks that hook into these sync points for testing will need to update.
  • Suggested fix: This is expected for API evolution. The existing tests are correctly updated. No action needed beyond awareness.
L2. FileSystemTracingWrapper Not Updated — env/file_system_tracer.h
  • Issue: FileSystemTracingWrapper overrides ReopenWritableFile for tracing but doesn't override SyncFile. Sync operations via the new API won't be traced.
  • Suggested fix: This is consistent with FileSystemTracingWrapper's behavior for other path-level operations (e.g., LinkFile is also not traced). The tracing happens at the file-operation level via wrapped file objects, not at the filesystem-operation level. Consider adding tracing in a follow-up if sync tracing is desired.
L3. use_fsync Boolean Parameter — include/rocksdb/file_system.h:631
  • Issue: Using a bool use_fsync parameter instead of separate SyncFile/FsyncFile methods. Some API design guidelines prefer avoiding boolean parameters that change behavior.
  • Suggested fix: The current design matches how use_fsync is used throughout RocksDB (e.g., db_options_.use_fsync). The bool parameter is pragmatic here — having separate methods would double the API surface and wrapper implementations for minimal benefit. No change needed.
L4. Release Note Could Mention Behavioral Change — unreleased_history/public_api_changes/sync_file_api.md
  • Issue: Consider noting the behavioral change in external SST file ingestion (close errors now surfaced) in the release note.
  • Suggested fix: Optional enhancement.

Cross-Component Analysis

Context SyncFile path Correct?
PosixFileSystem Inherits base FileSystem::SyncFile → calls own ReopenWritableFile
WinFileSystem Inherits base FileSystem::SyncFile → calls own ReopenWritableFile
EncryptedFileSystem FileSystemWrapper::SyncFile → delegates to target ✅ (sync doesn't need encryption)
FaultInjectionTestFS Calls FileSystem::SyncFile base → virtual dispatch to own ReopenWritableFile
FaultInjectionTestEnv Calls Env::SyncFile base → virtual dispatch to own ReopenWritableFile
ReadOnlyFileSystem Returns FailReadOnly() ✅ (added in PR)
RemapFileSystem Remaps path via EncodePathWithNewBasename → delegates ✅ (added in PR)
MockFileSystem Returns IOStatus::OK() (no-op)
CompositeEnv Bridges Env::SyncFileFileSystem::SyncFile ✅ (added in PR)
FileSystemWrapper Delegates to target_->SyncFile ✅ (added in PR)
EnvWrapper Delegates to target_.env->SyncFile ✅ (added in PR)
LegacyFileSystemWrapper Bridges FileSystem::SyncFileEnv::SyncFile ✅ (added in PR)
FileSystemTracingWrapper Inherits FileSystemWrapper delegation (not traced) ⚠️ Consistent with LinkFile
CountedFileSystem Inherits FileSystemWrapper delegation (counters bypassed) ⚠️ M2 above

Assumption Stress Test

Claim: "SyncFile default is equivalent to old manual pattern"

  • Precondition 1: ReopenWritableFile behavior unchanged → ✅ Same call
  • Precondition 2: Sync/Fsync behavior unchanged → ✅ Same call with same IOOptions
  • Precondition 3: Close behavior equivalent → ⚠️ Old: implicit (destructor, errors lost). New: explicit (errors captured). See M1.
  • Precondition 4: Error precedence correct → ✅ Sync error returned over close error

Claim: "Fault injection calls base class to exercise wrapper hooks"

  • Verified via virtual dispatch: FaultInjectionTestFS::SyncFileFileSystem::SyncFilethis->ReopenWritableFile() → virtual dispatch to FaultInjectionTestFS::ReopenWritableFile. ✅ Correct.

Claim: "EncryptedFileSystem delegation is safe"

  • EncryptedFileSystem inherits FileSystemWrapper::SyncFiletarget_->SyncFile. The target (e.g., PosixFS) reopens the raw file and fsyncs. Since fsync only flushes OS buffers and doesn't read/write data content, encryption is irrelevant. ✅ Correct.

Double-close safety:

  • PosixWritableFile::Close() sets fd_ = -1. Destructor checks if (fd_ >= 0). No double-close. ✅ Safe.

Positive Observations

  • Clean API design: Follows the established FileSystem pattern perfectly. Default implementation provided, can be overridden by custom filesystems.
  • Fault injection strategy: Calling base class default instead of forwarding is a clever approach that preserves hook exercising. Well-commented.
  • Comprehensive test coverage: Unit tests cover sync/fsync selection, reopen failure, close failure, and sync-vs-close error precedence — both for Env and FileSystem layers.
  • Error precedence: The sync-before-close error priority is a good design choice — the sync error is more actionable than the close error.
  • Code simplification: The ingestion job code is significantly simpler with the new API — reduced from ~15 lines of manual logic to a single API call.
  • Release note present: Public API change is documented.
  • Resolves technical debt: Addresses the TODO comment and issue Avoid using ReopenWritableFile for sync file to disk #13741 that explicitly requested this API.

ℹ️ About this response

Generated by Claude Code.
Review methodology: claude_md/code_review.md

Limitations:

  • Claude may miss context from files not in the diff
  • Large PRs may be truncated
  • Always apply human judgment to AI suggestions

Commands:

  • /claude-review [context] — Request a code review
  • /claude-query <question> — Ask about the PR or codebase

Summary:

Adds public path-level file sync APIs to RocksDB so callers can ask `Env` or `FileSystem` to sync a named file without hand-rolled reopen logic.

This diff:

- Adds public `Env::SyncFile` and `FileSystem::SyncFile` APIs for syncing or fsyncing a file by name without requiring callers to reopen it directly.
- Documents the `SyncFile` contract: filesystems may override it as a no-op when flush/close already provides durability, and RocksDB callers should use it instead of hand-rolled `ReopenWritableFile()+Sync()/Fsync()` so filesystems can reject post-close data-write reopen paths.
- Implements the default `Env` and `FileSystem` behavior by reopening the file as writable, calling `Sync` or `Fsync`, closing the handle, returning the sync/fsync error ahead of a close error when both fail, and explicitly consuming the close status on the sync-failure path.
- Wires `SyncFile` through the Env/FileSystem bridge and wrapper layers, including `EnvWrapper`, `FileSystemWrapper`, `CompositeEnvWrapper`, `EncryptedFileSystem`, `RemapFileSystem`, read-only file systems, mock file systems, and fault-injection wrappers.
- Keeps fault-injection `SyncFile` overrides on the base default implementation so the operation still exercises the wrapper's `ReopenWritableFile`, wrapped-file `Sync`/`Fsync`, and `Close` hooks instead of bypassing them through target forwarding.
- Updates external SST ingestion to call `FileSystem::SyncFile` instead of manually reopening an ingested file as writable, while preserving the `NotSupported` skip behavior and failure logging.
- Adds release-note coverage for the new public API and unit coverage for default `Env`/`FileSystem` success, reopen failure, close failure, sync-versus-close failure precedence, and external SST sync behavior.

There was an earlier version of this API which got reverted (PR facebook#13987) due to internal change broken. Re-apply the change. The internal issue will be resolved in next release.

Reviewed By: pdillinger

Differential Revision: D104918547
@meta-codesync meta-codesync Bot changed the title Add FileSystem SyncFile API Add FileSystem SyncFile API (#14739) May 23, 2026
@xingbowang xingbowang force-pushed the export-D104918547 branch from 99e3a62 to 4c8887c Compare May 23, 2026 21:14
@github-actions
Copy link
Copy Markdown

🟡 Codex Code Review

Auto-triggered after CI passed — reviewing commit 4c8887c


Codex review failed before producing findings.

WARNING: proceeding, even though we could not update PATH: Refusing to create helper binaries under temporary dir "/tmp" (codex_home: AbsolutePathBuf("/tmp/codex-home"))
error: the argument '--base <BRANCH>' cannot be used with '[PROMPT]'

Usage: codex exec review --commit <SHA> --base <BRANCH> --title <TITLE> --model <MODEL> --config <key=value> --dangerously-bypass-approvals-and-sandbox --output-last-message <FILE> [PROMPT]

For more information, try '--help'.

ℹ️ About this response

Generated by Codex CLI.
Review methodology: claude_md/code_review.md

Limitations:

  • Codex may miss context from files not in the diff
  • Large PRs may be truncated
  • Always apply human judgment to AI suggestions

Commands:

  • /codex-review [context] — Request a code review
  • /codex-query <question> — Ask about the PR or codebase

@github-actions
Copy link
Copy Markdown

✅ Claude Code Review

Auto-triggered after CI passed — reviewing commit 4c8887c


Summary

Well-designed API addition that follows established RocksDB patterns. The FileSystem::SyncFile / Env::SyncFile API cleanly replaces the manual reopen+sync+close pattern, is properly wired through all critical wrapper layers, and includes good unit test coverage. No correctness or data-integrity issues found.

High-severity findings (0):

No high-severity findings.

Full review (click to expand)

Findings

🟡 MEDIUM

M1. Missing SyncFile in observability wrappers — utilities/counted_fs.h, utilities/env_timed.h, env/file_system_tracer.h
  • Issue: Three FileSystem wrappers that instrument other path-level operations (LinkFile, RenameFile, DeleteFile) do not override SyncFile. TimedFileSystem overrides LinkFile (line 81) with timing; CountedFileSystem overrides DeleteFile/RenameFile with counters. SyncFile calls will silently bypass these instrumentation layers.
  • Root cause: These wrappers inherit the working FileSystemWrapper::SyncFile forwarding, so they function correctly — they just lose observability for this new operation.
  • Suggested fix: Add SyncFile overrides to these three wrappers following the same pattern as their existing path-level operation overrides. This could be done in a follow-up PR.
M2. API signature includes FileOptions unlike sibling path-level methods — include/rocksdb/file_system.h
  • Issue: FileSystem::SyncFile(fname, file_opts, io_opts, use_fsync, dbg) takes FileOptions while peer methods like LinkFile(src, target, io_options, dbg) and RenameFile(src, target, io_options, dbg) do not. This creates a minor API inconsistency.
  • Root cause: The default implementation calls ReopenWritableFile which needs FileOptions. Custom overrides that implement sync natively wouldn't need FileOptions.
  • Suggested fix: Reasonable design tradeoff. Consider documenting why FileOptions is included in the API comment.
M3. Behavioral change: explicit close error checking in ingestion path — db/external_sst_file_ingestion_job.cc:168
  • Issue: The old ingestion code let the unique_ptr<FSWritableFile> destructor call Close(), ignoring errors via PermitUncheckedError(). The new FileSystem::SyncFile explicitly calls Close() and propagates close errors when sync succeeds. This is stricter error handling.
  • Root cause: Intentional improvement — the new code checks close errors that were previously silently dropped.
  • Suggested fix: No code change needed. Worth noting in release documentation.

🟢 LOW / NIT

L1. use_fsync as standalone bool rather than part of options struct
  • Issue: Separate bool use_fsync parameter rather than via structured options.
  • Suggested fix: Acceptable as-is — conceptually a per-call behavioral choice.
L2. Test helper classes could use parameterized tests — env/env_test.cc
  • Issue: Near-identical SyncFileTestEnv and SyncFileTestFileSystem hierarchies with parallel test cases.
  • Suggested fix: Consider parameterized tests in a follow-up.
L3. No Fsync-error-specific test case — env/env_test.cc
  • Issue: SyncFileTestResult::kFsyncError enum value is never used in a dedicated test. Fsync success is tested but Fsync error propagation is not explicitly verified.
  • Suggested fix: Add a test with fsync_result = kFsyncError and use_fsync=true.

Cross-Component Analysis

Context Implementation Correct?
ReadOnlyFileSystem Returns FailReadOnly() Yes
EncryptedFileSystem Delegates to FileSystemWrapper (target forwarding) Yes — sync doesn't touch content
FaultInjectionTestFS Calls FileSystem::SyncFile base default Yes — virtual dispatch exercises wrapped ReopenWritableFile
RemapFileSystem Remaps path then forwards Yes
MockFileSystem Returns IOStatus::OK() Yes
CompositeEnv Bridges Env→FileSystem Yes
CountedFS/TimedFS/TracingFS Inherits forwarding Works but not instrumented

Positive Observations

  • Clean API design following the established dual-layer (Env + FileSystem) pattern
  • Correct error handling with sync-error-over-close-error precedence
  • Smart fault injection strategy using base class virtual dispatch
  • Good unit test coverage (8 new tests covering success, failure, and error precedence)
  • Addresses the longstanding TODO in external_sst_file_ingestion_job.cc:169-175

ℹ️ About this response

Generated by Claude Code.
Review methodology: claude_md/code_review.md

Limitations:

  • Claude may miss context from files not in the diff
  • Large PRs may be truncated
  • Always apply human judgment to AI suggestions

Commands:

  • /claude-review [context] — Request a code review
  • /claude-query <question> — Ask about the PR or codebase

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants