]>
Commit | Line | Data |
---|---|---|
20effc67 TL |
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 "utilities/transactions/lock/point/point_lock_tracker.h" | |
7 | ||
8 | namespace ROCKSDB_NAMESPACE { | |
9 | ||
10 | namespace { | |
11 | ||
12 | class TrackedKeysColumnFamilyIterator | |
13 | : public LockTracker::ColumnFamilyIterator { | |
14 | public: | |
15 | explicit TrackedKeysColumnFamilyIterator(const TrackedKeys& keys) | |
16 | : tracked_keys_(keys), it_(keys.begin()) {} | |
17 | ||
18 | bool HasNext() const override { return it_ != tracked_keys_.end(); } | |
19 | ||
20 | ColumnFamilyId Next() override { return (it_++)->first; } | |
21 | ||
22 | private: | |
23 | const TrackedKeys& tracked_keys_; | |
24 | TrackedKeys::const_iterator it_; | |
25 | }; | |
26 | ||
27 | class TrackedKeysIterator : public LockTracker::KeyIterator { | |
28 | public: | |
29 | TrackedKeysIterator(const TrackedKeys& keys, ColumnFamilyId id) | |
30 | : key_infos_(keys.at(id)), it_(key_infos_.begin()) {} | |
31 | ||
32 | bool HasNext() const override { return it_ != key_infos_.end(); } | |
33 | ||
34 | const std::string& Next() override { return (it_++)->first; } | |
35 | ||
36 | private: | |
37 | const TrackedKeyInfos& key_infos_; | |
38 | TrackedKeyInfos::const_iterator it_; | |
39 | }; | |
40 | ||
41 | } // namespace | |
42 | ||
43 | void PointLockTracker::Track(const PointLockRequest& r) { | |
44 | auto& keys = tracked_keys_[r.column_family_id]; | |
45 | #ifdef __cpp_lib_unordered_map_try_emplace | |
46 | // use c++17's try_emplace if available, to avoid rehashing the key | |
47 | // in case it is not already in the map | |
48 | auto result = keys.try_emplace(r.key, r.seq); | |
49 | auto it = result.first; | |
50 | if (!result.second && r.seq < it->second.seq) { | |
51 | // Now tracking this key with an earlier sequence number | |
52 | it->second.seq = r.seq; | |
53 | } | |
54 | #else | |
55 | auto it = keys.find(r.key); | |
56 | if (it == keys.end()) { | |
57 | auto result = keys.emplace(r.key, TrackedKeyInfo(r.seq)); | |
58 | it = result.first; | |
59 | } else if (r.seq < it->second.seq) { | |
60 | // Now tracking this key with an earlier sequence number | |
61 | it->second.seq = r.seq; | |
62 | } | |
63 | #endif | |
64 | // else we do not update the seq. The smaller the tracked seq, the stronger it | |
65 | // the guarantee since it implies from the seq onward there has not been a | |
66 | // concurrent update to the key. So we update the seq if it implies stronger | |
67 | // guarantees, i.e., if it is smaller than the existing tracked seq. | |
68 | ||
69 | if (r.read_only) { | |
70 | it->second.num_reads++; | |
71 | } else { | |
72 | it->second.num_writes++; | |
73 | } | |
74 | ||
75 | it->second.exclusive = it->second.exclusive || r.exclusive; | |
76 | } | |
77 | ||
78 | UntrackStatus PointLockTracker::Untrack(const PointLockRequest& r) { | |
79 | auto cf_keys = tracked_keys_.find(r.column_family_id); | |
80 | if (cf_keys == tracked_keys_.end()) { | |
81 | return UntrackStatus::NOT_TRACKED; | |
82 | } | |
83 | ||
84 | auto& keys = cf_keys->second; | |
85 | auto it = keys.find(r.key); | |
86 | if (it == keys.end()) { | |
87 | return UntrackStatus::NOT_TRACKED; | |
88 | } | |
89 | ||
90 | bool untracked = false; | |
91 | auto& info = it->second; | |
92 | if (r.read_only) { | |
93 | if (info.num_reads > 0) { | |
94 | info.num_reads--; | |
95 | untracked = true; | |
96 | } | |
97 | } else { | |
98 | if (info.num_writes > 0) { | |
99 | info.num_writes--; | |
100 | untracked = true; | |
101 | } | |
102 | } | |
103 | ||
104 | bool removed = false; | |
105 | if (info.num_reads == 0 && info.num_writes == 0) { | |
106 | keys.erase(it); | |
107 | if (keys.empty()) { | |
108 | tracked_keys_.erase(cf_keys); | |
109 | } | |
110 | removed = true; | |
111 | } | |
112 | ||
113 | if (removed) { | |
114 | return UntrackStatus::REMOVED; | |
115 | } | |
116 | if (untracked) { | |
117 | return UntrackStatus::UNTRACKED; | |
118 | } | |
119 | return UntrackStatus::NOT_TRACKED; | |
120 | } | |
121 | ||
122 | void PointLockTracker::Merge(const LockTracker& tracker) { | |
123 | const PointLockTracker& t = static_cast<const PointLockTracker&>(tracker); | |
124 | for (const auto& cf_keys : t.tracked_keys_) { | |
125 | ColumnFamilyId cf = cf_keys.first; | |
126 | const auto& keys = cf_keys.second; | |
127 | ||
128 | auto current_cf_keys = tracked_keys_.find(cf); | |
129 | if (current_cf_keys == tracked_keys_.end()) { | |
130 | tracked_keys_.emplace(cf_keys); | |
131 | } else { | |
132 | auto& current_keys = current_cf_keys->second; | |
133 | for (const auto& key_info : keys) { | |
134 | const std::string& key = key_info.first; | |
135 | const TrackedKeyInfo& info = key_info.second; | |
136 | // If key was not previously tracked, just copy the whole struct over. | |
137 | // Otherwise, some merging needs to occur. | |
138 | auto current_info = current_keys.find(key); | |
139 | if (current_info == current_keys.end()) { | |
140 | current_keys.emplace(key_info); | |
141 | } else { | |
142 | current_info->second.Merge(info); | |
143 | } | |
144 | } | |
145 | } | |
146 | } | |
147 | } | |
148 | ||
149 | void PointLockTracker::Subtract(const LockTracker& tracker) { | |
150 | const PointLockTracker& t = static_cast<const PointLockTracker&>(tracker); | |
151 | for (const auto& cf_keys : t.tracked_keys_) { | |
152 | ColumnFamilyId cf = cf_keys.first; | |
153 | const auto& keys = cf_keys.second; | |
154 | ||
155 | auto& current_keys = tracked_keys_.at(cf); | |
156 | for (const auto& key_info : keys) { | |
157 | const std::string& key = key_info.first; | |
158 | const TrackedKeyInfo& info = key_info.second; | |
159 | uint32_t num_reads = info.num_reads; | |
160 | uint32_t num_writes = info.num_writes; | |
161 | ||
162 | auto current_key_info = current_keys.find(key); | |
163 | assert(current_key_info != current_keys.end()); | |
164 | ||
165 | // Decrement the total reads/writes of this key by the number of | |
166 | // reads/writes done since the last SavePoint. | |
167 | if (num_reads > 0) { | |
168 | assert(current_key_info->second.num_reads >= num_reads); | |
169 | current_key_info->second.num_reads -= num_reads; | |
170 | } | |
171 | if (num_writes > 0) { | |
172 | assert(current_key_info->second.num_writes >= num_writes); | |
173 | current_key_info->second.num_writes -= num_writes; | |
174 | } | |
175 | if (current_key_info->second.num_reads == 0 && | |
176 | current_key_info->second.num_writes == 0) { | |
177 | current_keys.erase(current_key_info); | |
178 | } | |
179 | } | |
180 | } | |
181 | } | |
182 | ||
183 | LockTracker* PointLockTracker::GetTrackedLocksSinceSavePoint( | |
184 | const LockTracker& save_point_tracker) const { | |
185 | // Examine the number of reads/writes performed on all keys written | |
186 | // since the last SavePoint and compare to the total number of reads/writes | |
187 | // for each key. | |
188 | LockTracker* t = new PointLockTracker(); | |
189 | const PointLockTracker& save_point_t = | |
190 | static_cast<const PointLockTracker&>(save_point_tracker); | |
191 | for (const auto& cf_keys : save_point_t.tracked_keys_) { | |
192 | ColumnFamilyId cf = cf_keys.first; | |
193 | const auto& keys = cf_keys.second; | |
194 | ||
195 | auto& current_keys = tracked_keys_.at(cf); | |
196 | for (const auto& key_info : keys) { | |
197 | const std::string& key = key_info.first; | |
198 | const TrackedKeyInfo& info = key_info.second; | |
199 | uint32_t num_reads = info.num_reads; | |
200 | uint32_t num_writes = info.num_writes; | |
201 | ||
202 | auto current_key_info = current_keys.find(key); | |
203 | assert(current_key_info != current_keys.end()); | |
204 | assert(current_key_info->second.num_reads >= num_reads); | |
205 | assert(current_key_info->second.num_writes >= num_writes); | |
206 | ||
207 | if (current_key_info->second.num_reads == num_reads && | |
208 | current_key_info->second.num_writes == num_writes) { | |
209 | // All the reads/writes to this key were done in the last savepoint. | |
210 | PointLockRequest r; | |
211 | r.column_family_id = cf; | |
212 | r.key = key; | |
213 | r.seq = info.seq; | |
214 | r.read_only = (num_writes == 0); | |
215 | r.exclusive = info.exclusive; | |
216 | t->Track(r); | |
217 | } | |
218 | } | |
219 | } | |
220 | return t; | |
221 | } | |
222 | ||
223 | PointLockStatus PointLockTracker::GetPointLockStatus( | |
224 | ColumnFamilyId column_family_id, const std::string& key) const { | |
225 | assert(IsPointLockSupported()); | |
226 | PointLockStatus status; | |
227 | auto it = tracked_keys_.find(column_family_id); | |
228 | if (it == tracked_keys_.end()) { | |
229 | return status; | |
230 | } | |
231 | ||
232 | const auto& keys = it->second; | |
233 | auto key_it = keys.find(key); | |
234 | if (key_it == keys.end()) { | |
235 | return status; | |
236 | } | |
237 | ||
238 | const TrackedKeyInfo& key_info = key_it->second; | |
239 | status.locked = true; | |
240 | status.exclusive = key_info.exclusive; | |
241 | status.seq = key_info.seq; | |
242 | return status; | |
243 | } | |
244 | ||
245 | uint64_t PointLockTracker::GetNumPointLocks() const { | |
246 | uint64_t num_keys = 0; | |
247 | for (const auto& cf_keys : tracked_keys_) { | |
248 | num_keys += cf_keys.second.size(); | |
249 | } | |
250 | return num_keys; | |
251 | } | |
252 | ||
253 | LockTracker::ColumnFamilyIterator* PointLockTracker::GetColumnFamilyIterator() | |
254 | const { | |
255 | return new TrackedKeysColumnFamilyIterator(tracked_keys_); | |
256 | } | |
257 | ||
258 | LockTracker::KeyIterator* PointLockTracker::GetKeyIterator( | |
259 | ColumnFamilyId column_family_id) const { | |
260 | assert(tracked_keys_.find(column_family_id) != tracked_keys_.end()); | |
261 | return new TrackedKeysIterator(tracked_keys_, column_family_id); | |
262 | } | |
263 | ||
264 | void PointLockTracker::Clear() { tracked_keys_.clear(); } | |
265 | ||
266 | } // namespace ROCKSDB_NAMESPACE |