Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RPageStorageS3.hxx
Go to the documentation of this file.
1/// \file ROOT/RPageStorageS3.hxx
2/// \author Jas Mehta <jasmehta805@gmail.com>
3/// \date 2026-06-01
4
5/*************************************************************************
6 * Copyright (C) 1995-2026, Rene Brun and Fons Rademakers. *
7 * All rights reserved. *
8 * *
9 * For the licensing terms see $ROOTSYS/LICENSE. *
10 * For the list of contributors see $ROOTSYS/README/CREDITS. *
11 *************************************************************************/
12
13#ifndef ROOT_RPageStorageS3
14#define ROOT_RPageStorageS3
15
16#include <ROOT/RCurlConnection.hxx>
17#include <ROOT/RError.hxx>
18#include <ROOT/RNTuple.hxx>
19#include <ROOT/RPageStorage.hxx>
20#include <ROOT/RSpan.hxx>
21
22#include <cstddef>
23#include <cstdint>
24#include <memory>
25#include <string>
26#include <string_view>
27#include <vector>
28
29namespace ROOT {
30namespace Experimental {
31namespace Internal {
32
33// clang-format off
34/**
35\class ROOT::Experimental::Internal::RNTupleAnchorS3
36\ingroup NTuple
37\brief Entry point for an RNTuple stored in S3-compatible object storage.
38
39The anchor is serialized as a JSON object and stored at the base URL of the ntuple.
40It contains the information needed to locate and read the header and footer envelopes.
41The anchor is always the last object written during CommitDatasetImpl, ensuring atomicity:
42if the anchor exists, the entire ntuple is complete.
43*/
44// clang-format on
46 friend class RPageSinkS3;
47
48private:
49 /// Allows evolving the anchor JSON schema in future versions
50 std::uint32_t fVersionAnchor = 0;
51 /// Version of the RNTuple binary format supported by the writer
56 /// Pattern for resolving object IDs to full S3 URLs. ${baseurl} is replaced with the anchor URL,
57 /// ${objid} with the numeric object ID. Defaults to the scheme this writer uses; the reader
58 /// overrides it from the stored anchor.
59 std::string fUrlTemplate = "${baseurl}/${objid}";
60 /// Pattern for resolving clone (attribute-set) names to base URLs.
61 /// ${baseurl} is replaced with the anchor URL, ${name} with the clone name.
62 std::string fCloneTemplate = "${baseurl}/_clone/${name}";
63 /// Object ID and byte offset of the compressed header within the S3 object
64 std::uint64_t fHeaderObjId = 0;
65 std::uint64_t fHeaderOffset = 0;
66 /// Compressed and uncompressed sizes of the header envelope
67 std::uint64_t fNBytesHeader = 0;
68 std::uint64_t fLenHeader = 0;
69 /// Object ID and byte offset of the compressed footer within the S3 object
70 std::uint64_t fFooterObjId = 0;
71 std::uint64_t fFooterOffset = 0;
72 /// Compressed and uncompressed sizes of the footer envelope
73 std::uint64_t fNBytesFooter = 0;
74 std::uint64_t fLenFooter = 0;
75
76public:
77 RNTupleAnchorS3() = default;
78
79 /// Deserialize the anchor from a JSON string. Returns an error on malformed or incompatible input.
80 static RResult<RNTupleAnchorS3> CreateFromJSON(const std::string &json);
81 /// Serialize the anchor to a JSON string suitable for storage at the base URL
82 std::string ToJSON() const;
83
84 bool operator==(const RNTupleAnchorS3 &other) const;
85 bool operator!=(const RNTupleAnchorS3 &other) const { return !(*this == other); }
86
87 std::uint32_t GetVersionAnchor() const { return fVersionAnchor; }
88 std::uint16_t GetVersionEpoch() const { return fVersionEpoch; }
89 std::uint16_t GetVersionMajor() const { return fVersionMajor; }
90 std::uint16_t GetVersionMinor() const { return fVersionMinor; }
91 std::uint16_t GetVersionPatch() const { return fVersionPatch; }
92 const std::string &GetUrlTemplate() const { return fUrlTemplate; }
93 const std::string &GetCloneTemplate() const { return fCloneTemplate; }
94 std::uint64_t GetHeaderObjId() const { return fHeaderObjId; }
95 std::uint64_t GetHeaderOffset() const { return fHeaderOffset; }
96 std::uint64_t GetNBytesHeader() const { return fNBytesHeader; }
97 std::uint64_t GetLenHeader() const { return fLenHeader; }
98 std::uint64_t GetFooterObjId() const { return fFooterObjId; }
99 std::uint64_t GetFooterOffset() const { return fFooterOffset; }
100 std::uint64_t GetNBytesFooter() const { return fNBytesFooter; }
101 std::uint64_t GetLenFooter() const { return fLenFooter; }
102};
103
104/// \brief Translate an ntpl+s3 URI into its plain HTTP(S) equivalent.
105///
106/// Accepts `ntpl+s3+http://host/bucket/path` and `ntpl+s3+https://host/bucket/path`, returning the
107/// URL with the scheme replaced by http or https respectively. Returns an error result for any other
108/// scheme or a malformed URI (rather than throwing), so callers on untrusted input can handle it.
109RResult<std::string> ParseS3Url(std::string_view uri);
110
111// clang-format off
112/**
113\class ROOT::Experimental::Internal::RPageSinkS3
114\ingroup NTuple
115\brief Storage provider that writes ntuple pages into S3-compatible object storage.
116
117Currently implements Mode B (one sealed page per S3 object, kTypeObject64 locators).
118Mode A (multiple packed pages per object, kTypeMulti locators) will be added separately.
119
120\warning The S3 backend is experimental and under active development.
121*/
122// clang-format on
124private:
125 /// HTTP base URL for this ntuple (derived from the s3 scheme URI); never has a trailing slash
126 std::string fBaseUrl;
127 /// One HTTP connection reused for every upload, so curl keeps it alive across objects on the same
128 /// host instead of re-handshaking per object.
129 ROOT::Internal::RCurlConnection fConnection;
130 /// Object ID counter; incremented for each object written.
131 std::uint64_t fObjectId{0};
132 /// Tracks the number of bytes committed to the current cluster (reset in StageClusterImpl)
133 std::uint64_t fNBytesCurrentCluster{0};
134 /// Anchor metadata populated during the write path and uploaded last in CommitDatasetImpl
136
137 /// Resolve a numeric object ID to its full HTTP URL
138 std::string MakeObjectUrl(std::uint64_t objId) const;
139 /// Upload raw bytes to the given S3 URL via an HTTP PUT request
140 void PutObject(const std::string &url, const unsigned char *data, std::size_t size);
141
142 /// Tag to select the internal constructor that takes an already-resolved base URL.
143 struct RFromBaseUrl {};
144 /// Internal constructor used by CloneAsHidden: the public constructor derives the base URL by parsing
145 /// an s3 scheme URI, whereas a clone already has a resolved base URL to write under.
146 RPageSinkS3(std::string_view ntupleName, std::string_view baseUrl, const ROOT::RNTupleWriteOptions &options,
148
149protected:
151 void InitImpl(unsigned char *serializedHeader, std::uint32_t length) final;
153 CommitSealedPageImpl(ROOT::DescriptorId_t physicalColumnId, const RPageStorage::RSealedPage &sealedPage) final;
154 std::uint64_t StageClusterImpl() final;
155 RNTupleLocator CommitClusterGroupImpl(unsigned char *serializedPageList, std::uint32_t length) final;
157 ROOT::Internal::RNTupleLink CommitDatasetImpl(unsigned char *serializedFooter, std::uint32_t length) final;
158
159public:
160 RPageSinkS3(std::string_view ntupleName, std::string_view uri, const ROOT::RNTupleWriteOptions &options);
161 ~RPageSinkS3() override;
162
163 std::unique_ptr<ROOT::Internal::RPageSink>
164 CloneAsHidden(std::string_view name, const ROOT::RNTupleWriteOptions &opts) const final;
165}; // class RPageSinkS3
166
167// clang-format off
168/**
169\class ROOT::Experimental::Internal::RPageSourceS3
170\ingroup NTuple
171\brief Storage provider that reads ntuple pages from S3-compatible object storage.
172
173Counterpart of RPageSinkS3: implements Mode B reads (one sealed page per S3 object, kTypeObject64
174locators). Pages are fetched one object at a time; batching them into concurrent GETs is left to a
175follow-up.
176
177The anchor is read in a single GET that deliberately asks for more bytes than it holds: its size is not
178known up front, and a range running past the end of an object returns only what exists, so the length of
179the reply is the size of the anchor.
180
181\warning The S3 backend is experimental and under active development.
182*/
183// clang-format on
184class RPageSourceS3 : public ROOT::Internal::RPageSource {
185private:
186 /// HTTP base URL for this ntuple (derived from the s3 scheme URI); never has a trailing slash
187 std::string fBaseUrl;
188 /// Connection used by everything that runs on the calling thread: the anchor, header and footer in
189 /// LoadStructureImpl, the page lists in LoadPageListImpl and single pages in LoadSealedPageImpl.
190 /// Reused across objects so curl keeps it alive instead of re-handshaking per object.
191 ROOT::Internal::RCurlConnection fMainConnection;
192 /// Connection used exclusively by LoadClusters(), which the cluster pool calls on its own I/O thread.
193 /// A libcurl easy handle carries per-request state and must not be driven by two threads at once, so
194 /// the prefetch path needs a handle of its own rather than sharing fMainConnection.
195 ROOT::Internal::RCurlConnection fClusterConnection;
196 /// Anchor metadata, fetched and parsed in LoadStructureImpl
198 /// The anchor's URL template with ${baseurl} already substituted, so that resolving an object ID only
199 /// has to replace ${objid}. Set from the anchor in LoadStructureImpl and carried into clones.
201 /// Populated by LoadStructureImpl and AttachImpl, moved out at the end of AttachImpl
203
204 /// Resolve a numeric object ID to its full HTTP URL through the anchor's URL template
205 std::string MakeObjectUrl(std::uint64_t objId) const;
206 /// Download `size` bytes from `url` into the caller-provided `buffer` via an HTTP GET request.
207 /// The connection is explicit because the caller's thread determines which one may be used.
208 void GetObject(ROOT::Internal::RCurlConnection &connection, const std::string &url, unsigned char *buffer,
209 std::size_t size);
210 /// Download at most `size` bytes of `url`, returning how many arrived. Used to read an object of
211 /// unknown length, where a reply shorter than the request is the answer rather than an error.
212 std::size_t GetShortObject(ROOT::Internal::RCurlConnection &connection, const std::string &url,
213 unsigned char *buffer, std::size_t size);
214
215 /// Tag to select the internal constructor that takes an already-resolved base URL.
216 struct RFromBaseUrl {};
217 /// Internal constructor used by CloneImpl: the public constructor derives the base URL by parsing an
218 /// s3 scheme URI, whereas a clone of an open source already has one and must not re-parse it.
219 RPageSourceS3(std::string_view ntupleName, std::string_view baseUrl, const ROOT::RNTupleReadOptions &options,
221
222protected:
223 void LoadPageListImpl(const RNTupleLocator &locator, unsigned char *buffer) final;
224 void LoadSealedPageImpl(const RNTupleLocator &locator, RSealedPage &sealedPage) final;
225 void LoadStructureImpl() final;
226 ROOT::RNTupleDescriptor AttachImpl() final;
227 /// The cloned page source opens its own pair of HTTP connections to the same base URL.
228 std::unique_ptr<RPageSource> CloneImpl() const final;
229
230public:
231 RPageSourceS3(std::string_view ntupleName, std::string_view uri, const ROOT::RNTupleReadOptions &options);
232 ~RPageSourceS3() override;
233
234 std::vector<std::unique_ptr<ROOT::Internal::RCluster>>
235 LoadClusters(std::span<ROOT::Internal::RCluster::RKey> clusterKeys) final;
236
237 std::unique_ptr<RPageSource> OpenWithDifferentAnchor(const ROOT::Internal::RNTupleLink &anchorLink,
238 const ROOT::RNTupleReadOptions &options = {}) final;
239}; // class RPageSourceS3
240
241} // namespace Internal
242} // namespace Experimental
243} // namespace ROOT
244
245#endif
nlohmann::json json
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void data
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h length
char name[80]
Definition TGX11.cxx:142
Entry point for an RNTuple stored in S3-compatible object storage.
bool operator==(const RNTupleAnchorS3 &other) const
Field-by-field equality check across all data members.
std::uint64_t fHeaderObjId
Object ID and byte offset of the compressed header within the S3 object.
std::string fUrlTemplate
Pattern for resolving object IDs to full S3 URLs.
std::uint32_t fVersionAnchor
Allows evolving the anchor JSON schema in future versions.
std::uint16_t fVersionEpoch
Version of the RNTuple binary format supported by the writer.
bool operator!=(const RNTupleAnchorS3 &other) const
std::string fCloneTemplate
Pattern for resolving clone (attribute-set) names to base URLs.
std::uint64_t fNBytesHeader
Compressed and uncompressed sizes of the header envelope.
std::string ToJSON() const
Serialize the anchor to a JSON string suitable for storage at the base URL.
std::uint64_t fNBytesFooter
Compressed and uncompressed sizes of the footer envelope.
static RResult< RNTupleAnchorS3 > CreateFromJSON(const std::string &json)
Deserialize the anchor from a JSON string. Returns an error on malformed or incompatible input.
std::uint64_t fFooterObjId
Object ID and byte offset of the compressed footer within the S3 object.
Storage provider that writes ntuple pages into S3-compatible object storage.
ROOT::Internal::RCurlConnection fConnection
One HTTP connection reused for every upload, so curl keeps it alive across objects on the same host i...
void InitImpl(unsigned char *serializedHeader, std::uint32_t length) final
std::uint64_t StageClusterImpl() final
Returns the number of bytes written to storage (excluding metadata)
RNTupleLocator CommitClusterGroupImpl(unsigned char *serializedPageList, std::uint32_t length) final
Returns the locator of the page list envelope of the given buffer that contains the serialized page l...
std::unique_ptr< ROOT::Internal::RPageSink > CloneAsHidden(std::string_view name, const ROOT::RNTupleWriteOptions &opts) const final
Creates a new sink with the same underlying storage as this but writing to a different RNTuple named ...
RNTupleAnchorS3 fAnchor
Anchor metadata populated during the write path and uploaded last in CommitDatasetImpl.
std::string MakeObjectUrl(std::uint64_t objId) const
Resolve a numeric object ID to its full HTTP URL.
std::string fBaseUrl
HTTP base URL for this ntuple (derived from the s3 scheme URI); never has a trailing slash.
RNTupleLocator CommitSealedPageImpl(ROOT::DescriptorId_t physicalColumnId, const RPageStorage::RSealedPage &sealedPage) final
std::uint64_t fNBytesCurrentCluster
Tracks the number of bytes committed to the current cluster (reset in StageClusterImpl)
RPageSinkS3(std::string_view ntupleName, std::string_view baseUrl, const ROOT::RNTupleWriteOptions &options, RFromBaseUrl)
Internal constructor used by CloneAsHidden: the public constructor derives the base URL by parsing an...
std::uint64_t fObjectId
Object ID counter; incremented for each object written.
void PutObject(const std::string &url, const unsigned char *data, std::size_t size)
Upload raw bytes to the given S3 URL via an HTTP PUT request.
Storage provider that reads ntuple pages from S3-compatible object storage.
ROOT::Internal::RCurlConnection fClusterConnection
Connection used exclusively by LoadClusters(), which the cluster pool calls on its own I/O thread.
RNTupleAnchorS3 fAnchor
Anchor metadata, fetched and parsed in LoadStructureImpl.
std::string fBaseUrl
HTTP base URL for this ntuple (derived from the s3 scheme URI); never has a trailing slash.
std::string fResolvedUrlTemplate
The anchor's URL template with ${baseurl} already substituted, so that resolving an object ID only ha...
ROOT::Internal::RNTupleDescriptorBuilder fDescriptorBuilder
Populated by LoadStructureImpl and AttachImpl, moved out at the end of AttachImpl.
ROOT::Internal::RCurlConnection fMainConnection
Connection used by everything that runs on the calling thread: the anchor, header and footer in LoadS...
An in-memory subset of the packed and compressed pages of a cluster.
Definition RCluster.hxx:147
A helper class for piece-wise construction of an RNTupleDescriptor.
Base class for a sink with a physical storage backend.
virtual void InitImpl(unsigned char *serializedHeader, std::uint32_t length)=0
Abstract interface to write data into an ntuple.
Abstract interface to read data from an ntuple.
The on-storage metadata of an RNTuple.
Generic information about the physical location of data.
Common user-tunable settings for reading RNTuples.
Common user-tunable settings for storing RNTuples.
static constexpr std::uint16_t kVersionPatch
Definition RNTuple.hxx:82
static constexpr std::uint16_t kVersionMajor
Definition RNTuple.hxx:80
static constexpr std::uint16_t kVersionEpoch
Definition RNTuple.hxx:79
static constexpr std::uint16_t kVersionMinor
Definition RNTuple.hxx:81
The class is used as a return type for operations that can fail; wraps a value of type T or an RError...
Definition RError.hxx:222
RResult< std::string > ParseS3Url(std::string_view uri)
Translate an ntpl+s3 URI into its plain HTTP(S) equivalent.
std::uint64_t DescriptorId_t
Distriniguishes elements of the same type within a descriptor, e.g. different fields.
Tag to select the internal constructor that takes an already-resolved base URL.
Tag to select the internal constructor that takes an already-resolved base URL.
A sealed page contains the bytes of a page as written to storage (packed & compressed).