diff --git a/Trigger/TrigAnalysis/TrigDecisionTool/Root/CacheGlobalMemory.cxx b/Trigger/TrigAnalysis/TrigDecisionTool/Root/CacheGlobalMemory.cxx
index 8dbd32782f2ca1f8d73dfb80e9556bc3376cf52f..6ab34b2b6a4bf3195c97c4d6a462fb118d4c2bcb 100644
--- a/Trigger/TrigAnalysis/TrigDecisionTool/Root/CacheGlobalMemory.cxx
+++ b/Trigger/TrigAnalysis/TrigDecisionTool/Root/CacheGlobalMemory.cxx
@@ -61,7 +61,8 @@ Trig::CacheGlobalMemory::CacheGlobalMemory() :
   m_oldDecisionKeyPtr(nullptr),
   m_oldEventInfoKeyPtr(nullptr),
 #endif
-  m_navigationKeyPtr(nullptr),
+  m_run2NavigationKeyPtr(nullptr),
+  m_run3NavigationKeyPtr(nullptr),
   m_bgCode(0)
 {}
 
@@ -378,7 +379,7 @@ bool Trig::CacheGlobalMemory::assert_decision() {
     // over DecisionUnpackerAthena
     if ( contains_xAOD_decision ){
       ATH_MSG_INFO("SG contains xAOD decision, use DecisionUnpackerStandalone");
-      setUnpacker(new DecisionUnpackerStandalone(m_decisionKeyPtr, m_navigationKeyPtr));
+      setUnpacker(new DecisionUnpackerStandalone(m_decisionKeyPtr, m_run2NavigationKeyPtr));
     }
     else if( is_l1result_configured ){
       ATH_MSG_INFO("SG contains AOD decision, use DecisionUnpackerAthena");
@@ -391,7 +392,7 @@ bool Trig::CacheGlobalMemory::assert_decision() {
 #else
     if ( contains_xAOD_decision ){
       ATH_MSG_INFO("SG contains xAOD decision, use DecisionUnpackerStandalone");
-      setUnpacker(new DecisionUnpackerStandalone(m_decisionKeyPtr, m_navigationKeyPtr));
+      setUnpacker(new DecisionUnpackerStandalone(m_decisionKeyPtr, m_run2NavigationKeyPtr));
     }
 #endif
     
diff --git a/Trigger/TrigAnalysis/TrigDecisionTool/Root/TrigDecisionTool.cxx b/Trigger/TrigAnalysis/TrigDecisionTool/Root/TrigDecisionTool.cxx
index 932d4278c117e36d605e42ada978c43a335e87d4..b3c598ceebec75a60e2facd7d8881a75bb094b3b 100644
--- a/Trigger/TrigAnalysis/TrigDecisionTool/Root/TrigDecisionTool.cxx
+++ b/Trigger/TrigAnalysis/TrigDecisionTool/Root/TrigDecisionTool.cxx
@@ -143,6 +143,7 @@ Trig::TrigDecisionTool::initialize() {
 #endif
 
    cgm()->navigation(&*m_navigation);
+
    cgm()->setStore(&*evtStore()); // Use of this is deprecated, and should be phased out.
    
 #ifndef XAOD_STANDALONE
@@ -188,7 +189,8 @@ StatusCode Trig::TrigDecisionTool::beginEvent() {
 
   CacheGlobalMemory* cgmPtr = cgm();
   cgmPtr->setDecisionKeyPtr( &m_decisionKey );
-  cgmPtr->setNavigationKeyPtr( &m_navigationKey );
+  cgmPtr->setRun2NavigationKeyPtr( &m_navigationKey );
+  cgmPtr->setRun3NavigationKeyPtr( &m_HLTSummaryKeyIn );
 
   size_t slot = 0;
 #if !defined(XAOD_STANDALONE) && !defined(XAOD_ANALYSIS) // Full athena
diff --git a/Trigger/TrigAnalysis/TrigDecisionTool/TrigDecisionTool/CacheGlobalMemory.h b/Trigger/TrigAnalysis/TrigDecisionTool/TrigDecisionTool/CacheGlobalMemory.h
index 6722804588566321a4c4cc300f4db7075d0754d2..9220505554ff14bdba7e84618ce0d4ce2f128cf4 100644
--- a/Trigger/TrigAnalysis/TrigDecisionTool/TrigDecisionTool/CacheGlobalMemory.h
+++ b/Trigger/TrigAnalysis/TrigDecisionTool/TrigDecisionTool/CacheGlobalMemory.h
@@ -144,7 +144,9 @@ namespace Trig {
     EventPtr_t store() const { return m_store; }
 
     void setDecisionKeyPtr(SG::ReadHandleKey<xAOD::TrigDecision>* k) { m_decisionKeyPtr = k; }
-    void setNavigationKeyPtr(SG::ReadHandleKey<xAOD::TrigNavigation>* k) { m_navigationKeyPtr = k; }
+    void setRun2NavigationKeyPtr(SG::ReadHandleKey<xAOD::TrigNavigation>* k) { m_run2NavigationKeyPtr = k; }
+    void setRun3NavigationKeyPtr(SG::ReadHandleKey<TrigCompositeUtils::DecisionContainer>* k) { m_run3NavigationKeyPtr = k; }
+    SG::ReadHandleKey<TrigCompositeUtils::DecisionContainer>& getRun3NavigationKeyPtr() { return *m_run3NavigationKeyPtr; }
 
 #if !defined(XAOD_STANDALONE) && !defined(XAOD_ANALYSIS) // Full Athena
     void setOldDecisionKeyPtr(SG::ReadHandleKey<TrigDec::TrigDecision>* k) { m_oldDecisionKeyPtr = k; }
@@ -223,7 +225,8 @@ namespace Trig {
     SG::ReadHandleKey<EventInfo>* m_oldEventInfoKeyPtr; //!< Parent TDT's read handle key
 #endif
 
-    SG::ReadHandleKey<xAOD::TrigNavigation>* m_navigationKeyPtr; //!< Parent TDT's read handle key
+    SG::ReadHandleKey<xAOD::TrigNavigation>* m_run2NavigationKeyPtr; //!< Parent TDT's read handle key
+    SG::ReadHandleKey<TrigCompositeUtils::DecisionContainer>* m_run3NavigationKeyPtr; //!< Parent TDT's read handle key
 
     typedef std::unordered_map<std::string, const TrigConf::HLTChain*> ChainHashMap_t;
     ChainHashMap_t     m_mConfChains;            //!< map of conf chains
diff --git a/Trigger/TrigAnalysis/TrigDecisionTool/TrigDecisionTool/ChainGroup.h b/Trigger/TrigAnalysis/TrigDecisionTool/TrigDecisionTool/ChainGroup.h
index 127f492b3f5f85cd35418596cddbc60487412155..37ea788fad2bf46636dedbe5ddbd153090fa6e25 100644
--- a/Trigger/TrigAnalysis/TrigDecisionTool/TrigDecisionTool/ChainGroup.h
+++ b/Trigger/TrigAnalysis/TrigDecisionTool/TrigDecisionTool/ChainGroup.h
@@ -119,6 +119,7 @@ namespace Trig {
        * @brief returns typed features related to given chain group of HLT chains or L1 items
        * Note: This is a RUN 3 (and on) function.
        * @param[in] eventStore Event store pointer. To migrate to readHandles with the rest of the TDT soon
+       * @param[in] HLTSummaryKeyIn SG Key to the navigation summary container
        * @param[in] condition Condition requirement. Only TrigDefs::Physics and TrigDefs::includeFailedDecisions are supported.
        * @param[in] containerSGKey Optional requirement to return only features within the specified container name. Not checked if not specified. 
        * @param[in] featureCollectionMode For lastFeatureOfType, stop exploring each route through the navigation once one matching feature has been found.
@@ -127,6 +128,7 @@ namespace Trig {
        **/  
       template<class CONTAINER>
       std::vector< TrigCompositeUtils::LinkInfo<CONTAINER> > features(EventPtr_t eventStore,
+                SG::ReadHandleKey<TrigCompositeUtils::DecisionContainer>& HLTSummaryKeyIn,
                 unsigned int condition = TrigDefs::Physics,
                 const std::string& containerSGKey = "",
                 const unsigned int featureCollectionMode = TrigDefs::lastFeatureOfType,
diff --git a/Trigger/TrigAnalysis/TrigDecisionTool/TrigDecisionTool/ChainGroup.icc b/Trigger/TrigAnalysis/TrigDecisionTool/TrigDecisionTool/ChainGroup.icc
index 880d797b4781b528692210eeaf661355902da34b..4985288a161216ea2ed14aadf77e8ce8db7b51b9 100644
--- a/Trigger/TrigAnalysis/TrigDecisionTool/TrigDecisionTool/ChainGroup.icc
+++ b/Trigger/TrigAnalysis/TrigDecisionTool/TrigDecisionTool/ChainGroup.icc
@@ -1,5 +1,13 @@
+/*
+  Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include "AsgDataHandles/ReadHandle.h"
+#include "AsgDataHandles/ReadHandleKey.h"
+
 template<class CONTAINER>
 std::vector< TrigCompositeUtils::LinkInfo<CONTAINER> > Trig::ChainGroup::features(EventPtr_t eventStore,
+        SG::ReadHandleKey<TrigCompositeUtils::DecisionContainer>& HLTSummaryKeyIn,
         unsigned int condition, const std::string& containerSGKey,
         const unsigned int featureCollectionMode, const std::string& navElementLinkKey) const {
 
@@ -19,8 +27,8 @@ std::vector< TrigCompositeUtils::LinkInfo<CONTAINER> > Trig::ChainGroup::feature
   }
 
   // TODO when we decide what happens to CacheGlobalMemory - this needs to be updated to use a ReadHandle
-  const TrigCompositeUtils::DecisionContainer* navigationSummaryContainer = nullptr;
-  if (eventStore->retrieve(navigationSummaryContainer, "HLTNav_Summary").isFailure() || navigationSummaryContainer == nullptr) {
+  SG::ReadHandle<TrigCompositeUtils::DecisionContainer> navigationSummaryRH = SG::ReadHandle(HLTSummaryKeyIn);
+  if (!navigationSummaryRH.isValid()) {
     ATH_MSG_ERROR("Unable to read Run 3 trigger navigation. Cannot retrieve features.");
     errState = true;
   }
@@ -28,7 +36,7 @@ std::vector< TrigCompositeUtils::LinkInfo<CONTAINER> > Trig::ChainGroup::feature
   // We always want to search from the passed raw terminus node to find features for passed chains.
   const TrigCompositeUtils::Decision* terminusNode = nullptr;
   if (!errState) {
-    for (const TrigCompositeUtils::Decision* decision : *navigationSummaryContainer) {
+    for (const TrigCompositeUtils::Decision* decision : *navigationSummaryRH) {
       if (decision->name() == "HLTPassRaw") {
         terminusNode = decision;
         break;
diff --git a/Trigger/TrigAnalysis/TrigDecisionTool/TrigDecisionTool/DecisionAccess.icc b/Trigger/TrigAnalysis/TrigDecisionTool/TrigDecisionTool/DecisionAccess.icc
index 56c02e8abc6f10032f6b83640aa6533f178e3292..57bfddb4848192c2a7de452ac0c016f9b4394a08 100644
--- a/Trigger/TrigAnalysis/TrigDecisionTool/TrigDecisionTool/DecisionAccess.icc
+++ b/Trigger/TrigAnalysis/TrigDecisionTool/TrigDecisionTool/DecisionAccess.icc
@@ -49,8 +49,7 @@ std::vector< TrigCompositeUtils::LinkInfo<CONTAINER> > Trig::DecisionAccess::fea
                                                                   const std::string& containerSGKey,
                                                                   const unsigned int featureCollectionMode,
                                                                   const std::string& navElementLinkKey) const {
-  // const TrigCompositeUtils::DecisionContainer* terminusNode = SG::get(m_HLTSummaryKeyIn/*, context*/);
-  return group->features<CONTAINER>(cgm()->store(), condition, containerSGKey, featureCollectionMode, navElementLinkKey);
+  return group->features<CONTAINER>(cgm()->store(), cgm()->getRun3NavigationKeyPtr(), condition, containerSGKey, featureCollectionMode, navElementLinkKey);
 }
 
 template<class CONTAINER>
diff --git a/Trigger/TrigSteer/TrigCompositeUtils/Root/TrigCompositeUtilsRoot.cxx b/Trigger/TrigSteer/TrigCompositeUtils/Root/TrigCompositeUtilsRoot.cxx
index 8b3529bf22f0765a894073adf1acc65d0c008814..7a579b52e2d866d4f66a6458c964e55168f7208b 100644
--- a/Trigger/TrigSteer/TrigCompositeUtils/Root/TrigCompositeUtilsRoot.cxx
+++ b/Trigger/TrigSteer/TrigCompositeUtils/Root/TrigCompositeUtilsRoot.cxx
@@ -18,6 +18,7 @@ CLASS_DEF( xAOD::IParticleContainer, 1241842700, 1 )
 #include <unordered_map>
 #include <regex>
 #include <iomanip> // std::setfill
+#include <mutex>
 
 static const SG::AuxElement::Accessor< std::vector<TrigCompositeUtils::DecisionID> > readWriteAccessor("decisions");
 static const SG::AuxElement::ConstAccessor< std::vector<TrigCompositeUtils::DecisionID> > readOnlyAccessor("decisions");
@@ -220,28 +221,37 @@ namespace TrigCompositeUtils {
     // The list of containers we need to read can change on a file-by-file basis (it depends on the SMK)
     // Hence we query SG for all collections rather than maintain a large and ever changing ReadHandleKeyArray
 
-    std::vector<std::string> keys;
+    static std::vector<std::string> keys ATLAS_THREAD_SAFE;
+    static std::mutex keysMutex;
     // TODO TODO TODO NEED TO REPLACE THIS WITH A STANDALONE-FRIENDLY VERSION
 #ifndef XAOD_STANDALONE
-    eventStore->keys(static_cast<CLID>( ClassID_traits< DecisionContainer >::ID() ), keys);
+    {
+      std::lock_guard<std::mutex> lock(keysMutex);
+      if (keys.size() == 0) {
+        // In theory this can change from file to file, 
+        // the use case for this function is monitoring, and this is typically over a single run.
+        eventStore->keys(static_cast<CLID>( ClassID_traits< DecisionContainer >::ID() ), keys);
+      }
+    }
 #else
+    eventStore->event(); // Avoid unused warning
     throw std::runtime_error("Cannot yet obtain rejected HLT features in AnalysisBase");
 #endif
 
     // Loop over each DecisionContainer,
     for (const std::string& key : keys) {
       // Get and check this container
-      if ( key.find("HLTNav") != 0 ) {
+      if ( key.find("HLTNav_") != 0 ) {
         continue; // Only concerned about the decision containers which make up the navigation, they have name prefix of HLTNav
       }
       if ( key == "HLTNav_Summary" ) {
         continue; //  This is where accepted paths start. We are looking for rejected ones
       }
-      const DecisionContainer* container = nullptr;
-      if ( eventStore->retrieve( container, key ).isFailure() ) {
+      SG::ReadHandle<DecisionContainer> containerRH(key);
+      if (!containerRH.isValid()) {
         throw std::runtime_error("Unable to retrieve " + key + " from event store.");
       }
-      for (const Decision* d : *container) {
+      for (const Decision* d : *containerRH) {
         if (!d->hasObjectLink(featureString())) {
           // TODO add logic for ComboHypo where this is expected
           continue; // Only want Decision objects created by HypoAlgs