]>
git.proxmox.com Git - ceph.git/blob - 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
4 * Ceph - scalable distributed file system
6 * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
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.
15 #include "SnapMapper.h"
17 #include <fmt/printf.h>
18 #include <fmt/ranges.h>
20 #include "global/global_context.h"
21 #include "osd/osd_types_fmt.h"
22 #include "SnapMapReaderI.h"
24 #define dout_context cct
25 #define dout_subsys ceph_subsys_osd
27 #define dout_prefix *_dout << "snap_mapper."
38 using ceph::timespan_str
;
39 using result_t
= Scrub::SnapMapReaderI::result_t
;
40 using code_t
= Scrub::SnapMapReaderI::result_t::code_t
;
43 const string
SnapMapper::LEGACY_MAPPING_PREFIX
= "MAP_";
44 const string
SnapMapper::MAPPING_PREFIX
= "SNA_";
45 const string
SnapMapper::OBJECT_PREFIX
= "OBJ_";
47 const char *SnapMapper::PURGED_SNAP_PREFIX
= "PSN_";
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).
62 + hobject_t::to_str() ("%llx.%8x.%lx.name...." % pool, hash, snap)
63 -> SnapMapping::Mapping { snap, hoid }
72 + hobject_t::to_str() ("%llx.%8x.%lx.name...." % pool, hash, snap)
73 -> SnapMapping::Mapping { snap, hoid }
78 -> SnapMapper::object_snaps { oid, set<snapid_t> }
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
>;
90 ::crimson::interruptible::interruptor
<
91 ::crimson::osd::IOInterruptCondition
>;
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
)
98 CRIMSON_DEBUG("OSDriver::{}:{}", __func__
, __LINE__
);
99 using crimson::os::FuturizedStore
;
100 return interruptor::green_get(os
->omap_get_values(
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
);
106 }, FuturizedStore::Shard::read_errorator::all_same_way([] (auto& e
) {
107 assert(e
.value() > 0);
109 }))); // this requires seastar::thread
110 CRIMSON_DEBUG("OSDriver::{}:{}", __func__
, __LINE__
);
113 int OSDriver::get_next(
114 const std::string
&key
,
115 std::pair
<std::string
, ceph::buffer::list
> *next
)
117 CRIMSON_DEBUG("OSDriver::{}:{}", __func__
, __LINE__
);
118 using crimson::os::FuturizedStore
;
119 return interruptor::green_get(os
->omap_get_values(
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__
);
127 CRIMSON_DEBUG("OSDriver::{}:{}", "get_next", __LINE__
);
128 assert(nit
->first
> key
);
132 }, FuturizedStore::Shard::read_errorator::all_same_way([] {
133 CRIMSON_DEBUG("OSDriver::{}:{}", "get_next", __LINE__
);
135 }))); // this requires seastar::thread
136 CRIMSON_DEBUG("OSDriver::{}:{}", __func__
, __LINE__
);
139 int OSDriver::get_next_or_current(
140 const std::string
&key
,
141 std::pair
<std::string
, ceph::buffer::list
> *next_or_current
)
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]));
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__
);
160 int OSDriver::get_keys(
161 const std::set
<std::string
> &keys
,
162 std::map
<std::string
, ceph::buffer::list
> *out
)
164 return os
->omap_get_values(ch
, hoid
, keys
, out
);
167 int OSDriver::get_next(
168 const std::string
&key
,
169 std::pair
<std::string
, ceph::buffer::list
> *next
)
171 ObjectMap::ObjectMapIterator iter
=
172 os
->get_omap_iterator(ch
, hoid
);
177 iter
->upper_bound(key
);
180 *next
= make_pair(iter
->key(), iter
->value());
187 int OSDriver::get_next_or_current(
188 const std::string
&key
,
189 std::pair
<std::string
, ceph::buffer::list
> *next_or_current
)
191 ObjectMap::ObjectMapIterator iter
=
192 os
->get_omap_iterator(ch
, hoid
);
197 iter
->lower_bound(key
);
200 *next_or_current
= make_pair(iter
->key(), iter
->value());
206 #endif // WITH_SEASTAR
208 string
SnapMapper::get_prefix(int64_t pool
, snapid_t snap
)
210 static_assert(sizeof(pool
) == 8, "assumed by the formatting code");
211 return fmt::sprintf("%s%lld_%.16X_",
217 string
SnapMapper::to_raw_key(
218 const pair
<snapid_t
, hobject_t
> &in
) const
220 return get_prefix(in
.second
.pool
, in
.first
) + shard_prefix
+ in
.second
.to_str();
223 std::string
SnapMapper::to_raw_key(snapid_t snap
, const hobject_t
&clone
) const
225 return get_prefix(clone
.pool
, snap
) + shard_prefix
+ clone
.to_str();
228 pair
<string
, ceph::buffer::list
> SnapMapper::to_raw(
229 const pair
<snapid_t
, hobject_t
> &in
) const
231 ceph::buffer::list bl
;
232 encode(Mapping(in
), bl
);
233 return make_pair(to_raw_key(in
), bl
);
236 pair
<snapid_t
, hobject_t
> SnapMapper::from_raw(
237 const pair
<std::string
, ceph::buffer::list
> &image
)
241 ceph::buffer::list
bl(image
.second
);
242 auto bp
= bl
.cbegin();
244 return make_pair(map
.snap
, map
.hoid
);
247 std::pair
<snapid_t
, hobject_t
> SnapMapper::from_raw(
248 const ceph::buffer::list
&image
)
252 auto bp
= image
.cbegin();
254 return make_pair(map
.snap
, map
.hoid
);
257 bool SnapMapper::is_mapping(const string
&to_test
)
259 return to_test
.substr(0, MAPPING_PREFIX
.size()) == MAPPING_PREFIX
;
262 string
SnapMapper::to_object_key(const hobject_t
&hoid
) const
264 return OBJECT_PREFIX
+ shard_prefix
+ hoid
.to_str();
267 void SnapMapper::object_snaps::encode(ceph::buffer::list
&bl
) const
269 ENCODE_START(1, 1, bl
);
275 void SnapMapper::object_snaps::decode(ceph::buffer::list::const_iterator
&bl
)
283 bool SnapMapper::check(const hobject_t
&hoid
) const
285 if (hoid
.match(mask_bits
, match
)) {
288 derr
<< __func__
<< " " << hoid
<< " mask_bits " << mask_bits
289 << " match 0x" << std::hex
<< match
<< std::dec
<< " is false"
294 int SnapMapper::get_snaps(const hobject_t
&oid
, object_snaps
*out
) const
296 auto snaps
= get_snaps_common(oid
);
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
:
306 case code_t::inconsistent
:
307 // As this is a legacy interface, we cannot surprise the user with
308 // a new error code here.
311 // Can't happen. Just to keep the compiler happy.
312 ceph_abort("get_snaps_common() returned invalid error code");
316 tl::expected
<std::set
<snapid_t
>, Scrub::SnapMapReaderI::result_t
>
317 SnapMapper::get_snaps(const hobject_t
&oid
) const
319 auto snaps
= get_snaps_common(oid
);
323 return tl::unexpected(snaps
.error());
326 tl::expected
<SnapMapper::object_snaps
, Scrub::SnapMapReaderI::result_t
>
327 SnapMapper::get_snaps_common(const hobject_t
&oid
) const
329 ceph_assert(check(oid
));
330 set
<string
> keys
{to_object_key(oid
)};
331 dout(20) << fmt::format("{}: key string: {} oid:{}", __func__
, keys
, oid
)
334 map
<string
, ceph::buffer::list
> got
;
335 int r
= backend
.get_keys(keys
, &got
);
337 dout(10) << __func__
<< " " << oid
<< " got err " << r
<< dendl
;
338 return tl::unexpected(result_t
{code_t::backend_error
, r
});
341 dout(10) << __func__
<< " " << oid
<< " got.empty()" << dendl
;
342 return tl::unexpected(result_t
{code_t::not_found
, -ENOENT
});
346 auto bp
= got
.begin()->second
.cbegin();
350 dout(1) << __func__
<< ": " << oid
<< " decode failed" << dendl
;
351 return tl::unexpected(result_t
{code_t::backend_error
, -EIO
});
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
);
362 std::set
<std::string
> SnapMapper::to_raw_keys(
363 const hobject_t
&clone
,
364 const std::set
<snapid_t
> &snaps
) const
366 std::set
<std::string
> keys
;
367 for (auto snap
: snaps
) {
368 keys
.insert(to_raw_key(snap
, clone
));
370 dout(20) << fmt::format(
371 "{}: clone:{} snaps:{} -> keys: {}", __func__
, clone
, snaps
,
377 tl::expected
<std::set
<snapid_t
>, result_t
>
378 SnapMapper::get_snaps_check_consistency(const hobject_t
&hoid
) const
380 // derive the set of snaps from the 'OBJ_' entry
381 auto obj_snaps
= get_snaps(hoid
);
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.
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
);
394 dout(10) << fmt::format(
395 "{}: backend error ({}) for cobject {}", __func__
, r
, hoid
)
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
400 return tl::unexpected(result_t
{code_t::inconsistent
, r
});
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
);
409 dout(1) << fmt::format(
410 "{}: unexpected object ID {} for key{} (expected {})",
411 __func__
, obj
, k
, hoid
)
413 return tl::unexpected(result_t
{code_t::inconsistent
});
415 snaps_from_mapping
.insert(sn
);
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
)
423 return tl::unexpected(result_t
{code_t::inconsistent
});
425 dout(10) << fmt::format(
426 "{}: snaps for {}: {}", __func__
, hoid
, snaps_from_mapping
)
431 void SnapMapper::clear_snaps(
432 const hobject_t
&oid
,
433 MapCacher::Transaction
<std::string
, ceph::buffer::list
> *t
)
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
;
444 backend
.remove_keys(to_remove
, t
);
447 void SnapMapper::set_snaps(
448 const hobject_t
&oid
,
449 const object_snaps
&in
,
450 MapCacher::Transaction
<std::string
, ceph::buffer::list
> *t
)
452 ceph_assert(check(oid
));
453 map
<string
, ceph::buffer::list
> to_set
;
454 ceph::buffer::list 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
;
463 backend
.set_keys(to_set
, t
);
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
)
472 dout(20) << __func__
<< " " << oid
<< " " << new_snaps
473 << " was " << (old_snaps_check
? *old_snaps_check
: set
<snapid_t
>())
475 ceph_assert(check(oid
));
476 if (new_snaps
.empty())
477 return remove_oid(oid
, t
);
480 int r
= get_snaps(oid
, &out
);
481 // Tolerate missing keys but not disk errors
482 if (r
< 0 && r
!= -ENOENT
)
485 ceph_assert(out
.snaps
== *old_snaps_check
);
487 object_snaps
in(oid
, new_snaps
);
488 set_snaps(oid
, in
, t
);
490 set
<string
> to_remove
;
491 for (set
<snapid_t
>::iterator i
= out
.snaps
.begin();
492 i
!= out
.snaps
.end();
494 if (!new_snaps
.count(*i
)) {
495 to_remove
.insert(to_raw_key(make_pair(*i
, oid
)));
498 if (g_conf()->subsys
.should_gather
<ceph_subsys_osd
, 20>()) {
499 for (auto& i
: to_remove
) {
500 dout(20) << __func__
<< " rm " << i
<< dendl
;
503 backend
.remove_keys(to_remove
, t
);
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
)
512 dout(20) << __func__
<< " " << oid
<< " " << snaps
<< dendl
;
513 ceph_assert(!snaps
.empty());
514 ceph_assert(check(oid
));
517 int r
= get_snaps(oid
, &out
);
519 derr
<< __func__
<< " found existing snaps mapped on " << oid
520 << ", removing" << dendl
;
521 ceph_assert(!cct
->_conf
->osd_debug_verify_snaps
);
526 object_snaps
_snaps(oid
, snaps
);
527 set_snaps(oid
, _snaps
, t
);
529 map
<string
, ceph::buffer::list
> to_add
;
530 for (set
<snapid_t
>::iterator i
= snaps
.begin();
533 to_add
.insert(to_raw(make_pair(*i
, oid
)));
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
;
540 backend
.set_keys(to_add
, t
);
543 int SnapMapper::get_next_objects_to_trim(
546 vector
<hobject_t
> *out
)
549 ceph_assert(out
->empty());
551 // if max would be 0, we return ENOENT and the caller would mistakenly
552 // trim the snaptrim queue
553 ceph_assert(max
> 0);
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;
560 string
prefix(get_prefix(pool
, snap
) + *i
);
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
;
571 if (next
.first
.substr(0, prefix
.size()) !=
573 break; // Done with this prefix
576 ceph_assert(is_mapping(next
.first
));
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
));
583 out
->push_back(next_decoded
.second
);
587 if (out
->size() == 0) {
595 int SnapMapper::remove_oid(
596 const hobject_t
&oid
,
597 MapCacher::Transaction
<std::string
, ceph::buffer::list
> *t
)
599 dout(20) << __func__
<< " " << oid
<< dendl
;
600 ceph_assert(check(oid
));
601 return _remove_oid(oid
, t
);
604 int SnapMapper::_remove_oid(
605 const hobject_t
&oid
,
606 MapCacher::Transaction
<std::string
, ceph::buffer::list
> *t
)
608 dout(20) << __func__
<< " " << oid
<< dendl
;
610 int r
= get_snaps(oid
, &out
);
616 set
<string
> to_remove
;
617 for (set
<snapid_t
>::iterator i
= out
.snaps
.begin();
618 i
!= out
.snaps
.end();
620 to_remove
.insert(to_raw_key(make_pair(*i
, oid
)));
622 if (g_conf()->subsys
.should_gather
<ceph_subsys_osd
, 20>()) {
623 for (auto& i
: to_remove
) {
624 dout(20) << __func__
<< " rm " << i
<< dendl
;
627 backend
.remove_keys(to_remove
, t
);
631 int SnapMapper::get_snaps(
632 const hobject_t
&oid
,
633 std::set
<snapid_t
> *snaps
) const
635 ceph_assert(check(oid
));
637 int r
= get_snaps(oid
, &out
);
641 snaps
->swap(out
.snaps
);
646 // -- purged snaps --
648 string
SnapMapper::make_purged_snap_key(int64_t pool
, snapid_t last
)
650 return fmt::sprintf("%s_%lld_%016llx",
656 void SnapMapper::make_purged_snap_key_value(
657 int64_t pool
, snapid_t begin
, snapid_t end
, map
<string
,ceph::buffer::list
> *m
)
659 string k
= make_purged_snap_key(pool
, end
- 1);
661 ceph::encode(pool
, v
);
662 ceph::encode(begin
, v
);
663 ceph::encode(end
, v
);
666 int SnapMapper::_lookup_purged_snap(
669 int64_t pool
, snapid_t snap
,
670 snapid_t
*begin
, snapid_t
*end
)
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
;
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
;
685 ceph::buffer::list v
= kv
.second
;
691 if (snap
< *begin
|| snap
>= *end
) {
692 dout(20) << __func__
<< " pool " << pool
<< " snap " << snap
693 << " found [" << *begin
<< "," << *end
<< "), no overlap" << dendl
;
699 void SnapMapper::record_purged_snaps(
702 OSDriver::OSTransaction
&& txn
,
703 map
<epoch_t
,mempool::osdmap::map
<int64_t,snap_interval_set_t
>> purged_snaps
)
705 dout(10) << __func__
<< " purged_snaps " << purged_snaps
<< dendl
;
706 map
<string
,ceph::buffer::list
> m
;
708 for (auto& [epoch
, bypool
] : purged_snaps
) {
709 // index by (pool, snap)
710 for (auto& [pool
, snaps
] : bypool
) {
711 for (auto i
= snaps
.begin();
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
);
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
);
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
);
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
);
743 make_purged_snap_key_value(pool
, begin
, end
, &m
);
750 dout(10) << __func__
<< " rm " << rm
.size() << " keys, set " << m
.size()
756 bool SnapMapper::Scrubber::_parse_p()
758 if (!psit
->valid()) {
762 if (psit
->key().find(PURGED_SNAP_PREFIX
) != 0) {
766 ceph::buffer::list v
= psit
->value();
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
;
777 bool SnapMapper::Scrubber::_parse_m()
779 if (!mapit
->valid()) {
782 if (mapit
->key().find(MAPPING_PREFIX
) != 0) {
785 auto v
= mapit
->value();
790 unsigned long long p
, s
;
792 string k
= mapit
->key();
793 int r
= sscanf(k
.c_str(), "SNA_%lld_%llx.%lx", &p
, &s
, &sh
);
795 shard
= shard_id_t::NO_SHARD
;
797 shard
= shard_id_t(sh
);
800 dout(20) << __func__
<< " mapping pool " << mapping
.hoid
.pool
801 << " snap " << mapping
.snap
802 << " shard " << shard
803 << " " << mapping
.hoid
<< dendl
;
808 void SnapMapper::Scrubber::run()
810 dout(10) << __func__
<< dendl
;
812 psit
= store
->get_omap_iterator(ch
, purged_snaps_hoid
);
813 psit
->upper_bound(PURGED_SNAP_PREFIX
);
816 mapit
= store
->get_omap_iterator(ch
, mapping_hoid
);
817 mapit
->upper_bound(MAPPING_PREFIX
);
820 // advance to next purged_snaps range?
822 (mapping
.hoid
.pool
> pool
||
823 (mapping
.hoid
.pool
== pool
&& mapping
.snap
>= end
))) {
827 dout(10) << __func__
<< " passed final purged_snaps interval, rest ok"
831 if (mapping
.hoid
.pool
< pool
||
832 mapping
.snap
< begin
) {
834 dout(20) << __func__
<< " ok " << mapping
.hoid
835 << " snap " << mapping
.snap
836 << " precedes pool " << pool
837 << " purged_snaps [" << begin
<< "," << end
<< ")" << dendl
;
839 assert(mapping
.snap
>= begin
&&
840 mapping
.snap
< end
&&
841 mapping
.hoid
.pool
== pool
);
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(),
855 dout(10) << __func__
<< " end, found " << stray
.size() << " stray" << dendl
;
856 psit
= ObjectMap::ObjectMapIterator();
857 mapit
= ObjectMap::ObjectMapIterator();
859 #endif // !WITH_SEASTAR
862 // -------------------------------------
863 // legacy conversion/support
865 string
SnapMapper::get_legacy_prefix(snapid_t snap
)
867 return fmt::sprintf("%s%.16X_",
868 LEGACY_MAPPING_PREFIX
,
872 string
SnapMapper::to_legacy_raw_key(
873 const pair
<snapid_t
, hobject_t
> &in
)
875 return get_legacy_prefix(in
.first
) + shard_prefix
+ in
.second
.to_str();
878 bool SnapMapper::is_legacy_mapping(const string
&to_test
)
880 return to_test
.substr(0, LEGACY_MAPPING_PREFIX
.size()) ==
881 LEGACY_MAPPING_PREFIX
;
885 /* Octopus modified the SnapMapper key format from
887 * <LEGACY_MAPPING_PREFIX><snapid>_<shardid>_<hobject_t::to_str()>
891 * <MAPPING_PREFIX><pool>_<snapid>_<shardid>_<hobject_t::to_str()>
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>.
897 * Note that the original version of this conversion introduced in 94ebe0ea
898 * had a crucial bug which essentially destroyed legacy keys by mapping
901 * <MAPPING_PREFIX><poolid>_<snapid>_
903 * without the object-unique suffix.
904 * See https://tracker.ceph.com/issues/56147
906 std::string
SnapMapper::convert_legacy_key(
907 const std::string
& old_key
,
908 const ceph::buffer::list
& value
)
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
;
917 int SnapMapper::convert_legacy(
920 ObjectStore::CollectionHandle
& ch
,
926 ObjectMap::ObjectMapIterator iter
= store
->get_omap_iterator(ch
, hoid
);
931 auto start
= ceph::mono_clock::now();
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());
939 convert_legacy_key(iter
->key(), iter
->value()),
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
));
953 dout(10) << __func__
<< " converted " << n
<< " keys" << dendl
;
957 auto end
= ceph::mono_clock::now();
959 dout(1) << __func__
<< " converted " << n
<< " keys in "
960 << timespan_str(end
- start
) << dendl
;
962 // remove the old keys
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
,
970 int r
= store
->queue_transaction(ch
, std::move(t
));
975 #endif // !WITH_SEASTAR