diff --git a/Event/ByteStreamCnvSvcBase/ByteStreamCnvSvcBase/ByteStreamCnvSvcBase.h b/Event/ByteStreamCnvSvcBase/ByteStreamCnvSvcBase/ByteStreamCnvSvcBase.h
index 53e2a00c37d627d5b0776f1941b9978f7bacec39..ea564f11670f66b0d3bfbd2b4b7bd6979989ea8d 100755
--- a/Event/ByteStreamCnvSvcBase/ByteStreamCnvSvcBase/ByteStreamCnvSvcBase.h
+++ b/Event/ByteStreamCnvSvcBase/ByteStreamCnvSvcBase/ByteStreamCnvSvcBase.h
@@ -11,13 +11,9 @@
 #include "ByteStreamData/RawEvent.h"
 
 /**
-  @class ByteStreamCnvSvcBase
-  @brief base class for ByteStream conversion service.
-
-  description
-         This class is used as a conversion service in online HLT
-	 and it is the base class for offline bytestream conversion service.
-*/
+ * @class ByteStreamCnvSvcBase
+ * @brief The base class for offline and HLT ByteStream conversion services
+ */
 class ByteStreamCnvSvcBase : public ::AthCnvSvc,
 		public virtual IIncidentListener,
 		public virtual IByteStreamEventAccess {
diff --git a/HLT/Event/TrigByteStreamCnvSvc/src/TrigByteStreamCnvSvc.cxx b/HLT/Event/TrigByteStreamCnvSvc/src/TrigByteStreamCnvSvc.cxx
index 56b749809ac314e6b3f8f5b91bb09ec3bd45c899..1d947bebe7bcbedafca88bf45ba6817e9ae0b2a4 100644
--- a/HLT/Event/TrigByteStreamCnvSvc/src/TrigByteStreamCnvSvc.cxx
+++ b/HLT/Event/TrigByteStreamCnvSvc/src/TrigByteStreamCnvSvc.cxx
@@ -84,7 +84,7 @@ namespace {
 // Standard constructor
 // =============================================================================
 TrigByteStreamCnvSvc::TrigByteStreamCnvSvc(const std::string& name, ISvcLocator* svcLoc)
-: ByteStreamCnvSvc(name, svcLoc) {}
+: ByteStreamCnvSvcBase(name, svcLoc) {}
 
 // =============================================================================
 // Standard destructor
@@ -96,7 +96,7 @@ TrigByteStreamCnvSvc::~TrigByteStreamCnvSvc() {}
 // =============================================================================
 StatusCode TrigByteStreamCnvSvc::initialize() {
   ATH_MSG_VERBOSE("start of " << __FUNCTION__);
-  ATH_CHECK(ByteStreamCnvSvc::initialize());
+  ATH_CHECK(ByteStreamCnvSvcBase::initialize());
   ATH_CHECK(m_evtStore.retrieve());
   ATH_CHECK(m_robDataProviderSvc.retrieve());
   if (!m_monTool.empty()) ATH_CHECK(m_monTool.retrieve());
@@ -114,7 +114,7 @@ StatusCode TrigByteStreamCnvSvc::finalize() {
   if (m_evtStore.release().isFailure())
     ATH_MSG_WARNING("Failed to release service " << m_evtStore.typeAndName());
   ATH_MSG_VERBOSE("end of " << __FUNCTION__);
-  ATH_CHECK(ByteStreamCnvSvc::finalize());
+  ATH_CHECK(ByteStreamCnvSvcBase::finalize());
   return StatusCode::SUCCESS;
 }
 
@@ -122,22 +122,19 @@ StatusCode TrigByteStreamCnvSvc::finalize() {
 // Implementation of IConversionSvc::connectOutput
 // The argument outputFile is not used
 // =============================================================================
-StatusCode TrigByteStreamCnvSvc::connectOutput(const std::string& /*outputFile*/) {
-  ATH_MSG_VERBOSE("start of " << __FUNCTION__);
+StatusCode TrigByteStreamCnvSvc::connectOutput(const std::string& outputFile) {
+  const EventContext* eventContext = currentContext();
+  if (eventContext == nullptr) return StatusCode::FAILURE;
+  return connectOutput(outputFile, *eventContext);
+}
 
-  // Get the EventContext via event store because the interface doesn't allow passing it explicitly as an argument
-  // and we don't want to use ThreadLocalContext. Don't use ReadHandle here because it calls ThreadLocalContext if
-  // not given a context (which we want to retrieve).
-  const EventContext* eventContext = nullptr;
-  if (m_evtStore->retrieve(eventContext).isFailure()) {
-    ATH_MSG_ERROR("Failed to retrieve EventContext from the event store");
-    return StatusCode::FAILURE;
-  }
+StatusCode TrigByteStreamCnvSvc::connectOutput(const std::string& /*outputFile*/, const EventContext& eventContext) {
+  ATH_MSG_VERBOSE("start of " << __FUNCTION__);
 
-  ATH_MSG_DEBUG("Creating new RawEventWrite for EventContext = " << *eventContext);
+  ATH_MSG_DEBUG("Creating new RawEventWrite for EventContext = " << eventContext);
   // Create a new RawEventWrite and copy the header from the input RawEvent
-  RawEventWrite* re = setRawEvent (std::make_unique<RawEventWrite>());
-  const uint32_t* inputRawEvent = m_robDataProviderSvc->getEvent(*eventContext)->start();
+  RawEventWrite* re = setRawEvent(std::make_unique<RawEventWrite>(), eventContext);
+  const uint32_t* inputRawEvent = m_robDataProviderSvc->getEvent(eventContext)->start();
   if (!inputRawEvent) {
     ATH_MSG_ERROR("Input RawEvent is nullptr, cannot create output");
     return StatusCode::FAILURE;
@@ -163,12 +160,18 @@ StatusCode TrigByteStreamCnvSvc::connectOutput(const std::string& outputFile, co
 // The arguments outputFile and do_commit are not used
 // NOTE: In online HLT, m_rawEventWrite is not a full event, it contains only the HLTResult ROBFragments
 // =============================================================================
-StatusCode TrigByteStreamCnvSvc::commitOutput(const std::string& /*outputFile*/, bool /*do_commit*/) {
+StatusCode TrigByteStreamCnvSvc::commitOutput(const std::string& outputFile, bool do_commit) {
+  const EventContext* eventContext = currentContext();
+  if (eventContext == nullptr) return StatusCode::FAILURE;
+  return commitOutput(outputFile, do_commit, *eventContext);
+}
+
+StatusCode TrigByteStreamCnvSvc::commitOutput(const std::string& /*outputFile*/, bool /*do_commit*/, const EventContext& eventContext) {
   ATH_MSG_VERBOSE("start of " << __FUNCTION__);
 
-  if (msgLvl(MSG::DEBUG)) printRawEvent();
+  if (msgLvl(MSG::DEBUG)) printRawEvent(eventContext);
 
-  RawEventWrite* re = getRawEvent();
+  RawEventWrite* re = getRawEvent(eventContext);
 
   // Serialise the output FullEventFragment
   std::unique_ptr<uint32_t[]> rawEventPtr;
@@ -184,10 +187,12 @@ StatusCode TrigByteStreamCnvSvc::commitOutput(const std::string& /*outputFile*/,
   }
   catch (const std::exception& e) {
     ATH_MSG_ERROR("FullEventFragment serialisation failed, caught an unexpected std::exception " << e.what());
+    clearRawEvent(eventContext);
     return StatusCode::FAILURE;
   }
   catch (...) {
     ATH_MSG_ERROR("FullEventFragment serialisation failed, caught an unexpected exception");
+    clearRawEvent(eventContext);
     return StatusCode::FAILURE;
   }
 
@@ -215,12 +220,48 @@ StatusCode TrigByteStreamCnvSvc::commitOutput(const std::string& /*outputFile*/,
     result = StatusCode::FAILURE;
   }
 
-  setRawEvent (std::unique_ptr<RawEventWrite>());
+  clearRawEvent(eventContext);
 
   ATH_MSG_VERBOSE("end of " << __FUNCTION__);
   return result;
 }
 
+// =============================================================================
+const EventContext* TrigByteStreamCnvSvc::currentContext() const {
+  // Get the EventContext via event store because the base class doesn't allow passing it explicitly as an argument
+  // and we don't want to use ThreadLocalContext. Don't use ReadHandle here because it calls ThreadLocalContext if
+  // not given a context (which we want to retrieve). This relies on IHiveWhiteBoard::selectStore being called on the
+  // current thread before we arrive here (it is done in HltEventLoopMgr).
+  const EventContext* eventContext = nullptr;
+  if (m_evtStore->retrieve(eventContext).isFailure()) {
+    ATH_MSG_ERROR("Failed to retrieve EventContext from the event store");
+  }
+  return eventContext;
+}
+
+// =============================================================================
+RawEventWrite* TrigByteStreamCnvSvc::getRawEvent() {
+  const EventContext* eventContext = currentContext();
+  if (eventContext == nullptr) return nullptr;
+  return getRawEvent(*eventContext);
+}
+
+// =============================================================================
+RawEventWrite* TrigByteStreamCnvSvc::getRawEvent(const EventContext& eventContext) const {
+  return m_rawEventWriteCache.get(eventContext)->get();
+}
+
+// =============================================================================
+RawEventWrite* TrigByteStreamCnvSvc::setRawEvent(std::unique_ptr<RawEventWrite>&& rawEventWrite, const EventContext& eventContext) {
+  *(m_rawEventWriteCache.get(eventContext)) = std::move(rawEventWrite);
+  return getRawEvent(eventContext);
+}
+
+// =============================================================================
+void TrigByteStreamCnvSvc::clearRawEvent(const EventContext& eventContext) {
+  m_rawEventWriteCache.get(eventContext)->reset();
+}
+
 // =============================================================================
 void TrigByteStreamCnvSvc::monitorRawEvent(const std::unique_ptr<uint32_t[]>& rawEventPtr) const {
   // Create a read fragment from the pointer
@@ -346,8 +387,8 @@ void TrigByteStreamCnvSvc::monitorRawEvent(const std::unique_ptr<uint32_t[]>& ra
 }
 
 // =============================================================================
-void TrigByteStreamCnvSvc::printRawEvent() {
-  RawEventWrite* re = getRawEvent();
+void TrigByteStreamCnvSvc::printRawEvent(const EventContext& eventContext) const {
+  RawEventWrite* re = getRawEvent(eventContext);
 
   if (!re) {
     ATH_MSG_WARNING("RawEventWrite pointer is null");
diff --git a/HLT/Event/TrigByteStreamCnvSvc/src/TrigByteStreamCnvSvc.h b/HLT/Event/TrigByteStreamCnvSvc/src/TrigByteStreamCnvSvc.h
index 55e6e0d026e72b8f855d191427a97fddff0c2717..3f3cb49387bf81b1664ea6bc843c60815983e52f 100644
--- a/HLT/Event/TrigByteStreamCnvSvc/src/TrigByteStreamCnvSvc.h
+++ b/HLT/Event/TrigByteStreamCnvSvc/src/TrigByteStreamCnvSvc.h
@@ -5,8 +5,9 @@
 #ifndef TRIGBYTESTREAMCNVSVC_H
 #define TRIGBYTESTREAMCNVSVC_H
 
-#include "ByteStreamCnvSvc/ByteStreamCnvSvc.h"
+#include "ByteStreamCnvSvcBase/ByteStreamCnvSvcBase.h"
 #include "AthenaMonitoringKernel/Monitored.h"
+#include "AthenaKernel/SlotSpecificObj.h"
 
 // Forward declarations
 class StoreGateSvc;
@@ -17,11 +18,8 @@ class IROBDataProviderSvc;
  *
  *  It overrides the connectOutput and commitOutput methods of the base class. In this implementation, they create
  *  the specific online HLT output and send it out directly to the TDAQ infrastructure without using an output service.
- *
- *  It needs to inherit from ByteStreamCnvSvc rather than ByteStreamCnvSvcBase, because some elements of the athena
- *  framework (particularly converters) rely on the properties or functionality of the offline ByteStreamCnvSvc.
  **/
-class TrigByteStreamCnvSvc : public ByteStreamCnvSvc {
+class TrigByteStreamCnvSvc : public ByteStreamCnvSvcBase {
 public:
   /// Standard constructor
   TrigByteStreamCnvSvc(const std::string& name, ISvcLocator* svcLoc);
@@ -34,24 +32,42 @@ public:
 
   // ------------------------- IConversionSvc methods --------------------------
   /// In the case of online BS data, this method creates the output FullEventFragment and fills its header
-  using ByteStreamCnvSvcBase::connectOutput;
   virtual StatusCode connectOutput(const std::string& outputFile) override;
+  /// In the case of online BS data, this method creates the output FullEventFragment and fills its header
+  StatusCode connectOutput(const std::string& outputFile, const EventContext& eventContext);
   /// This overload is kept only for interface compatibility
   virtual StatusCode connectOutput(const std::string& outputFile, const std::string& openMode) override;
   /// In the case of online BS data, this method binds and sends out the output FullEventFragment
   virtual StatusCode commitOutput(const std::string& outputFile, bool do_commit) override;
+  /// In the case of online BS data, this method binds and sends out the output FullEventFragment
+  StatusCode commitOutput(const std::string& outputFile, bool do_commit, const EventContext& eventContext);
+
+  // ------------------------- IByteStreamEventAccess methods ------------------
+  /// Return a pointer to the raw event for the current event context
+  virtual RawEventWrite* getRawEvent() override;
+  /// Return a pointer to the raw event for the given event context
+  RawEventWrite* getRawEvent(const EventContext& eventContext) const;
 
 private:
   // ------------------------- Helper methods ----------------------------------
-  /// Print contents of m_rawEventWrite
-  void printRawEvent();
+  /// Store new raw event in the cache
+  RawEventWrite* setRawEvent(std::unique_ptr<RawEventWrite>&& rawEventWrite, const EventContext& eventContext);
+  /// Delete raw event from the cache
+  void clearRawEvent(const EventContext& eventContext);
+  /// Print contents of the raw event
+  void printRawEvent(const EventContext& eventContext) const;
   /// Fill histograms from contents of a FullEventFragment
   void monitorRawEvent(const std::unique_ptr<uint32_t[]>& rawEventPtr) const;
+  /// Hack used in HLT to avoid using ThreadLocalContext, see explanation in the implementation
+  const EventContext* currentContext() const;
 
   // ------------------------- Service / Tool handles --------------------------
   ServiceHandle<StoreGateSvc> m_evtStore {this, "EventStore", "StoreGateSvc"};
   ServiceHandle<IROBDataProviderSvc> m_robDataProviderSvc {this, "ROBDataProvider", "ROBDataProviderSvc"};
   ToolHandle<GenericMonitoringTool> m_monTool {this, "MonTool", "" , "Monitoring tool"};
+
+  // ------------------------- Other private members ---------------------------
+  SG::SlotSpecificObj<std::unique_ptr<RawEventWrite>> m_rawEventWriteCache;
 };
 
 #endif // TRIGBYTESTREAMCNVSVC_H