# HG changeset patch # User Alain Mazy # Date 1632996859 -7200 # Node ID 434843934307f45934ec825a245f9e9bff362ad2 # Parent 9754d5f2f38af5dfd97980c383eb9651abfa6f9c Added a StorageCache in the StorageAccessor diff -r 9754d5f2f38a -r 434843934307 NEWS --- a/NEWS Tue Sep 14 14:51:12 2021 +0200 +++ b/NEWS Thu Sep 30 12:14:19 2021 +0200 @@ -1,6 +1,19 @@ Pending changes in the mainline =============================== +General +------- + +* Added a storage cache in RAM to avoid reading the same files multiple times from + the storage. This greatly improves, among other things, the performance of WADO-RS + retrieval of individual frames of multiframe instances. +* New configuration option "MaximumStorageCacheSize" to configure the size of + the new storage cache. + + +Maintenance +----------- + * Fix handling of option "DeidentifyLogs", notably for tags (0010,0010) and (0010,0020) diff -r 9754d5f2f38a -r 434843934307 OrthancFramework/Resources/CMake/OrthancFrameworkConfiguration.cmake --- a/OrthancFramework/Resources/CMake/OrthancFrameworkConfiguration.cmake Tue Sep 14 14:51:12 2021 +0200 +++ b/OrthancFramework/Resources/CMake/OrthancFrameworkConfiguration.cmake Thu Sep 30 12:14:19 2021 +0200 @@ -385,6 +385,7 @@ ${CMAKE_CURRENT_LIST_DIR}/../../Sources/Compression/HierarchicalZipWriter.cpp ${CMAKE_CURRENT_LIST_DIR}/../../Sources/Compression/ZipWriter.cpp ${CMAKE_CURRENT_LIST_DIR}/../../Sources/FileStorage/StorageAccessor.cpp + ${CMAKE_CURRENT_LIST_DIR}/../../Sources/FileStorage/StorageCache.cpp ) endif() endif() diff -r 9754d5f2f38a -r 434843934307 OrthancFramework/Sources/Cache/MemoryObjectCache.h --- a/OrthancFramework/Sources/Cache/MemoryObjectCache.h Tue Sep 14 14:51:12 2021 +0200 +++ b/OrthancFramework/Sources/Cache/MemoryObjectCache.h Thu Sep 30 12:14:19 2021 +0200 @@ -37,6 +37,9 @@ namespace Orthanc { + /** + * Note: this class is thread safe + **/ class ORTHANC_PUBLIC MemoryObjectCache : public boost::noncopyable { private: diff -r 9754d5f2f38a -r 434843934307 OrthancFramework/Sources/Cache/MemoryStringCache.cpp --- a/OrthancFramework/Sources/Cache/MemoryStringCache.cpp Tue Sep 14 14:51:12 2021 +0200 +++ b/OrthancFramework/Sources/Cache/MemoryStringCache.cpp Thu Sep 30 12:14:19 2021 +0200 @@ -35,7 +35,12 @@ content_(content) { } - + + explicit StringValue(const char* buffer, size_t size) : + content_(buffer, size) + { + } + const std::string& GetContent() const { return content_; @@ -63,6 +68,13 @@ cache_.Acquire(key, new StringValue(value)); } + void MemoryStringCache::Add(const std::string& key, + const void* buffer, + size_t size) + { + cache_.Acquire(key, new StringValue(reinterpret_cast(buffer), size)); + } + void MemoryStringCache::Invalidate(const std::string &key) { cache_.Invalidate(key); diff -r 9754d5f2f38a -r 434843934307 OrthancFramework/Sources/Cache/MemoryStringCache.h --- a/OrthancFramework/Sources/Cache/MemoryStringCache.h Tue Sep 14 14:51:12 2021 +0200 +++ b/OrthancFramework/Sources/Cache/MemoryStringCache.h Thu Sep 30 12:14:19 2021 +0200 @@ -29,6 +29,8 @@ /** * Facade object around "MemoryObjectCache" that caches a dictionary * of strings, using the "fetch/add" paradigm of memcached. + * + * Note: this class is thread safe **/ class ORTHANC_PUBLIC MemoryStringCache : public boost::noncopyable { @@ -44,7 +46,11 @@ void Add(const std::string& key, const std::string& value); - + + void Add(const std::string& key, + const void* buffer, + size_t size); + void Invalidate(const std::string& key); bool Fetch(std::string& value, diff -r 9754d5f2f38a -r 434843934307 OrthancFramework/Sources/FileStorage/StorageAccessor.cpp --- a/OrthancFramework/Sources/FileStorage/StorageAccessor.cpp Tue Sep 14 14:51:12 2021 +0200 +++ b/OrthancFramework/Sources/FileStorage/StorageAccessor.cpp Thu Sep 30 12:14:19 2021 +0200 @@ -22,7 +22,10 @@ #include "../PrecompiledHeaders.h" #include "StorageAccessor.h" +#include "StorageCache.h" +#include "../Logging.h" +#include "../StringMemoryBuffer.h" #include "../Compatibility.h" #include "../Compression/ZlibCompressor.h" #include "../MetricsRegistry.h" @@ -58,14 +61,18 @@ }; - StorageAccessor::StorageAccessor(IStorageArea &area) : + StorageAccessor::StorageAccessor(IStorageArea &area, StorageCache& cache) : area_(area), + cache_(cache), metrics_(NULL) { } - StorageAccessor::StorageAccessor(IStorageArea &area, MetricsRegistry &metrics) : + StorageAccessor::StorageAccessor(IStorageArea &area, + StorageCache& cache, + MetricsRegistry &metrics) : area_(area), + cache_(cache), metrics_(&metrics) { } @@ -93,6 +100,8 @@ MetricsTimer timer(*this, METRICS_CREATE); area_.Create(uuid, data, size, type); + cache_.Add(uuid, type, data, size); + return FileInfo(uuid, type, size, md5); } @@ -123,6 +132,7 @@ } } + cache_.Add(uuid, type, data, size); return FileInfo(uuid, type, size, md5, CompressionType_ZlibWithSize, compressed.size(), compressedMD5); } @@ -145,6 +155,13 @@ void StorageAccessor::Read(std::string& content, const FileInfo& info) { + if (cache_.Fetch(content, info.GetUuid(), info.GetContentType())) + { + LOG(INFO) << "Read attachment \"" << info.GetUuid() << "\" " + << "content type from cache"; + return; + } + switch (info.GetCompressionType()) { case CompressionType_None: @@ -152,7 +169,9 @@ MetricsTimer timer(*this, METRICS_READ); std::unique_ptr buffer(area_.Read(info.GetUuid(), info.GetContentType())); - buffer->MoveToString(content); + buffer->MoveToString(content); + + cache_.Add(info.GetUuid(), info.GetContentType(), content); break; } @@ -168,6 +187,8 @@ } zlib.Uncompress(content, compressed->GetData(), compressed->GetSize()); + + cache_.Add(info.GetUuid(), info.GetContentType(), content); break; } @@ -196,6 +217,14 @@ { MetricsTimer timer(*this, METRICS_REMOVE); area_.Remove(fileUuid, type); + + cache_.Invalidate(fileUuid, type); + + // in ReadStartRange, we might have cached only the start of the file -> try to remove it + if (type == FileContentType_Dicom) + { + cache_.Invalidate(fileUuid, FileContentType_DicomUntilPixelData); + } } void StorageAccessor::Remove(const FileInfo &info) @@ -203,15 +232,56 @@ Remove(info.GetUuid(), info.GetContentType()); } + IMemoryBuffer* StorageAccessor::ReadStartRange(const std::string& fileUuid, + FileContentType contentType, + uint64_t end /* exclusive */, + FileContentType startFileContentType) + { + std::string content; + if (cache_.Fetch(content, fileUuid, contentType)) + { + LOG(INFO) << "Read attachment \"" << fileUuid << "\" " + << "(range from " << 0 << " to " << end << ") from cache"; + + return StringMemoryBuffer::CreateFromCopy(content, 0, end); + } + + if (cache_.Fetch(content, fileUuid, startFileContentType)) + { + LOG(INFO) << "Read attachment \"" << fileUuid << "\" " + << "(range from " << 0 << " to " << end << ") from cache"; + + assert(content.size() == end); + return StringMemoryBuffer::CreateFromCopy(content); + } + + std::unique_ptr buffer(area_.ReadRange(fileUuid, contentType, 0, end)); + + // we've read only the first part of the file -> add an entry in the cache + // note the uuid is still the uuid of the full file but the type is the type of the start of the file ! + assert(buffer->GetSize() == end); + cache_.Add(fileUuid, startFileContentType, buffer->GetData(), buffer->GetSize()); + return buffer.release(); + } + + #if ORTHANC_ENABLE_CIVETWEB == 1 || ORTHANC_ENABLE_MONGOOSE == 1 void StorageAccessor::SetupSender(BufferHttpSender& sender, const FileInfo& info, const std::string& mime) { + if (cache_.Fetch(sender.GetBuffer(), info.GetUuid(), info.GetContentType())) + { + LOG(INFO) << "Read attachment \"" << info.GetUuid() << "\" " + << "content type from cache"; + } + else { MetricsTimer timer(*this, METRICS_READ); std::unique_ptr buffer(area_.Read(info.GetUuid(), info.GetContentType())); buffer->MoveToString(sender.GetBuffer()); + + cache_.Add(info.GetUuid(), info.GetContentType(), sender.GetBuffer()); } sender.SetContentType(mime); diff -r 9754d5f2f38a -r 434843934307 OrthancFramework/Sources/FileStorage/StorageAccessor.h --- a/OrthancFramework/Sources/FileStorage/StorageAccessor.h Tue Sep 14 14:51:12 2021 +0200 +++ b/OrthancFramework/Sources/FileStorage/StorageAccessor.h Thu Sep 30 12:14:19 2021 +0200 @@ -54,6 +54,7 @@ namespace Orthanc { class MetricsRegistry; + class StorageCache; /** * This class handles the compression/decompression of the raw files @@ -66,6 +67,7 @@ class MetricsTimer; IStorageArea& area_; + StorageCache& cache_; MetricsRegistry* metrics_; #if ORTHANC_ENABLE_CIVETWEB == 1 || ORTHANC_ENABLE_MONGOOSE == 1 @@ -75,9 +77,11 @@ #endif public: - explicit StorageAccessor(IStorageArea& area); + explicit StorageAccessor(IStorageArea& area, + StorageCache& cache); StorageAccessor(IStorageArea& area, + StorageCache& cache, MetricsRegistry& metrics); FileInfo Write(const void* data, @@ -97,6 +101,11 @@ void ReadRaw(std::string& content, const FileInfo& info); + IMemoryBuffer* ReadStartRange(const std::string& fileUuid, + FileContentType fullFileContentType, + uint64_t end /* exclusive */, + FileContentType startFileContentType); + void Remove(const std::string& fileUuid, FileContentType type); diff -r 9754d5f2f38a -r 434843934307 OrthancFramework/Sources/FileStorage/StorageCache.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancFramework/Sources/FileStorage/StorageCache.cpp Thu Sep 30 12:14:19 2021 +0200 @@ -0,0 +1,118 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2021 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program. If not, see + * . + **/ + + +#include "../PrecompiledHeaders.h" +#include "StorageCache.h" + +#include "../Compatibility.h" +#include "../OrthancException.h" + + + +namespace Orthanc +{ + bool IsAcceptedContentType(FileContentType contentType) + { + return contentType == FileContentType_Dicom || + contentType == FileContentType_DicomUntilPixelData || + contentType == FileContentType_DicomAsJson; + } + + const char* ToString(FileContentType contentType) + { + switch (contentType) + { + case FileContentType_Dicom: + return "dicom"; + case FileContentType_DicomUntilPixelData: + return "dicom-header"; + case FileContentType_DicomAsJson: + return "dicom-json"; + default: + throw OrthancException(ErrorCode_InternalError, + "ContentType not supported in StorageCache"); + } + } + + void GetCacheKey(std::string& key, const std::string& uuid, FileContentType contentType) + { + key = uuid + ":" + std::string(ToString(contentType)); + } + + void StorageCache::SetMaximumSize(size_t size) + { + cache_.SetMaximumSize(size); + } + + void StorageCache::Add(const std::string& uuid, + FileContentType contentType, + const std::string& value) + { + if (!IsAcceptedContentType(contentType)) + { + return; + } + + std::string key; + GetCacheKey(key, uuid, contentType); + cache_.Add(key, value); + } + + void StorageCache::Add(const std::string& uuid, + FileContentType contentType, + const void* buffer, + size_t size) + { + if (!IsAcceptedContentType(contentType)) + { + return; + } + + std::string key; + GetCacheKey(key, uuid, contentType); + cache_.Add(key, buffer, size); + } + + void StorageCache::Invalidate(const std::string& uuid, FileContentType contentType) + { + std::string key; + GetCacheKey(key, uuid, contentType); + cache_.Invalidate(key); + } + + bool StorageCache::Fetch(std::string& value, + const std::string& uuid, + FileContentType contentType) + { + if (!IsAcceptedContentType(contentType)) + { + return false; + } + + std::string key; + GetCacheKey(key, uuid, contentType); + + return cache_.Fetch(value, key); + } + + +} \ No newline at end of file diff -r 9754d5f2f38a -r 434843934307 OrthancFramework/Sources/FileStorage/StorageCache.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancFramework/Sources/FileStorage/StorageCache.h Thu Sep 30 12:14:19 2021 +0200 @@ -0,0 +1,59 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2021 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program. If not, see + * . + **/ + + +#pragma once + +#include "../Cache/MemoryStringCache.h" + +#include "../Compatibility.h" // For ORTHANC_OVERRIDE + +#include +#include + +namespace Orthanc +{ + /** + * Note: this class is thread safe + **/ + class ORTHANC_PUBLIC StorageCache : public boost::noncopyable + { + MemoryStringCache cache_; + public: + void SetMaximumSize(size_t size); + + void Add(const std::string& uuid, + FileContentType contentType, + const std::string& value); + + void Add(const std::string& uuid, + FileContentType contentType, + const void* buffer, + size_t size); + + void Invalidate(const std::string& uuid, FileContentType contentType); + + bool Fetch(std::string& value, + const std::string& uuid, + FileContentType contentType); + + }; +} diff -r 9754d5f2f38a -r 434843934307 OrthancFramework/Sources/StringMemoryBuffer.cpp --- a/OrthancFramework/Sources/StringMemoryBuffer.cpp Tue Sep 14 14:51:12 2021 +0200 +++ b/OrthancFramework/Sources/StringMemoryBuffer.cpp Thu Sep 30 12:14:19 2021 +0200 @@ -47,4 +47,14 @@ result->Copy(buffer); return result.release(); } + + + IMemoryBuffer* StringMemoryBuffer::CreateFromCopy(const std::string& buffer, + size_t start /* inclusive */, + size_t end /* exclusive */) + { + std::unique_ptr result(new StringMemoryBuffer); + result->Copy(buffer, start, end); + return result.release(); + } } diff -r 9754d5f2f38a -r 434843934307 OrthancFramework/Sources/StringMemoryBuffer.h --- a/OrthancFramework/Sources/StringMemoryBuffer.h Tue Sep 14 14:51:12 2021 +0200 +++ b/OrthancFramework/Sources/StringMemoryBuffer.h Thu Sep 30 12:14:19 2021 +0200 @@ -38,6 +38,11 @@ buffer_ = buffer; } + void Copy(const std::string& buffer, size_t start /* inclusive */, size_t end /* exclusive */) + { + buffer_.assign(buffer, start, end - start); + } + void Swap(std::string& buffer) { buffer_.swap(buffer); @@ -58,5 +63,7 @@ static IMemoryBuffer* CreateFromSwap(std::string& buffer); static IMemoryBuffer* CreateFromCopy(const std::string& buffer); + + static IMemoryBuffer* CreateFromCopy(const std::string& buffer, size_t start /* inclusive */, size_t end /* exclusive */); }; } diff -r 9754d5f2f38a -r 434843934307 OrthancFramework/UnitTestsSources/FileStorageTests.cpp --- a/OrthancFramework/UnitTestsSources/FileStorageTests.cpp Tue Sep 14 14:51:12 2021 +0200 +++ b/OrthancFramework/UnitTestsSources/FileStorageTests.cpp Thu Sep 30 12:14:19 2021 +0200 @@ -29,6 +29,7 @@ #include "../Sources/FileStorage/FilesystemStorage.h" #include "../Sources/FileStorage/StorageAccessor.h" +#include "../Sources/FileStorage/StorageCache.h" #include "../Sources/HttpServer/BufferHttpSender.h" #include "../Sources/HttpServer/FilesystemHttpSender.h" #include "../Sources/Logging.h" @@ -124,7 +125,8 @@ TEST(StorageAccessor, NoCompression) { FilesystemStorage s("UnitTestsStorage"); - StorageAccessor accessor(s); + StorageCache cache; + StorageAccessor accessor(s, cache); std::string data = "Hello world"; FileInfo info = accessor.Write(data, FileContentType_Dicom, CompressionType_None, true); @@ -145,7 +147,8 @@ TEST(StorageAccessor, Compression) { FilesystemStorage s("UnitTestsStorage"); - StorageAccessor accessor(s); + StorageCache cache; + StorageAccessor accessor(s, cache); std::string data = "Hello world"; FileInfo info = accessor.Write(data, FileContentType_Dicom, CompressionType_ZlibWithSize, true); @@ -165,7 +168,8 @@ TEST(StorageAccessor, Mix) { FilesystemStorage s("UnitTestsStorage"); - StorageAccessor accessor(s); + StorageCache cache; + StorageAccessor accessor(s, cache); std::string r; std::string compressedData = "Hello"; diff -r 9754d5f2f38a -r 434843934307 OrthancServer/Resources/Configuration.json --- a/OrthancServer/Resources/Configuration.json Tue Sep 14 14:51:12 2021 +0200 +++ b/OrthancServer/Resources/Configuration.json Thu Sep 30 12:14:19 2021 +0200 @@ -40,7 +40,13 @@ // in the storage (a value of "0" indicates no limit on the number // of patients) "MaximumPatientCount" : 0, - + + // Maximum size of the storage cache in MB. The storage cache + // is stored in RAM and contains a copy of recently accessed + // files (written or read). A value of "0" indicates the cache + // is disabled. (new in Orthanc 1.9.8) + "MaximumStorageCacheSize" : 128, + // List of paths to the custom Lua scripts that are to be loaded // into this instance of Orthanc "LuaScripts" : [ diff -r 9754d5f2f38a -r 434843934307 OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp --- a/OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp Tue Sep 14 14:51:12 2021 +0200 +++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp Thu Sep 30 12:14:19 2021 +0200 @@ -2969,7 +2969,7 @@ std::string publicId = call.GetUriComponent("id", ""); std::string dicomContent; - context.ReadDicom(dicomContent, publicId); + context.ReadDicomForHeader(dicomContent, publicId); // TODO Consider using "DicomMap::ParseDicomMetaInformation()" to // speed up things here diff -r 9754d5f2f38a -r 434843934307 OrthancServer/Sources/ServerContext.cpp --- a/OrthancServer/Sources/ServerContext.cpp Tue Sep 14 14:51:12 2021 +0200 +++ b/OrthancServer/Sources/ServerContext.cpp Thu Sep 30 12:14:19 2021 +0200 @@ -484,7 +484,7 @@ void ServerContext::RemoveFile(const std::string& fileUuid, FileContentType type) { - StorageAccessor accessor(area_, GetMetricsRegistry()); + StorageAccessor accessor(area_, storageCache_, GetMetricsRegistry()); accessor.Remove(fileUuid, type); } @@ -526,7 +526,7 @@ try { MetricsRegistry::Timer timer(GetMetricsRegistry(), "orthanc_store_dicom_duration_ms"); - StorageAccessor accessor(area_, GetMetricsRegistry()); + StorageAccessor accessor(area_, storageCache_, GetMetricsRegistry()); DicomInstanceHasher hasher(summary); resultPublicId = hasher.HashInstance(); @@ -760,7 +760,7 @@ } else { - StorageAccessor accessor(area_, GetMetricsRegistry()); + StorageAccessor accessor(area_, storageCache_, GetMetricsRegistry()); accessor.AnswerFile(output, attachment, GetFileContentMime(content)); } } @@ -790,7 +790,7 @@ std::string content; - StorageAccessor accessor(area_, GetMetricsRegistry()); + StorageAccessor accessor(area_, storageCache_, GetMetricsRegistry()); accessor.Read(content, attachment); FileInfo modified = accessor.Write(content.empty() ? NULL : content.c_str(), @@ -846,7 +846,7 @@ std::string dicom; { - StorageAccessor accessor(area_, GetMetricsRegistry()); + StorageAccessor accessor(area_, storageCache_, GetMetricsRegistry()); accessor.Read(dicom, attachment); } @@ -911,8 +911,8 @@ std::unique_ptr dicom; { - MetricsRegistry::Timer timer(GetMetricsRegistry(), "orthanc_storage_read_range_duration_ms"); - dicom.reset(area_.ReadRange(attachment.GetUuid(), FileContentType_Dicom, 0, pixelDataOffset)); + StorageAccessor accessor(area_, storageCache_, GetMetricsRegistry()); + dicom.reset(accessor.ReadStartRange(attachment.GetUuid(), FileContentType_Dicom, pixelDataOffset, FileContentType_DicomUntilPixelData)); } if (dicom.get() == NULL) @@ -941,7 +941,7 @@ std::string dicomAsJson; { - StorageAccessor accessor(area_, GetMetricsRegistry()); + StorageAccessor accessor(area_, storageCache_, GetMetricsRegistry()); accessor.Read(dicomAsJson, attachment); } @@ -1010,7 +1010,15 @@ int64_t revision; ReadAttachment(dicom, revision, instancePublicId, FileContentType_Dicom, true /* uncompress */); } - + + void ServerContext::ReadDicomForHeader(std::string& dicom, + const std::string& instancePublicId) + { + if (!ReadDicomUntilPixelData(dicom, instancePublicId)) + { + ReadDicom(dicom, instancePublicId); + } + } bool ServerContext::ReadDicomUntilPixelData(std::string& dicom, const std::string& instancePublicId) @@ -1039,8 +1047,10 @@ { uint64_t pixelDataOffset = boost::lexical_cast(s); + StorageAccessor accessor(area_, storageCache_, GetMetricsRegistry()); + std::unique_ptr buffer( - area_.ReadRange(attachment.GetUuid(), attachment.GetContentType(), 0, pixelDataOffset)); + accessor.ReadStartRange(attachment.GetUuid(), attachment.GetContentType(), pixelDataOffset, FileContentType_DicomUntilPixelData)); buffer->MoveToString(dicom); return true; // Success } @@ -1071,7 +1081,7 @@ assert(attachment.GetContentType() == content); { - StorageAccessor accessor(area_, GetMetricsRegistry()); + StorageAccessor accessor(area_, storageCache_, GetMetricsRegistry()); if (uncompressIfNeeded) { @@ -1171,7 +1181,7 @@ // TODO Should we use "gzip" instead? CompressionType compression = (compressionEnabled_ ? CompressionType_ZlibWithSize : CompressionType_None); - StorageAccessor accessor(area_, GetMetricsRegistry()); + StorageAccessor accessor(area_, storageCache_, GetMetricsRegistry()); FileInfo attachment = accessor.Write(data, size, attachmentType, compression, storeMD5_); try diff -r 9754d5f2f38a -r 434843934307 OrthancServer/Sources/ServerContext.h --- a/OrthancServer/Sources/ServerContext.h Tue Sep 14 14:51:12 2021 +0200 +++ b/OrthancServer/Sources/ServerContext.h Thu Sep 30 12:14:19 2021 +0200 @@ -43,6 +43,7 @@ #include "../../OrthancFramework/Sources/DicomParsing/DicomModification.h" #include "../../OrthancFramework/Sources/DicomParsing/IDicomTranscoder.h" #include "../../OrthancFramework/Sources/DicomParsing/ParsedDicomCache.h" +#include "../../OrthancFramework/Sources/FileStorage/StorageCache.h" #include "../../OrthancFramework/Sources/MultiThreading/Semaphore.h" @@ -169,6 +170,7 @@ ServerIndex index_; IStorageArea& area_; + StorageCache storageCache_; bool compressionEnabled_; bool storeMD5_; @@ -288,6 +290,11 @@ return index_; } + void SetMaximumStorageCacheSize(size_t size) + { + return storageCache_.SetMaximumSize(size); + } + void SetCompressionEnabled(bool enabled); bool IsCompressionEnabled() const @@ -325,7 +332,10 @@ void ReadDicom(std::string& dicom, const std::string& instancePublicId); - + + void ReadDicomForHeader(std::string& dicom, + const std::string& instancePublicId); + bool ReadDicomUntilPixelData(std::string& dicom, const std::string& instancePublicId); diff -r 9754d5f2f38a -r 434843934307 OrthancServer/Sources/ServerToolbox.cpp --- a/OrthancServer/Sources/ServerToolbox.cpp Tue Sep 14 14:51:12 2021 +0200 +++ b/OrthancServer/Sources/ServerToolbox.cpp Thu Sep 30 12:14:19 2021 +0200 @@ -36,6 +36,7 @@ #include "../../OrthancFramework/Sources/DicomParsing/ParsedDicomFile.h" #include "../../OrthancFramework/Sources/FileStorage/StorageAccessor.h" +#include "../../OrthancFramework/Sources/FileStorage/StorageCache.h" #include "../../OrthancFramework/Sources/Logging.h" #include "../../OrthancFramework/Sources/OrthancException.h" #include "Database/IDatabaseWrapper.h" @@ -176,7 +177,8 @@ try { // Read and parse the content of the DICOM file - StorageAccessor accessor(storageArea); + StorageCache cache; // we create a temporary cache for this operation (required by the StorageAccessor) + StorageAccessor accessor(storageArea, cache); std::string content; accessor.Read(content, attachment); diff -r 9754d5f2f38a -r 434843934307 OrthancServer/Sources/main.cpp --- a/OrthancServer/Sources/main.cpp Tue Sep 14 14:51:12 2021 +0200 +++ b/OrthancServer/Sources/main.cpp Thu Sep 30 12:14:19 2021 +0200 @@ -1521,6 +1521,16 @@ { context.GetIndex().SetMaximumStorageSize(0); } + + try + { + uint64_t size = lock.GetConfiguration().GetUnsignedIntegerParameter("MaximumStorageCacheSize", 128); + context.SetMaximumStorageCacheSize(size * 1024 * 1024); + } + catch (...) + { + context.SetMaximumStorageCacheSize(128); + } } {