Project
Loading...
Searching...
No Matches
DataInputDirector.cxx
Go to the documentation of this file.
1// Copyright 2019-2020 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#include "DataInputDirector.h"
13#include "Framework/Logger.h"
17#include "Framework/Output.h"
18#include "Framework/Signpost.h"
20#include "Headers/DataHeader.h"
21#include "Monitoring/Tags.h"
22#include "Monitoring/Metric.h"
23#include "Monitoring/Monitoring.h"
24
25#include "rapidjson/document.h"
26#include "rapidjson/prettywriter.h"
27#include "rapidjson/filereadstream.h"
28
29#include "TGrid.h"
30#include "TObjString.h"
31#include "TMap.h"
32#include "TFile.h"
33
34#include <arrow/dataset/file_base.h>
35#include <arrow/dataset/dataset.h>
36#include <uv.h>
37#include <exception>
38#include <memory>
39
40#if __has_include(<TJAlienFile.h>)
41#include <TJAlienFile.h>
42
43#include <utility>
44#endif
45
46#include <dlfcn.h>
47O2_DECLARE_DYNAMIC_LOG(reader_memory_dump);
48
49namespace o2::framework
50{
51using namespace rapidjson;
52
54{
55
56 FileNameHolder holder;
57 holder.fileName = fileName;
58 return holder;
59}
60
62 : mAlienSupport(alienSupport),
63 mContext(context),
64 mLevel(level)
65{
66 std::vector<char const*> capabilitiesSpecs = {
67 "O2Framework:RNTupleObjectReadingCapability",
68 "O2Framework:TTreeObjectReadingCapability",
69 };
70
71 std::vector<LoadablePlugin> plugins;
72 for (auto spec : capabilitiesSpecs) {
73 auto morePlugins = PluginManager::parsePluginSpecString(spec);
74 for (auto& extra : morePlugins) {
75 plugins.push_back(extra);
76 }
77 }
78
79 PluginManager::loadFromPlugin<RootObjectReadingCapability, RootObjectReadingCapabilityPlugin>(plugins, mFactory.capabilities);
80}
81
83{
84 LOGP(info, "DataInputDescriptor");
85 LOGP(info, " Table name : {}", tablename);
86 LOGP(info, " Tree name : {}", treename);
87 LOGP(info, " Input files file : {}", getInputfilesFilename());
88 LOGP(info, " File name regex : {}", getFilenamesRegexString());
89 LOGP(info, " Input files : {}", mfilenames.size());
90 for (auto& fn : mfilenames) {
91 LOGP(info, " {} {}", fn.fileName, fn.numberOfTimeFrames);
92 }
93 LOGP(info, " Total number of TF: {}", getNumberTimeFrames());
94}
95
97{
98 return (minputfilesFile.empty() && minputfilesFilePtr) ? (std::string)*minputfilesFilePtr : minputfilesFile;
99}
100
102{
103 return (mFilenameRegex.empty() && mFilenameRegexPtr) ? (std::string)*mFilenameRegexPtr : mFilenameRegex;
104}
105
107{
108 return std::regex(getFilenamesRegexString());
109}
110
112{
113 // remove leading file:// from file name
114 if (fn.fileName.rfind("file://", 0) == 0) {
115 fn.fileName.erase(0, 7);
116 } else if (!mAlienSupport && fn.fileName.rfind("alien://", 0) == 0 && !gGrid) {
117 LOGP(debug, "AliEn file requested. Enabling support.");
118 TGrid::Connect("alien://");
119 mAlienSupport = true;
120 }
121
122 mtotalNumberTimeFrames += fn.numberOfTimeFrames;
123 mfilenames.emplace_back(fn);
124}
125
126bool DataInputDescriptor::setFile(int counter, int wantedParentLevel, std::string_view origin)
127{
128 // no files left
129 if (counter >= getNumberInputfiles()) {
130 return false;
131 }
132
133 // In case the origin starts with a anything but AOD, we add the origin as the suffix
134 // of the filename. In the future we might expand this for proper rewriting of the
135 // filename based on the origin and the original file information.
136 std::string filename = mfilenames[counter].fileName;
137 // In case we do not need to remap parent levels, the requested origin is what
138 // drives the filename.
139 if (wantedParentLevel == -1 && !origin.starts_with("AOD")) {
140 filename = std::regex_replace(filename, std::regex("[.]root$"), fmt::format("_{}.root", origin));
141 }
142
143 // open file
144 auto rootFS = std::dynamic_pointer_cast<TFileFileSystem>(mCurrentFilesystem);
145 if (rootFS.get()) {
146 if (rootFS->GetFile()->GetName() == filename) {
147 return true;
148 }
150 }
151
152 TFile* tfile = nullptr;
153 bool externalFile = false;
154 for (auto& [name, f] : mContext.openFiles) {
155 if (name == filename) {
156 tfile = f;
157 externalFile = true;
158 break;
159 }
160 }
161 if (tfile == nullptr) {
162 tfile = TFile::Open(filename.c_str());
163 }
164 mCurrentFilesystem = std::make_shared<TFileFileSystem>(tfile, 50 * 1024 * 1024, mFactory, !externalFile);
165 if (!mCurrentFilesystem.get()) {
166 throw std::runtime_error(fmt::format("Couldn't open file \"{}\"!", filename));
167 }
168 rootFS = std::dynamic_pointer_cast<TFileFileSystem>(mCurrentFilesystem);
170
171 // get the parent file map if exists
172 mParentFileMap = (TMap*)rootFS->GetFile()->Get("parentFiles"); // folder name (DF_XXX) --> parent file (absolute path)
173 if (mParentFileMap && !mContext.parentFileReplacement.empty()) {
174 auto pos = mContext.parentFileReplacement.find(';');
175 if (pos == std::string::npos) {
176 throw std::runtime_error(fmt::format("Invalid syntax in aod-parent-base-path-replacement: \"{}\"", mContext.parentFileReplacement.c_str()));
177 }
178 auto from = mContext.parentFileReplacement.substr(0, pos);
179 auto to = mContext.parentFileReplacement.substr(pos + 1);
180
181 auto it = mParentFileMap->MakeIterator();
182 while (auto obj = it->Next()) {
183 auto objString = (TObjString*)mParentFileMap->GetValue(obj);
184 objString->String().ReplaceAll(from.c_str(), to.c_str());
185 }
186 delete it;
187 }
188
189 // get the directory names
190 if (mfilenames[counter].numberOfTimeFrames <= 0) {
191 const std::regex TFRegex = std::regex("/?DF_([0-9]+)(|-.*)$");
192 TList* keyList = rootFS->GetFile()->GetListOfKeys();
193 std::vector<std::string> finalList;
194
195 // extract TF numbers and sort accordingly
196 // We use an extra seen set to make sure we preserve the order in which
197 // we instert things in the final list and to make sure we do not have duplicates.
198 // Multiple folder numbers can happen if we use a flat structure /DF_<df>-<tablename>
199 std::unordered_set<size_t> seen;
200 for (auto key : *keyList) {
201 std::smatch matchResult;
202 std::string keyName = ((TObjString*)key)->GetString().Data();
203 bool match = std::regex_match(keyName, matchResult, TFRegex);
204 if (match) {
205 auto folderNumber = std::stoul(matchResult[1].str());
206 if (seen.find(folderNumber) == seen.end()) {
207 seen.insert(folderNumber);
208 mfilenames[counter].listOfTimeFrameNumbers.emplace_back(folderNumber);
209 }
210 }
211 }
212
213 if (mParentFileMap != nullptr) {
214 // If we have a parent map, we should not process in DF alphabetical order but according to parent file to avoid swapping between files
215 std::ranges::sort(mfilenames[counter].listOfTimeFrameNumbers,
216 [this](long const& l1, long const& l2) -> bool {
217 auto p1 = (TObjString*)this->mParentFileMap->GetValue(("DF_" + std::to_string(l1)).c_str());
218 auto p2 = (TObjString*)this->mParentFileMap->GetValue(("DF_" + std::to_string(l2)).c_str());
219 return p1->GetString().CompareTo(p2->GetString()) < 0;
220 });
221 } else {
222 std::sort(mfilenames[counter].listOfTimeFrameNumbers.begin(), mfilenames[counter].listOfTimeFrameNumbers.end());
223 }
224
225 mfilenames[counter].alreadyRead.resize(mfilenames[counter].alreadyRead.size() + mfilenames[counter].listOfTimeFrameNumbers.size(), false);
226 mfilenames[counter].numberOfTimeFrames = mfilenames[counter].listOfTimeFrameNumbers.size();
227 }
228
229 mCurrentFileID = counter;
230 mCurrentFileStartedAt = uv_hrtime();
231 mIOTime = 0;
232
233 return true;
234}
235
236uint64_t DataInputDescriptor::getTimeFrameNumber(int counter, int numTF, int wantedParentLevel, std::string_view wantedOrigin)
237{
238
239 // open file
240 if (!setFile(counter, wantedParentLevel, wantedOrigin)) {
241 return 0ul;
242 }
243
244 // no TF left
245 if (mfilenames[counter].numberOfTimeFrames > 0 && numTF >= mfilenames[counter].numberOfTimeFrames) {
246 return 0ul;
247 }
248
249 return (mfilenames[counter].listOfTimeFrameNumbers)[numTF];
250}
251
252std::pair<std::shared_ptr<DataInputDescriptor>, int> DataInputDescriptor::navigateToLevel(int counter, int numTF, int wantedParentLevel, std::string_view wantedOrigin)
253{
254 if (!setFile(counter, wantedParentLevel, wantedOrigin)) {
255 return {nullptr, -1};
256 }
257 auto folderName = fmt::format("DF_{}", mfilenames[counter].listOfTimeFrameNumbers[numTF]);
258 auto parentFile = getParentFile(counter, numTF, "", wantedParentLevel, wantedOrigin);
259 if (parentFile == nullptr) {
260 return {nullptr, -1};
261 }
262 return {parentFile, parentFile->findDFNumber(0, folderName)};
263}
264
265arrow::dataset::FileSource DataInputDescriptor::getFileFolder(int counter, int numTF, int wantedParentLevel, std::string_view wantedOrigin)
266{
267 // If mapped to a parent level deeper than current, skip directly to the right level.
268 if ((wantedParentLevel != -1) && (mLevel < wantedParentLevel)) {
269 auto [parentFile, parentNumTF] = navigateToLevel(counter, numTF, wantedParentLevel, wantedOrigin);
270 if (parentFile == nullptr || parentNumTF == -1) {
271 return {};
272 }
273 return parentFile->getFileFolder(0, parentNumTF, wantedParentLevel, wantedOrigin);
274 }
275
276 // open file
277 if (!setFile(counter, wantedParentLevel, wantedOrigin)) {
278 return {};
279 }
280
281 // no TF left
282 if ((mfilenames[counter].numberOfTimeFrames > 0) && (numTF >= mfilenames[counter].numberOfTimeFrames)) {
283 return {};
284 }
285
286 mfilenames[counter].alreadyRead[numTF] = true;
287
288 return {fmt::format("DF_{}", mfilenames[counter].listOfTimeFrameNumbers[numTF]), mCurrentFilesystem};
289}
290
292{
293 if (mCurrentFileID >= 0 && numTF >= 0 && numTF < mfilenames[mCurrentFileID].numberOfTimeFrames) {
294 mfilenames[mCurrentFileID].alreadyRead[numTF] = false;
295 return ++mfilenames[mCurrentFileID].invalidReadSkipped;
296 }
297 return 0;
298}
299
300std::shared_ptr<DataInputDescriptor> DataInputDescriptor::getParentFile(int counter, int numTF, std::string treename, int wantedParentLevel, std::string_view wantedOrigin)
301{
302 if (!mParentFileMap) {
303 // This file has no parent map
304 return nullptr;
305 }
306
307 auto folderName = fmt::format("DF_{}", mfilenames[counter].listOfTimeFrameNumbers[numTF]);
308 auto parentFileName = (TObjString*)mParentFileMap->GetValue(folderName.c_str());
309 // The current DF is not found in the parent map (this should not happen and is a fatal error)
310 auto rootFS = std::dynamic_pointer_cast<TFileFileSystem>(mCurrentFilesystem);
311 if (!parentFileName) {
312 throw std::runtime_error(fmt::format(R"(parent file map exists but does not contain the current DF "{}" in file "{}")", folderName.c_str(), rootFS->GetFile()->GetName()));
313 return nullptr;
314 }
315
316 if (mParentFile) {
317 // Is this still the corresponding to the correct file?
318 auto parentRootFS = std::dynamic_pointer_cast<TFileFileSystem>(mParentFile->mCurrentFilesystem);
319 if (parentFileName->GetString().CompareTo(parentRootFS->GetFile()->GetName()) == 0) {
320 return mParentFile;
321 } else {
322 mParentFile->closeInputFile();
323 mParentFile.reset();
324 }
325 }
326
327 if (mLevel == mContext.allowedParentLevel) {
328 throw std::runtime_error(fmt::format(R"(while looking for tree "{}", the parent file was requested but we are already at level {} of maximal allowed level {} for DF "{}" in file "{}")", treename.c_str(), mLevel, mContext.allowedParentLevel, folderName.c_str(),
329 rootFS->GetFile()->GetName()));
330 }
331
332 LOGP(info, "Opening parent file {} for DF {}", parentFileName->GetString().Data(), folderName.c_str());
333 mParentFile = std::make_shared<DataInputDescriptor>(mAlienSupport, mLevel + 1, mContext);
334 mParentFile->mdefaultFilenamesPtr.emplace_back(makeFileNameHolder(parentFileName->GetString().Data()));
335 mParentFile->fillInputfiles();
336 mParentFile->setFile(0, wantedParentLevel, wantedOrigin);
337 return mParentFile;
338}
339
341{
342 return mfilenames.at(counter).numberOfTimeFrames;
343}
344
346{
347 auto& list = mfilenames.at(counter).alreadyRead;
348 return std::count(list.begin(), list.end(), true);
349}
350
352{
353 auto rootFS = std::dynamic_pointer_cast<TFileFileSystem>(mCurrentFilesystem);
354 auto f = dynamic_cast<TFile*>(rootFS->GetFile());
355 std::string monitoringInfo(fmt::format("lfn={},size={}", f->GetName(), f->GetSize()));
356#if __has_include(<TJAlienFile.h>)
357 auto alienFile = dynamic_cast<TJAlienFile*>(f);
358 if (alienFile) {
359 monitoringInfo += fmt::format(",se={},open_time={:.1f}", alienFile->GetSE(), alienFile->GetElapsed());
360 }
361#endif
362 if (mContext.monitoring) {
363 mContext.monitoring->send(o2::monitoring::Metric{monitoringInfo, "aod-file-open-info"}.addTag(o2::monitoring::tags::Key::Subsystem, o2::monitoring::tags::Value::DPL));
364 }
365 LOGP(info, "Opening file: {}", monitoringInfo);
366}
367
369{
370 int64_t wait_time = (int64_t)uv_hrtime() - (int64_t)mCurrentFileStartedAt - (int64_t)mIOTime;
371 if (wait_time < 0) {
372 wait_time = 0;
373 }
374 auto rootFS = std::dynamic_pointer_cast<TFileFileSystem>(mCurrentFilesystem);
375 auto f = dynamic_cast<TFile*>(rootFS->GetFile());
376 std::string monitoringInfo(fmt::format("lfn={},size={},total_df={},read_df={},skipped_df={},read_bytes={},read_calls={},io_time={:.1f},wait_time={:.1f},level={}", f->GetName(),
377 f->GetSize(), getTimeFramesInFile(mCurrentFileID), getReadTimeFramesInFile(mCurrentFileID), mfilenames.at(mCurrentFileID).invalidReadSkipped, f->GetBytesRead(), f->GetReadCalls(),
378 ((float)mIOTime / 1e9), ((float)wait_time / 1e9), mLevel));
379#if __has_include(<TJAlienFile.h>)
380 auto alienFile = dynamic_cast<TJAlienFile*>(f);
381 if (alienFile) {
382 monitoringInfo += fmt::format(",se={},open_time={:.1f}", alienFile->GetSE(), alienFile->GetElapsed());
383 }
384#endif
385 if (mContext.monitoring) {
386 mContext.monitoring->send(o2::monitoring::Metric{monitoringInfo, "aod-file-read-info"}.addTag(o2::monitoring::tags::Key::Subsystem, o2::monitoring::tags::Value::DPL));
387 }
388 LOGP(info, "Read info: {}", monitoringInfo);
389}
390
392{
393 if (mCurrentFilesystem.get()) {
394 if (mParentFile) {
395 mParentFile->closeInputFile();
396 mParentFile.reset();
397 }
398
399 delete mParentFileMap;
400 mParentFileMap = nullptr;
401
403 mCurrentFilesystem.reset();
404 }
405}
406
408{
409 if (getNumberInputfiles() > 0) {
410 // 1. mfilenames
411 return getNumberInputfiles();
412 }
413
414 auto fileName = getInputfilesFilename();
415 if (!fileName.empty()) {
416 // 2. getFilenamesRegex() @ getInputfilesFilename()
417 try {
418 std::ifstream filelist(fileName);
419 if (!filelist.is_open()) {
420 throw std::runtime_error(fmt::format(R"(Couldn't open file "{}")", fileName));
421 }
422 while (std::getline(filelist, fileName)) {
423 // remove white spaces, empty lines are skipped
424 fileName.erase(std::remove_if(fileName.begin(), fileName.end(), ::isspace), fileName.end());
425 if (!fileName.empty() && (getFilenamesRegexString().empty() ||
426 std::regex_match(fileName, getFilenamesRegex()))) {
428 }
429 }
430 } catch (...) {
431 LOGP(error, "Check the input files file! Unable to process \"{}\"!", getInputfilesFilename());
432 return 0;
433 }
434 } else {
435 // 3. getFilenamesRegex() @ mdefaultFilenamesPtr
436 if (!mdefaultFilenamesPtr.empty()) {
437 for (auto& fileNameHolder : mdefaultFilenamesPtr) {
438 if (getFilenamesRegexString().empty() ||
439 std::regex_match(fileNameHolder.fileName, getFilenamesRegex())) {
440 addFileNameHolder(fileNameHolder);
441 }
442 }
443 }
444 }
445
446 return getNumberInputfiles();
447}
448
449int DataInputDescriptor::findDFNumber(int file, std::string dfName)
450{
451 auto dfList = mfilenames[file].listOfTimeFrameNumbers;
452 auto it = std::find_if(dfList.begin(), dfList.end(), [dfName](size_t i) { return fmt::format("DF_{}", i) == dfName; });
453 if (it == dfList.end()) {
454 return -1;
455 }
456 return it - dfList.begin();
457}
458
461 : mTarget(target)
462 {
463 start = uv_hrtime();
464 }
466 {
467 if (!active) {
468 return;
469 }
470 O2_SIGNPOST_ACTION(reader_memory_dump, [](void*) {
471 void (*dump_)(const char*);
472 if (void* sym = dlsym(nullptr, "igprof_dump_now")) {
473 dump_ = __extension__(void (*)(const char*)) sym;
474 if (dump_) {
475 std::string filename = fmt::format("reader-memory-dump-{}.gz", uv_hrtime());
476 dump_(filename.c_str());
477 }
478 }
479 });
480 mTarget += (uv_hrtime() - start);
481 }
482
484 {
485 active = false;
486 }
487
488 bool active = true;
489 uint64_t& mTarget;
490 uint64_t start;
491 uint64_t stop;
492};
493
494bool DataInputDescriptor::readTree(DataAllocator& outputs, header::DataHeader dh, int counter, int numTF, std::string treename, size_t& totalSizeCompressed, size_t& totalSizeUncompressed)
495{
496 CalculateDelta t(mIOTime);
497 std::string wantedOrigin = dh.dataOrigin.as<std::string>();
498 int wantedLevel = mContext.levelForOrigin(wantedOrigin);
499
500 // If this origin is mapped to a parent level deeper than current, skip directly without
501 // attempting to read from this level.
502 if (wantedLevel != -1 && mLevel < wantedLevel) {
503 auto [parentFile, parentNumTF] = navigateToLevel(counter, numTF, wantedLevel, wantedOrigin);
504 if (parentFile == nullptr) {
505 auto rootFS = std::dynamic_pointer_cast<TFileFileSystem>(mCurrentFilesystem);
506 throw std::runtime_error(fmt::format(R"(No parent file found for "{}" while looking for level {} in "{}")", treename, wantedLevel, rootFS->GetFile()->GetName()));
507 }
508 if (parentNumTF == -1) {
509 auto parentRootFS = std::dynamic_pointer_cast<TFileFileSystem>(parentFile->mCurrentFilesystem);
510 throw std::runtime_error(fmt::format(R"(DF not found in parent file "{}")", parentRootFS->GetFile()->GetName()));
511 }
512 t.deactivate();
513 return parentFile->readTree(outputs, dh, 0, parentNumTF, treename, totalSizeCompressed, totalSizeUncompressed);
514 }
515
516 auto folder = getFileFolder(counter, numTF, wantedLevel, wantedOrigin);
517 if (!folder.filesystem()) {
518 t.deactivate();
519 return false;
520 }
521
522 auto rootFS = std::dynamic_pointer_cast<TFileFileSystem>(folder.filesystem());
523
524 if (!rootFS) {
525 t.deactivate();
526 throw std::runtime_error(fmt::format(R"(Not a TFile filesystem!)"));
527 }
528 // FIXME: Ugly. We should detect the format from the treename, good enough for now.
529 std::shared_ptr<arrow::dataset::FileFormat> format;
530 FragmentToBatch::StreamerCreator creator = nullptr;
531
532 auto fullpath = arrow::dataset::FileSource{folder.path() + "/" + treename, folder.filesystem()};
533
534 for (auto& capability : mFactory.capabilities) {
535 auto objectPath = capability.lfn2objectPath(fullpath.path());
536 void* handle = capability.getHandle(rootFS, objectPath);
537 if (handle) {
538 format = capability.factory().format();
539 creator = capability.factory().deferredOutputStreamer;
540 // Account for the bytes we are about to read. This used to sit further down, where
541 // the TTree was opened by hand; moving the reading to the arrow::Dataset API left
542 // the accounting behind, which is why aod-bytes-read-* and the --aod-max-read-rate
543 // pacing that derives from them both read zero. Each format reports its own size,
544 // so we just ask; here is where the object is resolved and its size is known.
545 if (capability.accountBytes) {
546 capability.accountBytes(handle, totalSizeCompressed, totalSizeUncompressed);
547 }
548 break;
549 }
550 }
551
552 // FIXME: we should distinguish between an actually missing object and one which has a non compatible
553 // format.
554 if (!format) {
555 t.deactivate();
556 LOGP(debug, "Could not find tree {}. Trying in parent file.", fullpath.path());
557 std::shared_ptr<DataInputDescriptor> parentFile;
558 try {
559 parentFile = getParentFile(counter, numTF, treename, wantedLevel, wantedOrigin);
560 } catch (...) {
561 std::throw_with_nested(InvalidAODReadError(fmt::format("Unable to resolve parent file for tree {}", treename)));
562 }
563 if (parentFile == nullptr) {
564 auto rootFS = std::dynamic_pointer_cast<TFileFileSystem>(mCurrentFilesystem);
565 throw std::runtime_error(fmt::format(R"(Couldn't get TTree "{}" from "{}". Please check https://aliceo2group.github.io/analysis-framework/docs/troubleshooting/#tree-not-found for more information.)", fullpath.path(), rootFS->GetFile()->GetName()));
566 }
567 int parentNumTF = parentFile->findDFNumber(0, folder.path());
568 if (parentNumTF == -1) {
569 auto parentRootFS = std::dynamic_pointer_cast<TFileFileSystem>(parentFile->mCurrentFilesystem);
570 throw InvalidAODReadError(fmt::format(R"(DF {} listed in parent file map but not found in the corresponding file "{}")", folder.path(), parentRootFS->GetFile()->GetName()));
571 }
572 // first argument is 0 as the parent file object contains only 1 file
573 return parentFile->readTree(outputs, dh, 0, parentNumTF, treename, totalSizeCompressed, totalSizeUncompressed);
574 }
575
576 auto schemaOpt = format->Inspect(fullpath);
577 auto physicalSchema = schemaOpt;
578 std::vector<std::shared_ptr<arrow::Field>> fields;
579 for (auto& original : (*schemaOpt)->fields()) {
580 if (original->name().ends_with("_size")) {
581 continue;
582 }
583 fields.push_back(original);
584 }
585 auto datasetSchema = std::make_shared<arrow::Schema>(fields);
586
587 auto fragment = format->MakeFragment(fullpath, {}, *physicalSchema);
588
589 // create table output
590 auto o = Output(dh);
591
592 // FIXME: This should allow me to create a memory pool
593 // which I can then use to scan the dataset.
594 auto f2b = outputs.make<FragmentToBatch>(o, creator, *fragment);
595
598 f2b->setLabel(treename.c_str());
599 char const* operation = "read";
600 try {
601 f2b->fill(datasetSchema, format);
602 operation = "finalize";
603 f2b.release();
604 } catch (...) {
605 f2b.discard();
606 std::throw_with_nested(InvalidAODReadError(fmt::format("Unable to {} tree {}", operation, treename)));
607 }
608
609 return true;
610}
611
612DataInputDirector::DataInputDirector(std::vector<std::string> inputFiles, DataInputDirectorContext&& context)
613 : mContext{context}
614{
615 if (inputFiles.size() == 1 && !inputFiles[0].empty() && inputFiles[0][0] == '@') {
616 setInputfilesFile(inputFiles.back().substr(1, -1));
617 } else {
618 for (auto inputFile : inputFiles) {
619 mdefaultInputFiles.emplace_back(makeFileNameHolder(inputFile));
620 }
621 }
622
624}
625
627{
628 mdefaultInputFiles.clear();
629 mdefaultDataInputDescriptor = nullptr;
630
631 mdataInputDescriptors.clear();
632}
633
635{
636 mdataInputDescriptors.clear();
637 mdefaultInputFiles.clear();
638 mFilenameRegex = std::string("");
639};
640
642{
643 if (mdefaultDataInputDescriptor) {
644 mdefaultDataInputDescriptor.reset();
645 }
646 mdefaultDataInputDescriptor = std::make_shared<DataInputDescriptor>(mAlienSupport, 0, mContext);
647
648 mdefaultDataInputDescriptor->setInputfilesFile(minputfilesFile);
649 mdefaultDataInputDescriptor->setFilenamesRegex(mFilenameRegex);
650 mdefaultDataInputDescriptor->setDefaultInputfiles(mdefaultInputFiles);
651 mdefaultDataInputDescriptor->tablename = "any";
652 mdefaultDataInputDescriptor->treename = "any";
653 mdefaultDataInputDescriptor->fillInputfiles();
654
655 mAlienSupport &= mdefaultDataInputDescriptor->isAlienSupportOn();
656}
657
658bool DataInputDirector::readJson(std::string const& fnjson)
659{
660 // open the file
661 FILE* f = fopen(fnjson.c_str(), "r");
662 if (!f) {
663 LOGP(error, "Could not open JSON file \"{}\"!", fnjson);
664 return false;
665 }
666
667 // create streamer
668 char readBuffer[65536];
669 FileReadStream inputStream(f, readBuffer, sizeof(readBuffer));
670
671 // parse the json file
672 Document jsonDoc;
673 jsonDoc.ParseStream(inputStream);
674 auto status = readJsonDocument(&jsonDoc);
675
676 // clean up
677 fclose(f);
678
679 return status;
680}
681
682bool DataInputDirector::readJsonDocument(Document* jsonDoc)
683{
684 // initialisations
685 std::string fileName("");
686 const char* itemName;
687
688 // is it a proper json document?
689 if (jsonDoc->HasParseError()) {
690 LOGP(error, "Check the JSON document! There is a problem with the format!");
691 return false;
692 }
693
694 // InputDirector
695 itemName = "InputDirector";
696 const Value& didirItem = (*jsonDoc)[itemName];
697 if (!didirItem.IsObject()) {
698 LOGP(info, "No \"{}\" object found in the JSON document!", itemName);
699 return true;
700 }
701
702 // now read various items
703 itemName = "debugmode";
704 if (didirItem.HasMember(itemName)) {
705 if (didirItem[itemName].IsBool()) {
706 mDebugMode = (didirItem[itemName].GetBool());
707 } else {
708 LOGP(error, "Check the JSON document! Item \"{}\" must be a boolean!", itemName);
709 return false;
710 }
711 } else {
712 mDebugMode = false;
713 }
714
715 if (mDebugMode) {
716 StringBuffer buffer;
717 buffer.Clear();
718 PrettyWriter<StringBuffer> writer(buffer);
719 didirItem.Accept(writer);
720 LOGP(info, "InputDirector object: {}", std::string(buffer.GetString()));
721 }
722
723 itemName = "fileregex";
724 if (didirItem.HasMember(itemName)) {
725 if (didirItem[itemName].IsString()) {
726 setFilenamesRegex(didirItem[itemName].GetString());
727 } else {
728 LOGP(error, "Check the JSON document! Item \"{}\" must be a string!", itemName);
729 return false;
730 }
731 }
732
733 itemName = "resfiles";
734 if (didirItem.HasMember(itemName)) {
735 if (didirItem[itemName].IsString()) {
736 fileName = didirItem[itemName].GetString();
737 if (fileName.size() && fileName[0] == '@') {
738 fileName.erase(0, 1);
739 setInputfilesFile(fileName);
740 } else {
742 mdefaultInputFiles.emplace_back(makeFileNameHolder(fileName));
743 }
744 } else if (didirItem[itemName].IsArray()) {
746 auto fns = didirItem[itemName].GetArray();
747 for (auto& fn : fns) {
748 mdefaultInputFiles.emplace_back(makeFileNameHolder(fn.GetString()));
749 }
750 } else {
751 LOGP(error, "Check the JSON document! Item \"{}\" must be a string or an array!", itemName);
752 return false;
753 }
754 }
755
756 itemName = "InputDescriptors";
757 if (didirItem.HasMember(itemName)) {
758 if (!didirItem[itemName].IsArray()) {
759 LOGP(error, "Check the JSON document! Item \"{}\" must be an array!", itemName);
760 return false;
761 }
762
763 // loop over DataInputDescriptors
764 for (auto& didescItem : didirItem[itemName].GetArray()) {
765 if (!didescItem.IsObject()) {
766 LOGP(error, "Check the JSON document! \"{}\" must be objects!", itemName);
767 return false;
768 }
769 // create a new dataInputDescriptor
770 auto didesc = DataInputDescriptor(mAlienSupport, 0, mContext);
771 didesc.setDefaultInputfiles(mdefaultInputFiles);
772
773 itemName = "table";
774 if (didescItem.HasMember(itemName)) {
775 if (didescItem[itemName].IsString()) {
776 didesc.tablename = didescItem[itemName].GetString();
777 didesc.matcher = DataDescriptorQueryBuilder::buildNode(didesc.tablename);
778 } else {
779 LOGP(error, "Check the JSON document! Item \"{}\" must be a string!", itemName);
780 return false;
781 }
782 } else {
783 LOGP(error, "Check the JSON document! Item \"{}\" is missing!", itemName);
784 return false;
785 }
786
787 itemName = "treename";
788 if (didescItem.HasMember(itemName)) {
789 if (didescItem[itemName].IsString()) {
790 didesc.treename = didescItem[itemName].GetString();
791 } else {
792 LOGP(error, "Check the JSON document! Item \"{}\" must be a string!", itemName);
793 return false;
794 }
795 } else {
796 auto m = DataDescriptorQueryBuilder::getTokens(didesc.tablename);
797 didesc.treename = m[2];
798 }
799
800 itemName = "fileregex";
801 if (didescItem.HasMember(itemName)) {
802 if (didescItem[itemName].IsString()) {
803 if (didesc.getNumberInputfiles() == 0) {
804 didesc.setFilenamesRegex(didescItem[itemName].GetString());
805 }
806 } else {
807 LOGP(error, "Check the JSON document! Item \"{}\" must be a string!", itemName);
808 return false;
809 }
810 } else {
811 if (didesc.getNumberInputfiles() == 0) {
812 didesc.setFilenamesRegex(mFilenameRegexPtr);
813 }
814 }
815
816 itemName = "resfiles";
817 if (didescItem.HasMember(itemName)) {
818 if (didescItem[itemName].IsString()) {
819 fileName = didescItem[itemName].GetString();
820 if (fileName.size() && fileName[0] == '@') {
821 didesc.setInputfilesFile(fileName.erase(0, 1));
822 } else {
823 if (didesc.getFilenamesRegexString().empty() ||
824 std::regex_match(fileName, didesc.getFilenamesRegex())) {
825 didesc.addFileNameHolder(makeFileNameHolder(fileName));
826 }
827 }
828 } else if (didescItem[itemName].IsArray()) {
829 auto fns = didescItem[itemName].GetArray();
830 for (auto& fn : fns) {
831 if (didesc.getFilenamesRegexString().empty() ||
832 std::regex_match(fn.GetString(), didesc.getFilenamesRegex())) {
833 didesc.addFileNameHolder(makeFileNameHolder(fn.GetString()));
834 }
835 }
836 } else {
837 LOGP(error, "Check the JSON document! Item \"{}\" must be a string or an array!", itemName);
838 return false;
839 }
840 } else {
841 didesc.setInputfilesFile(minputfilesFilePtr);
842 }
843
844 // fill mfilenames and add InputDescriptor to InputDirector
845 if (didesc.fillInputfiles() > 0) {
846 mdataInputDescriptors.emplace_back(didesc);
847 } else {
848 didesc.printOut();
849 LOGP(info, "This DataInputDescriptor is ignored because its file list is empty!");
850 }
851 mAlienSupport &= didesc.isAlienSupportOn();
852 }
853 }
854
855 // add a default DataInputDescriptor
857
858 // check that all DataInputDescriptors have the same number of input files
859 if (!isValid()) {
860 printOut();
861 return false;
862 }
863
864 // print the DataIputDirector
865 if (mDebugMode) {
866 printOut();
867 }
868
869 return true;
870}
871
873{
874 // compute list of matching outputs
876
877 for (auto& didesc : mdataInputDescriptors) {
878 if (didesc.matcher->match(dh, context)) {
879 return &didesc;
880 }
881 }
882
883 return nullptr;
884}
885
886arrow::dataset::FileSource DataInputDirector::getFileFolder(header::DataHeader dh, int counter, int numTF)
887{
888 auto didesc = getDataInputDescriptor(dh);
889 // if NOT match then use defaultDataInputDescriptor
890 if (!didesc) {
891 didesc = mdefaultDataInputDescriptor.get();
892 }
893 std::string origin = dh.dataOrigin.as<std::string>();
894 int wantedLevel = mContext.levelForOrigin(origin);
895
896 return didesc->getFileFolder(counter, numTF, wantedLevel, origin);
897}
898
900{
901 auto didesc = getDataInputDescriptor(dh);
902 if (!didesc) {
903 didesc = mdefaultDataInputDescriptor.get();
904 }
905 didesc->markTimeFrameSkipped(numTF);
906}
907
909{
910 auto didesc = getDataInputDescriptor(dh);
911 // if NOT match then use defaultDataInputDescriptor
912 if (!didesc) {
913 didesc = mdefaultDataInputDescriptor.get();
914 }
915
916 return didesc->getTimeFramesInFile(counter);
917}
918
920{
921 auto didesc = getDataInputDescriptor(dh);
922 // if NOT match then use defaultDataInputDescriptor
923 if (!didesc) {
924 didesc = mdefaultDataInputDescriptor.get();
925 }
926 std::string origin = dh.dataOrigin.as<std::string>();
927 int wantedLevel = mContext.levelForOrigin(origin);
928
929 return didesc->getTimeFrameNumber(counter, numTF, wantedLevel, origin);
930}
931
932bool DataInputDirector::readTree(DataAllocator& outputs, header::DataHeader dh, int counter, int numTF, size_t& totalSizeCompressed, size_t& totalSizeUncompressed, bool wasAOD)
933{
934 std::string treename;
935
936 auto didesc = getDataInputDescriptor(dh);
937 if (didesc) {
938 // if match then use filename and treename from DataInputDescriptor
939 treename = didesc->treename;
940 } else {
941 // if NOT match then use
942 // . filename from defaultDataInputDescriptor
943 // . treename from DataHeader
944 didesc = mdefaultDataInputDescriptor.get();
945 treename = aod::datamodel::getTreeName(dh, wasAOD);
946 }
947 std::string origin = dh.dataOrigin.as<std::string>();
948
949 auto result = didesc->readTree(outputs, dh, counter, numTF, treename, totalSizeCompressed, totalSizeUncompressed);
950 return result;
951}
952
954{
955 mdefaultDataInputDescriptor->closeInputFile();
956 for (auto& didesc : mdataInputDescriptors) {
957 didesc.closeInputFile();
958 }
959}
960
961bool DataInputDirector::isValid()
962{
963 bool status = true;
964 int numberFiles = mdefaultDataInputDescriptor->getNumberInputfiles();
965 for (auto& didesc : mdataInputDescriptors) {
966 status &= didesc.getNumberInputfiles() == numberFiles;
967 }
968
969 return status;
970}
971
973{
974 bool status = mdefaultDataInputDescriptor->getNumberInputfiles() <= counter;
975 for (auto& didesc : mdataInputDescriptors) {
976 status &= (didesc.getNumberInputfiles() <= counter);
977 }
978
979 return status;
980}
981
983{
984 LOGP(info, "DataInputDirector");
985 LOGP(info, " Default input files file : {}", minputfilesFile);
986 LOGP(info, " Default file name regex : {}", mFilenameRegex);
987 LOGP(info, " Default file names : {}", mdefaultInputFiles.size());
988 for (auto const& fn : mdefaultInputFiles) {
989 LOGP(info, " {} {}", fn.fileName, fn.numberOfTimeFrames);
990 }
991 LOGP(info, " Default DataInputDescriptor:");
992 mdefaultDataInputDescriptor->printOut();
993 LOGP(info, " DataInputDescriptors : {}", getNumberInputDescriptors());
994 for (auto const& didesc : mdataInputDescriptors) {
995 didesc.printOut();
996 }
997}
998
1000{
1001 return mContext.levelForOrigin(origin.as<std::string>());
1002}
1003
1004} // namespace o2::framework
header::DataOrigin origin
o2::monitoring::tags::Value Value
BasicOp operation
std::vector< std::shared_ptr< arrow::Field > > fields
std::ostringstream debug
int32_t i
constexpr int p2()
constexpr int p1()
constexpr to accelerate the coordinates changing
uint16_t pos
Definition RawData.h:3
#define O2_DECLARE_DYNAMIC_LOG(name)
Definition Signpost.h:490
#define O2_SIGNPOST_ACTION(log, callback)
Definition Signpost.h:511
StringRef key
decltype(auto) make(const Output &spec, Args... args)
uint64_t getTimeFrameNumber(int counter, int numTF, int wantedParentLevel, std::string_view wantedOrigin)
std::shared_ptr< DataInputDescriptor > getParentFile(int counter, int numTF, std::string treename, int wantedParentLevel, std::string_view wantedOrigin)
bool readTree(DataAllocator &outputs, header::DataHeader dh, int counter, int numTF, std::string treename, size_t &totalSizeCompressed, size_t &totalSizeUncompressed)
arrow::dataset::FileSource getFileFolder(int counter, int numTF, int wantedParentLevel, std::string_view wantedOrigin)
DataInputDescriptor(bool alienSupport, int level, DataInputDirectorContext &context)
void addFileNameHolder(FileNameHolder fn)
bool setFile(int counter, int wantedParentLevel, std::string_view wantedOrigin)
int findDFNumber(int file, std::string dfName)
std::pair< std::shared_ptr< DataInputDescriptor >, int > navigateToLevel(int counter, int numTF, int wantedParentLevel, std::string_view wantedOrigin)
DataInputDescriptor * getDataInputDescriptor(header::DataHeader dh)
DataInputDirector(std::vector< std::string > inputFiles, DataInputDirectorContext &&context)
arrow::dataset::FileSource getFileFolder(header::DataHeader dh, int counter, int numTF)
void setInputfilesFile(std::string iffn)
int getTimeFramesInFile(header::DataHeader dh, int counter)
uint64_t getTimeFrameNumber(header::DataHeader dh, int counter, int numTF)
void markTimeFrameSkipped(header::DataHeader dh, int numTF)
int getLevelForOrigin(header::DataOrigin origin) const
void setFilenamesRegex(std::string dfn)
bool readJson(std::string const &fnjson)
bool readTree(DataAllocator &outputs, header::DataHeader dh, int counter, int numTF, size_t &totalSizeCompressed, size_t &totalSizeUncompressed, bool wasAOD)
std::function< std::shared_ptr< arrow::io::OutputStream >(std::shared_ptr< arrow::dataset::FileFragment >, const std::shared_ptr< arrow::ResizableBuffer > &buffer)> StreamerCreator
void setLabel(const char *label)
bool match(const std::vector< std::string > &queries, const char *pattern)
Definition dcs-ccdb.cxx:229
const GLfloat * m
Definition glcorearb.h:4066
GLuint64EXT * result
Definition glcorearb.h:5662
GLuint buffer
Definition glcorearb.h:655
GLuint const GLchar * name
Definition glcorearb.h:781
GLdouble f
Definition glcorearb.h:310
GLenum target
Definition glcorearb.h:1641
typedef void(APIENTRYP PFNGLCULLFACEPROC)(GLenum mode)
GLuint start
Definition glcorearb.h:469
GLint GLint GLsizei GLint GLenum format
Definition glcorearb.h:275
GLuint counter
Definition glcorearb.h:3987
std::string getTreeName(header::DataHeader dh, bool wasAOD)
Defining ITS Vertex explicitly as messageable.
Definition Cartesian.h:288
FileNameHolder makeFileNameHolder(std::string fileName)
std::string to_string(gsl::span< T, Size > span)
Definition common.h:52
std::string filename()
void empty(int)
static std::unique_ptr< data_matcher::DataDescriptorMatcher > buildNode(std::string const &nodeString)
static std::vector< std::string > getTokens(std::string const &nodeString)
o2::monitoring::Monitoring * monitoring
std::vector< std::pair< std::string, TFile * > > openFiles
int levelForOrigin(std::string_view origin) const
static std::vector< LoadablePlugin > parsePluginSpecString(char const *str)
Parse a comma separated list of <library>:<plugin-name> plugin declarations.
std::vector< RootObjectReadingCapability > capabilities
the main header struct
Definition DataHeader.h:620
std::enable_if_t< std::is_same< T, std::string >::value==true, T > as() const
get the descriptor as std::string
Definition DataHeader.h:301
const std::string str