Files
rocksdb/table/prepared_file_info.h
T
Josh Kang 36d4b2e075 [rocksdb] Pass file metadata to IngestExternalFile to improve ingestion latency
External file ingestion (`DB::IngestExternalFile[s]`) re-opens every SST file and scans it -- footer, properties, index, filter, and the first/last data blocks -- to recompute the boundary keys, sequence-number bounds, and table properties before committing. For cold, I/O-bound files this scan dominates ingest latency, even when the file is moved/linked rather than copied.

This change lets a caller skip that work when it already has the file's metadata. `SstFileWriter::Finish()` now populates the full `ExternalSstFileInfo` (its `smallest_internal_key`, `largest_internal_key`, and `table_properties`), and a new `IngestExternalFileArg::file_infos` field carries one `ExternalSstFileInfo` per file into `IngestExternalFiles()`. When set, ingestion reuses that metadata instead of re-opening and scanning each file. The file is still copied/linked, and the checksum is still verified when `verify_checksums_before_ingest` is set (the fast path opens the file only for that). Range tombstones and user-defined-timestamp files (including the "UDT in Memtables only" format, whose boundary keys carry no timestamp) are handled.

Internally, ingestion-job metadata acquisition was split into `GetIngestedFileInfoFromFileInfo` (reuse caller metadata) and `GetIngestedFileInfoFromFile` (open + scan).

## Benchmark results

`db_bench` `ingestexternalfile` benchmark, release build, on SSD (btrfs). Files are linked (`move_files`) so the measurement isolates the ingest path rather than file-copy throughput.

| Config | Ingest latency / call (P50) |
| --- | --- |
| Baseline (open + scan each file) | 28,111 us |
| `--ingest_external_file_use_file_info=true` | 18,444 us |

~34% lower median ingest latency by skipping the per-file open + scan (a warm/page-cache number; the gain is larger for cold files).

Command:

```
make clean && DEBUG_LEVEL=0 make -j$(nproc) db_bench
B=/data/$USER/ingest_bench
for fi in false true; do
  rm -rf $B
  ./db_bench --benchmarks=ingestexternalfile --num=1000000 --value_size=100 \
    --compression_type=none --ingest_external_file_batch_size=10 \
    --ingest_external_file_num_batches=10 --disable_auto_compactions=1 \
    --statistics --db=$B --ingest_external_file_use_file_info=$fi 2>&1 \
    | grep rocksdb.ingest.external.file.micros
done
```

Differential Revision: [D107721261](https://our.internmc.facebook.com/intern/diff/D107721261/)

**NOTE FOR REVIEWERS**: This PR has internal Meta-specific changes or comments, please review them on [Phabricator](https://our.internmc.facebook.com/intern/diff/D107721261/)!
ghstack-source-id: 390599675
Pull-Request: https://github.com/facebook/rocksdb/pull/14835
2026-06-08 16:45:56 -07:00

34 lines
1.2 KiB
C++

// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
// This source code is licensed under both the GPLv2 (found in the
// COPYING file in the root directory) and Apache 2.0 License
// (found in the LICENSE.Apache file in the root directory).
#pragma once
#include <cstdint>
#include <string>
#include "rocksdb/table_properties.h"
namespace ROCKSDB_NAMESPACE {
// Definition of the type that is opaque to public API users (declared only as a
// forward declaration in rocksdb/options.h and rocksdb/sst_file_writer.h).
// Produced by SstFileWriter::Finish and consumed by ExternalSstFileIngestionJob
// so ingestion can skip re-opening and scanning the file to recompute its
// metadata.
struct PreparedFileInfo {
uint64_t file_size = 0;
// Encoded internal keys (user key + 8-byte footer), with the user-defined
// timestamp stripped when timestamps are not persisted.
std::string smallest_internal_key;
std::string largest_internal_key;
// Range-deletion boundary user keys (timestamp-stripped when applicable);
// empty when the file has no range deletions.
std::string smallest_range_del_key;
std::string largest_range_del_key;
TableProperties table_properties;
};
} // namespace ROCKSDB_NAMESPACE