Coverage Report

Created: 2026-11-12 08:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/rocksdb/db/blob/blob_file_partition_manager.h
Line
Count
Source
1
//  Copyright (c) Meta Platforms, Inc. and affiliates.
2
//  This source code is licensed under both the GPLv2 (found in the
3
//  COPYING file in the root directory) and Apache 2.0 License
4
//  (found in the LICENSE.Apache file in the root directory).
5
#pragma once
6
7
#include <atomic>
8
#include <cstdint>
9
#include <deque>
10
#include <functional>
11
#include <memory>
12
#include <string>
13
#include <unordered_map>
14
#include <vector>
15
16
#include "db/blob/blob_file_addition.h"
17
#include "db/blob/blob_file_garbage.h"
18
#include "db/blob/blob_log_format.h"
19
#include "db/blob/blob_write_batch_transformer.h"
20
#include "port/port.h"
21
#include "rocksdb/compression_type.h"
22
#include "rocksdb/env.h"
23
#include "rocksdb/file_system.h"
24
#include "rocksdb/options.h"
25
#include "rocksdb/rocksdb_namespace.h"
26
#include "rocksdb/slice.h"
27
#include "rocksdb/status.h"
28
#include "util/hash_containers.h"
29
namespace ROCKSDB_NAMESPACE {
30
31
class BlobFileCache;
32
class BlobFileCompletionCallback;
33
class BlobIndex;
34
class BlobLogWriter;
35
class FilePrefetchBuffer;
36
class IOTracer;
37
class Logger;
38
class PinnableSlice;
39
class Version;
40
class WritableFileWriter;
41
struct FileOptions;
42
struct ReadOptions;
43
44
// Manages per-partition blob files for write-path blob direct write.
45
//
46
// The v1 design keeps each memtable switch as one FIFO generation batch.
47
// Direct-write files for that memtable are sealed and registered when the
48
// matching flush commits. The current implementation still serializes blob
49
// appends through one manager mutex; follow-up PRs can add independent
50
// per-partition locks to allow concurrent blob writes without changing the
51
// generation/flush contract. Follow-up PRs can also broaden compatibility as
52
// the feature matures.
53
class BlobFilePartitionManager {
54
 public:
55
  using FileNumberAllocator = std::function<uint64_t()>;
56
57
  // Creates the per-column-family manager for write-path blob files.
58
  BlobFilePartitionManager(
59
      uint32_t num_partitions,
60
      std::shared_ptr<BlobFilePartitionStrategy> strategy,
61
      FileNumberAllocator file_number_allocator, FileSystem* fs,
62
      SystemClock* clock, Statistics* statistics,
63
      const FileOptions& file_options, std::string db_path,
64
      std::string column_family_name, uint64_t blob_file_size, bool use_fsync,
65
      uint64_t blob_writer_max_buffer_size, BlobFileCache* blob_file_cache,
66
      BlobFileCompletionCallback* blob_callback,
67
      const std::vector<std::shared_ptr<EventListener>>& listeners,
68
      FileChecksumGenFactory* file_checksum_gen_factory,
69
      const FileTypeSet& checksum_handoff_file_types,
70
      const std::shared_ptr<IOTracer>& io_tracer, std::string db_id,
71
      std::string db_session_id, Logger* info_log);
72
73
  // Evicts cached readers for manager-owned files during shutdown.
74
  ~BlobFilePartitionManager();
75
76
  // Appends one blob record to a partition file and returns the resulting
77
  // BlobIndex location metadata.
78
  Status WriteBlob(const WriteOptions& write_options, uint32_t column_family_id,
79
                   CompressionType compression, const Slice& key,
80
                   const Slice& value, uint64_t* blob_file_number,
81
                   uint64_t* blob_offset, uint64_t* blob_size,
82
                   const BlobDirectWriteSettings& settings,
83
                   const uint32_t* partition_idx = nullptr);
84
85
  // Selects the partition to use for all blob-backed columns of one PutEntity
86
  // operation. The return value is already normalized to [0, num_partitions_).
87
  uint32_t SelectWideColumnPartition(uint32_t column_family_id,
88
                                     const Slice& key,
89
                                     const WideColumns& columns) const;
90
91
  // Move the current active partition files into the generation of the
92
  // memtable `memtable_id`, which just became immutable. Called from
93
  // SwitchMemtable() while DB mutex is held. No I/O is performed here.
94
  void RotateCurrentGeneration(uint64_t memtable_id);
95
96
  // Seal the queued generations of the memtables `memtable_ids` and return all
97
  // blob additions and initial-garbage updates that must be registered with
98
  // the flush of those memtables. Concurrent flushes pick different
99
  // memtables, so generations are matched by memtable ID, not by queue
100
  // position. Generations stay queued until CommitFlushedGenerations() removes
101
  // them. If `generation_blob_file_numbers` is non-null, it receives the
102
  // sealed blob file numbers for each memtable, in `memtable_ids` order.
103
  Status PrepareFlushAdditions(const WriteOptions& write_options,
104
                               const std::vector<uint64_t>& memtable_ids,
105
                               std::vector<BlobFileAddition>* additions,
106
                               std::vector<BlobFileGarbage>* garbages,
107
                               std::vector<std::vector<uint64_t>>*
108
                                   generation_blob_file_numbers = nullptr);
109
110
  // Remove the generations of the memtables older than
111
  // `earliest_unflushed_memtable_id`, whose flush results are installed, and
112
  // stop tracking their sealed files as manager-owned. A flush that finishes
113
  // before an older one is installed with it, or rolled back if it fails, so
114
  // the flush that prepared a generation cannot remove it on its own.
115
  void CommitFlushedGenerations(uint64_t earliest_unflushed_memtable_id);
116
117
  // In v1 flush-on-write mode each AddRecord flushes to the OS immediately, so
118
  // there is nothing extra to do for the non-sync write path.
119
0
  Status FlushAllOpenFiles(const WriteOptions& /*write_options*/) {
120
0
    return Status::OK();
121
0
  }
122
123
  // Sync the active open blob files before a sync WAL write.
124
  Status SyncAllOpenFiles(const WriteOptions& write_options);
125
126
  // Returns blob files still owned by the manager and therefore not yet safe
127
  // to consider obsolete.
128
  void GetActiveBlobFileNumbers(UnorderedSet<uint64_t>* file_numbers) const;
129
130
  // Returns sealed blob files that are still reachable through live memtables
131
  // or old SuperVersions and therefore must not be purged yet.
132
  void GetProtectedBlobFileNumbers(UnorderedSet<uint64_t>* file_numbers) const;
133
134
  // Returns true when the blob file is still owned by the write path or
135
  // protected by a live memtable / old SuperVersion.
136
  bool IsTrackedBlobFileNumber(uint64_t file_number) const;
137
138
0
  void SetBlobWriterMaxBufferSize(uint64_t blob_writer_max_buffer_size) {
139
0
    blob_writer_max_buffer_size_.store(blob_writer_max_buffer_size,
140
0
                                       std::memory_order_relaxed);
141
0
  }
142
143
  // Increments / decrements memtable-held protection on sealed blob files.
144
  void ProtectSealedBlobFileNumbers(const std::vector<uint64_t>& file_numbers);
145
  void UnprotectSealedBlobFileNumbers(
146
      const std::vector<uint64_t>& file_numbers);
147
148
  // Stops protecting file numbers that are now registered in MANIFEST.
149
  void RemoveFilePartitionMappings(const std::vector<uint64_t>& file_numbers);
150
151
  // Marks blob records from a failed transformed write as initial garbage for
152
  // the target blob file while keeping the physical bytes in place.
153
  // Returns Corruption if the manager can no longer match the blob file.
154
  Status MarkBlobWriteAsGarbage(uint64_t file_number, uint64_t blob_count,
155
                                uint64_t blob_bytes);
156
157
  // Resolves a direct-write BlobIndex by consulting manifest-visible state
158
  // first, then falling back to direct blob-file reads only when the target
159
  // blob file is still write-path-owned and therefore not yet tracked by
160
  // Version. Existing manifest-visible read results, including I/O failures,
161
  // are returned directly rather than masked by fallback logic.
162
  static Status ResolveBlobDirectWriteIndex(
163
      const ReadOptions& read_options, const Slice& user_key,
164
      const BlobIndex& blob_idx, const Version* version,
165
      BlobFileCache* blob_file_cache, FilePrefetchBuffer* prefetch_buffer,
166
      PinnableSlice* blob_value, uint64_t* bytes_read);
167
168
 private:
169
  struct Partition {
170
    // Active writer for this partition, or null when the partition is idle.
171
    std::unique_ptr<BlobLogWriter> writer;
172
    // Metadata tracked while the active file remains open.
173
    uint64_t file_number = 0;
174
    uint64_t file_size = 0;
175
    uint64_t blob_count = 0;
176
    uint64_t total_blob_bytes = 0;
177
    // Failed transformed writes already appended to this physical file.
178
    uint64_t garbage_blob_count = 0;
179
    uint64_t garbage_blob_bytes = 0;
180
    uint32_t column_family_id = 0;
181
    CompressionType compression = kNoCompression;
182
    bool sync_required = false;
183
  };
184
185
  struct DeferredFile {
186
0
    DeferredFile() = default;
187
0
    DeferredFile(DeferredFile&&) = default;
188
    DeferredFile& operator=(DeferredFile&&) = default;
189
    ~DeferredFile();
190
191
    // Blob writer moved out of an active partition at memtable rotation.
192
    std::unique_ptr<BlobLogWriter> writer;
193
    // Metadata preserved until flush preparation seals the file.
194
    uint64_t file_number = 0;
195
    uint64_t blob_count = 0;
196
    uint64_t total_blob_bytes = 0;
197
    // Failed transformed writes already appended before the file is sealed.
198
    uint64_t garbage_blob_count = 0;
199
    uint64_t garbage_blob_bytes = 0;
200
    // Set once sealing fails. The writer cannot be finalized again, and
201
    // memtables still reference the file, so every flush of its generation
202
    // fails with this status.
203
    Status seal_status;
204
  };
205
206
  struct SealedFile {
207
    // Blob-file metadata ready to be added to MANIFEST.
208
    BlobFileAddition addition;
209
    // Initial unreachable bytes that should be registered as garbage
210
    // alongside the file addition.
211
    uint64_t garbage_blob_count = 0;
212
    uint64_t garbage_blob_bytes = 0;
213
  };
214
215
  struct GenerationBatch {
216
    // The memtable that became immutable when this generation was rotated.
217
    uint64_t memtable_id = 0;
218
    // Files that were still open when the memtable became immutable.
219
    std::deque<DeferredFile> deferred_files;
220
    // Files already sealed for this memtable generation.
221
    std::vector<SealedFile> sealed_files;
222
  };
223
224
  // Clears all active-file bookkeeping from a partition after sealing.
225
  void ResetPartitionState(Partition* partition);
226
  // Returns the queued generation of `memtable_id`, or null.
227
  GenerationBatch* FindPendingGeneration(uint64_t memtable_id);
228
  // Moves a partition's open file out of the partition and resets it.
229
  DeferredFile DetachPartitionFile(Partition* partition);
230
  // Marks a blob file as manager-owned until it is committed or abandoned.
231
  void AddFilePartitionMapping(uint64_t file_number, uint32_t partition_idx);
232
  // Stops tracking a blob file after open failure.
233
  void RemoveFilePartitionMapping(uint64_t file_number);
234
235
  // Opens a new active blob file for one partition.
236
  Status OpenNewBlobFile(Partition* partition, uint32_t column_family_id,
237
                         CompressionType compression, uint32_t partition_idx);
238
  // Finalizes one active partition file and returns the MANIFEST addition.
239
  // Sets `*space_limit_reached` if the SstFileManager's max allowed space is
240
  // reached; the seal still succeeds.
241
  Status SealActiveBlobFile(const WriteOptions& write_options,
242
                            Partition* partition, SealedFile* sealed_file,
243
                            bool* space_limit_reached);
244
  // Finalizes one deferred file that was carried over to flush preparation.
245
  Status SealDeferredFile(const WriteOptions& write_options,
246
                          DeferredFile* deferred, SealedFile* sealed_file);
247
  // Appends the footer, reports file completion, and builds the resulting
248
  // BlobFileAddition for one sealed blob file.
249
  Status FinalizeBlobFile(const WriteOptions& write_options,
250
                          BlobLogWriter* writer, uint64_t file_number,
251
                          uint64_t blob_count, uint64_t total_blob_bytes,
252
                          BlobFileAddition* addition,
253
                          bool* space_limit_reached);
254
  // Appends one initial-garbage record to the flush output if any failed
255
  // transformed writes targeted the sealed blob file.
256
  static void AddSealedFileGarbage(const SealedFile& sealed_file,
257
                                   std::vector<BlobFileGarbage>* garbages);
258
  // Returns true if the active open file matches `file_number` and consumes
259
  // the failed-write garbage accounting.
260
  static bool MarkPartitionGarbage(Partition* partition, uint64_t file_number,
261
                                   uint64_t blob_count, uint64_t blob_bytes);
262
  // Returns true if the deferred flush-preparation file matches `file_number`
263
  // and consumes the failed-write garbage accounting.
264
  static bool MarkDeferredFileGarbage(DeferredFile* deferred,
265
                                      uint64_t file_number, uint64_t blob_count,
266
                                      uint64_t blob_bytes);
267
  // Returns true if one already-sealed file matches `file_number` and
268
  // consumes the failed-write garbage accounting.
269
  static bool MarkSealedFileGarbage(std::vector<SealedFile>* sealed_files,
270
                                    uint64_t file_number, uint64_t blob_count,
271
                                    uint64_t blob_bytes);
272
  // Seeds the blob cache with the original uncompressed value when configured.
273
  Status MaybePrepopulateBlobCache(const BlobDirectWriteSettings& settings,
274
                                   const Slice& original_value,
275
                                   uint64_t blob_file_number,
276
                                   uint64_t blob_offset);
277
278
  // Configured partition fanout and write-selection strategy.
279
  const uint32_t num_partitions_;
280
  std::shared_ptr<BlobFilePartitionStrategy> strategy_;
281
  // Shared services needed to create, track, and cache blob files.
282
  FileNumberAllocator file_number_allocator_;
283
  FileSystem* fs_;
284
  SystemClock* clock_;
285
  Statistics* statistics_;
286
  FileOptions file_options_;
287
  // Column-family context used for file naming and callbacks.
288
  std::string db_path_;
289
  std::string column_family_name_;
290
  // File creation policy and shared blob-file infrastructure.
291
  uint64_t blob_file_size_;
292
  bool use_fsync_;
293
  std::atomic<uint64_t> blob_writer_max_buffer_size_;
294
  BlobFileCache* blob_file_cache_;
295
  BlobFileCompletionCallback* blob_callback_;
296
  std::vector<std::shared_ptr<EventListener>> listeners_;
297
  FileChecksumGenFactory* file_checksum_gen_factory_;
298
  FileTypeSet checksum_handoff_file_types_;
299
  std::shared_ptr<IOTracer> io_tracer_;
300
  // DB identity used when constructing cache keys.
301
  std::string db_id_;
302
  std::string db_session_id_;
303
  Logger* info_log_;
304
305
  // One active writer slot per configured partition.
306
  std::vector<std::unique_ptr<Partition>> partitions_;
307
308
  // Protects partition state and generation queues.
309
  mutable port::Mutex mutex_;
310
  // Files already sealed while the current mutable memtable was active.
311
  std::vector<SealedFile> current_generation_sealed_files_;
312
  // Files whose seal failed while the current mutable memtable was active.
313
  std::vector<DeferredFile> current_generation_failed_files_;
314
  // FIFO immutable memtable generations waiting to be flushed.
315
  std::deque<GenerationBatch> pending_generations_;
316
317
  // Tracks blob files still owned by active or deferred write-path state until
318
  // MANIFEST commit publishes them.
319
  std::unordered_map<uint64_t, uint32_t> file_to_partition_;
320
  // Sealed direct-write files that remain reachable from live memtables, such
321
  // as old SuperVersions serving lazy iterator reads after flush commit.
322
  std::unordered_map<uint64_t, uint32_t> protected_blob_file_refs_;
323
  // Protects file_to_partition_ and protected_blob_file_refs_.
324
  mutable port::RWMutex file_partition_mutex_;
325
};
326
327
}  // namespace ROCKSDB_NAMESPACE