@@ -161,10 +161,13 @@ bool DataInputDescriptor::setFile(int counter, int wantedParentLevel, std::strin
161161 if (tfile == nullptr ) {
162162 tfile = TFile::Open (filename.c_str ());
163163 }
164- mCurrentFilesystem = std::make_shared<TFileFileSystem>(tfile, 50 * 1024 * 1024 , mFactory , !externalFile);
165- if (!mCurrentFilesystem .get ()) {
164+ if (!tfile || tfile->IsZombie ()) {
165+ if (tfile && !externalFile) {
166+ delete tfile;
167+ }
166168 throw std::runtime_error (fmt::format (" Couldn't open file \" {}\" !" , filename));
167169 }
170+ mCurrentFilesystem = std::make_shared<TFileFileSystem>(tfile, 50 * 1024 * 1024 , mFactory , !externalFile);
168171 rootFS = std::dynamic_pointer_cast<TFileFileSystem>(mCurrentFilesystem );
169172 printFileOpening ();
170173
@@ -254,6 +257,10 @@ std::pair<std::shared_ptr<DataInputDescriptor>, int> DataInputDescriptor::naviga
254257 if (!setFile (counter, wantedParentLevel, wantedOrigin)) {
255258 return {nullptr , -1 };
256259 }
260+ if (numTF < 0 || numTF >= mfilenames[counter].numberOfTimeFrames ) {
261+ return {nullptr , -1 };
262+ }
263+ recordTimeFrameRead (counter, numTF);
257264 auto folderName = fmt::format (" DF_{}" , mfilenames[counter].listOfTimeFrameNumbers [numTF]);
258265 auto parentFile = getParentFile (counter, numTF, " " , wantedParentLevel, wantedOrigin);
259266 if (parentFile == nullptr ) {
@@ -283,18 +290,65 @@ arrow::dataset::FileSource DataInputDescriptor::getFileFolder(int counter, int n
283290 return {};
284291 }
285292
293+ recordTimeFrameRead (counter, numTF);
286294 mfilenames[counter].alreadyRead [numTF] = true ;
287295
288296 return {fmt::format (" DF_{}" , mfilenames[counter].listOfTimeFrameNumbers [numTF]), mCurrentFilesystem };
289297}
290298
291- uint64_t DataInputDescriptor::markTimeFrameSkipped ( int numTF)
299+ void DataInputDescriptor::recordTimeFrameRead ( int counter, int numTF)
292300{
293- if (mCurrentFileID >= 0 && numTF >= 0 && numTF < mfilenames[mCurrentFileID ].numberOfTimeFrames ) {
294- mfilenames[mCurrentFileID ].alreadyRead [numTF] = false ;
295- return ++mfilenames[mCurrentFileID ].invalidReadSkipped ;
301+ auto read = std::pair{counter, numTF};
302+ if (std::find (mTimeFrameReads .begin (), mTimeFrameReads .end (), read) == mTimeFrameReads .end ()) {
303+ mTimeFrameReads .push_back (read);
304+ }
305+ }
306+
307+ void DataInputDescriptor::beginTimeFrame ()
308+ {
309+ mTimeFrameReads .clear ();
310+ mTimeFrameActive = true ;
311+ if (mParentFile ) {
312+ mParentFile ->beginTimeFrame ();
313+ }
314+ }
315+
316+ void DataInputDescriptor::finishTimeFrame (bool skipped)
317+ {
318+ mTimeFrameActive = false ;
319+ if (skipped) {
320+ for (auto [file, df] : mTimeFrameReads ) {
321+ mfilenames[file].alreadyRead [df] = false ;
322+ ++mfilenames[file].invalidReadSkipped ;
323+ }
324+ }
325+ mTimeFrameReads .clear ();
326+ if (mParentFile ) {
327+ mParentFile ->finishTimeFrame (skipped);
328+ }
329+ for (auto & parent : mRetainedParents ) {
330+ parent->finishTimeFrame (skipped);
331+ parent->closeInputFile ();
332+ }
333+ mRetainedParents .clear ();
334+ for (auto & [counter, info] : mPendingFileStatistics ) {
335+ reportFileStatistics (counter, std::move (info));
336+ }
337+ mPendingFileStatistics .clear ();
338+ }
339+
340+ void DataInputDescriptor::releaseParentFile ()
341+ {
342+ if (!mParentFile ) {
343+ return ;
344+ }
345+ if (mTimeFrameActive ) {
346+ // Keep both the read bookkeeping and the open file for final statistics.
347+ mRetainedParents .push_back (std::move (mParentFile ));
348+ } else {
349+ mParentFile ->closeInputFile ();
350+ mParentFile .reset ();
296351 }
297- return 0 ;
298352}
299353
300354std::shared_ptr<DataInputDescriptor> DataInputDescriptor::getParentFile (int counter, int numTF, std::string treename, int wantedParentLevel, std::string_view wantedOrigin)
@@ -309,8 +363,7 @@ std::shared_ptr<DataInputDescriptor> DataInputDescriptor::getParentFile(int coun
309363 // The current DF is not found in the parent map (this should not happen and is a fatal error)
310364 auto rootFS = std::dynamic_pointer_cast<TFileFileSystem>(mCurrentFilesystem );
311365 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 ;
366+ throw InvalidAODReadError (fmt::format (R"( parent file map exists but does not contain the current DF "{}" in file "{}")" , folderName.c_str (), rootFS->GetFile ()->GetName ()));
314367 }
315368
316369 if (mParentFile ) {
@@ -319,21 +372,28 @@ std::shared_ptr<DataInputDescriptor> DataInputDescriptor::getParentFile(int coun
319372 if (parentFileName->GetString ().CompareTo (parentRootFS->GetFile ()->GetName ()) == 0 ) {
320373 return mParentFile ;
321374 } else {
322- mParentFile ->closeInputFile ();
323- mParentFile .reset ();
375+ releaseParentFile ();
324376 }
325377 }
326378
327379 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 ()));
380+ throw InvalidAODReadError (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 (),
381+ rootFS->GetFile ()->GetName ()));
330382 }
331383
332384 LOGP (info, " Opening parent file {} for DF {}" , parentFileName->GetString ().Data (), folderName.c_str ());
333385 mParentFile = std::make_shared<DataInputDescriptor>(mAlienSupport , mLevel + 1 , mContext );
334386 mParentFile ->mdefaultFilenamesPtr .emplace_back (makeFileNameHolder (parentFileName->GetString ().Data ()));
335387 mParentFile ->fillInputfiles ();
336- mParentFile ->setFile (0 , wantedParentLevel, wantedOrigin);
388+ try {
389+ mParentFile ->setFile (0 , wantedParentLevel, wantedOrigin);
390+ if (mTimeFrameActive ) {
391+ mParentFile ->beginTimeFrame ();
392+ }
393+ } catch (...) {
394+ mParentFile .reset ();
395+ std::throw_with_nested (InvalidAODReadError (fmt::format (" Unable to open parent file \" {}\" for {}" , parentFileName->GetString ().Data (), folderName)));
396+ }
337397 return mParentFile ;
338398}
339399
@@ -373,15 +433,26 @@ void DataInputDescriptor::printFileStatistics()
373433 }
374434 auto rootFS = std::dynamic_pointer_cast<TFileFileSystem>(mCurrentFilesystem );
375435 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 (),
436+ std::string monitoringInfo (fmt::format (" lfn={},size={},total_df={},read_bytes={},read_calls={},io_time={:.1f},wait_time={:.1f},level={}" , f->GetName (),
437+ f->GetSize (), getTimeFramesInFile (mCurrentFileID ), f->GetBytesRead (), f->GetReadCalls (),
378438 ((float )mIOTime / 1e9 ), ((float )wait_time / 1e9 ), mLevel ));
379439#if __has_include(<TJAlienFile.h>)
380440 auto alienFile = dynamic_cast <TJAlienFile*>(f);
381441 if (alienFile) {
382442 monitoringInfo += fmt::format (" ,se={},open_time={:.1f}" , alienFile->GetSE (), alienFile->GetElapsed ());
383443 }
384444#endif
445+ if (mTimeFrameActive ) {
446+ // Snapshot I/O statistics now, but publish DF counts after commit or rollback.
447+ mPendingFileStatistics .emplace_back (mCurrentFileID , std::move (monitoringInfo));
448+ } else {
449+ reportFileStatistics (mCurrentFileID , std::move (monitoringInfo));
450+ }
451+ }
452+
453+ void DataInputDescriptor::reportFileStatistics (int counter, std::string monitoringInfo)
454+ {
455+ monitoringInfo += fmt::format (" ,read_df={},skipped_df={}" , getReadTimeFramesInFile (counter), mfilenames.at (counter).invalidReadSkipped );
385456 if (mContext .monitoring ) {
386457 mContext .monitoring ->send (o2::monitoring::Metric{monitoringInfo, " aod-file-read-info" }.addTag (o2::monitoring::tags::Key::Subsystem, o2::monitoring::tags::Value::DPL ));
387458 }
@@ -391,10 +462,7 @@ void DataInputDescriptor::printFileStatistics()
391462void DataInputDescriptor::closeInputFile ()
392463{
393464 if (mCurrentFilesystem .get ()) {
394- if (mParentFile ) {
395- mParentFile ->closeInputFile ();
396- mParentFile .reset ();
397- }
465+ releaseParentFile ();
398466
399467 delete mParentFileMap ;
400468 mParentFileMap = nullptr ;
@@ -492,7 +560,7 @@ struct CalculateDelta {
492560};
493561
494562bool DataInputDescriptor::readTree (DataAllocator& outputs, header::DataHeader dh, int counter, int numTF, std::string treename, size_t & totalSizeCompressed, size_t & totalSizeUncompressed)
495- {
563+ try {
496564 CalculateDelta t (mIOTime );
497565 std::string wantedOrigin = dh.dataOrigin .as <std::string>();
498566 int wantedLevel = mContext .levelForOrigin (wantedOrigin);
@@ -501,13 +569,17 @@ bool DataInputDescriptor::readTree(DataAllocator& outputs, header::DataHeader dh
501569 // attempting to read from this level.
502570 if (wantedLevel != -1 && mLevel < wantedLevel) {
503571 auto [parentFile, parentNumTF] = navigateToLevel (counter, numTF, wantedLevel, wantedOrigin);
572+ if (counter >= getNumberInputfiles () || numTF < 0 || numTF >= mfilenames[counter].numberOfTimeFrames ) {
573+ t.deactivate ();
574+ return false ;
575+ }
504576 if (parentFile == nullptr ) {
505577 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 ()));
578+ throw InvalidAODReadError (fmt::format (R"( No parent file found for "{}" while looking for level {} in "{}")" , treename, wantedLevel, rootFS->GetFile ()->GetName ()));
507579 }
508580 if (parentNumTF == -1 ) {
509581 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 ()));
582+ throw InvalidAODReadError (fmt::format (R"( DF not found in parent file "{}")" , parentRootFS->GetFile ()->GetName ()));
511583 }
512584 t.deactivate ();
513585 return parentFile->readTree (outputs, dh, 0 , parentNumTF, treename, totalSizeCompressed, totalSizeUncompressed);
@@ -546,12 +618,7 @@ bool DataInputDescriptor::readTree(DataAllocator& outputs, header::DataHeader dh
546618 if (!format) {
547619 t.deactivate ();
548620 LOGP (debug, " Could not find tree {}. Trying in parent file." , fullpath.path ());
549- std::shared_ptr<DataInputDescriptor> parentFile;
550- try {
551- parentFile = getParentFile (counter, numTF, treename, wantedLevel, wantedOrigin);
552- } catch (...) {
553- std::throw_with_nested (InvalidAODReadError (fmt::format (" Unable to resolve parent file for tree {}" , treename)));
554- }
621+ auto parentFile = getParentFile (counter, numTF, treename, wantedLevel, wantedOrigin);
555622 if (parentFile == nullptr ) {
556623 auto rootFS = std::dynamic_pointer_cast<TFileFileSystem>(mCurrentFilesystem );
557624 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 ()));
@@ -599,6 +666,12 @@ bool DataInputDescriptor::readTree(DataAllocator& outputs, header::DataHeader dh
599666 }
600667
601668 return true ;
669+ } catch (InvalidAODReadError const &) {
670+ auto rootFS = std::dynamic_pointer_cast<TFileFileSystem>(mCurrentFilesystem );
671+ auto filename = rootFS ? std::string (rootFS->GetFile ()->GetName ()) : mfilenames.at (counter).fileName ;
672+ auto const & dfs = mfilenames.at (counter).listOfTimeFrameNumbers ;
673+ auto df = numTF >= 0 && static_cast <size_t >(numTF) < dfs.size () ? fmt::format (" DF_{}" , dfs[numTF]) : fmt::format (" timeframe index {}" , numTF);
674+ std::throw_with_nested (InvalidAODReadError (fmt::format (" Reading tree {} in {} from file \" {}\" at parent level {}" , treename, df, filename, mLevel )));
602675}
603676
604677DataInputDirector::DataInputDirector (std::vector<std::string> inputFiles, DataInputDirectorContext&& context)
@@ -888,13 +961,20 @@ arrow::dataset::FileSource DataInputDirector::getFileFolder(header::DataHeader d
888961 return didesc->getFileFolder (counter, numTF, wantedLevel, origin);
889962}
890963
891- void DataInputDirector::markTimeFrameSkipped (header::DataHeader dh, int numTF )
964+ void DataInputDirector::beginTimeFrame ( )
892965{
893- auto didesc = getDataInputDescriptor (dh);
894- if (!didesc) {
895- didesc = mdefaultDataInputDescriptor.get ();
966+ mdefaultDataInputDescriptor->beginTimeFrame ();
967+ for (auto & descriptor : mdataInputDescriptors) {
968+ descriptor.beginTimeFrame ();
969+ }
970+ }
971+
972+ void DataInputDirector::finishTimeFrame (bool skipped)
973+ {
974+ mdefaultDataInputDescriptor->finishTimeFrame (skipped);
975+ for (auto & descriptor : mdataInputDescriptors) {
976+ descriptor.finishTimeFrame (skipped);
896977 }
897- didesc->markTimeFrameSkipped (numTF);
898978}
899979
900980int DataInputDirector::getTimeFramesInFile (header::DataHeader dh, int counter)
@@ -944,6 +1024,7 @@ bool DataInputDirector::readTree(DataAllocator& outputs, header::DataHeader dh,
9441024
9451025void DataInputDirector::closeInputFiles ()
9461026{
1027+ finishTimeFrame ();
9471028 mdefaultDataInputDescriptor->closeInputFile ();
9481029 for (auto & didesc : mdataInputDescriptors) {
9491030 didesc.closeInputFile ();
0 commit comments