Mercurial > hg > orthanc
changeset 6911:89677acd8f8d streaming
optimization: avoid reading from storage area if extracting range from a cached attachment
| author | Sebastien Jodogne <s.jodogne@gmail.com> |
|---|---|
| date | Tue, 02 Jun 2026 16:13:19 +0200 |
| parents | 3417bcf097a0 |
| children | a66b5e2dfb6e |
| files | OrthancFramework/Sources/DataSource/DataSourceReader.cpp OrthancFramework/Sources/DataSource/DicomDataSource.cpp OrthancFramework/Sources/DataSource/DicomDataSource.h OrthancFramework/Sources/DataSource/IDataSource.h OrthancFramework/Sources/DataSource/StorageAreaDataSource.cpp OrthancFramework/Sources/DataSource/StorageAreaDataSource.h OrthancFramework/Sources/DataSource/TranscoderDataSource.cpp OrthancFramework/Sources/DataSource/TranscoderDataSource.h |
| diffstat | 8 files changed, 134 insertions(+), 19 deletions(-) [+] |
line wrap: on
line diff
--- a/OrthancFramework/Sources/DataSource/DataSourceReader.cpp Tue Jun 02 15:03:11 2026 +0200 +++ b/OrthancFramework/Sources/DataSource/DataSourceReader.cpp Tue Jun 02 16:13:19 2026 +0200 @@ -180,7 +180,7 @@ preReservation.reset(new Internals::DataSourceMemoryBudget::Lock(*budget_, estimatedSize)); } - value.reset(source_.Load(*id_)); + value.reset(source_.Load(*id_, cache_)); if (!value) {
--- a/OrthancFramework/Sources/DataSource/DicomDataSource.cpp Tue Jun 02 15:03:11 2026 +0200 +++ b/OrthancFramework/Sources/DataSource/DicomDataSource.cpp Tue Jun 02 16:13:19 2026 +0200 @@ -183,7 +183,8 @@ } - IDynamicObject* DicomDataSource::Load(const IDataIdentifier& obj) + IDynamicObject* DicomDataSource::Load(const IDataIdentifier& obj, + const boost::shared_ptr<SharedObjectCache>& readerCache) { const Identifier& id = dynamic_cast<const Identifier&>(obj);
--- a/OrthancFramework/Sources/DataSource/DicomDataSource.h Tue Jun 02 15:03:11 2026 +0200 +++ b/OrthancFramework/Sources/DataSource/DicomDataSource.h Tue Jun 02 16:13:19 2026 +0200 @@ -60,7 +60,8 @@ public: explicit DicomDataSource(const boost::shared_ptr<DataSourceReader>& storageAreaReader); - virtual IDynamicObject* Load(const IDataIdentifier& obj) ORTHANC_OVERRIDE; + virtual IDynamicObject* Load(const IDataIdentifier& obj, + const boost::shared_ptr<SharedObjectCache>& readerCache /* could be NULL */) ORTHANC_OVERRIDE; virtual size_t GetValueSize(const IDynamicObject& obj) const ORTHANC_OVERRIDE;
--- a/OrthancFramework/Sources/DataSource/IDataSource.h Tue Jun 02 15:03:11 2026 +0200 +++ b/OrthancFramework/Sources/DataSource/IDataSource.h Tue Jun 02 16:13:19 2026 +0200 @@ -27,8 +27,13 @@ #include "../IDynamicObject.h" #include "IDataIdentifier.h" +#include <boost/shared_ptr.hpp> + + namespace Orthanc { + class SharedObjectCache; + class ORTHANC_PUBLIC IDataSource : public boost::noncopyable { public: @@ -36,7 +41,8 @@ { } - virtual IDynamicObject* Load(const IDataIdentifier& identifier) = 0; + virtual IDynamicObject* Load(const IDataIdentifier& identifier, + const boost::shared_ptr<SharedObjectCache>& readerCache /* could be NULL */) = 0; virtual size_t GetValueSize(const IDynamicObject& value) const = 0; };
--- a/OrthancFramework/Sources/DataSource/StorageAreaDataSource.cpp Tue Jun 02 15:03:11 2026 +0200 +++ b/OrthancFramework/Sources/DataSource/StorageAreaDataSource.cpp Tue Jun 02 16:13:19 2026 +0200 @@ -25,6 +25,7 @@ #include "../PrecompiledHeaders.h" #include "StorageAreaDataSource.h" +#include "../Cache/SharedObjectCache.h" #include "../MetricsRegistry.h" #include "../OrthancException.h" #include "../StringMemoryBuffer.h" @@ -42,6 +43,19 @@ namespace Orthanc { + static std::string ComputeCacheKey(std::string uuid, + FileContentType type, + uint64_t start, + uint64_t end) + { + // custom data is not part of the cache key, as it is for internal use by the plugin + // (it is opaque to the Orthanc core) + return (uuid + "|" + + boost::lexical_cast<std::string>(type) + "|" + + boost::lexical_cast<std::string>(start) + "|" + + boost::lexical_cast<std::string>(end)); + } + class StorageAreaDataSource::Value : public IDynamicObject { private: @@ -70,6 +84,21 @@ { return checkMD5_; } + + void ExtractRange(std::string& target, + uint64_t start, + uint64_t end) const + { + if (start >= buffer_->GetSize() || + end > buffer_->GetSize()) + { + throw OrthancException(ErrorCode_BadRange); + } + else + { + target.assign(reinterpret_cast<const char*>(buffer_->GetData()) + start, end - start); + } + } }; @@ -94,6 +123,8 @@ uint64_t end_; std::string customData_; std::unique_ptr<IPostProcessing> postProcessing_; + bool hasWholeKey_; + std::string wholeKey_; public: Identifier(const std::string& uuid, @@ -105,7 +136,8 @@ type_(type), start_(start), end_(end), - customData_(customData) + customData_(customData), + hasWholeKey_(false) { if (start > end) { @@ -119,6 +151,54 @@ } } + uint64_t GetStart() const + { + return start_; + } + + uint64_t GetEnd() const + { + return end_; + } + + /** + * WARNING: SetWholeKey() must only be used to retrieve compressed + * date from the storage area (or if there is no compression). + **/ + void SetWholeKey(const std::string& key) + { + if (hasWholeKey_) + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + else if (key.empty()) + { + throw OrthancException(ErrorCode_ParameterOutOfRange); + } + else + { + hasWholeKey_ = true; + wholeKey_ = key; + } + } + + bool HasWholeKey() const + { + return hasWholeKey_; + } + + const std::string GetWholeKey() const + { + if (hasWholeKey_) + { + return wholeKey_; + } + else + { + throw OrthancException(ErrorCode_BadSequenceOfCalls); + } + } + IMemoryBuffer* Read(IPluginStorageArea& area) const { return area.ReadRange(uuid_, type_, start_, end_, customData_); @@ -126,12 +206,7 @@ virtual bool GetCacheKey(std::string& key) const ORTHANC_OVERRIDE { - // custom data is not part of the cache key, as it is for internal use by the plugin - // (it is opaque to the Orthanc core) - key = (uuid_ + "|" + - boost::lexical_cast<std::string>(type_) + "|" + - boost::lexical_cast<std::string>(start_) + "|" + - boost::lexical_cast<std::string>(end_)); + key = ComputeCacheKey(uuid_, type_, start_, end_); return true; } @@ -305,10 +380,27 @@ } - IDynamicObject* StorageAreaDataSource::Load(const IDataIdentifier& identifier) + IDynamicObject* StorageAreaDataSource::Load(const IDataIdentifier& identifier, + const boost::shared_ptr<SharedObjectCache>& readerCache) { const Identifier& id = dynamic_cast<const Identifier&>(identifier); + if (id.HasWholeKey() && + readerCache) + { + // Extract the request range from the whole compressed attachment if the latter is already present in the cache + boost::shared_ptr<IDynamicObject> wholeCached = readerCache->GetCachedValue(id.GetWholeKey()); + if (wholeCached) + { + const Value& wholeRange = dynamic_cast<const Value&>(*wholeCached); + + std::string content; + wholeRange.ExtractRange(content, id.GetStart(), id.GetEnd()); + + return new Value(StringMemoryBuffer::CreateFromSwap(content), false); + } + } + std::unique_ptr<IMemoryBuffer> buffer; { @@ -430,8 +522,14 @@ throw OrthancException(ErrorCode_ParameterOutOfRange); } - return CreateRangeRequest(attachment.GetUuid(), attachment.GetContentType(), - 0, untilPosition, attachment.GetCustomData()); + std::unique_ptr<Identifier> id(new Identifier(attachment.GetUuid(), attachment.GetContentType(), + 0, untilPosition, attachment.GetCustomData())); + + // 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())); + + return id.release(); } @@ -468,8 +566,14 @@ } else { - return Execute(reader, CreateRangeRequest(attachment.GetUuid(), attachment.GetContentType(), - start, endExclusive, attachment.GetCustomData())); + std::unique_ptr<Identifier> id(new Identifier(attachment.GetUuid(), attachment.GetContentType(), + start, endExclusive, attachment.GetCustomData())); + + // 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())); + + return Execute(reader, id.release()); } } }
--- a/OrthancFramework/Sources/DataSource/StorageAreaDataSource.h Tue Jun 02 15:03:11 2026 +0200 +++ b/OrthancFramework/Sources/DataSource/StorageAreaDataSource.h Tue Jun 02 16:13:19 2026 +0200 @@ -69,7 +69,8 @@ virtual size_t GetValueSize(const IDynamicObject& value) const ORTHANC_OVERRIDE; - virtual IDynamicObject* Load(const IDataIdentifier& identifier) ORTHANC_OVERRIDE; + virtual IDynamicObject* Load(const IDataIdentifier& identifier, + const boost::shared_ptr<SharedObjectCache>& readerCache /* could be NULL */) ORTHANC_OVERRIDE; class ORTHANC_PUBLIC Range : public boost::noncopyable {
--- a/OrthancFramework/Sources/DataSource/TranscoderDataSource.cpp Tue Jun 02 15:03:11 2026 +0200 +++ b/OrthancFramework/Sources/DataSource/TranscoderDataSource.cpp Tue Jun 02 16:13:19 2026 +0200 @@ -245,7 +245,8 @@ } - IDynamicObject* TranscoderDataSource::Load(const IDataIdentifier& id) + IDynamicObject* TranscoderDataSource::Load(const IDataIdentifier& id, + const boost::shared_ptr<SharedObjectCache>& readerCache) { return dynamic_cast<const Identifier&>(id).Load(*transcoder_, *storageAreaReader_); }
--- a/OrthancFramework/Sources/DataSource/TranscoderDataSource.h Tue Jun 02 15:03:11 2026 +0200 +++ b/OrthancFramework/Sources/DataSource/TranscoderDataSource.h Tue Jun 02 16:13:19 2026 +0200 @@ -49,7 +49,8 @@ TranscoderDataSource(const boost::shared_ptr<IDicomTranscoder>& transcoder, const boost::shared_ptr<DataSourceReader>& storageAreaReader); - virtual IDynamicObject* Load(const IDataIdentifier& id) ORTHANC_OVERRIDE; + virtual IDynamicObject* Load(const IDataIdentifier& id, + const boost::shared_ptr<SharedObjectCache>& readerCache /* could be NULL */) ORTHANC_OVERRIDE; virtual size_t GetValueSize(const IDynamicObject& value) const ORTHANC_OVERRIDE;
