From abe6dbc4fd5277254a90f2a4ab9abc96d0181a5d Mon Sep 17 00:00:00 2001
From: Eric Cano <Eric.Cano@cern.ch>
Date: Wed, 26 Jul 2017 15:33:07 +0200
Subject: [PATCH] Added automatic trimming of empty queues at schedule time.

---
 scheduler/OStoreDB/OStoreDB.cpp        | 51 ++++++++++++++++++++++++++
 scheduler/OStoreDB/OStoreDB.hpp        |  1 +
 scheduler/OStoreDB/OStoreDBFactory.hpp |  6 ++-
 scheduler/Scheduler.cpp                |  1 +
 scheduler/SchedulerDatabase.hpp        |  7 ++++
 5 files changed, 65 insertions(+), 1 deletion(-)

diff --git a/scheduler/OStoreDB/OStoreDB.cpp b/scheduler/OStoreDB/OStoreDB.cpp
index ee79056d31..8ab430f75a 100644
--- a/scheduler/OStoreDB/OStoreDB.cpp
+++ b/scheduler/OStoreDB/OStoreDB.cpp
@@ -106,6 +106,8 @@ void OStoreDB::fetchMountInfo(SchedulerDatabase::TapeMountDecisionInfo& tmdi, Ro
       m.maxDrivesAllowed = aqueue.getJobsSummary().maxDrivesAllowed;
       m.minArchiveRequestAge = aqueue.getJobsSummary().minArchiveRequestAge;
       m.logicalLibrary = "";
+    } else {
+      tmdi.queueTrimRequired = true;
     }
   }
   // Walk the retrieve queues for statistics
@@ -129,6 +131,8 @@ void OStoreDB::fetchMountInfo(SchedulerDatabase::TapeMountDecisionInfo& tmdi, Ro
       m.maxDrivesAllowed = rqueue.getJobsSummary().maxDrivesAllowed;
       m.minArchiveRequestAge = rqueue.getJobsSummary().minArchiveRequestAge;
       m.logicalLibrary = ""; // The logical library is not known here, and will be determined by the caller.
+    } else {
+      tmdi.queueTrimRequired = true;
     }
   }
   // Collect information about the existing and next mounts
@@ -202,6 +206,53 @@ std::unique_ptr<SchedulerDatabase::TapeMountDecisionInfo>
   return ret;
 }
 
+//------------------------------------------------------------------------------
+// OStoreDB::trimEmptyQueues()
+//------------------------------------------------------------------------------
+void OStoreDB::trimEmptyQueues(log::LogContext& lc) {
+  // We will trim empty queues from the root entry.
+  lc.log(log::INFO, "In OStoreDB::trimEmptyQueues(): will start trimming empty queues");
+  // Get an exclusive lock on the root entry, we have good chances to need it.
+  RootEntry re(m_objectStore);
+  ScopedExclusiveLock rel(re);
+  try {
+    auto archiveQueueList = re.dumpArchiveQueues();
+    for (auto & a: archiveQueueList) {
+      ArchiveQueue aq(a.address, m_objectStore);
+      ScopedSharedLock aql(aq);
+      aq.fetch();
+      if (!aq.dumpJobs().size()) {
+        aql.release();
+        re.removeArchiveQueueAndCommit(a.tapePool);
+        log::ScopedParamContainer params(lc);
+        params.add("tapePool", a.tapePool)
+              .add("queueObject", a.address);
+        lc.log(log::INFO, "In OStoreDB::trimEmptyQueues(): deleted empty archive queue.");
+      }
+    }
+    auto retrieveQeueueList = re.dumpRetrieveQueues();
+    for (auto & r:retrieveQeueueList) {
+      RetrieveQueue rq(r.address, m_objectStore);
+      ScopedSharedLock rql(rq);
+      rq.fetch();
+      if (!rq.dumpJobs().size()) {
+        rql.release();
+        re.removeRetrieveQueueAndCommit(r.vid);
+        log::ScopedParamContainer params(lc);
+        params.add("vid", r.vid)
+              .add("queueObject", r.address);
+        lc.log(log::INFO, "In OStoreDB::trimEmptyQueues(): deleted empty retrieve queue.");
+      }
+    }
+  } catch (cta::exception::Exception & ex) {
+    log::ScopedParamContainer params(lc);
+    params.add("exceptionMessage", ex.getMessageValue());
+    lc.log(log::ERR, "In OStoreDB::trimEmptyQueues(): got an exception. Stack trace follows.");
+    lc.logBacktrace(log::ERR, ex.backtrace());
+  }
+}
+
+
 //------------------------------------------------------------------------------
 // OStoreDB::getMountInfoNoLock()
 //------------------------------------------------------------------------------
