Compare commits

...

1 Commits

Author SHA1 Message Date
Josh Kang f813e17706 Add File Ingestion Histograms
Adds a public `Statistics` histogram, `INGEST_EXTERNAL_FILE_TIME` (`"rocksdb.ingest.external.file.micros"`), recording the end-to-end latency in microseconds of each `IngestExternalFile(s)` call. Ingestion timing was previously only available through per-thread `perf_context` counters, which require setting a `PerfLevel` and are not aggregated, so there was no process-wide latency distribution (p50/p99/max) for dashboards.

It is recorded with an RAII `StopWatch` at the top of `DBImpl::IngestExternalFiles` -- one sample per call (not per column family), covering all return paths. It is null-safe and self-gating on the stats level, so there is no cost when statistics are off, and ingestion is not a hot path. Java bindings are kept in sync per the `statistics.h` requirement; the C API needs no change.

New `ExternalSSTFileTest.IngestionTimingHistogram` ingests two external files with statistics enabled and asserts the histogram count goes 1 -> 2 with `max > 0`, confirming per-call (not per-CF) recording. The existing `StatisticsTest.SanityHistograms` covers enum/name-map sync.

- `make -j192 statistics_test external_sst_file_test` -- build OK
- `./statistics_test --gtest_filter='*Sanity*'` -- 2 PASSED
- `./external_sst_file_test --gtest_filter='*IngestionTimingHistogram*'` -- PASSED, and 5/5 under `COERCE_CONTEXT_SWITCH=1 --gtest_repeat=5`
- `make format-auto` / `make check-sources` -- clean

ghstack-source-id: f387d74fc5
Pull-Request: https://github.com/facebook/rocksdb/pull/14813
2026-06-03 11:39:48 -07:00
7 changed files with 50 additions and 0 deletions
+5
View File
@@ -6675,6 +6675,11 @@ Status DBImpl::IngestExternalFile(
Status DBImpl::IngestExternalFiles(
const std::vector<IngestExternalFileArg>& args) {
PERF_TIMER_GUARD(file_ingestion_nanos);
// Aggregate end-to-end ingestion latency, one sample per call (not per CF).
// StopWatch records on every return path; it is null-safe and self-gating on
// stats level, so there is no cost when statistics are disabled.
StopWatch ingest_external_file_sw(immutable_db_options_.clock, stats_,
INGEST_EXTERNAL_FILE_TIME);
// TODO: plumb Env::IOActivity, Env::IOPriority
const WriteOptions write_options;
+28
View File
@@ -298,6 +298,34 @@ class ExternalSSTFileTest
int last_file_id_ = 0;
};
TEST_F(ExternalSSTFileTest, IngestionTimingHistogram) {
Options options = CurrentOptions();
options.statistics = CreateDBStatistics();
options.statistics->set_stats_level(StatsLevel::kAll); // enable timers
DestroyAndReopen(options);
ASSERT_OK(GenerateAndAddExternalFile(
options, {{"k1", "v1"}, {"k2", "v2"}, {"k3", "v3"}}, /*file_id=*/-1,
/*allow_global_seqno=*/true, /*write_global_seqno=*/false,
/*verify_checksums_before_ingest=*/true, /*ingest_behind=*/false,
/*sort_data=*/true));
HistogramData hd;
options.statistics->histogramData(INGEST_EXTERNAL_FILE_TIME, &hd);
ASSERT_EQ(1, hd.count);
ASSERT_GT(hd.max, 0.0);
// A second call adds exactly one more sample: timing is recorded per
// IngestExternalFile(s) call, not per column family.
ASSERT_OK(GenerateAndAddExternalFile(
options, {{"k4", "v4"}, {"k5", "v5"}}, /*file_id=*/-1,
/*allow_global_seqno=*/true, /*write_global_seqno=*/false,
/*verify_checksums_before_ingest=*/true, /*ingest_behind=*/false,
/*sort_data=*/true));
options.statistics->histogramData(INGEST_EXTERNAL_FILE_TIME, &hd);
ASSERT_EQ(2, hd.count);
}
TEST_F(ExternalSSTFileTest, ComparatorMismatch) {
Options options = CurrentOptions();
Options options_diff_ucmp = options;
+5
View File
@@ -765,6 +765,11 @@ enum Histograms : uint32_t {
// blocks for uniform key distribution tracking.
BLOCK_KEY_DISTRIBUTION_CV,
// Time (microseconds) spent in a single DB::IngestExternalFile(s) call,
// measured end to end. One sample is recorded per call, not per column
// family. Requires stats level > kExceptTimers.
INGEST_EXTERNAL_FILE_TIME,
HISTOGRAM_ENUM_MAX
};
+4
View File
@@ -5998,6 +5998,8 @@ class HistogramTypeJni {
return 0x41;
case ROCKSDB_NAMESPACE::Histograms::BLOCK_KEY_DISTRIBUTION_CV:
return 0x42;
case ROCKSDB_NAMESPACE::Histograms::INGEST_EXTERNAL_FILE_TIME:
return 0x43;
case ROCKSDB_NAMESPACE::Histograms::HISTOGRAM_ENUM_MAX:
// 0x3E is reserved for backwards compatibility on current minor
// version.
@@ -6151,6 +6153,8 @@ class HistogramTypeJni {
return ROCKSDB_NAMESPACE::Histograms::MULTISCAN_BLOCKS_PER_PREPARE;
case 0x42:
return ROCKSDB_NAMESPACE::Histograms::BLOCK_KEY_DISTRIBUTION_CV;
case 0x43:
return ROCKSDB_NAMESPACE::Histograms::INGEST_EXTERNAL_FILE_TIME;
case 0x3E:
// 0x3E is reserved for backwards compatibility on current minor
// version.
@@ -231,6 +231,12 @@ public enum HistogramType {
*/
BLOCK_KEY_DISTRIBUTION_CV((byte) 0x42),
/**
* Time (microseconds) spent in a single IngestExternalFile(s) call,
* measured end to end.
*/
INGEST_EXTERNAL_FILE_TIME((byte) 0x43),
// 0x3E is reserved for backwards compatibility on current minor version.
HISTOGRAM_ENUM_MAX((byte) 0x3E);
+1
View File
@@ -386,6 +386,7 @@ const std::vector<std::pair<Histograms, std::string>> HistogramsNameMap = {
{MULTISCAN_PREPARE_MICROS, "rocksdb.multiscan.prepare.micros"},
{MULTISCAN_BLOCKS_PER_PREPARE, "rocksdb.multiscan.blocks.per.prepare"},
{BLOCK_KEY_DISTRIBUTION_CV, "rocksdb.block.key.distribution.cv"},
{INGEST_EXTERNAL_FILE_TIME, "rocksdb.ingest.external.file.micros"},
};
std::shared_ptr<Statistics> CreateDBStatistics() {
@@ -0,0 +1 @@
Added a new statistics histogram `rocksdb.ingest.external.file.micros` reporting the end-to-end latency (in microseconds) of each `IngestExternalFile`/`IngestExternalFiles` call.