changeset 6914:689d73b259f1 streaming

optimization: store attachments into the cache after their creation
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 02 Jun 2026 18:31:10 +0200
parents a66b5e2dfb6e
children ff30e25e081e
files OrthancFramework/Sources/Cache/SharedObjectCache.cpp OrthancFramework/Sources/Cache/SharedObjectCache.h OrthancFramework/Sources/DataSource/DataSourceReader.cpp OrthancFramework/Sources/DataSource/DataSourceReader.h OrthancFramework/Sources/DataSource/StorageAreaDataSource.cpp OrthancFramework/Sources/DataSource/StorageAreaDataSource.h OrthancServer/Sources/ServerContext.cpp
diffstat 7 files changed, 112 insertions(+), 21 deletions(-) [+]
line wrap: on
line diff
--- a/OrthancFramework/Sources/Cache/SharedObjectCache.cpp	Tue Jun 02 18:06:22 2026 +0200
+++ b/OrthancFramework/Sources/Cache/SharedObjectCache.cpp	Tue Jun 02 18:31:10 2026 +0200
@@ -191,12 +191,10 @@
   }
 
 
-  void SharedObjectCache::GetStatistics(size_t& capacity,
-                                        size_t& currentCount,
+  void SharedObjectCache::GetStatistics(size_t& currentCount,
                                         size_t& currentSize)
   {
     Mutex::ScopedLock lock(mutex_);
-    capacity = capacity_;
     currentCount = lru_.GetSize();
     currentSize = currentSize_;
   }
--- a/OrthancFramework/Sources/Cache/SharedObjectCache.h	Tue Jun 02 18:06:22 2026 +0200
+++ b/OrthancFramework/Sources/Cache/SharedObjectCache.h	Tue Jun 02 18:31:10 2026 +0200
@@ -43,7 +43,7 @@
     Mutex                                mutex_;
     LeastRecentlyUsedIndex<std::string>  lru_;
     Content                              content_;
-    size_t                               capacity_;
+    const size_t                         capacity_;
     size_t                               currentSize_;
 
     void MakeRoom(size_t newObjectSize);
@@ -70,8 +70,12 @@
 
     void Invalidate(const std::string& id);
 
-    void GetStatistics(size_t& capacity,
-                       size_t& currentCount,
+    size_t GetCapacity() const
+    {
+      return capacity_;  // This is a constant value, it can be read without the mutex
+    }
+
+    void GetStatistics(size_t& currentCount,
                        size_t& currentSize);
   };
 }
--- a/OrthancFramework/Sources/DataSource/DataSourceReader.cpp	Tue Jun 02 18:06:22 2026 +0200
+++ b/OrthancFramework/Sources/DataSource/DataSourceReader.cpp	Tue Jun 02 18:31:10 2026 +0200
@@ -75,8 +75,8 @@
   {
     if (metrics_)
     {
-      size_t capacity, count, size;
-      cache.GetStatistics(capacity, count, size);
+      size_t count, size;
+      cache.GetStatistics(count, size);
 
       metrics_->SetFloatValue(cacheSizeMegabytesName_, static_cast<float>(size) / static_cast<float>(MEGABYTE));
       metrics_->SetIntegerValue(cacheCountName_, count);
@@ -322,4 +322,38 @@
       budgetCopy->GetStatistics(tasksMaximumMemory, tasksCurrentMemory, tasksReservations);
     }
   }
+
+
+  size_t DataSourceReader::GetCacheCapacity() const
+  {
+    boost::shared_ptr<SharedObjectCache> lock(cache_);
+
+    if (lock)
+    {
+      return lock->GetCapacity();
+    }
+    else
+    {
+      return 0;
+    }
+  }
+
+
+  void DataSourceReader::StoreIntoCache(const std::string& key,
+                                        IDynamicObject* value /* takes ownership */)
+  {
+    boost::shared_ptr<IDynamicObject> protection(value);
+
+    if (value == NULL)
+    {
+      throw OrthancException(ErrorCode_NullPointer);
+    }
+
+    if (cache_)
+    {
+      size_t size = source_->GetValueSize(*value);
+      cache_->Store(key, protection, size);
+      metricsConfiguration_.SetCacheStatistics(*cache_);
+    }
+  }
 }
--- a/OrthancFramework/Sources/DataSource/DataSourceReader.h	Tue Jun 02 18:06:22 2026 +0200
+++ b/OrthancFramework/Sources/DataSource/DataSourceReader.h	Tue Jun 02 18:31:10 2026 +0200
@@ -77,6 +77,7 @@
 
     boost::shared_ptr<IExecutorService>                   executor_;
     std::unique_ptr<IDataSource>                          source_;
+    size_t                                                cacheSize_;
     boost::shared_ptr<SharedObjectCache>                  cache_;
     boost::shared_ptr<Internals::DataSourceMemoryBudget>  budget_;
     MetricsConfiguration                                  metricsConfiguration_;
@@ -120,5 +121,10 @@
     void GetStatistics(uint64_t& tasksMaximumMemory,
                        uint64_t& tasksCurrentMemory,
                        unsigned int& tasksReservations);
+
+    size_t GetCacheCapacity() const;
+
+    void StoreIntoCache(const std::string& key,
+                        IDynamicObject* value /* takes ownership */);
   };
 }
--- a/OrthancFramework/Sources/DataSource/StorageAreaDataSource.cpp	Tue Jun 02 18:06:22 2026 +0200
+++ b/OrthancFramework/Sources/DataSource/StorageAreaDataSource.cpp	Tue Jun 02 18:31:10 2026 +0200
@@ -26,6 +26,7 @@
 #include "StorageAreaDataSource.h"
 
 #include "../Cache/SharedObjectCache.h"