diff --git a/scheduler/OStoreDB/OStoreDB.hpp b/scheduler/OStoreDB/OStoreDB.hpp
index 32df3d41c7..4df089c2ea 100644
--- a/scheduler/OStoreDB/OStoreDB.hpp
+++ b/scheduler/OStoreDB/OStoreDB.hpp
@@ -113,6 +113,7 @@ private:
 public:
   std::unique_ptr<SchedulerDatabase::TapeMountDecisionInfo> getMountInfo() override;
   std::unique_ptr<SchedulerDatabase::TapeMountDecisionInfo> getMountInfoNoLock() override;
+  void trimEmptyQueues(log::LogContext& lc) override;
 
   /* === Archive Mount handling ============================================= */
   class ArchiveMount: public SchedulerDatabase::ArchiveMount {
diff --git a/scheduler/OStoreDB/OStoreDBFactory.hpp b/scheduler/OStoreDB/OStoreDBFactory.hpp
index 24a846c338..0727665033 100644
--- a/scheduler/OStoreDB/OStoreDBFactory.hpp
+++ b/scheduler/OStoreDB/OStoreDBFactory.hpp
@@ -129,7 +129,11 @@ public:
   std::unique_ptr<TapeMountDecisionInfo> getMountInfo() override {
     return m_OStoreDB.getMountInfo();
   }
-
+  
+  void trimEmptyQueues(log::LogContext& lc) override {
+    m_OStoreDB.trimEmptyQueues(lc);
+  }
+  
   std::unique_ptr<TapeMountDecisionInfo> getMountInfoNoLock() override {
     return m_OStoreDB.getMountInfoNoLock();
   }
diff --git a/scheduler/Scheduler.cpp b/scheduler/Scheduler.cpp
index 337b605ff3..061deb1dbb 100644
--- a/scheduler/Scheduler.cpp
+++ b/scheduler/Scheduler.cpp
@@ -405,6 +405,7 @@ std::unique_ptr<TapeMount> Scheduler::getNextMount(const std::string &logicalLib
   // First, get the mount-related info from the DB
   std::unique_ptr<SchedulerDatabase::TapeMountDecisionInfo> mountInfo;
   mountInfo = m_db.getMountInfo();
+  if (mountInfo->queueTrimRequired) m_db.trimEmptyQueues(lc);
   __attribute__((unused)) SchedulerDatabase::TapeMountDecisionInfo & debugMountInfo = *mountInfo;
   
   // The library information is not know for the tapes involved in retrieves. We 
diff --git a/scheduler/SchedulerDatabase.hpp b/scheduler/SchedulerDatabase.hpp
index 012f19cd0f..6a46678a19 100644
--- a/scheduler/SchedulerDatabase.hpp
+++ b/scheduler/SchedulerDatabase.hpp
@@ -430,6 +430,7 @@ public:
     std::vector<PotentialMount> potentialMounts; /**< All the potential mounts */
     std::vector<ExistingMount> existingOrNextMounts; /**< Existing mounts */
     std::map<std::string, DedicationEntry> dedicationInfo; /**< Drives dedication info */
+    bool queueTrimRequired = false; /**< Indicates an empty queue was encountered */
     /**
      * Create a new archive mount. This implicitly releases the global scheduling
      * lock.
@@ -457,6 +458,12 @@ public:
    */
   virtual std::unique_ptr<TapeMountDecisionInfo> getMountInfo() = 0;
   
+  /**
+   * A function running a queue trim. This should be called if the corresponding
+   * bit was set in the TapeMountDecisionInfo returned by getMountInfo().
+   */
+  virtual void trimEmptyQueues(log::LogContext & lc) = 0;
+  
   /**
    * A function dumping the relevant mount information for reporting the system
    * status. It is identical to getMountInfo, yet does not take the global lock.
-- 
GitLab