diff options
Diffstat (limited to 'src/osd/SnapMapper.h')
-rw-r--r-- | src/osd/SnapMapper.h | 378 |
1 files changed, 378 insertions, 0 deletions
diff --git a/src/osd/SnapMapper.h b/src/osd/SnapMapper.h new file mode 100644 index 000000000..eb43a23c2 --- /dev/null +++ b/src/osd/SnapMapper.h @@ -0,0 +1,378 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab +/* + * Ceph - scalable distributed file system + * + * Copyright (C) 2013 Inktank Storage, Inc. + * + * This is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software + * Foundation. See file COPYING. + * + */ + +#ifndef SNAPMAPPER_H +#define SNAPMAPPER_H + +#include <cstring> +#include <set> +#include <string> +#include <utility> + +#include "common/hobject.h" +#include "common/map_cacher.hpp" +#ifdef WITH_SEASTAR +# include "crimson/os/futurized_store.h" +# include "crimson/os/futurized_collection.h" +#endif +#include "include/buffer.h" +#include "include/encoding.h" +#include "include/object.h" +#include "os/ObjectStore.h" +#include "osd/OSDMap.h" +#include "osd/SnapMapReaderI.h" + +class OSDriver : public MapCacher::StoreDriver<std::string, ceph::buffer::list> { +#ifdef WITH_SEASTAR + using ObjectStoreT = crimson::os::FuturizedStore::Shard; + using CollectionHandleT = ObjectStoreT::CollectionRef; +#else + using ObjectStoreT = ObjectStore; + using CollectionHandleT = ObjectStoreT::CollectionHandle; +#endif + + ObjectStoreT *os; + CollectionHandleT ch; + ghobject_t hoid; + +public: + class OSTransaction : public MapCacher::Transaction<std::string, ceph::buffer::list> { + friend class OSDriver; + coll_t cid; + ghobject_t hoid; + ceph::os::Transaction *t; + OSTransaction( + const coll_t &cid, + const ghobject_t &hoid, + ceph::os::Transaction *t) + : cid(cid), hoid(hoid), t(t) {} + public: + void set_keys( + const std::map<std::string, ceph::buffer::list> &to_set) override { + t->omap_setkeys(cid, hoid, to_set); + } + void remove_keys( + const std::set<std::string> &to_remove) override { + t->omap_rmkeys(cid, hoid, to_remove); + } + void add_callback( + Context *c) override { + t->register_on_applied(c); + } + }; + + OSTransaction get_transaction( + ceph::os::Transaction *t) const { + return OSTransaction(ch->get_cid(), hoid, t); + } + +#ifndef WITH_SEASTAR + OSDriver(ObjectStoreT *os, const coll_t& cid, const ghobject_t &hoid) : + OSDriver(os, os->open_collection(cid), hoid) {} +#endif + OSDriver(ObjectStoreT *os, CollectionHandleT ch, const ghobject_t &hoid) : + os(os), + ch(ch), + hoid(hoid) {} + + int get_keys( + const std::set<std::string> &keys, + std::map<std::string, ceph::buffer::list> *out) override; + int get_next( + const std::string &key, + std::pair<std::string, ceph::buffer::list> *next) override; + int get_next_or_current( + const std::string &key, + std::pair<std::string, ceph::buffer::list> *next_or_current) override; +}; + +/** + * SnapMapper + * + * Manages two mappings: + * 1) hobject_t -> {snapid} + * 2) snapid -> {hobject_t} + * + * We accomplish this using two sets of keys: + * 1) OBJECT_PREFIX + obj.str() -> encoding of object_snaps + * 2) MAPPING_PREFIX + poolid + snapid_t + obj.str() -> encoding of std::pair<snapid_t, obj> + * + * The on disk strings and encodings are implemented in to_raw, to_raw_key, + * from_raw, to_object_key. + * + * The object -> {snapid} mapping is primarily included so that the + * SnapMapper state can be verified against the external PG state during + * scrub etc. + * + * The 2) mapping is arranged such that all objects in a particular + * snap will sort together, and so that all objects in a pg for a + * particular snap will group under up to 8 prefixes. + */ +class SnapMapper : public Scrub::SnapMapReaderI { + friend class MapperVerifier; // unit-test support + friend class DirectMapper; // unit-test support +public: + CephContext* cct; + struct object_snaps { + hobject_t oid; + std::set<snapid_t> snaps; + object_snaps(hobject_t oid, const std::set<snapid_t> &snaps) + : oid(oid), snaps(snaps) {} + object_snaps() {} + void encode(ceph::buffer::list &bl) const; + void decode(ceph::buffer::list::const_iterator &bp); + }; + + struct Mapping { + snapid_t snap; + hobject_t hoid; + explicit Mapping(const std::pair<snapid_t, hobject_t> &in) + : snap(in.first), hoid(in.second) {} + Mapping() : snap(0) {} + void encode(ceph::buffer::list &bl) const { + ENCODE_START(1, 1, bl); + encode(snap, bl); + encode(hoid, bl); + ENCODE_FINISH(bl); + } + void decode(ceph::buffer::list::const_iterator &bl) { + DECODE_START(1, bl); + decode(snap, bl); + decode(hoid, bl); + DECODE_FINISH(bl); + } + }; + + static const std::string LEGACY_MAPPING_PREFIX; + static const std::string MAPPING_PREFIX; + static const std::string OBJECT_PREFIX; + static const char *PURGED_SNAP_EPOCH_PREFIX; + static const char *PURGED_SNAP_PREFIX; + +#ifndef WITH_SEASTAR + struct Scrubber { + CephContext *cct; + ObjectStore *store; + ObjectStore::CollectionHandle ch; + ghobject_t mapping_hoid; + ghobject_t purged_snaps_hoid; + + ObjectMap::ObjectMapIterator psit; + int64_t pool; + snapid_t begin, end; + + bool _parse_p(); ///< advance the purged_snaps pointer + + ObjectMap::ObjectMapIterator mapit; + Mapping mapping; + shard_id_t shard; + + bool _parse_m(); ///< advance the (object) mapper pointer + + std::vector<std::tuple<int64_t, snapid_t, uint32_t, shard_id_t>> stray; + + Scrubber( + CephContext *cct, + ObjectStore *store, + ObjectStore::CollectionHandle& ch, + ghobject_t mapping_hoid, + ghobject_t purged_snaps_hoid) + : cct(cct), + store(store), + ch(ch), + mapping_hoid(mapping_hoid), + purged_snaps_hoid(purged_snaps_hoid) {} + + void run(); + }; + + static std::string convert_legacy_key( + const std::string& old_key, + const bufferlist& value); + + static int convert_legacy( + CephContext *cct, + ObjectStore *store, + ObjectStore::CollectionHandle& ch, + ghobject_t hoid, + unsigned max); +#endif + + static void record_purged_snaps( + CephContext *cct, + OSDriver& backend, + OSDriver::OSTransaction&& txn, + std::map<epoch_t,mempool::osdmap::map<int64_t,snap_interval_set_t>> purged_snaps); + +private: + static int _lookup_purged_snap( + CephContext *cct, + OSDriver& backend, + int64_t pool, snapid_t snap, + snapid_t *begin, snapid_t *end); + static void make_purged_snap_key_value( + int64_t pool, snapid_t begin, + snapid_t end, std::map<std::string,ceph::buffer::list> *m); + static std::string make_purged_snap_key(int64_t pool, snapid_t last); + + // note: marked 'mutable', as functions as a cache and used in some 'const' + // functions. + mutable MapCacher::MapCacher<std::string, ceph::buffer::list> backend; + + static std::string get_legacy_prefix(snapid_t snap); + std::string to_legacy_raw_key( + const std::pair<snapid_t, hobject_t> &to_map); + static bool is_legacy_mapping(const std::string &to_test); + + static std::string get_prefix(int64_t pool, snapid_t snap); + std::string to_raw_key( + const std::pair<snapid_t, hobject_t> &to_map) const; + + std::string to_raw_key(snapid_t snap, const hobject_t& clone) const; + + std::pair<std::string, ceph::buffer::list> to_raw( + const std::pair<snapid_t, hobject_t> &to_map) const; + + static bool is_mapping(const std::string &to_test); + + static std::pair<snapid_t, hobject_t> from_raw( + const std::pair<std::string, ceph::buffer::list> &image); + + static std::pair<snapid_t, hobject_t> from_raw( + const ceph::buffer::list& image); + + std::string to_object_key(const hobject_t &hoid) const; + + int get_snaps(const hobject_t &oid, object_snaps *out) const; + + std::set<std::string> to_raw_keys( + const hobject_t &clone, + const std::set<snapid_t> &snaps) const; + + void set_snaps( + const hobject_t &oid, + const object_snaps &out, + MapCacher::Transaction<std::string, ceph::buffer::list> *t); + + void clear_snaps( + const hobject_t &oid, + MapCacher::Transaction<std::string, ceph::buffer::list> *t); + + // True if hoid belongs in this mapping based on mask_bits and match + bool check(const hobject_t &hoid) const; + + int _remove_oid( + const hobject_t &oid, ///< [in] oid to remove + MapCacher::Transaction<std::string, ceph::buffer::list> *t ///< [out] transaction + ); + + /// Get snaps (as an 'object_snaps' object) for oid + tl::expected<object_snaps, SnapMapReaderI::result_t> get_snaps_common( + const hobject_t &hoid) const; + + public: + static std::string make_shard_prefix(shard_id_t shard) { + if (shard == shard_id_t::NO_SHARD) + return std::string(); + char buf[20]; + int r = snprintf(buf, sizeof(buf), ".%x", (int)shard); + ceph_assert(r < (int)sizeof(buf)); + return std::string(buf, r) + '_'; + } + uint32_t mask_bits; + const uint32_t match; + std::string last_key_checked; + const int64_t pool; + const shard_id_t shard; + const std::string shard_prefix; + SnapMapper( + CephContext* cct, + MapCacher::StoreDriver<std::string, ceph::buffer::list> *driver, + uint32_t match, ///< [in] pgid + uint32_t bits, ///< [in] current split bits + int64_t pool, ///< [in] pool + shard_id_t shard ///< [in] shard + ) + : cct(cct), backend(driver), mask_bits(bits), match(match), pool(pool), + shard(shard), shard_prefix(make_shard_prefix(shard)) { + update_bits(mask_bits); + } + + std::set<std::string> prefixes; + /// Update bits in case of pg split or merge + void update_bits( + uint32_t new_bits ///< [in] new split bits + ) { + mask_bits = new_bits; + std::set<std::string> _prefixes = hobject_t::get_prefixes( + mask_bits, + match, + pool); + prefixes.clear(); + for (auto i = _prefixes.begin(); i != _prefixes.end(); ++i) { + prefixes.insert(shard_prefix + *i); + } + } + + /// Update snaps for oid, empty new_snaps removes the mapping + int update_snaps( + const hobject_t &oid, ///< [in] oid to update + const std::set<snapid_t> &new_snaps, ///< [in] new snap std::set + const std::set<snapid_t> *old_snaps, ///< [in] old snaps (for debugging) + MapCacher::Transaction<std::string, ceph::buffer::list> *t ///< [out] transaction + ); ///@ return error, 0 on success + + /// Add mapping for oid, must not already be mapped + void add_oid( + const hobject_t &oid, ///< [in] oid to add + const std::set<snapid_t>& new_snaps, ///< [in] snaps + MapCacher::Transaction<std::string, ceph::buffer::list> *t ///< [out] transaction + ); + + /// Returns first object with snap as a snap + int get_next_objects_to_trim( + snapid_t snap, ///< [in] snap to check + unsigned max, ///< [in] max to get + std::vector<hobject_t> *out ///< [out] next objects to trim (must be empty) + ); ///< @return error, -ENOENT if no more objects + + /// Remove mapping for oid + int remove_oid( + const hobject_t &oid, ///< [in] oid to remove + MapCacher::Transaction<std::string, ceph::buffer::list> *t ///< [out] transaction + ); ///< @return error, -ENOENT if the object is not mapped + + /// Get snaps for oid + int get_snaps( + const hobject_t &oid, ///< [in] oid to get snaps for + std::set<snapid_t> *snaps ///< [out] snaps + ) const; ///< @return error, -ENOENT if oid is not recorded + + /// Get snaps for oid - alternative interface + tl::expected<std::set<snapid_t>, SnapMapReaderI::result_t> get_snaps( + const hobject_t &hoid) const final; + + /** + * get_snaps_check_consistency + * + * Returns snaps for hoid as in get_snaps(), but additionally validates the + * snap->hobject_t mappings ('SNA_' entries). + */ + tl::expected<std::set<snapid_t>, SnapMapReaderI::result_t> + get_snaps_check_consistency(const hobject_t &hoid) const final; +}; +WRITE_CLASS_ENCODER(SnapMapper::object_snaps) +WRITE_CLASS_ENCODER(SnapMapper::Mapping) + +#endif |