/src/rocksdb/db/blob/blob_garbage_meter.cc
Line | Count | Source |
1 | | // Copyright (c) 2011-present, Facebook, Inc. All rights reserved. |
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 | | |
6 | | #include "db/blob/blob_garbage_meter.h" |
7 | | |
8 | | #include "db/blob/blob_index.h" |
9 | | #include "db/blob/blob_log_format.h" |
10 | | #include "db/dbformat.h" |
11 | | #include "db/wide/wide_column_serialization.h" |
12 | | |
13 | | namespace ROCKSDB_NAMESPACE { |
14 | | |
15 | 0 | Status BlobGarbageMeter::ProcessInFlow(const Slice& key, const Slice& value) { |
16 | 0 | return ProcessFlow(key, value, /*is_inflow=*/true); |
17 | 0 | } |
18 | | |
19 | 0 | Status BlobGarbageMeter::ProcessOutFlow(const Slice& key, const Slice& value) { |
20 | 0 | return ProcessFlow(key, value, /*is_inflow=*/false); |
21 | 0 | } |
22 | | |
23 | | Status BlobGarbageMeter::GetBlobReferenceDetails(const ParsedInternalKey& ikey, |
24 | | const BlobIndex& blob_index, |
25 | | uint64_t* blob_file_number, |
26 | 0 | uint64_t* bytes) { |
27 | 0 | assert(blob_file_number); |
28 | 0 | assert(*blob_file_number == kInvalidBlobFileNumber); |
29 | 0 | assert(bytes); |
30 | 0 | assert(*bytes == 0); |
31 | |
|
32 | 0 | if (blob_index.IsInlined() || blob_index.HasTTL()) { |
33 | 0 | return Status::Corruption("Unexpected TTL/inlined blob index"); |
34 | 0 | } |
35 | 0 | if (blob_index.IsSameFile()) { |
36 | 0 | return Status::OK(); |
37 | 0 | } |
38 | | |
39 | 0 | *blob_file_number = blob_index.file_number(); |
40 | 0 | *bytes = |
41 | 0 | blob_index.size() + |
42 | 0 | BlobLogRecord::CalculateAdjustmentForRecordHeader(ikey.user_key.size()); |
43 | |
|
44 | 0 | return Status::OK(); |
45 | 0 | } |
46 | | |
47 | | Status BlobGarbageMeter::ParseBlobIndexReference(const ParsedInternalKey& ikey, |
48 | | const Slice& value, |
49 | | uint64_t* blob_file_number, |
50 | 0 | uint64_t* bytes) { |
51 | 0 | assert(blob_file_number); |
52 | 0 | assert(*blob_file_number == kInvalidBlobFileNumber); |
53 | 0 | assert(bytes); |
54 | 0 | assert(*bytes == 0); |
55 | |
|
56 | 0 | if (ikey.type != kTypeBlobIndex) { |
57 | 0 | return Status::OK(); |
58 | 0 | } |
59 | | |
60 | 0 | BlobIndex blob_index; |
61 | |
|
62 | 0 | { |
63 | 0 | const Status s = blob_index.DecodeFrom(value); |
64 | 0 | if (!s.ok()) { |
65 | 0 | return s; |
66 | 0 | } |
67 | 0 | } |
68 | | |
69 | 0 | return GetBlobReferenceDetails(ikey, blob_index, blob_file_number, bytes); |
70 | 0 | } |
71 | | |
72 | | void BlobGarbageMeter::AddFlow(uint64_t blob_file_number, uint64_t bytes, |
73 | 0 | bool is_inflow) { |
74 | 0 | if (is_inflow) { |
75 | 0 | flows_[blob_file_number].AddInFlow(bytes); |
76 | 0 | return; |
77 | 0 | } |
78 | | |
79 | | // Note: in order to measure the amount of additional garbage, we only need to |
80 | | // track the outflow for preexisting files, i.e. those that also had inflow. |
81 | | // (Newly written files would only have outflow.) |
82 | 0 | auto it = flows_.find(blob_file_number); |
83 | 0 | if (it != flows_.end()) { |
84 | 0 | it->second.AddOutFlow(bytes); |
85 | 0 | } |
86 | 0 | } |
87 | | |
88 | | Status BlobGarbageMeter::ProcessFlow(const Slice& key, const Slice& value, |
89 | 0 | bool is_inflow) { |
90 | 0 | ParsedInternalKey ikey; |
91 | |
|
92 | 0 | { |
93 | 0 | constexpr bool log_err_key = false; |
94 | 0 | const Status s = ParseInternalKey(key, &ikey, log_err_key); |
95 | 0 | if (!s.ok()) { |
96 | 0 | return s; |
97 | 0 | } |
98 | 0 | } |
99 | | |
100 | 0 | uint64_t blob_file_number = kInvalidBlobFileNumber; |
101 | 0 | uint64_t bytes = 0; |
102 | 0 | if (Status s = |
103 | 0 | ParseBlobIndexReference(ikey, value, &blob_file_number, &bytes); |
104 | 0 | !s.ok()) { |
105 | 0 | return s; |
106 | 0 | } |
107 | | |
108 | 0 | if (blob_file_number != kInvalidBlobFileNumber) { |
109 | 0 | AddFlow(blob_file_number, bytes, is_inflow); |
110 | 0 | return Status::OK(); |
111 | 0 | } |
112 | | |
113 | 0 | return ProcessEntityBlobReferences(ikey, value, is_inflow); |
114 | 0 | } |
115 | | |
116 | | Status BlobGarbageMeter::ProcessEntityBlobReferences( |
117 | 0 | const ParsedInternalKey& ikey, const Slice& value, bool is_inflow) { |
118 | 0 | if (ikey.type != kTypeWideColumnEntity) { |
119 | 0 | return Status::OK(); |
120 | 0 | } |
121 | | |
122 | 0 | return WideColumnSerialization::ForEachBlobFileNumber( |
123 | 0 | value, [&](const BlobIndex& blob_index) -> Status { |
124 | 0 | uint64_t file_number = kInvalidBlobFileNumber; |
125 | 0 | uint64_t blob_bytes = 0; |
126 | 0 | if (const Status s = GetBlobReferenceDetails(ikey, blob_index, |
127 | 0 | &file_number, &blob_bytes); |
128 | 0 | !s.ok()) { |
129 | 0 | return s; |
130 | 0 | } |
131 | 0 | if (file_number != kInvalidBlobFileNumber) { |
132 | 0 | AddFlow(file_number, blob_bytes, is_inflow); |
133 | 0 | } |
134 | 0 | return Status::OK(); |
135 | 0 | }); |
136 | 0 | } |
137 | | |
138 | | } // namespace ROCKSDB_NAMESPACE |