Mercurial > hg > orthanc
comparison OrthancServer/Sources/ServerContext.cpp @ 4457:789676a8c96a
Refactoring and improvements to the cache of DICOM files in ServerContext
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Tue, 19 Jan 2021 19:05:04 +0100 |
parents | d9473bd5ed43 |
children | 6831de40acd9 |
comparison
equal
deleted
inserted
replaced
4456:3e4f7b7840f0 | 4457:789676a8c96a |
---|---|
56 #include "StorageCommitmentReports.h" | 56 #include "StorageCommitmentReports.h" |
57 | 57 |
58 #include <dcmtk/dcmdata/dcfilefo.h> | 58 #include <dcmtk/dcmdata/dcfilefo.h> |
59 | 59 |
60 | 60 |
61 | 61 static size_t DICOM_CACHE_SIZE = 128 * 1024 * 1024; // 128 MB |
62 #define ENABLE_DICOM_CACHE 1 | 62 |
63 | |
64 static const size_t DICOM_CACHE_SIZE = 2; | |
65 | 63 |
66 /** | 64 /** |
67 * IMPORTANT: We make the assumption that the same instance of | 65 * IMPORTANT: We make the assumption that the same instance of |
68 * FileStorage can be accessed from multiple threads. This seems OK | 66 * FileStorage can be accessed from multiple threads. This seems OK |
69 * since the filesystem implements the required locking mechanisms, | 67 * since the filesystem implements the required locking mechanisms, |
270 } | 268 } |
271 } | 269 } |
272 } | 270 } |
273 | 271 |
274 | 272 |
273 void ServerContext::PublishDicomCacheMetrics() | |
274 { | |
275 metricsRegistry_->SetValue("orthanc_dicom_cache_size", | |
276 static_cast<float>(dicomCache_.GetCurrentSize()) / static_cast<float>(1024 * 1024)); | |
277 metricsRegistry_->SetValue("orthanc_dicom_cache_count", | |
278 static_cast<float>(dicomCache_.GetNumberOfItems())); | |
279 } | |
280 | |
281 | |
275 ServerContext::ServerContext(IDatabaseWrapper& database, | 282 ServerContext::ServerContext(IDatabaseWrapper& database, |
276 IStorageArea& area, | 283 IStorageArea& area, |
277 bool unitTesting, | 284 bool unitTesting, |
278 size_t maxCompletedJobs) : | 285 size_t maxCompletedJobs) : |
279 index_(*this, database, (unitTesting ? 20 : 500)), | 286 index_(*this, database, (unitTesting ? 20 : 500)), |
280 area_(area), | 287 area_(area), |
281 compressionEnabled_(false), | 288 compressionEnabled_(false), |
282 storeMD5_(true), | 289 storeMD5_(true), |
283 provider_(*this), | 290 largeDicomThrottler_(1), |
284 dicomCache_(provider_, DICOM_CACHE_SIZE), | 291 dicomCache_(DICOM_CACHE_SIZE), |
285 mainLua_(*this), | 292 mainLua_(*this), |
286 filterLua_(*this), | 293 filterLua_(*this), |
287 luaListener_(*this), | 294 luaListener_(*this), |
288 jobsEngine_(maxCompletedJobs), | 295 jobsEngine_(maxCompletedJobs), |
289 #if ORTHANC_ENABLE_PLUGINS == 1 | 296 #if ORTHANC_ENABLE_PLUGINS == 1 |
526 { | 533 { |
527 LOG(INFO) << "An incoming instance has been discarded by the filter"; | 534 LOG(INFO) << "An incoming instance has been discarded by the filter"; |
528 return StoreStatus_FilteredOut; | 535 return StoreStatus_FilteredOut; |
529 } | 536 } |
530 | 537 |
531 { | 538 // Remove the file from the DicomCache (useful if |
532 // Remove the file from the DicomCache (useful if | 539 // "OverwriteInstances" is set to "true") |
533 // "OverwriteInstances" is set to "true") | 540 dicomCache_.Invalidate(resultPublicId); |
534 boost::mutex::scoped_lock lock(dicomCacheMutex_); | 541 PublishDicomCacheMetrics(); |
535 dicomCache_.Invalidate(resultPublicId); | |
536 } | |
537 | 542 |
538 // TODO Should we use "gzip" instead? | 543 // TODO Should we use "gzip" instead? |
539 CompressionType compression = (compressionEnabled_ ? CompressionType_ZlibWithSize : CompressionType_None); | 544 CompressionType compression = (compressionEnabled_ ? CompressionType_ZlibWithSize : CompressionType_None); |
540 | 545 |
541 FileInfo dicomInfo = accessor.Write(dicom.GetBufferData(), dicom.GetBufferSize(), | 546 FileInfo dicomInfo = accessor.Write(dicom.GetBufferData(), dicom.GetBufferSize(), |
876 StorageAccessor accessor(area_, GetMetricsRegistry()); | 881 StorageAccessor accessor(area_, GetMetricsRegistry()); |
877 accessor.Read(result, attachment); | 882 accessor.Read(result, attachment); |
878 } | 883 } |
879 | 884 |
880 | 885 |
881 IDynamicObject* ServerContext::DicomCacheProvider::Provide(const std::string& instancePublicId) | 886 ServerContext::DicomCacheLocker::DicomCacheLocker(ServerContext& context, |
882 { | 887 const std::string& instancePublicId) : |
883 std::string content; | 888 context_(context), |
884 context_.ReadDicom(content, instancePublicId); | 889 instancePublicId_(instancePublicId) |
885 return new ParsedDicomFile(content); | 890 { |
886 } | 891 accessor_.reset(new ParsedDicomCache::Accessor(context_.dicomCache_, instancePublicId)); |
887 | 892 |
888 | 893 if (!accessor_->IsValid()) |
889 ServerContext::DicomCacheLocker::DicomCacheLocker(ServerContext& that, | 894 { |
890 const std::string& instancePublicId) : | 895 accessor_.reset(NULL); |
891 that_(that), | 896 |
892 lock_(that_.dicomCacheMutex_) | 897 // Throttle to avoid loading several large DICOM files simultaneously |
893 { | 898 largeDicomLocker_.reset(new Semaphore::Locker(context.largeDicomThrottler_)); |
894 #if ENABLE_DICOM_CACHE == 0 | 899 |
895 static std::unique_ptr<IDynamicObject> p; | 900 std::string content; |
896 p.reset(that_.provider_.Provide(instancePublicId)); | 901 context_.ReadDicom(content, instancePublicId); |
897 dicom_ = dynamic_cast<ParsedDicomFile*>(p.get()); | 902 |
898 #else | 903 // Release the throttle if loading "small" DICOM files (under |
899 dicom_ = &dynamic_cast<ParsedDicomFile&>(that_.dicomCache_.Access(instancePublicId)); | 904 // 50MB, which is an arbitrary value) |
900 #endif | 905 if (content.size() < 50 * 1024 * 1024) |
906 { | |
907 largeDicomLocker_.reset(NULL); | |
908 } | |
909 | |
910 dicom_.reset(new ParsedDicomFile(content)); | |
911 dicomSize_ = content.size(); | |
912 } | |
913 | |
914 assert(accessor_.get() != NULL || | |
915 dicom_.get() != NULL); | |
901 } | 916 } |
902 | 917 |
903 | 918 |
904 ServerContext::DicomCacheLocker::~DicomCacheLocker() | 919 ServerContext::DicomCacheLocker::~DicomCacheLocker() |
905 { | 920 { |
906 } | 921 if (dicom_.get() != NULL) |
907 | 922 { |
908 | 923 try |
924 { | |
925 context_.dicomCache_.Acquire(instancePublicId_, dicom_.release(), dicomSize_); | |
926 context_.PublishDicomCacheMetrics(); | |
927 } | |
928 catch (OrthancException&) | |
929 { | |
930 } | |
931 } | |
932 } | |
933 | |
934 | |
935 ParsedDicomFile& ServerContext::DicomCacheLocker::GetDicom() const | |
936 { | |
937 if (dicom_.get() != NULL) | |
938 { | |
939 return *dicom_; | |
940 } | |
941 else | |
942 { | |
943 assert(accessor_.get() != NULL); | |
944 return accessor_->GetDicom(); | |
945 } | |
946 } | |
947 | |
948 | |
909 void ServerContext::SetStoreMD5ForAttachments(bool storeMD5) | 949 void ServerContext::SetStoreMD5ForAttachments(bool storeMD5) |
910 { | 950 { |
911 LOG(INFO) << "Storing MD5 for attachments: " << (storeMD5 ? "yes" : "no"); | 951 LOG(INFO) << "Storing MD5 for attachments: " << (storeMD5 ? "yes" : "no"); |
912 storeMD5_ = storeMD5; | 952 storeMD5_ = storeMD5; |
913 } | 953 } |
944 ResourceType expectedType) | 984 ResourceType expectedType) |
945 { | 985 { |
946 if (expectedType == ResourceType_Instance) | 986 if (expectedType == ResourceType_Instance) |
947 { | 987 { |
948 // remove the file from the DicomCache | 988 // remove the file from the DicomCache |
949 boost::mutex::scoped_lock lock(dicomCacheMutex_); | |
950 dicomCache_.Invalidate(uuid); | 989 dicomCache_.Invalidate(uuid); |
990 PublishDicomCacheMetrics(); | |
951 } | 991 } |
952 | 992 |
953 return index_.DeleteResource(target, uuid, expectedType); | 993 return index_.DeleteResource(target, uuid, expectedType); |
954 } | 994 } |
955 | 995 |
956 | 996 |
957 void ServerContext::SignalChange(const ServerIndexChange& change) | 997 void ServerContext::SignalChange(const ServerIndexChange& change) |
958 { | 998 { |
999 if (change.GetResourceType() == ResourceType_Instance && | |
1000 change.GetChangeType() == ChangeType_Deleted) | |
1001 { | |
1002 dicomCache_.Invalidate(change.GetPublicId()); | |
1003 PublishDicomCacheMetrics(); | |
1004 } | |
1005 | |
959 pendingChanges_.Enqueue(change.Clone()); | 1006 pendingChanges_.Enqueue(change.Clone()); |
960 } | 1007 } |
961 | 1008 |
962 | 1009 |
963 #if ORTHANC_ENABLE_PLUGINS == 1 | 1010 #if ORTHANC_ENABLE_PLUGINS == 1 |