+#include "../Logging.h"
 #include "../MetricsRegistry.h"
 #include "../OrthancException.h"
 #include "../StringMemoryBuffer.h"
@@ -56,6 +57,13 @@
             boost::lexical_cast<std::string>(end));
   }
 
+
+  static std::string ComputeCacheKeyForWholeAttachment(const FileInfo& attachment)
+  {
+    return ComputeCacheKey(attachment.GetUuid(), attachment.GetContentType(), 0, attachment.GetCompressedSize());
+  }
+
+
   class StorageAreaDataSource::Value : public IDynamicObject
   {
   private:
@@ -527,7 +535,7 @@
 
     // Using "SetWholeKey()" allows to extract a range if the whole attachment is already in the reader cache
     assert(attachment.GetCompressionType() == CompressionType_None);
-    id->SetWholeKey(ComputeCacheKey(attachment.GetUuid(), attachment.GetContentType(), 0, attachment.GetCompressedSize()));
+    id->SetWholeKey(ComputeCacheKeyForWholeAttachment(attachment));
 
     return id.release();
   }
@@ -571,7 +579,7 @@
 
         // Using "SetWholeKey()" allows to extract a range if the whole compressed attachment is already in the reader cache
         assert(!uncompress || attachment.GetCompressionType() == CompressionType_None);
-        id->SetWholeKey(ComputeCacheKey(attachment.GetUuid(), attachment.GetContentType(), 0, attachment.GetCompressedSize()));
+        id->SetWholeKey(ComputeCacheKeyForWholeAttachment(attachment));
 
         return Execute(reader, id.release());
       }
@@ -591,4 +599,27 @@
       return new Range(reader.ReadSingle(request));
     }
   }
+
+
+  void StorageAreaDataSource::StoreIntoCache(DataSourceReader& reader,
+                                             const FileInfo& attachment,
+                                             const void* data,
+                                             size_t size)
+  {
+    if (size != attachment.GetCompressedSize())
+    {
+      throw OrthancException(ErrorCode_ParameterOutOfRange);
+    }
+
+    const size_t capacity = reader.GetCacheCapacity();
+
+    if (capacity != 0 &&
+        size <= capacity)
+    {
+      const std::string key = ComputeCacheKeyForWholeAttachment(attachment);
+      std::unique_ptr<Value> value(new Value(StringMemoryBuffer::CreateFromBuffer(data, size), false /* no MD5 */));
+
+      reader.StoreIntoCache(key, value.release());
+    }
+  }
 }
--- a/OrthancFramework/Sources/DataSource/StorageAreaDataSource.h	Tue Jun 02 18:06:22 2026 +0200
+++ b/OrthancFramework/Sources/DataSource/StorageAreaDataSource.h	Tue Jun 02 18:31:10 2026 +0200
@@ -120,5 +120,10 @@
                             const FileInfo& attachment,
                             const StorageRange& range,
                             bool uncompress);
+
+    static void StoreIntoCache(DataSourceReader& reader,
+                               const FileInfo& attachment,
+                               const void* data,
+                               size_t size);
   };
 }
--- a/OrthancServer/Sources/ServerContext.cpp	Tue Jun 02 18:06:22 2026 +0200
+++ b/OrthancServer/Sources/ServerContext.cpp	Tue Jun 02 18:31:10 2026 +0200
@@ -168,6 +168,16 @@
 
         info = FileInfo(uuid, type, size, md5);
         info.SetCustomData(customData);
+
+        try
+        {
+          StorageAreaDataSource::StoreIntoCache(*storageAreaReader_, info, data, size);
+        }
+        catch (OrthancException& e)
+        {
+          LOG(WARNING) << "Unable to store a new attachment into the cache: " << e.What();
+        }
+
         return;
       }
 
@@ -178,6 +188,8 @@
         std::string compressed;
         zlib.Compress(compressed, data, size);
 
+        const void* compressedData = compressed.empty() ? NULL : compressed.c_str();
+
         std::string compressedMD5;
 
         if (storeMD5_)
@@ -187,22 +199,23 @@
 
         {
           MetricsRegistry::Timer timer(*metricsRegistry_, METRICS_STORAGE_AREA_CREATE_DURATION);
-
-          if (compressed.size() > 0)
-          {
-            area_.Create(customData, uuid, &compressed[0], compressed.size(), type, compression, instance);
-          }
-          else
-          {
-            area_.Create(customData, uuid, NULL, 0, type, compression, instance);
-          }
+          area_.Create(customData, uuid, compressedData, compressed.size(), type, compression, instance);
         }
 
         metricsRegistry_->IncrementIntegerValue(METRICS_STORAGE_AREA_WRITTEN_BYTES, static_cast<int64_t>(compressed.size()));
 
-        info = FileInfo(uuid, type, size, md5,
-                        CompressionType_ZlibWithSize, compressed.size(), compressedMD5);
+        info = FileInfo(uuid, type, size, md5, CompressionType_ZlibWithSize, compressed.size(), compressedMD5);
         info.SetCustomData(customData);
+
+        try
+        {
+          StorageAreaDataSource::StoreIntoCache(*storageAreaReader_, info, compressedData, compressed.size());
+        }
+        catch (OrthancException& e)
+        {
+          LOG(WARNING) << "Unable to store a new attachment into the cache: " << e.What();
+        }
+
         return;
       }