|
4 | 4 | #include <pingcap/kv/Scanner.h> |
5 | 5 | #include <pingcap/kv/Snapshot.h> |
6 | 6 |
|
| 7 | +#include <algorithm> |
| 8 | +#include <limits> |
| 9 | + |
7 | 10 | namespace pingcap |
8 | 11 | { |
9 | 12 | namespace kv |
10 | 13 | { |
11 | 14 | constexpr int scan_batch_size = 256; |
12 | 15 |
|
| 16 | +namespace |
| 17 | +{ |
| 18 | + |
| 19 | +uint32_t requestLimit(uint32_t remaining) |
| 20 | +{ |
| 21 | + if (remaining == std::numeric_limits<uint32_t>::max()) |
| 22 | + return remaining; |
| 23 | + return remaining + 1; |
| 24 | +} |
| 25 | + |
| 26 | +std::string keyBefore(const std::string & upper_bound) |
| 27 | +{ |
| 28 | + // Used to locate the region containing keys immediately below a reverse scan upper bound. |
| 29 | + if (upper_bound.empty()) |
| 30 | + return std::string(1, static_cast<char>(0xff)); |
| 31 | + |
| 32 | + std::string key = upper_bound; |
| 33 | + auto last = static_cast<unsigned char>(key.back()); |
| 34 | + if (last == 0) |
| 35 | + { |
| 36 | + key.pop_back(); |
| 37 | + return key; |
| 38 | + } |
| 39 | + |
| 40 | + key.back() = static_cast<char>(last - 1); |
| 41 | + key.push_back(static_cast<char>(0xff)); |
| 42 | + return key; |
| 43 | +} |
| 44 | + |
| 45 | +bool forwardRangeFinished(const std::string & cursor, const std::string & end_key) |
| 46 | +{ |
| 47 | + return cursor.empty() || (!end_key.empty() && cursor >= end_key); |
| 48 | +} |
| 49 | + |
| 50 | +bool reverseRangeFinished(const std::string & cursor, const std::string & end_key) |
| 51 | +{ |
| 52 | + return cursor.empty() || (!end_key.empty() && cursor <= end_key); |
| 53 | +} |
| 54 | + |
| 55 | +void setScanContext(kvrpcpb::ScanRequest & request, const MinCommitTSPushed & min_commit_ts_pushed) |
| 56 | +{ |
| 57 | + auto * context = request.mutable_context(); |
| 58 | + context->set_priority(::kvrpcpb::Normal); |
| 59 | + context->set_not_fill_cache(false); |
| 60 | + for (auto ts : min_commit_ts_pushed.getTimestamps()) |
| 61 | + { |
| 62 | + context->add_resolved_locks(ts); |
| 63 | + } |
| 64 | +} |
| 65 | + |
| 66 | +} // namespace |
| 67 | + |
13 | 68 | //bool extractLockFromKeyErr() |
14 | 69 |
|
15 | 70 | kvrpcpb::MvccInfo Snapshot::mvccGet(const std::string & key) |
@@ -113,6 +168,164 @@ std::string Snapshot::Get(Backoffer & bo, const std::string & key) |
113 | 168 | } |
114 | 169 | } |
115 | 170 |
|
| 171 | +ScanResult Snapshot::ScanOnce(const ScanOptions & options) |
| 172 | +{ |
| 173 | + Backoffer bo(scanMaxBackoff); |
| 174 | + return ScanOnce(bo, options); |
| 175 | +} |
| 176 | + |
| 177 | +ScanResult Snapshot::ScanOnce(Backoffer & bo, const ScanOptions & options) |
| 178 | +{ |
| 179 | + ScanResult result; |
| 180 | + if (options.limit == 0) |
| 181 | + return result; |
| 182 | + |
| 183 | + const uint64_t read_version = options.version == 0 ? version : options.version; |
| 184 | + std::string cursor = options.start_key; |
| 185 | + |
| 186 | + if (!options.reverse && !options.end_key.empty() && cursor >= options.end_key) |
| 187 | + return result; |
| 188 | + if (options.reverse && !cursor.empty() && !options.end_key.empty() && options.end_key >= cursor) |
| 189 | + return result; |
| 190 | + |
| 191 | + while (result.pairs.size() < options.limit) |
| 192 | + { |
| 193 | + auto loc = options.reverse ? cluster->region_cache->locateKey(bo, keyBefore(cursor)) : cluster->region_cache->locateKey(bo, cursor); |
| 194 | + |
| 195 | + kvrpcpb::ScanRequest request; |
| 196 | + if (options.reverse) |
| 197 | + { |
| 198 | + std::string request_end_key = loc.start_key; |
| 199 | + if (!options.end_key.empty() && options.end_key > request_end_key) |
| 200 | + request_end_key = options.end_key; |
| 201 | + |
| 202 | + std::string request_start_key = cursor; |
| 203 | + if (!loc.end_key.empty() && (request_start_key.empty() || request_start_key > loc.end_key)) |
| 204 | + request_start_key = loc.end_key; |
| 205 | + |
| 206 | + if (!request_start_key.empty() && request_start_key <= request_end_key) |
| 207 | + { |
| 208 | + cursor = loc.start_key; |
| 209 | + if (reverseRangeFinished(cursor, options.end_key)) |
| 210 | + return result; |
| 211 | + continue; |
| 212 | + } |
| 213 | + |
| 214 | + request.set_start_key(request_start_key); |
| 215 | + request.set_end_key(request_end_key); |
| 216 | + request.set_reverse(true); |
| 217 | + } |
| 218 | + else |
| 219 | + { |
| 220 | + std::string request_end_key = options.end_key; |
| 221 | + if (!request_end_key.empty() && !loc.end_key.empty() && loc.end_key < request_end_key) |
| 222 | + request_end_key = loc.end_key; |
| 223 | + |
| 224 | + request.set_start_key(cursor); |
| 225 | + request.set_end_key(request_end_key); |
| 226 | + } |
| 227 | + |
| 228 | + const auto remaining = static_cast<uint32_t>(options.limit - result.pairs.size()); |
| 229 | + request.set_limit(requestLimit(remaining)); |
| 230 | + request.set_version(read_version); |
| 231 | + request.set_key_only(options.key_only); |
| 232 | + setScanContext(request, min_commit_ts_pushed); |
| 233 | + |
| 234 | + kvrpcpb::ScanResponse response; |
| 235 | + RegionClient region_client(cluster, loc.region); |
| 236 | + try |
| 237 | + { |
| 238 | + region_client.sendReqToRegion<RPC_NAME(KvScan)>(bo, request, &response); |
| 239 | + } |
| 240 | + catch (Exception & e) |
| 241 | + { |
| 242 | + bo.backoff(boRegionMiss, e); |
| 243 | + continue; |
| 244 | + } |
| 245 | + |
| 246 | + if (response.has_error()) |
| 247 | + { |
| 248 | + auto lock = extractLockFromKeyErr(response.error()); |
| 249 | + std::vector<LockPtr> locks{lock}; |
| 250 | + std::vector<uint64_t> pushed{}; |
| 251 | + auto ms_before_expired = cluster->lock_resolver->resolveLocks(bo, read_version, locks, pushed); |
| 252 | + if (!pushed.empty()) |
| 253 | + { |
| 254 | + min_commit_ts_pushed.addTimestamps(pushed); |
| 255 | + } |
| 256 | + if (ms_before_expired > 0) |
| 257 | + { |
| 258 | + bo.backoffWithMaxSleep( |
| 259 | + BackoffType::boTxnLockFast, |
| 260 | + ms_before_expired, |
| 261 | + Exception("key is locked during scanning", ErrorCodes::LockError)); |
| 262 | + } |
| 263 | + continue; |
| 264 | + } |
| 265 | + |
| 266 | + std::vector<LockPtr> locks; |
| 267 | + for (int i = 0; i < response.pairs_size(); i++) |
| 268 | + { |
| 269 | + const auto & pair = response.pairs(i); |
| 270 | + if (pair.has_error()) |
| 271 | + locks.push_back(extractLockFromKeyErr(pair.error())); |
| 272 | + } |
| 273 | + if (!locks.empty()) |
| 274 | + { |
| 275 | + std::vector<uint64_t> pushed{}; |
| 276 | + auto ms_before_expired = cluster->lock_resolver->resolveLocks(bo, read_version, locks, pushed); |
| 277 | + if (!pushed.empty()) |
| 278 | + { |
| 279 | + min_commit_ts_pushed.addTimestamps(pushed); |
| 280 | + } |
| 281 | + if (ms_before_expired > 0) |
| 282 | + { |
| 283 | + bo.backoffWithMaxSleep( |
| 284 | + BackoffType::boTxnLockFast, |
| 285 | + ms_before_expired, |
| 286 | + Exception("key is locked during scanning", ErrorCodes::LockError)); |
| 287 | + } |
| 288 | + continue; |
| 289 | + } |
| 290 | + |
| 291 | + const auto pairs_size = static_cast<size_t>(response.pairs_size()); |
| 292 | + const auto append_size = std::min<size_t>(pairs_size, remaining); |
| 293 | + for (size_t i = 0; i < append_size; i++) |
| 294 | + { |
| 295 | + result.pairs.push_back(response.pairs(static_cast<int>(i))); |
| 296 | + } |
| 297 | + |
| 298 | + if (pairs_size > remaining) |
| 299 | + { |
| 300 | + result.has_more = true; |
| 301 | + result.next_start_key = options.reverse ? result.pairs.back().key() : alphabeticalNext(result.pairs.back().key()); |
| 302 | + return result; |
| 303 | + } |
| 304 | + |
| 305 | + if (options.reverse) |
| 306 | + { |
| 307 | + cursor = loc.start_key; |
| 308 | + if (reverseRangeFinished(cursor, options.end_key)) |
| 309 | + return result; |
| 310 | + } |
| 311 | + else |
| 312 | + { |
| 313 | + cursor = loc.end_key; |
| 314 | + if (forwardRangeFinished(cursor, options.end_key)) |
| 315 | + return result; |
| 316 | + } |
| 317 | + |
| 318 | + if (result.pairs.size() == options.limit) |
| 319 | + { |
| 320 | + result.has_more = true; |
| 321 | + result.next_start_key = cursor; |
| 322 | + return result; |
| 323 | + } |
| 324 | + } |
| 325 | + |
| 326 | + return result; |
| 327 | +} |
| 328 | + |
116 | 329 | Scanner Snapshot::Scan(const std::string & begin, const std::string & end) |
117 | 330 | { |
118 | 331 | return Scanner(*this, begin, end, scan_batch_size); |
|
0 commit comments