]> git.proxmox.com Git - ceph.git/blob - ceph/src/osd/SnapMapper.cc
bump version to 18.2.4-pve3
[ceph.git] / ceph / src / osd / SnapMapper.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3 /*
4 * Ceph - scalable distributed file system
5 *
6 * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
7 *
8 * This is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License version 2.1, as published by the Free Software
11 * Foundation. See file COPYING.
12 *
13 */
14
15 #include "SnapMapper.h"
16
17 #include <fmt/printf.h>
18 #include <fmt/ranges.h>
19
20 #include "global/global_context.h"
21 #include "osd/osd_types_fmt.h"
22 #include "SnapMapReaderI.h"
23
24 #define dout_context cct
25 #define dout_subsys ceph_subsys_osd
26 #undef dout_prefix
27 #define dout_prefix *_dout << "snap_mapper."
28
29 using std::make_pair;
30 using std::map;
31 using std::pair;
32 using std::set;
33 using std::string;
34 using std::vector;
35
36 using ceph::decode;
37 using ceph::encode;
38 using ceph::timespan_str;
39 using result_t = Scrub::SnapMapReaderI::result_t;
40 using code_t = Scrub::SnapMapReaderI::result_t::code_t;
41
42
43 const string SnapMapper::LEGACY_MAPPING_PREFIX = "MAP_";
44 const string SnapMapper::MAPPING_PREFIX = "SNA_";
45 const string SnapMapper::OBJECT_PREFIX = "OBJ_";
46
47 const char *SnapMapper::PURGED_SNAP_PREFIX = "PSN_";
48
49 /*
50
51 We have a bidirectional mapping, (1) from each snap+obj to object,
52 sorted by snapshot, such that we can enumerate to identify all clones
53 mapped to a particular snapshot, and (2) from object to snaps, so we
54 can identify which reverse mappings exist for any given object (and,
55 e.g., clean up on deletion).
56
57 "MAP_"
58 + ("%016x" % snapid)
59 + "_"
60 + (".%x" % shard_id)
61 + "_"
62 + hobject_t::to_str() ("%llx.%8x.%lx.name...." % pool, hash, snap)
63 -> SnapMapping::Mapping { snap, hoid }
64
65 "SNA_"
66 + ("%lld" % poolid)
67 + "_"
68 + ("%016x" % snapid)
69 + "_"
70 + (".%x" % shard_id)
71 + "_"
72 + hobject_t::to_str() ("%llx.%8x.%lx.name...." % pool, hash, snap)
73 -> SnapMapping::Mapping { snap, hoid }
74
75 "OBJ_" +
76 + (".%x" % shard_id)
77 + hobject_t::to_str()
78 -> SnapMapper::object_snaps { oid, set<snapid_t> }
79
80 */
81
82 #ifdef WITH_SEASTAR
83 #include "crimson/common/log.h"
84 #include "crimson/osd/pg_interval_interrupt_condition.h"
85 template <typename ValuesT = void>
86 using interruptible_future =
87 ::crimson::interruptible::interruptible_future<
88 ::crimson::osd::IOInterruptCondition, ValuesT>;
89 using interruptor =
90 ::crimson::interruptible::interruptor<
91 ::crimson::osd::IOInterruptCondition>;
92
93 #define CRIMSON_DEBUG(FMT_MSG, ...) crimson::get_logger(ceph_subsys_).debug(FMT_MSG, ##__VA_ARGS__)
94 int OSDriver::get_keys(
95 const std::set<std::string> &keys,
96 std::map<std::string, ceph::buffer::list> *out)
97 {
98 CRIMSON_DEBUG("OSDriver::{}:{}", __func__, __LINE__);
99 using crimson::os::FuturizedStore;
100 return interruptor::green_get(os->omap_get_values(
101 ch, hoid, keys
102 ).safe_then([out] (FuturizedStore::Shard::omap_values_t&& vals) {
103 // just the difference in comparator (`std::less<>` in omap_values_t`)
104 reinterpret_cast<FuturizedStore::Shard::omap_values_t&>(*out) = std::move(vals);
105 return 0;
106 }, FuturizedStore::Shard::read_errorator::all_same_way([] (auto& e) {
107 assert(e.value() > 0);
108 return -e.value();
109 }))); // this requires seastar::thread
110 CRIMSON_DEBUG("OSDriver::{}:{}", __func__, __LINE__);
111 }
112
113 int OSDriver::get_next(
114 const std::string &key,
115 std::pair<std::string, ceph::buffer::list> *next)
116 {
117 CRIMSON_DEBUG("OSDriver::{}:{}", __func__, __LINE__);
118 using crimson::os::FuturizedStore;
119 return interruptor::green_get(os->omap_get_values(
120 ch, hoid, key
121 ).safe_then_unpack([&key, next] (bool, FuturizedStore::Shard::omap_values_t&& vals) {
122 CRIMSON_DEBUG("OSDriver::{}:{}", "get_next", __LINE__);
123 if (auto nit = std::begin(vals); nit == std::end(vals)) {
124 CRIMSON_DEBUG("OSDriver::{}:{}", "get_next", __LINE__);
125 return -ENOENT;
126 } else {
127 CRIMSON_DEBUG("OSDriver::{}:{}", "get_next", __LINE__);
128 assert(nit->first > key);
129 *next = *nit;
130 return 0;
131 }
132 }, FuturizedStore::Shard::read_errorator::all_same_way([] {
133 CRIMSON_DEBUG("OSDriver::{}:{}", "get_next", __LINE__);
134 return -EINVAL;
135 }))); // this requires seastar::thread
136 CRIMSON_DEBUG("OSDriver::{}:{}", __func__, __LINE__);
137 }
138
139 int OSDriver::get_next_or_current(
140 const std::string &key,
141 std::pair<std::string, ceph::buffer::list> *next_or_current)
142 {
143 CRIMSON_DEBUG("OSDriver::{}:{}", __func__, __LINE__);
144 using crimson::os::FuturizedStore;
145 // let's try to get current first
146 return interruptor::green_get(os->omap_get_values(
147 ch, hoid, FuturizedStore::Shard::omap_keys_t{key}
148 ).safe_then([&key, next_or_current] (FuturizedStore::Shard::omap_values_t&& vals) {
149 assert(vals.size() == 1);
150 *next_or_current = std::make_pair(key, std::move(vals[0]));
151 return 0;
152 }, FuturizedStore::Shard::read_errorator::all_same_way(
153 [next_or_current, &key, this] {
154 // no current, try next
155 return get_next(key, next_or_current);
156 }))); // this requires seastar::thread
157 CRIMSON_DEBUG("OSDriver::{}:{}", __func__, __LINE__);
158 }
159 #else
160 int OSDriver::get_keys(
161 const std::set<std::string> &keys,
162 std::map<std::string, ceph::buffer::list> *out)
163 {
164 return os->omap_get_values(ch, hoid, keys, out);
165 }
166
167 int OSDriver::get_next(
168 const std::string &key,
169 std::pair<std::string, ceph::buffer::list> *next)
170 {
171 ObjectMap::ObjectMapIterator iter =
172 os->get_omap_iterator(ch, hoid);
173 if (!iter) {
174 ceph_abort();
175 return -EINVAL;
176 }
177 iter->upper_bound(key);
178 if (iter->valid()) {
179 if (next)
180 *next = make_pair(iter->key(), iter->value());
181 return 0;
182 } else {
183 return -ENOENT;
184 }
185 }
186
187 int OSDriver::get_next_or_current(
188 const std::string &key,
189 std::pair<std::string, ceph::buffer::list> *next_or_current)
190 {
191 ObjectMap::ObjectMapIterator iter =
192 os->get_omap_iterator(ch, hoid);
193 if (!iter) {
194 ceph_abort();
195 return -EINVAL;
196 }
197 iter->lower_bound(key);
198 if (iter->valid()) {
199 if (next_or_current)
200 *next_or_current = make_pair(iter->key(), iter->value());
201 return 0;
202 } else {
203 return -ENOENT;
204 }
205 }
206 #endif // WITH_SEASTAR
207
208 string SnapMapper::get_prefix(int64_t pool, snapid_t snap)
209 {
210 static_assert(sizeof(pool) == 8, "assumed by the formatting code");
211 return fmt::sprintf("%s%lld_%.16X_",
212 MAPPING_PREFIX,
213 pool,
214 snap);
215 }
216
217 string SnapMapper::to_raw_key(
218 const pair<snapid_t, hobject_t> &in) const
219 {
220 return get_prefix(in.second.pool, in.first) + shard_prefix + in.second.to_str();
221 }
222
223 std::string SnapMapper::to_raw_key(snapid_t snap, const hobject_t &clone) const
224 {
225 return get_prefix(clone.pool, snap) + shard_prefix + clone.to_str();
226 }
227
228 pair<string, ceph::buffer::list> SnapMapper::to_raw(
229 const pair<snapid_t, hobject_t> &in) const
230 {
231 ceph::buffer::list bl;
232 encode(Mapping(in), bl);
233 return make_pair(to_raw_key(in), bl);
234 }
235
236 pair<snapid_t, hobject_t> SnapMapper::from_raw(
237 const pair<std::string, ceph::buffer::list> &image)
238 {
239 using ceph::decode;
240 Mapping map;
241 ceph::buffer::list bl(image.second);
242 auto bp = bl.cbegin();
243 decode(map, bp);
244 return make_pair(map.snap, map.hoid);
245 }
246
247 std::pair<snapid_t, hobject_t> SnapMapper::from_raw(
248 const ceph::buffer::list &image)
249 {
250 using ceph::decode;
251 Mapping map;
252 auto bp = image.cbegin();
253 decode(map, bp);
254 return make_pair(map.snap, map.hoid);
255 }
256
257 bool SnapMapper::is_mapping(const string &to_test)
258 {
259 return to_test.substr(0, MAPPING_PREFIX.size()) == MAPPING_PREFIX;
260 }
261
262 string SnapMapper::to_object_key(const hobject_t &hoid) const
263 {
264 return OBJECT_PREFIX + shard_prefix + hoid.to_str();
265 }
266
267 void SnapMapper::object_snaps::encode(ceph::buffer::list &bl) const
268 {
269 ENCODE_START(1, 1, bl);
270 encode(oid, bl);
271 encode(snaps, bl);
272 ENCODE_FINISH(bl);
273 }
274
275 void SnapMapper::object_snaps::decode(ceph::buffer::list::const_iterator &bl)
276 {
277 DECODE_START(1, bl);
278 decode(oid, bl);
279 decode(snaps, bl);
280 DECODE_FINISH(bl);
281 }
282
283 bool SnapMapper::check(const hobject_t &hoid) const
284 {
285 if (hoid.match(mask_bits, match)) {
286 return true;
287 }
288 derr << __func__ << " " << hoid << " mask_bits " << mask_bits
289 << " match 0x" << std::hex << match << std::dec << " is false"
290 << dendl;
291 return false;
292 }
293
294 int SnapMapper::get_snaps(const hobject_t &oid, object_snaps *out) const
295 {
296 auto snaps = get_snaps_common(oid);
297 if (snaps) {
298 *out = *snaps;
299 return 0;
300 }
301 switch (auto e = snaps.error(); e.code) {
302 case code_t::backend_error:
303 return e.backend_error;
304 case code_t::not_found:
305 return -ENOENT;
306 case code_t::inconsistent:
307 // As this is a legacy interface, we cannot surprise the user with
308 // a new error code here.
309 return -ENOENT;
310 default:
311 // Can't happen. Just to keep the compiler happy.
312 ceph_abort("get_snaps_common() returned invalid error code");
313 }
314 }
315
316 tl::expected<std::set<snapid_t>, Scrub::SnapMapReaderI::result_t>
317 SnapMapper::get_snaps(const hobject_t &oid) const
318 {
319 auto snaps = get_snaps_common(oid);
320 if (snaps) {
321 return snaps->snaps;
322 }
323 return tl::unexpected(snaps.error());
324 }
325
326 tl::expected<SnapMapper::object_snaps, Scrub::SnapMapReaderI::result_t>
327 SnapMapper::get_snaps_common(const hobject_t &oid) const
328 {
329 ceph_assert(check(oid));
330 set<string> keys{to_object_key(oid)};
331 dout(20) << fmt::format("{}: key string: {} oid:{}", __func__, keys, oid)
332 << dendl;
333
334 map<string, ceph::buffer::list> got;
335 int r = backend.get_keys(keys, &got);
336 if (r < 0) {
337 dout(10) << __func__ << " " << oid << " got err " << r << dendl;
338 return tl::unexpected(result_t{code_t::backend_error, r});
339 }
340 if (got.empty()) {
341 dout(10) << __func__ << " " << oid << " got.empty()" << dendl;
342 return tl::unexpected(result_t{code_t::not_found, -ENOENT});
343 }
344
345 object_snaps out;
346 auto bp = got.begin()->second.cbegin();
347 try {
348 decode(out, bp);
349 } catch (...) {
350 dout(1) << __func__ << ": " << oid << " decode failed" << dendl;
351 return tl::unexpected(result_t{code_t::backend_error, -EIO});
352 }
353
354 dout(20) << __func__ << " " << oid << " " << out.snaps << dendl;
355 if (out.snaps.empty()) {
356 dout(1) << __func__ << " " << oid << " empty snapset" << dendl;
357 ceph_assert(!cct->_conf->osd_debug_verify_snaps);
358 }
359 return out;
360 }
361
362 std::set<std::string> SnapMapper::to_raw_keys(
363 const hobject_t &clone,
364 const std::set<snapid_t> &snaps) const
365 {
366 std::set<std::string> keys;
367 for (auto snap : snaps) {
368 keys.insert(to_raw_key(snap, clone));
369 }
370 dout(20) << fmt::format(
371 "{}: clone:{} snaps:{} -> keys: {}", __func__, clone, snaps,
372 keys)
373 << dendl;
374 return keys;
375 }
376
377 tl::expected<std::set<snapid_t>, result_t>
378 SnapMapper::get_snaps_check_consistency(const hobject_t &hoid) const
379 {
380 // derive the set of snaps from the 'OBJ_' entry
381 auto obj_snaps = get_snaps(hoid);
382 if (!obj_snaps) {
383 return obj_snaps;
384 }
385
386 // make sure we have the expected set of SNA_ entries:
387 // we have the clone oid and the set of snaps relevant to this clone.
388 // Let's construct all expected SNA_ key, then fetch them.
389
390 auto mapping_keys = to_raw_keys(hoid, *obj_snaps);
391 map<string, ceph::buffer::list> kvmap;
392 auto r = backend.get_keys(mapping_keys, &kvmap);
393 if (r < 0) {
394 dout(10) << fmt::format(
395 "{}: backend error ({}) for cobject {}", __func__, r, hoid)
396 << dendl;
397 // that's a backend error, but for the SNA_ entries. Let's treat it as an
398 // internal consistency error (although a backend error would have made
399 // sense too).
400 return tl::unexpected(result_t{code_t::inconsistent, r});
401 }
402
403 std::set<snapid_t> snaps_from_mapping;
404 for (auto &[k, v] : kvmap) {
405 dout(20) << __func__ << " " << hoid << " " << k << dendl;
406 // extract the object ID from the value fetched for an SNA mapping key
407 auto [sn, obj] = SnapMapper::from_raw(v);
408 if (obj != hoid) {
409 dout(1) << fmt::format(
410 "{}: unexpected object ID {} for key{} (expected {})",
411 __func__, obj, k, hoid)
412 << dendl;
413 return tl::unexpected(result_t{code_t::inconsistent});
414 }
415 snaps_from_mapping.insert(sn);
416 }
417
418 if (snaps_from_mapping != *obj_snaps) {
419 dout(10) << fmt::format(
420 "{}: hoid:{} -> mapper internal inconsistency ({} vs {})",
421 __func__, hoid, *obj_snaps, snaps_from_mapping)
422 << dendl;
423 return tl::unexpected(result_t{code_t::inconsistent});
424 }
425 dout(10) << fmt::format(
426 "{}: snaps for {}: {}", __func__, hoid, snaps_from_mapping)
427 << dendl;
428 return obj_snaps;
429 }
430
431 void SnapMapper::clear_snaps(
432 const hobject_t &oid,
433 MapCacher::Transaction<std::string, ceph::buffer::list> *t)
434 {
435 dout(20) << __func__ << " " << oid << dendl;
436 ceph_assert(check(oid));
437 set<string> to_remove;
438 to_remove.insert(to_object_key(oid));
439 if (g_conf()->subsys.should_gather<ceph_subsys_osd, 20>()) {
440 for (auto& i : to_remove) {
441 dout(20) << __func__ << " rm " << i << dendl;
442 }
443 }
444 backend.remove_keys(to_remove, t);
445 }
446
447 void SnapMapper::set_snaps(
448 const hobject_t &oid,
449 const object_snaps &in,
450 MapCacher::Transaction<std::string, ceph::buffer::list> *t)
451 {
452 ceph_assert(check(oid));
453 map<string, ceph::buffer::list> to_set;
454 ceph::buffer::list bl;
455 encode(in, bl);
456 to_set[to_object_key(oid)] = bl;
457 dout(20) << __func__ << " " << oid << " " << in.snaps << dendl;
458 if (g_conf()->subsys.should_gather<ceph_subsys_osd, 20>()) {
459 for (auto& i : to_set) {
460 dout(20) << __func__ << " set " << i.first << dendl;
461 }
462 }
463 backend.set_keys(to_set, t);
464 }
465
466 int SnapMapper::update_snaps(
467 const hobject_t &oid,
468 const set<snapid_t> &new_snaps,
469 const set<snapid_t> *old_snaps_check,
470 MapCacher::Transaction<std::string, ceph::buffer::list> *t)
471 {
472 dout(20) << __func__ << " " << oid << " " << new_snaps
473 << " was " << (old_snaps_check ? *old_snaps_check : set<snapid_t>())
474 << dendl;
475 ceph_assert(check(oid));
476 if (new_snaps.empty())
477 return remove_oid(oid, t);
478
479 object_snaps out;
480 int r = get_snaps(oid, &out);
481 // Tolerate missing keys but not disk errors
482 if (r < 0 && r != -ENOENT)
483 return r;
484 if (old_snaps_check)
485 ceph_assert(out.snaps == *old_snaps_check);
486
487 object_snaps in(oid, new_snaps);
488 set_snaps(oid, in, t);
489
490 set<string> to_remove;
491 for (set<snapid_t>::iterator i = out.snaps.begin();
492 i != out.snaps.end();
493 ++i) {
494 if (!new_snaps.count(*i)) {
495 to_remove.insert(to_raw_key(make_pair(*i, oid)));
496 }
497 }
498 if (g_conf()->subsys.should_gather<ceph_subsys_osd, 20>()) {
499 for (auto& i : to_remove) {
500 dout(20) << __func__ << " rm " << i << dendl;
501 }
502 }
503 backend.remove_keys(to_remove, t);
504 return 0;
505 }
506
507 void SnapMapper::add_oid(
508 const hobject_t &oid,
509 const set<snapid_t>& snaps,
510 MapCacher::Transaction<std::string, ceph::buffer::list> *t)
511 {
512 dout(20) << __func__ << " " << oid << " " << snaps << dendl;
513 ceph_assert(!snaps.empty());
514 ceph_assert(check(oid));
515 {
516 object_snaps out;
517 int r = get_snaps(oid, &out);
518 if (r != -ENOENT) {
519 derr << __func__ << " found existing snaps mapped on " << oid
520 << ", removing" << dendl;
521 ceph_assert(!cct->_conf->osd_debug_verify_snaps);
522 remove_oid(oid, t);
523 }
524 }
525
526 object_snaps _snaps(oid, snaps);
527 set_snaps(oid, _snaps, t);
528
529 map<string, ceph::buffer::list> to_add;
530 for (set<snapid_t>::iterator i = snaps.begin();
531 i != snaps.end();
532 ++i) {
533 to_add.insert(to_raw(make_pair(*i, oid)));
534 }
535 if (g_conf()->subsys.should_gather<ceph_subsys_osd, 20>()) {
536 for (auto& i : to_add) {
537 dout(20) << __func__ << " set " << i.first << dendl;
538 }
539 }
540 backend.set_keys(to_add, t);
541 }
542
543 int SnapMapper::get_next_objects_to_trim(
544 snapid_t snap,
545 unsigned max,
546 vector<hobject_t> *out)
547 {
548 ceph_assert(out);
549 ceph_assert(out->empty());
550
551 // if max would be 0, we return ENOENT and the caller would mistakenly
552 // trim the snaptrim queue
553 ceph_assert(max > 0);
554 int r = 0;
555
556 /// \todo cache the prefixes-set in update_bits()
557 for (set<string>::iterator i = prefixes.begin();
558 i != prefixes.end() && out->size() < max && r == 0;
559 ++i) {
560 string prefix(get_prefix(pool, snap) + *i);
561 string pos = prefix;
562 while (out->size() < max) {
563 pair<string, ceph::buffer::list> next;
564 r = backend.get_next(pos, &next);
565 dout(20) << __func__ << " get_next(" << pos << ") returns " << r
566 << " " << next << dendl;
567 if (r != 0) {
568 break; // Done
569 }
570
571 if (next.first.substr(0, prefix.size()) !=
572 prefix) {
573 break; // Done with this prefix
574 }
575
576 ceph_assert(is_mapping(next.first));
577
578 dout(20) << __func__ << " " << next.first << dendl;
579 pair<snapid_t, hobject_t> next_decoded(from_raw(next));
580 ceph_assert(next_decoded.first == snap);
581 ceph_assert(check(next_decoded.second));
582
583 out->push_back(next_decoded.second);
584 pos = next.first;
585 }
586 }
587 if (out->size() == 0) {
588 return -ENOENT;
589 } else {
590 return 0;
591 }
592 }
593
594
595 int SnapMapper::remove_oid(
596 const hobject_t &oid,
597 MapCacher::Transaction<std::string, ceph::buffer::list> *t)
598 {
599 dout(20) << __func__ << " " << oid << dendl;
600 ceph_assert(check(oid));
601 return _remove_oid(oid, t);
602 }
603
604 int SnapMapper::_remove_oid(
605 const hobject_t &oid,
606 MapCacher::Transaction<std::string, ceph::buffer::list> *t)
607 {
608 dout(20) << __func__ << " " << oid << dendl;
609 object_snaps out;
610 int r = get_snaps(oid, &out);
611 if (r < 0)
612 return r;
613
614 clear_snaps(oid, t);
615
616 set<string> to_remove;
617 for (set<snapid_t>::iterator i = out.snaps.begin();
618 i != out.snaps.end();
619 ++i) {
620 to_remove.insert(to_raw_key(make_pair(*i, oid)));
621 }
622 if (g_conf()->subsys.should_gather<ceph_subsys_osd, 20>()) {
623 for (auto& i : to_remove) {
624 dout(20) << __func__ << " rm " << i << dendl;
625 }
626 }
627 backend.remove_keys(to_remove, t);
628 return 0;
629 }
630
631 int SnapMapper::get_snaps(
632 const hobject_t &oid,
633 std::set<snapid_t> *snaps) const
634 {
635 ceph_assert(check(oid));
636 object_snaps out;
637 int r = get_snaps(oid, &out);
638 if (r < 0)
639 return r;
640 if (snaps)
641 snaps->swap(out.snaps);
642 return 0;
643 }
644
645
646 // -- purged snaps --
647
648 string SnapMapper::make_purged_snap_key(int64_t pool, snapid_t last)
649 {
650 return fmt::sprintf("%s_%lld_%016llx",
651 PURGED_SNAP_PREFIX,
652 pool,
653 last);
654 }
655
656 void SnapMapper::make_purged_snap_key_value(
657 int64_t pool, snapid_t begin, snapid_t end, map<string,ceph::buffer::list> *m)
658 {
659 string k = make_purged_snap_key(pool, end - 1);
660 auto& v = (*m)[k];
661 ceph::encode(pool, v);
662 ceph::encode(begin, v);
663 ceph::encode(end, v);
664 }
665
666 int SnapMapper::_lookup_purged_snap(
667 CephContext *cct,
668 OSDriver& backend,
669 int64_t pool, snapid_t snap,
670 snapid_t *begin, snapid_t *end)
671 {
672 string k = make_purged_snap_key(pool, snap);
673 std::pair<std::string, ceph::buffer::list> kv;
674 if (auto ret = backend.get_next_or_current(k, &kv); ret == -ENOENT) {
675 dout(20) << __func__ << " pool " << pool << " snap " << snap
676 << " key '" << k << "' lower_bound not found" << dendl;
677 return -ENOENT;
678 }
679 if (kv.first.find(PURGED_SNAP_PREFIX) != 0) {
680 dout(20) << __func__ << " pool " << pool << " snap " << snap
681 << " key '" << k << "' lower_bound got mismatched prefix '"
682 << kv.first << "'" << dendl;
683 return -ENOENT;
684 }
685 ceph::buffer::list v = kv.second;
686 auto p = v.cbegin();
687 int64_t gotpool;
688 decode(gotpool, p);
689 decode(*begin, p);
690 decode(*end, p);
691 if (snap < *begin || snap >= *end) {
692 dout(20) << __func__ << " pool " << pool << " snap " << snap
693 << " found [" << *begin << "," << *end << "), no overlap" << dendl;
694 return -ENOENT;
695 }
696 return 0;
697 }
698
699 void SnapMapper::record_purged_snaps(
700 CephContext *cct,
701 OSDriver& backend,
702 OSDriver::OSTransaction&& txn,
703 map<epoch_t,mempool::osdmap::map<int64_t,snap_interval_set_t>> purged_snaps)
704 {
705 dout(10) << __func__ << " purged_snaps " << purged_snaps << dendl;
706 map<string,ceph::buffer::list> m;
707 set<string> rm;
708 for (auto& [epoch, bypool] : purged_snaps) {
709 // index by (pool, snap)
710 for (auto& [pool, snaps] : bypool) {
711 for (auto i = snaps.begin();
712 i != snaps.end();
713 ++i) {
714 snapid_t begin = i.get_start();
715 snapid_t end = i.get_end();
716 snapid_t before_begin, before_end;
717 snapid_t after_begin, after_end;
718 int b = _lookup_purged_snap(cct, backend,
719 pool, begin - 1, &before_begin, &before_end);
720 int a = _lookup_purged_snap(cct, backend,
721 pool, end, &after_begin, &after_end);
722 if (!b && !a) {
723 dout(10) << __func__
724 << " [" << begin << "," << end << ") - joins ["
725 << before_begin << "," << before_end << ") and ["
726 << after_begin << "," << after_end << ")" << dendl;
727 // erase only the begin record; we'll overwrite the end one
728 rm.insert(make_purged_snap_key(pool, before_end - 1));
729 make_purged_snap_key_value(pool, before_begin, after_end, &m);
730 } else if (!b) {
731 dout(10) << __func__
732 << " [" << begin << "," << end << ") - join with earlier ["
733 << before_begin << "," << before_end << ")" << dendl;
734 rm.insert(make_purged_snap_key(pool, before_end - 1));
735 make_purged_snap_key_value(pool, before_begin, end, &m);
736 } else if (!a) {
737 dout(10) << __func__
738 << " [" << begin << "," << end << ") - join with later ["
739 << after_begin << "," << after_end << ")" << dendl;
740 // overwrite after record
741 make_purged_snap_key_value(pool, begin, after_end, &m);
742 } else {
743 make_purged_snap_key_value(pool, begin, end, &m);
744 }
745 }
746 }
747 }
748 txn.remove_keys(rm);
749 txn.set_keys(m);
750 dout(10) << __func__ << " rm " << rm.size() << " keys, set " << m.size()
751 << " keys" << dendl;
752 }
753
754
755 #ifndef WITH_SEASTAR
756 bool SnapMapper::Scrubber::_parse_p()
757 {
758 if (!psit->valid()) {
759 pool = -1;
760 return false;
761 }
762 if (psit->key().find(PURGED_SNAP_PREFIX) != 0) {
763 pool = -1;
764 return false;
765 }
766 ceph::buffer::list v = psit->value();
767 auto p = v.cbegin();
768 ceph::decode(pool, p);
769 ceph::decode(begin, p);
770 ceph::decode(end, p);
771 dout(20) << __func__ << " purged_snaps pool " << pool
772 << " [" << begin << "," << end << ")" << dendl;
773 psit->next();
774 return true;
775 }
776
777 bool SnapMapper::Scrubber::_parse_m()
778 {
779 if (!mapit->valid()) {
780 return false;
781 }
782 if (mapit->key().find(MAPPING_PREFIX) != 0) {
783 return false;
784 }
785 auto v = mapit->value();
786 auto p = v.cbegin();
787 mapping.decode(p);
788
789 {
790 unsigned long long p, s;
791 long sh;
792 string k = mapit->key();
793 int r = sscanf(k.c_str(), "SNA_%lld_%llx.%lx", &p, &s, &sh);
794 if (r != 1) {
795 shard = shard_id_t::NO_SHARD;
796 } else {
797 shard = shard_id_t(sh);
798 }
799 }
800 dout(20) << __func__ << " mapping pool " << mapping.hoid.pool
801 << " snap " << mapping.snap
802 << " shard " << shard
803 << " " << mapping.hoid << dendl;
804 mapit->next();
805 return true;
806 }
807
808 void SnapMapper::Scrubber::run()
809 {
810 dout(10) << __func__ << dendl;
811
812 psit = store->get_omap_iterator(ch, purged_snaps_hoid);
813 psit->upper_bound(PURGED_SNAP_PREFIX);
814 _parse_p();
815
816 mapit = store->get_omap_iterator(ch, mapping_hoid);
817 mapit->upper_bound(MAPPING_PREFIX);
818
819 while (_parse_m()) {
820 // advance to next purged_snaps range?
821 while (pool >= 0 &&
822 (mapping.hoid.pool > pool ||
823 (mapping.hoid.pool == pool && mapping.snap >= end))) {
824 _parse_p();
825 }
826 if (pool < 0) {
827 dout(10) << __func__ << " passed final purged_snaps interval, rest ok"
828 << dendl;
829 break;
830 }
831 if (mapping.hoid.pool < pool ||
832 mapping.snap < begin) {
833 // ok
834 dout(20) << __func__ << " ok " << mapping.hoid
835 << " snap " << mapping.snap
836 << " precedes pool " << pool
837 << " purged_snaps [" << begin << "," << end << ")" << dendl;
838 } else {
839 assert(mapping.snap >= begin &&
840 mapping.snap < end &&
841 mapping.hoid.pool == pool);
842 // invalid
843 dout(10) << __func__ << " stray " << mapping.hoid
844 << " snap " << mapping.snap
845 << " in pool " << pool
846 << " shard " << shard
847 << " purged_snaps [" << begin << "," << end << ")" << dendl;
848 stray.emplace_back(std::tuple<int64_t,snapid_t,uint32_t,shard_id_t>(
849 pool, mapping.snap, mapping.hoid.get_hash(),
850 shard
851 ));
852 }
853 }
854
855 dout(10) << __func__ << " end, found " << stray.size() << " stray" << dendl;
856 psit = ObjectMap::ObjectMapIterator();
857 mapit = ObjectMap::ObjectMapIterator();
858 }
859 #endif // !WITH_SEASTAR
860
861
862 // -------------------------------------
863 // legacy conversion/support
864
865 string SnapMapper::get_legacy_prefix(snapid_t snap)
866 {
867 return fmt::sprintf("%s%.16X_",
868 LEGACY_MAPPING_PREFIX,
869 snap);
870 }
871
872 string SnapMapper::to_legacy_raw_key(
873 const pair<snapid_t, hobject_t> &in)
874 {
875 return get_legacy_prefix(in.first) + shard_prefix + in.second.to_str();
876 }
877
878 bool SnapMapper::is_legacy_mapping(const string &to_test)
879 {
880 return to_test.substr(0, LEGACY_MAPPING_PREFIX.size()) ==
881 LEGACY_MAPPING_PREFIX;
882 }
883
884 #ifndef WITH_SEASTAR
885 /* Octopus modified the SnapMapper key format from
886 *
887 * <LEGACY_MAPPING_PREFIX><snapid>_<shardid>_<hobject_t::to_str()>
888 *
889 * to
890 *
891 * <MAPPING_PREFIX><pool>_<snapid>_<shardid>_<hobject_t::to_str()>
892 *
893 * We can't reconstruct the new key format just from the value since the
894 * Mapping object contains an hobject rather than a ghobject. Instead,
895 * we exploit the fact that the new format is identical starting at <snapid>.
896 *
897 * Note that the original version of this conversion introduced in 94ebe0ea
898 * had a crucial bug which essentially destroyed legacy keys by mapping
899 * them to
900 *
901 * <MAPPING_PREFIX><poolid>_<snapid>_
902 *
903 * without the object-unique suffix.
904 * See https://tracker.ceph.com/issues/56147
905 */
906 std::string SnapMapper::convert_legacy_key(
907 const std::string& old_key,
908 const ceph::buffer::list& value)
909 {
910 auto old = from_raw(make_pair(old_key, value));
911 std::string object_suffix = old_key.substr(
912 SnapMapper::LEGACY_MAPPING_PREFIX.length());
913 return SnapMapper::MAPPING_PREFIX + std::to_string(old.second.pool)
914 + "_" + object_suffix;
915 }
916
917 int SnapMapper::convert_legacy(
918 CephContext *cct,
919 ObjectStore *store,
920 ObjectStore::CollectionHandle& ch,
921 ghobject_t hoid,
922 unsigned max)
923 {
924 uint64_t n = 0;
925
926 ObjectMap::ObjectMapIterator iter = store->get_omap_iterator(ch, hoid);
927 if (!iter) {
928 return -EIO;
929 }
930
931 auto start = ceph::mono_clock::now();
932
933 iter->upper_bound(SnapMapper::LEGACY_MAPPING_PREFIX);
934 map<string,ceph::buffer::list> to_set;
935 while (iter->valid()) {
936 bool valid = SnapMapper::is_legacy_mapping(iter->key());
937 if (valid) {
938 to_set.emplace(
939 convert_legacy_key(iter->key(), iter->value()),
940 iter->value());
941 ++n;
942 iter->next();
943 }
944 if (!valid || !iter->valid() || to_set.size() >= max) {
945 ObjectStore::Transaction t;
946 t.omap_setkeys(ch->cid, hoid, to_set);
947 int r = store->queue_transaction(ch, std::move(t));
948 ceph_assert(r == 0);
949 to_set.clear();
950 if (!valid) {
951 break;
952 }
953 dout(10) << __func__ << " converted " << n << " keys" << dendl;
954 }
955 }
956
957 auto end = ceph::mono_clock::now();
958
959 dout(1) << __func__ << " converted " << n << " keys in "
960 << timespan_str(end - start) << dendl;
961
962 // remove the old keys
963 {
964 ObjectStore::Transaction t;
965 string end = SnapMapper::LEGACY_MAPPING_PREFIX;
966 ++end[end.size()-1]; // turn _ to whatever comes after _
967 t.omap_rmkeyrange(ch->cid, hoid,
968 SnapMapper::LEGACY_MAPPING_PREFIX,
969 end);
970 int r = store->queue_transaction(ch, std::move(t));
971 ceph_assert(r == 0);
972 }
973 return 0;
974 }
975 #endif // !WITH_SEASTAR