Project
Loading...
Searching...
No Matches
NativeAOD.cxx
Go to the documentation of this file.
1// Copyright 2019-2026 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
12#include "NativeAOD.h"
13#include "AODNative/Errors.h"
15#include <algorithm>
16#include <cstdio>
17
18namespace o2::framework
19{
20
21using namespace o2::aod::native;
22
23namespace
24{
25Options withBudget(Options options, RootFile& file)
26{
27 if (options.bufferBudget == Options::kAutoBufferBudget) {
28 options.bufferBudget = (file.source().isRemote() ? 64 : 16) * size_t{1024 * 1024};
29 }
30 return options;
31}
32} // namespace
33
34NativeAODFileSystem::NativeAODFileSystem(std::shared_ptr<RootFile> file, Options options, RootObjectReadingFactory& factory)
35 : mFile(std::move(file)), mOptions(withBudget(options, *mFile)), mObjectFactory(factory), mPool(std::make_shared<ReadBufferPool>(mOptions.bufferBudget))
36{
37}
38
39namespace
40{
41// "DF_<n>/<tree>", with or without a leading slash. Empty strings if it is not that.
42std::pair<std::string, std::string> splitTreePath(std::string const& path)
43{
44 size_t start = path.starts_with("/") ? 1 : 0;
45 auto slash = path.find('/', start);
46 if (slash == std::string::npos) {
47 return {};
48 }
49 return {path.substr(start, slash - start), path.substr(slash + 1)};
50}
51} // namespace
52
54{
55 auto [df, treeName] = splitTreePath(path);
56 if (df.empty()) {
57 return nullptr;
58 }
59 std::lock_guard lock{mMutex};
60 return findTree(df, treeName);
61}
62
63std::shared_ptr<ReadPlan> NativeAODFileSystem::planFor(std::string const& path)
64{
65 auto [df, treeName] = splitTreePath(path);
66 if (df.empty()) {
67 return nullptr;
68 }
69 std::lock_guard lock{mMutex};
70 if (mFile->source().canMap()) {
71 releaseMappedDF(df);
72 return nullptr;
73 }
74 if (df != mPlanDF) {
75 auto previous = std::move(mPlanTrees);
76 mPlanTrees.clear();
77 mPlanOf.clear();
78 mPlanBytes = 0;
79 mPlanDF = df;
80 // Also when the directory is already known, e.g. the first DF, read at open.
81 adoptLookahead(df);
82 directory(df);
83 if (mAdoptedDF == df) {
84 mPlan = std::move(mAdoptedPlan);
85 for (auto& name : mAdoptedTrees) {
86 mPlanOf[name] = mPlan;
87 if (auto* handle = findTree(df, name)) {
88 mPlanBytes += ReadPlan::bytesOf(handle->tree);
89 }
90 }
91 mPlanTrees = std::move(mAdoptedTrees);
92 } else {
93 mPlan = std::make_shared<ReadPlan>(mFile->sourcePtr(), mOptions, mPool);
94 }
95 mAdoptedDF.clear();
96 auto trees = previous;
97 if (std::ranges::find(trees, treeName) == trees.end()) {
98 trees.push_back(treeName);
99 }
100 if (!previous.empty()) {
101 mFile->setTreesToPrefetch(trees);
102 }
103 for (auto& name : previous) {
104 addToPlan(name);
105 }
106 // Only once a whole DF has shown which trees the reader asks for.
107 if (!previous.empty() && mFile->source().isRemote() && mOptions.prefetchDepth > 0) {
108 startLookahead(df, std::move(trees));
109 }
110 }
111 addToPlan(treeName);
112 // Let go of the plan once its tree is handed out: the plan, and its buffers,
113 // then go away when the reads of its trees are done.
114 auto it = mPlanOf.find(treeName);
115 if (it == mPlanOf.end()) {
116 return nullptr;
117 }
118 auto plan = std::move(it->second);
119 mPlanOf.erase(it);
120 return plan;
121}
122
123void NativeAODFileSystem::releaseMappedDF(std::string const& df)
124{
125 if (df == mMappedDF) {
126 return;
127 }
128 // The pages of a DF which is done stay mapped and count in the RSS of the
129 // device until the kernel reclaims them: give them back. Rereading them is
130 // still correct, as they come back from the page cache.
131 uint64_t begin = UINT64_MAX;
132 uint64_t end = 0;
133 for (auto it = mTrees.lower_bound(mMappedDF + "/"); it != mTrees.end() && it->first.starts_with(mMappedDF + "/"); ++it) {
134 for (auto const& branch : it->second->tree.branches) {
135 for (auto const& basket : branch.baskets) {
136 if (!basket.embedded && basket.nbytes > 0) {
137 begin = std::min(begin, basket.seek);
138 end = std::max(end, basket.seek + basket.nbytes);
139 }
140 }
141 }
142 }
143 if (!mMappedDF.empty() && begin < end) {
144 mFile->source().dontNeed(begin, end - begin);
145 }
146 mMappedDF = df;
147}
148
149void NativeAODFileSystem::adoptLookahead(std::string const& df)
150{
151 if (!mLookahead.valid() || mLookaheadDF != df) {
152 return;
153 }
154 auto lookahead = mLookahead.get();
155 if (!lookahead) {
156 return;
157 }
158 mDirectories.emplace(df, std::move(lookahead->keys));
159 mAdoptedDF = df;
160 mAdoptedPlan = lookahead->plan;
161 mAdoptedTrees.clear();
162 for (auto& [name, tree] : lookahead->trees) {
163 // The plan already has the baskets of these trees.
164 mTrees.emplace(df + "/" + name, std::make_unique<NativeTreeHandle>(NativeTreeHandle{std::move(tree), mFile->sourcePtr(), mOptions}));
165 mAdoptedTrees.push_back(name);
166 }
167}
168
169std::vector<KeyInfo> const& NativeAODFileSystem::directory(std::string const& df)
170{
171 if (auto it = mDirectories.find(df); it != mDirectories.end()) {
172 return it->second;
173 }
174 adoptLookahead(df);
175 if (auto it = mDirectories.find(df); it != mDirectories.end()) {
176 return it->second;
177 }
178 return mDirectories.emplace(df, mFile->keysOf(df)).first->second;
179}
180
181std::vector<KeyInfo> NativeAODFileSystem::keysOf(std::string const& df)
182{
183 std::lock_guard lock{mMutex};
184 return directory(df);
185}
186
187void NativeAODFileSystem::setReadOrder(std::vector<std::string> dfs)
188{
189 std::lock_guard lock{mMutex};
190 mDataFrames = std::move(dfs);
191}
192
193void NativeAODFileSystem::startLookahead(std::string const& afterDF, std::vector<std::string> trees)
194{
195 if (mDataFrames.empty()) {
196 mDataFrames = mFile->dataFrames();
197 }
198 if (!mLookaheadFile) {
199 mLookaheadFile = std::make_shared<RootFile>(*mFile);
200 }
201 auto it = std::ranges::find(mDataFrames, afterDF);
202 if (it == mDataFrames.end() || ++it == mDataFrames.end()) {
203 return;
204 }
205 mLookaheadDF = *it;
206 // Errors are left to the normal path, which reports them when the DF is read.
207 mLookahead = std::async(std::launch::async, [file = mLookaheadFile, df = *it, trees = std::move(trees), options = mOptions, pool = mPool]() -> std::optional<Lookahead> {
208 try {
209 Lookahead lookahead{df, {}, {}, std::make_shared<ReadPlan>(file->sourcePtr(), options, pool)};
210 file->setTreesToPrefetch(trees);
211 lookahead.keys = file->keysOf(df);
212 // Only the trees which fit in the budget; the others are read when asked for.
213 uint64_t bytes = 0;
214 for (auto const& name : trees) {
215 for (auto const& key : lookahead.keys) {
216 if (key.name == name && key.className == "TTree") {
217 auto tree = file->readTree(key);
218 auto treeBytes = ReadPlan::bytesOf(tree);
219 if (options.bufferBudget > 0 && bytes > 0 && bytes + treeBytes > options.bufferBudget) {
220 lookahead.plan->fetch();
221 return lookahead;
222 }
223 bytes += treeBytes;
224 lookahead.trees.emplace_back(name, std::move(tree));
225 lookahead.plan->add(lookahead.trees.back().second);
226 }
227 }
228 }
229 lookahead.plan->fetch();
230 return lookahead;
231 } catch (std::exception const&) {
232 return std::nullopt;
233 }
234 });
235}
236
237void NativeAODFileSystem::addToPlan(std::string const& treeName)
238{
239 if (std::ranges::find(mPlanTrees, treeName) != mPlanTrees.end()) {
240 return;
241 }
242 NativeTreeHandle* handle = nullptr;
243 try {
244 handle = findTree(mPlanDF, treeName);
245 } catch (std::exception const&) {
246 // Reported when the tree itself is asked for.
247 }
248 if (handle) {
249 auto bytes = ReadPlan::bytesOf(handle->tree);
250 if (mOptions.bufferBudget > 0 && mPlanBytes > 0 && mPlanBytes + bytes > mOptions.bufferBudget) {
251 mPlan = std::make_shared<ReadPlan>(mFile->sourcePtr(), mOptions, mPool);
252 mPlanBytes = 0;
253 }
254 mPlan->add(handle->tree);
255 mPlanBytes += bytes;
256 mPlanOf[treeName] = mPlan;
257 mPlanTrees.push_back(treeName);
258 }
259}
260
261NativeTreeHandle* NativeAODFileSystem::findTree(std::string const& df, std::string const& treeName)
262{
263 auto path = df + "/" + treeName;
264 if (auto it = mTrees.find(path); it != mTrees.end()) {
265 return it->second.get();
266 }
267 if (!mDirectories.contains(df)) {
268 bool exists = false;
269 for (auto& k : mFile->topKeys()) {
270 exists |= k.name == df;
271 }
272 if (!exists) {
273 return nullptr;
274 }
275 }
276 for (auto& key : directory(df)) {
277 if (key.name == treeName && key.className == "TTree") {
278 auto handle = std::make_unique<NativeTreeHandle>(NativeTreeHandle{mFile->readTree(key), mFile->sourcePtr(), mOptions});
279 return mTrees.emplace(path, std::move(handle)).first->second.get();
280 }
281 }
282 return nullptr;
283}
284
285std::shared_ptr<RootObjectHandler> NativeAODFileSystem::GetObjectHandler(arrow::dataset::FileSource source)
286{
287 for (auto& capability : mObjectFactory.capabilities) {
288 if (capability.name != "native-ttree") {
289 continue;
290 }
291 auto objectPath = capability.lfn2objectPath(source.path());
292 void* handle = capability.getHandle(shared_from_this(), objectPath);
293 if (!handle) {
294 continue;
295 }
296 return std::make_shared<RootObjectHandler>(handle, capability.factory().format());
297 }
298 throw runtime_error_f("Unable to get handler for object %s", source.path().c_str());
299}
300
301bool NativeAODFileSystem::CheckSupport(arrow::dataset::FileSource source)
302{
303 for (auto& capability : mObjectFactory.capabilities) {
304 if (capability.name == "native-ttree" && capability.getHandle(shared_from_this(), capability.lfn2objectPath(source.path()))) {
305 return true;
306 }
307 }
308 return false;
309}
310
311arrow::Result<arrow::fs::FileInfo> NativeAODFileSystem::GetFileInfo(const std::string& path)
312{
313 arrow::fs::FileInfo result;
314 result.set_path(path);
315 result.set_type(arrow::fs::FileType::NotFound);
316 for (auto& k : mFile->topKeys()) {
317 if (k.name == path) {
318 result.set_type(k.className == "TTree" ? arrow::fs::FileType::File : arrow::fs::FileType::Directory);
319 return result;
320 }
321 }
322 if (getTreeHandle(path)) {
323 result.set_type(arrow::fs::FileType::File);
324 }
325 return result;
326}
327
328std::shared_ptr<NativeAODInputFile> NativeAODInputFile::open(std::string const& url, RootObjectReadingFactory& factory, Options const& options, std::string const& name)
329{
330 return open(ByteSource::open(url, options), factory, options, name.empty() ? url : name);
331}
332
333std::shared_ptr<NativeAODInputFile> NativeAODInputFile::open(std::unique_ptr<ByteSource> source, RootObjectReadingFactory& factory, Options const& options, std::string const& name)
334{
335 auto result = std::shared_ptr<NativeAODInputFile>(new NativeAODInputFile);
336 result->mName = name;
337 result->mFile = std::make_shared<RootFile>(std::move(source));
338 result->mFilesystem = std::make_shared<NativeAODFileSystem>(result->mFile, options, factory);
339 result->mFilesystem->setName(result->mName);
340 result->mParentFiles = result->mFile->readStringMap("parentFiles");
341 result->mMetaData = result->mFile->readStringMap("metaData");
342 // Parse the trees of the first DF now: a layout or version we do not support
343 // is then found while the file can still be read with ROOT instead.
344 auto dfs = result->mFile->dataFrames();
345 if (!dfs.empty()) {
346 for (auto& key : result->mFile->keysOf(dfs.front())) {
347 if (key.className == "TTree") {
348 result->mFilesystem->getTreeHandle(dfs.front() + "/" + key.name);
349 }
350 }
351 }
352 return result;
353}
354
355void NativeAODInputFile::setGridInfo(std::string se, double openTime)
356{
357 mSE = std::move(se);
358 mOpenTime = openTime;
359}
360
362{
363 if (mSE.empty()) {
364 return "";
365 }
366 char openTime[32];
367 std::snprintf(openTime, sizeof(openTime), "%.1f", mOpenTime);
368 return ",se=" + mSE + ",open_time=" + openTime;
369}
370
371uint64_t NativeAODInputFile::size() const { return mFile->source().size(); }
372uint64_t NativeAODInputFile::bytesRead() const { return mFile->source().bytesRead(); }
373uint64_t NativeAODInputFile::readCalls() const { return mFile->source().readCalls(); }
374
375std::vector<AODInputFile::Key> NativeAODInputFile::topLevelKeys()
376{
377 std::vector<Key> keys;
378 for (auto& k : mFile->topKeys()) {
379 keys.push_back({k.name, k.className});
380 }
381 return keys;
382}
383
384std::vector<std::string> NativeAODInputFile::keysIn(std::string const& dir)
385{
386 std::vector<std::string> names;
387 for (auto& k : mFile->topKeys()) {
388 if (k.name == dir && (k.className == "TDirectoryFile" || k.className == "TDirectory")) {
389 for (auto& key : mFilesystem->keysOf(dir)) {
390 names.push_back(key.name);
391 }
392 break;
393 }
394 }
395 return names;
396}
397
398} // namespace o2::framework
std::string url
std::vector< std::string > keys
CURL * handle
size_t bytes
StringRef key
static std::unique_ptr< ByteSource > open(std::string url, const Options &)
static uint64_t bytesOf(TreeInfo const &tree)
The bytes add(tree) registers.
Definition ReadPlan.cxx:106
TreeInfo readTree(const KeyInfo &)
std::shared_ptr< ByteSource > const & sourcePtr() const
Definition Container.h:62
void setTreesToPrefetch(std::vector< std::string > names)
The TTrees whose bytes keysOf() fetches along; all of them when empty.
Definition Container.h:73
std::vector< KeyInfo > keysOf(const std::string &df)
std::vector< o2::aod::native::KeyInfo > keysOf(std::string const &df)
The keys of DF df, read once (or prepared ahead) for all users.
void setReadOrder(std::vector< std::string > dfs)
arrow::Result< arrow::fs::FileInfo > GetFileInfo(const std::string &path) override
std::shared_ptr< o2::aod::native::ReadPlan > planFor(std::string const &path)
Definition NativeAOD.cxx:63
bool CheckSupport(arrow::dataset::FileSource source) override
o2::aod::native::RootFile & file()
Definition NativeAOD.h:67
NativeTreeHandle * getTreeHandle(std::string const &path)
Definition NativeAOD.cxx:53
NativeAODFileSystem(std::shared_ptr< o2::aod::native::RootFile > file, o2::aod::native::Options options, RootObjectReadingFactory &factory)
Definition NativeAOD.cxx:34
std::string const & name() const
Definition NativeAOD.h:70
std::shared_ptr< RootObjectHandler > GetObjectHandler(arrow::dataset::FileSource source) override
An AO2D read with the ROOT-free reader.
Definition NativeAOD.h:127
static std::shared_ptr< NativeAODInputFile > open(std::string const &url, RootObjectReadingFactory &factory, o2::aod::native::Options const &options, std::string const &name={})
std::vector< std::string > keysIn(std::string const &dir) override
Names of the keys inside the top-level directory dir, empty if there is none.
void setGridInfo(std::string se, double openTime)
The storage element and the seconds the grid lookup and open took, for monitoring.
uint64_t size() const override
uint64_t readCalls() const override
std::string name() const override
Definition NativeAOD.h:140
std::string monitoringDetails() const override
Extra key=value pairs for the file monitoring metrics, starting with a comma.
std::vector< Key > topLevelKeys() override
Top-level keys, in the order the file lists them, all cycles.
uint64_t bytesRead() const override
GLuint64EXT * result
Definition glcorearb.h:5662
GLuint GLuint end
Definition glcorearb.h:469
GLuint const GLchar * name
Definition glcorearb.h:781
GLint first
Definition glcorearb.h:399
GLsizei GLsizei GLchar * source
Definition glcorearb.h:798
GLsizei const GLchar *const * path
Definition glcorearb.h:3591
Defining ITS Vertex explicitly as messageable.
Definition Cartesian.h:288
RuntimeErrorRef runtime_error_f(const char *,...)
int32_t const char * file
std::vector< RootObjectReadingCapability > capabilities
std::unique_ptr< TTree > tree((TTree *) flIn.Get(std::string(o2::base::NameConf::CTFTREENAME).c_str()))