Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 10:10:37

0001 /// \file ROOT/RDaos.hxx
0002 /// \ingroup NTuple ROOT7
0003 /// \author Javier Lopez-Gomez <j.lopez@cern.ch>
0004 /// \date 2020-11-14
0005 /// \warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback
0006 /// is welcome!
0007 
0008 /*************************************************************************
0009  * Copyright (C) 1995-2021, Rene Brun and Fons Rademakers.               *
0010  * All rights reserved.                                                  *
0011  *                                                                       *
0012  * For the licensing terms see $ROOTSYS/LICENSE.                         *
0013  * For the list of contributors see $ROOTSYS/README/CREDITS.             *
0014  *************************************************************************/
0015 
0016 #ifndef ROOT7_RDaos
0017 #define ROOT7_RDaos
0018 
0019 #include <string_view>
0020 #include <ROOT/TypeTraits.hxx>
0021 #include <ROOT/RSpan.hxx>
0022 
0023 #include <daos.h>
0024 
0025 #include <cstdint>
0026 #include <functional>
0027 #include <memory>
0028 #include <string>
0029 #include <type_traits>
0030 #include <vector>
0031 #include <optional>
0032 #include <unordered_map>
0033 
0034 #ifndef DAOS_UUID_STR_SIZE
0035 #define DAOS_UUID_STR_SIZE 37
0036 #endif
0037 
0038 namespace ROOT {
0039 
0040 namespace Experimental {
0041 namespace Internal {
0042 
0043 struct RDaosEventQueue {
0044    daos_handle_t fQueue;
0045    RDaosEventQueue();
0046    ~RDaosEventQueue();
0047 
0048    /// \brief Sets event barrier for a given parent event and waits for the completion of all children launched before
0049    /// the barrier (must have at least one child).
0050    /// \return 0 on success; a DAOS error code otherwise (< 0).
0051    static int WaitOnParentBarrier(daos_event_t *ev_ptr);
0052    /// \brief Reserve event in queue, optionally tied to a parent event.
0053    /// \return 0 on success; a DAOS error code otherwise (< 0).
0054    int InitializeEvent(daos_event_t *ev_ptr, daos_event_t *parent_ptr = nullptr) const;
0055    /// \brief Release event data from queue.
0056    /// \return 0 on success; a DAOS error code otherwise (< 0).
0057    static int FinalizeEvent(daos_event_t *ev_ptr);
0058 };
0059 
0060 class RDaosContainer;
0061 
0062 /**
0063   \class RDaosPool
0064   \brief A RDaosPool provides access to containers in a specific DAOS pool.
0065   */
0066 class RDaosPool {
0067    friend class RDaosContainer;
0068 private:
0069    daos_handle_t fPoolHandle{};
0070    uuid_t fPoolUuid{};
0071    std::string fPoolLabel{};
0072    std::unique_ptr<RDaosEventQueue> fEventQueue;
0073 
0074 public:
0075    RDaosPool(const RDaosPool&) = delete;
0076    RDaosPool(std::string_view poolId);
0077    ~RDaosPool();
0078 
0079    RDaosPool& operator=(const RDaosPool&) = delete;
0080    std::string GetPoolUuid();
0081 };
0082 
0083 /**
0084   \class RDaosObject
0085   \brief Provides low-level access to DAOS objects in a container.
0086   */
0087 class RDaosObject {
0088 private:
0089    daos_handle_t fObjectHandle;
0090 public:
0091    using DistributionKey_t = std::uint64_t;
0092    using AttributeKey_t = std::uint64_t;
0093    /// \brief Wrap around a `daos_oclass_id_t`. An object class describes the schema of data distribution
0094    /// and protection.
0095    struct ObjClassId {
0096       daos_oclass_id_t fCid;
0097 
0098       ObjClassId(daos_oclass_id_t cid) : fCid(cid) {}
0099       ObjClassId(const std::string &name) : fCid(daos_oclass_name2id(name.data())) {}
0100 
0101       bool IsUnknown() const { return fCid == OC_UNKNOWN; }
0102       std::string ToString() const;
0103 
0104       /// This limit is currently not defined in any header and any call to
0105       /// `daos_oclass_id2name()` within DAOS uses a stack-allocated buffer
0106       /// whose length varies from 16 to 50, e.g. `https://github.com/daos-stack/daos/blob/master/src/utils/daos_dfs_hdlr.c#L78`.
0107       /// As discussed with the development team, 64 is a reasonable limit.
0108       static constexpr std::size_t kOCNameMaxLength = 64;
0109    };
0110 
0111    /// \brief Contains an attribute key and the associated IOVs for a single scatter-gather I/O request.
0112    struct RAkeyRequest {
0113       AttributeKey_t fAkey{};
0114       std::vector<d_iov_t> fIovs{};
0115 
0116       RAkeyRequest(const AttributeKey_t a, const std::vector<d_iov_t> &iovs) : fAkey(a), fIovs(iovs){};
0117       RAkeyRequest(const AttributeKey_t a, std::vector<d_iov_t> &&iovs) : fAkey(a), fIovs(std::move(iovs)){};
0118    };
0119 
0120    /// \brief Contains required information for a single fetch/update operation.
0121    struct FetchUpdateArgs {
0122       FetchUpdateArgs() = default;
0123       FetchUpdateArgs(const FetchUpdateArgs &) = delete;
0124       FetchUpdateArgs(FetchUpdateArgs &&fua) noexcept;
0125       FetchUpdateArgs(DistributionKey_t d, std::span<RAkeyRequest> rs, bool is_async = false);
0126       FetchUpdateArgs &operator=(const FetchUpdateArgs &) = delete;
0127       daos_event_t *GetEventPointer();
0128 
0129       /// \brief A `daos_key_t` is a type alias of `d_iov_t`. This type stores a pointer and a length.
0130       /// In order for `fDistributionKey` to point to memory that we own, `fDkey` holds the distribution key.
0131       DistributionKey_t fDkey{};
0132       /// \brief `fRequests` is a sequential container assumed to remain valid throughout the fetch/update operation,
0133       /// holding a list of `RAkeyRequest`-typed elements.
0134       std::span<RAkeyRequest> fRequests{};
0135 
0136       /// \brief The distribution key, as used by the `daos_obj_{fetch,update}` functions.
0137       daos_key_t fDistributionKey{};
0138       std::vector<daos_iod_t> fIods{};
0139       std::vector<d_sg_list_t> fSgls{};
0140       std::optional<daos_event_t> fEvent{};
0141    };
0142 
0143    RDaosObject() = delete;
0144    /// Provides low-level access to an object. If `cid` is OC_UNKNOWN, the user is responsible for
0145    /// calling `daos_obj_generate_oid()` to fill the reserved bits in `oid` before calling this constructor.
0146    RDaosObject(RDaosContainer &container, daos_obj_id_t oid, ObjClassId cid = OC_UNKNOWN);
0147    ~RDaosObject();
0148 
0149    int Fetch(FetchUpdateArgs &args);
0150    int Update(FetchUpdateArgs &args);
0151 };
0152 
0153 /**
0154   \class RDaosContainer
0155   \brief A RDaosContainer provides read/write access to objects in a given container.
0156   */
0157 class RDaosContainer {
0158    friend class RDaosObject;
0159 public:
0160    using DistributionKey_t = RDaosObject::DistributionKey_t;
0161    using AttributeKey_t = RDaosObject::AttributeKey_t;
0162    using ObjClassId_t = RDaosObject::ObjClassId;
0163 
0164    /// \brief A pair of <object ID, distribution key> that can be used to issue a fetch/update request for multiple
0165    /// attribute keys.
0166    struct ROidDkeyPair {
0167       daos_obj_id_t oid{};
0168       DistributionKey_t dkey{};
0169 
0170       inline bool operator==(const ROidDkeyPair &other) const
0171       {
0172          return this->oid.lo == other.oid.lo && this->oid.hi == other.oid.hi && this->dkey == other.dkey;
0173       }
0174 
0175       struct Hash {
0176          auto operator()(const ROidDkeyPair &x) const
0177          {
0178             /// Implementation borrowed from `boost::hash_combine`. Comparable to initial seeding with `oid.hi` followed
0179             /// by two subsequent hash calls for `oid.lo` and `dkey`.
0180             auto seed = std::hash<uint64_t>{}(x.oid.hi);
0181             seed ^= std::hash<uint64_t>{}(x.oid.lo) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
0182             seed ^= std::hash<DistributionKey_t>{}(x.dkey) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
0183             return seed;
0184          }
0185       };
0186    };
0187 
0188    /// \brief Describes a read/write operation on multiple attribute keys under the same object ID and distribution key,
0189    /// see the `ReadV`/`WriteV` functions.
0190    struct RWOperation {
0191       RWOperation() = default;
0192       RWOperation(daos_obj_id_t o, DistributionKey_t d, std::vector<RDaosObject::RAkeyRequest> &&rs)
0193          : fOid(o), fDistributionKey(d), fDataRequests(std::move(rs))
0194       {
0195          for (unsigned i = 0; i < fDataRequests.size(); i++)
0196             fIndices.emplace(fDataRequests[i].fAkey, i);
0197       };
0198       explicit RWOperation(ROidDkeyPair &k) : fOid(k.oid), fDistributionKey(k.dkey){};
0199       daos_obj_id_t fOid{};
0200       DistributionKey_t fDistributionKey{};
0201       std::vector<RDaosObject::RAkeyRequest> fDataRequests{};
0202       std::unordered_map<AttributeKey_t, unsigned> fIndices{};
0203 
0204       void Insert(AttributeKey_t attr, const d_iov_t &iov)
0205       {
0206          auto [it, ret] = fIndices.emplace(attr, fDataRequests.size());
0207          unsigned attrIndex = it->second;
0208 
0209          if (attrIndex == fDataRequests.size()) {
0210             fDataRequests.emplace_back(attr, std::initializer_list<d_iov_t>{iov});
0211          } else {
0212             fDataRequests[attrIndex].fIovs.emplace_back(iov);
0213          }
0214       }
0215 
0216       void Insert(AttributeKey_t attr, std::vector<d_iov_t> &iovs)
0217       {
0218          auto [it, ret] = fIndices.emplace(attr, fDataRequests.size());
0219          unsigned attrIndex = it->second;
0220 
0221          if (attrIndex == fDataRequests.size()) {
0222             fDataRequests.emplace_back(attr, iovs);
0223          } else {
0224             fDataRequests[attrIndex].fIovs.insert(std::end(fDataRequests[attrIndex].fIovs),
0225                                                   std::make_move_iterator(std::begin(iovs)),
0226                                                   std::make_move_iterator(std::end(iovs)));
0227          }
0228       }
0229    };
0230 
0231    using MultiObjectRWOperation_t = std::unordered_map<ROidDkeyPair, RWOperation, ROidDkeyPair::Hash>;
0232 
0233    std::string GetContainerUuid();
0234 
0235 private:
0236    daos_handle_t fContainerHandle{};
0237    uuid_t fContainerUuid{};
0238    std::string fContainerLabel{};
0239    std::shared_ptr<RDaosPool> fPool;
0240    ObjClassId_t fDefaultObjectClass{OC_SX};
0241 
0242    /**
0243      \brief Perform a vector read/write operation on different objects.
0244      \param map A `MultiObjectRWOperation_t` that describes read/write operations to perform.
0245      \param cid The `daos_oclass_id_t` used to qualify OIDs.
0246      \param fn Either `&RDaosObject::Fetch` (read) or `&RDaosObject::Update` (write).
0247      \return 0 if the operation succeeded; a negative DAOS error number otherwise.
0248      */
0249    int VectorReadWrite(MultiObjectRWOperation_t &map, ObjClassId_t cid,
0250                        int (RDaosObject::*fn)(RDaosObject::FetchUpdateArgs &));
0251 
0252 public:
0253    RDaosContainer(std::shared_ptr<RDaosPool> pool, std::string_view containerId, bool create = false);
0254    ~RDaosContainer();
0255 
0256    ObjClassId_t GetDefaultObjectClass() const { return fDefaultObjectClass; }
0257    void SetDefaultObjectClass(const ObjClassId_t cid) { fDefaultObjectClass = cid; }
0258 
0259    /**
0260      \brief Read data from a single object attribute key to the given buffer.
0261      \param buffer The address of a buffer that has capacity for at least `length` bytes.
0262      \param length Length of the buffer.
0263      \param oid A 128-bit DAOS object identifier.
0264      \param dkey The distribution key used for this operation.
0265      \param akey The attribute key used for this operation.
0266      \param cid An object class ID.
0267      \return 0 if the operation succeeded; a negative DAOS error number otherwise.
0268      */
0269    int ReadSingleAkey(void *buffer, std::size_t length, daos_obj_id_t oid,
0270                       DistributionKey_t dkey, AttributeKey_t akey, ObjClassId_t cid);
0271    int ReadSingleAkey(void *buffer, std::size_t length, daos_obj_id_t oid,
0272                       DistributionKey_t dkey, AttributeKey_t akey)
0273    { return ReadSingleAkey(buffer, length, oid, dkey, akey, fDefaultObjectClass); }
0274 
0275    /**
0276      \brief Write the given buffer to a single object attribute key.
0277      \param buffer The address of the source buffer.
0278      \param length Length of the buffer.
0279      \param oid A 128-bit DAOS object identifier.
0280      \param dkey The distribution key used for this operation.
0281      \param akey The attribute key used for this operation.
0282      \param cid An object class ID.
0283      \return 0 if the operation succeeded; a negative DAOS error number otherwise.
0284      */
0285    int WriteSingleAkey(const void *buffer, std::size_t length, daos_obj_id_t oid,
0286                        DistributionKey_t dkey, AttributeKey_t akey, ObjClassId_t cid);
0287    int WriteSingleAkey(const void *buffer, std::size_t length, daos_obj_id_t oid,
0288                        DistributionKey_t dkey, AttributeKey_t akey)
0289    { return WriteSingleAkey(buffer, length, oid, dkey, akey, fDefaultObjectClass); }
0290 
0291    /**
0292      \brief Perform a vector read operation on multiple objects.
0293      \param map A `MultiObjectRWOperation_t` that describes read operations to perform.
0294      \param cid An object class ID.
0295      \return Number of operations that could not complete.
0296      */
0297    int ReadV(MultiObjectRWOperation_t &map, ObjClassId_t cid) { return VectorReadWrite(map, cid, &RDaosObject::Fetch); }
0298    int ReadV(MultiObjectRWOperation_t &map) { return ReadV(map, fDefaultObjectClass); }
0299 
0300    /**
0301      \brief Perform a vector write operation on multiple objects.
0302      \param map A `MultiObjectRWOperation_t` that describes write operations to perform.
0303      \param cid An object class ID.
0304      \return Number of operations that could not complete.
0305      */
0306    int WriteV(MultiObjectRWOperation_t &map, ObjClassId_t cid)
0307    {
0308       return VectorReadWrite(map, cid, &RDaosObject::Update);
0309    }
0310    int WriteV(MultiObjectRWOperation_t &map) { return WriteV(map, fDefaultObjectClass); }
0311 };
0312 
0313 } // namespace Internal
0314 
0315 } // namespace Experimental
0316 } // namespace ROOT
0317 
0318 #endif