From b5f316237ed08ab7d8c038fc0c918ed84ee6d4f3 Mon Sep 17 00:00:00 2001
From: Philippe Canal <pcanal@fnal.gov>
Date: Fri, 5 Feb 2021 14:09:16 -0600
Subject: [PATCH] TFileMerger allow to delay the object writes.

Limited to TTree and Histogram.
---
 io/io/inc/TFileMerger.h     |  7 ++++---
 io/io/src/TBufferMerger.cxx |  6 +++++-
 io/io/src/TFileMerger.cxx   | 25 +++++++++++++++++++++++--
 3 files changed, 32 insertions(+), 6 deletions(-)

diff --git a/io/io/inc/TFileMerger.h b/io/io/inc/TFileMerger.h
index 339881a42d1..a7b04ee9a94 100644
--- a/io/io/inc/TFileMerger.h
+++ b/io/io/inc/TFileMerger.h
@@ -66,13 +66,14 @@ public:
       kIncremental  = BIT(1),        ///< Merge the input file with the content of the output file (if already exising).
       kResetable    = BIT(2),        ///< Only the objects with a MergeAfterReset member function.
       kNonResetable = BIT(3),        ///< Only the objects without a MergeAfterReset member function.
+      kDelayWrite   = BIT(4),        ///< Delay the TFile write (to reduce the number of write when reusing the file)
 
       kAll            = BIT(2)|BIT(3),      ///< Merge all type of objects (default)
       kAllIncremental = kIncremental | kAll, ///< Merge incrementally all type of objects.
 
-      kOnlyListed     = BIT(4),        ///< Only the objects specified in fObjectNames list
-      kSkipListed     = BIT(5),        ///< Skip objects specified in fObjectNames list
-      kKeepCompression= BIT(6)         ///< Keep compression level unchanged for each input files
+      kOnlyListed     = BIT(5),        ///< Only the objects specified in fObjectNames list
+      kSkipListed     = BIT(6),        ///< Skip objects specified in fObjectNames list
+      kKeepCompression= BIT(7)         ///< Keep compression level unchanged for each input files
    };
 
    TFileMerger(Bool_t isLocal = kTRUE, Bool_t histoOneGo = kTRUE);
diff --git a/io/io/src/TBufferMerger.cxx b/io/io/src/TBufferMerger.cxx
index c09ceae192e..6eae4df2dd5 100644
--- a/io/io/src/TBufferMerger.cxx
+++ b/io/io/src/TBufferMerger.cxx
@@ -49,6 +49,10 @@ TBufferMerger::~TBufferMerger()
 
    if (!fQueue.empty())
       Merge();
+
+   TFile *out = fMerger.GetOutputFile();
+   if (out)
+      out->Write("",TObject::kOverwrite);
 }
 
 std::shared_ptr<TBufferMergerFile> TBufferMerger::GetFile()
@@ -121,7 +125,7 @@ void TBufferMerger::Merge()
          queue.pop();
       }
 
-      fMerger.PartialMerge();
+      fMerger.PartialMerge(TFileMerger::kAll | TFileMerger::kIncremental | TFileMerger::kDelayWrite);
       fMerger.Reset();
       fMergeMutex.unlock();
    }
diff --git a/io/io/src/TFileMerger.cxx b/io/io/src/TFileMerger.cxx
index ef201431a07..cf8e80d4f56 100644
--- a/io/io/src/TFileMerger.cxx
+++ b/io/io/src/TFileMerger.cxx
@@ -512,6 +512,13 @@ Bool_t TFileMerger::MergeRecursive(TDirectory *target, TList *sourcelist, Int_t
                     key->GetName(), key->GetTitle());
                continue;
             }
+            Bool_t canBeFound = (type & kIncremental) && (current_sourcedir->GetList()->FindObject(key->GetName()) != nullptr);
+            Bool_t needWriteAnyWay = kFALSE;
+            if (canBeFound) {
+               // For now the code require a key (that might be ignored see search through current_sourcedir->GetList())
+               // in the output file in order for it to be even considered.
+               needWriteAnyWay = target->GetListOfKeys()->FindObject(key->GetName()) == nullptr;
+            }
             // if (cl->IsTObject())
             //    obj->ResetBit(kMustCleanup);
             if (cl->IsTObject() && cl != obj->IsA()) {
@@ -790,7 +797,7 @@ Bool_t TFileMerger::MergeRecursive(TDirectory *target, TList *sourcelist, Int_t
                }
                ((TCollection*)obj)->SetOwner();
                delete obj;
-            } else {
+            } else if (!canBeFound) { // Don't write the partial result for TTree and TH1
                // Don't overwrite, if the object were not merged.
                // NOTE: this is probably wrong for emulated objects.
                if (cl->IsTObject()) {
@@ -804,6 +811,17 @@ Bool_t TFileMerger::MergeRecursive(TDirectory *target, TList *sourcelist, Int_t
                   }
                }
                cl->Destructor(obj); // just in case the class is not loaded.
+            } else if (needWriteAnyWay) {
+               if (cl->IsTObject()) {
+                  if ( obj->Write( oldkeyname, canBeMerged ? TObject::kOverwrite : 0) <= 0) {
+                     status = kFALSE;
+                  }
+                  obj->ResetBit(kMustCleanup);
+               } else {
+                  if ( target->WriteObjectAny( (void*)obj, cl, oldkeyname, canBeMerged ? "OverWrite" : "" ) <= 0) {
+                     status = kFALSE;
+                  }
+               }
             }
             info.Reset();
          } // while ( ( TKey *key = (TKey*)nextkey() ) )
@@ -921,8 +939,11 @@ Bool_t TFileMerger::PartialMerge(Int_t in_type)
    } else {
       // Close or write is required so the file is complete.
       if (in_type & kIncremental) {
-         fOutputFile->Write("",TObject::kOverwrite);
+         if (!(in_type & kDelayWrite))
+            fOutputFile->Write("",TObject::kOverwrite);
       } else {
+         if (type & kIncremental)
+            fOutputFile->Write("",TObject::kOverwrite);
          gROOT->GetListOfFiles()->Remove(fOutputFile);
          fOutputFile->Close();
       }
-- 
GitLab