# HG changeset patch # User Alain Mazy # Date 1732719762 -3600 # Node ID 1c549458ea5dbf58ad25e259bea5c3a28a081eaf # Parent 00f650dc07bfd592e40ba04b6d98e1d644f74567 UpdateStatistics Thread -> DbHousekeeping diff -r 00f650dc07bf -r 1c549458ea5d NEWS --- a/NEWS Wed Nov 27 15:00:58 2024 +0100 +++ b/NEWS Wed Nov 27 16:02:42 2024 +0100 @@ -52,10 +52,9 @@ * DICOM TLS: "DicomTlsTrustedCertificates" is not required anymore when issuing an outgoing SCU connexion when "DicomTlsRemoteCertificateRequired" is set to false. -* Introduced a new thread to update the statistics at regular interval for the - DB plugins that are implementing the UpdateAndGetStatistics function (currently only - PostgreSQL). This avoids very long update times in case you don't call /statistics - for a long period. +* Introduced a new thread to perform DB Housekeeping at regular interval (1s) for the + DB plugins requiring it (currently only PostgreSQL). E.g: This avoids very long update + times in case you don't call /statistics for a long period. * Fix C-Find queries not returning computed tags like ModalitiesInStudy, NumberOfStudyRelatedSeries, ... in very specific use-cases. * Fix extremely rare error when 2 threads are trying to create the same folder in the File Storage diff -r 00f650dc07bf -r 1c549458ea5d OrthancServer/Plugins/Engine/OrthancPluginDatabaseV4.cpp --- a/OrthancServer/Plugins/Engine/OrthancPluginDatabaseV4.cpp Wed Nov 27 15:00:58 2024 +0100 +++ b/OrthancServer/Plugins/Engine/OrthancPluginDatabaseV4.cpp Wed Nov 27 16:02:42 2024 +0100 @@ -1063,6 +1063,12 @@ uncompressedSize = response.update_and_get_statistics().total_uncompressed_size(); } + virtual void PerformDbHousekeeping() ORTHANC_OVERRIDE + { + DatabasePluginMessages::TransactionResponse response; + ExecuteTransaction(response, DatabasePluginMessages::OPERATION_PERFORM_DB_HOUSEKEEPING); + } + virtual bool LookupMetadata(std::string& target, int64_t& revision, int64_t id, @@ -1881,10 +1887,11 @@ dbCapabilities_.SetRevisionsSupport(systemInfo.supports_revisions()); dbCapabilities_.SetLabelsSupport(systemInfo.supports_labels()); dbCapabilities_.SetAtomicIncrementGlobalProperty(systemInfo.supports_increment_global_property()); - dbCapabilities_.SetUpdateAndGetStatistics(systemInfo.has_update_and_get_statistics()); + dbCapabilities_.SetHasUpdateAndGetStatistics(systemInfo.has_update_and_get_statistics()); dbCapabilities_.SetMeasureLatency(systemInfo.has_measure_latency()); dbCapabilities_.SetHasExtendedChanges(systemInfo.has_extended_changes()); dbCapabilities_.SetHasFindSupport(systemInfo.supports_find()); + dbCapabilities_.SetHasDbHousekeeping(systemInfo.has_db_housekeeping()); } open_ = true; diff -r 00f650dc07bf -r 1c549458ea5d OrthancServer/Plugins/Include/orthanc/OrthancDatabasePlugin.proto --- a/OrthancServer/Plugins/Include/orthanc/OrthancDatabasePlugin.proto Wed Nov 27 15:00:58 2024 +0100 +++ b/OrthancServer/Plugins/Include/orthanc/OrthancDatabasePlugin.proto Wed Nov 27 16:02:42 2024 +0100 @@ -160,6 +160,7 @@ bool has_measure_latency = 7; bool supports_find = 8; // New in Orthanc 1.12.5 bool has_extended_changes = 9; // New in Orthanc 1.12.5 + bool has_db_housekeeping = 10; // New in Orthanc 1.12.5 } } @@ -315,6 +316,7 @@ OPERATION_FIND = 50; // New in Orthanc 1.12.5 OPERATION_GET_CHANGES_EXTENDED = 51; // New in Orthanc 1.12.5 OPERATION_COUNT_RESOURCES = 52; // New in Orthanc 1.12.5 + OPERATION_PERFORM_DB_HOUSEKEEPING = 53; // New in Orthanc 1.12.5 } message Rollback { @@ -705,6 +707,13 @@ } } +message PerformDbHousekeeping { + message Request { + } + message Response { + } +} + message ClearMainDicomTags { message Request { int64 id = 1; @@ -1033,6 +1042,7 @@ Find.Request find = 150; GetChangesExtended.Request get_changes_extended = 151; Find.Request count_resources = 152; + PerformDbHousekeeping.Request perform_db_housekeeping = 153; } message TransactionResponse { @@ -1089,6 +1099,7 @@ repeated Find.Response find = 150; // One message per found resources GetChangesExtended.Response get_changes_extended = 151; CountResources.Response count_resources = 152; + PerformDbHousekeeping.Response perform_db_housekeeping = 153; } enum RequestType { diff -r 00f650dc07bf -r 1c549458ea5d OrthancServer/Resources/ImplementationNotes/DatabasesClassHierarchy.txt --- a/OrthancServer/Resources/ImplementationNotes/DatabasesClassHierarchy.txt Wed Nov 27 15:00:58 2024 +0100 +++ b/OrthancServer/Resources/ImplementationNotes/DatabasesClassHierarchy.txt Wed Nov 27 16:02:42 2024 +0100 @@ -21,7 +21,7 @@ - define it in OrthancPluginDatabaseV4 - define a NotImplemented default implementation in BaseDatabaseWrapper - optionally define it in SQLiteDatabaseWrapper if it can be implemented in SQLite -- very likely define it as a DbCapabilities in IDatabaseWrapper::DbCapabilities (e.g: Has/SetUpdateAndGetStatistics()) such that the Orthanc +- very likely define it as a DbCapabilities in IDatabaseWrapper::DbCapabilities (e.g: Has/SetHasUpdateAndGetStatistics()) such that the Orthanc core knows if it can use it or not. Then, in the orthanc-databases repo, you should: diff -r 00f650dc07bf -r 1c549458ea5d OrthancServer/Sources/Database/BaseDatabaseWrapper.cpp --- a/OrthancServer/Sources/Database/BaseDatabaseWrapper.cpp Wed Nov 27 15:00:58 2024 +0100 +++ b/OrthancServer/Sources/Database/BaseDatabaseWrapper.cpp Wed Nov 27 16:02:42 2024 +0100 @@ -46,6 +46,11 @@ throw OrthancException(ErrorCode_NotImplemented); // Not supported } + void BaseDatabaseWrapper::BaseTransaction::PerformDbHousekeeping() + { + throw OrthancException(ErrorCode_NotImplemented); // Not supported + } + void BaseDatabaseWrapper::BaseTransaction::GetChangesExtended(std::list& target /*out*/, bool& done /*out*/, int64_t since, diff -r 00f650dc07bf -r 1c549458ea5d OrthancServer/Sources/Database/BaseDatabaseWrapper.h --- a/OrthancServer/Sources/Database/BaseDatabaseWrapper.h Wed Nov 27 15:00:58 2024 +0100 +++ b/OrthancServer/Sources/Database/BaseDatabaseWrapper.h Wed Nov 27 16:02:42 2024 +0100 @@ -50,6 +50,8 @@ int64_t& compressedSize, int64_t& uncompressedSize) ORTHANC_OVERRIDE; + virtual void PerformDbHousekeeping() ORTHANC_OVERRIDE; + virtual void ExecuteCount(uint64_t& count, const FindRequest& request, const Capabilities& capabilities) ORTHANC_OVERRIDE; diff -r 00f650dc07bf -r 1c549458ea5d OrthancServer/Sources/Database/IDatabaseWrapper.h --- a/OrthancServer/Sources/Database/IDatabaseWrapper.h Wed Nov 27 15:00:58 2024 +0100 +++ b/OrthancServer/Sources/Database/IDatabaseWrapper.h Wed Nov 27 16:02:42 2024 +0100 @@ -56,6 +56,7 @@ bool hasMeasureLatency_; bool hasFindSupport_; bool hasExtendedChanges_; + bool hasDbHousekeeping_; public: Capabilities() : @@ -66,7 +67,8 @@ hasUpdateAndGetStatistics_(false), hasMeasureLatency_(false), hasFindSupport_(false), - hasExtendedChanges_(false) + hasExtendedChanges_(false), + hasDbHousekeeping_(false) { } @@ -120,7 +122,7 @@ return hasAtomicIncrementGlobalProperty_; } - void SetUpdateAndGetStatistics(bool value) + void SetHasUpdateAndGetStatistics(bool value) { hasUpdateAndGetStatistics_ = value; } @@ -130,6 +132,16 @@ return hasUpdateAndGetStatistics_; } + void SetHasDbHousekeeping(bool value) + { + hasDbHousekeeping_ = value; + } + + bool HasDbHousekeeping() const + { + return hasDbHousekeeping_; + } + void SetMeasureLatency(bool value) { hasMeasureLatency_ = value; @@ -401,6 +413,9 @@ int64_t to, uint32_t limit, const std::set& filterType) = 0; + + // New in Orthanc 1.12.5 + virtual void PerformDbHousekeeping() = 0; }; diff -r 00f650dc07bf -r 1c549458ea5d OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp --- a/OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp Wed Nov 27 15:00:58 2024 +0100 +++ b/OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp Wed Nov 27 16:02:42 2024 +0100 @@ -3476,4 +3476,24 @@ } } } + + void StatelessDatabaseOperations::PerformDbHousekeeping() + { + class Operations : public IReadWriteOperations + { + public: + Operations() + { + } + + virtual void Apply(ReadWriteTransaction& transaction) ORTHANC_OVERRIDE + { + transaction.PerformDbHousekeeping(); + } + }; + + Operations operations; + Apply(operations); + } + } diff -r 00f650dc07bf -r 1c549458ea5d OrthancServer/Sources/Database/StatelessDatabaseOperations.h --- a/OrthancServer/Sources/Database/StatelessDatabaseOperations.h Wed Nov 27 15:00:58 2024 +0100 +++ b/OrthancServer/Sources/Database/StatelessDatabaseOperations.h Wed Nov 27 16:02:42 2024 +0100 @@ -406,6 +406,11 @@ return transaction_.UpdateAndGetStatistics(patientsCount, studiesCount, seriesCount, instancesCount, compressedSize, uncompressedSize); } + void PerformDbHousekeeping() + { + return transaction_.PerformDbHousekeeping(); + } + void SetMetadata(int64_t id, MetadataType type, const std::string& value, @@ -735,5 +740,7 @@ void ExecuteCount(uint64_t& count, const FindRequest& request); + void PerformDbHousekeeping(); + }; } diff -r 00f650dc07bf -r 1c549458ea5d OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp --- a/OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp Wed Nov 27 15:00:58 2024 +0100 +++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestResources.cpp Wed Nov 27 16:02:42 2024 +0100 @@ -3101,18 +3101,8 @@ "Limit the number of reported resources", false) .SetRequestField(KEY_SINCE, RestApiCallDocumentation::Type_Number, "Show only the resources since the provided index (in conjunction with `Limit`)", false) - // .SetRequestField(KEY_REQUESTED_TAGS, RestApiCallDocumentation::Type_JsonListOfStrings, - // "A list of DICOM tags to include in the response (applicable only if \"Expand\" is set to true). " - // "The tags requested tags are returned in the 'RequestedTags' field in the response. " - // "Note that, if you are requesting tags that are not listed in the Main Dicom Tags stored in DB, building the response " - // "might be slow since Orthanc will need to access the DICOM files. If not specified, Orthanc will return " - // "all Main Dicom Tags to keep backward compatibility with Orthanc prior to 1.11.0.", false) .SetRequestField(KEY_ORDER_BY, RestApiCallDocumentation::Type_JsonListOfObjects, "Array of associative arrays containing the requested ordering (new in Orthanc 1.12.5)", true) - // .SetRequestField(KEY_RESPONSE_CONTENT, RestApiCallDocumentation::Type_JsonListOfStrings, - // "Defines the content of response for each returned resource. Allowed values are `MainDicomTags`, " - // "`Metadata`, `Children`, `Parent`, `Labels`, `Status`, `IsStable`, `Attachments`. " - // "(new in Orthanc 1.12.5)", true) .AddAnswerType(MimeType_Json, "JSON array containing either the Orthanc identifiers, or detailed information " "about the reported resources (if `Expand` argument is `true`)"); diff -r 00f650dc07bf -r 1c549458ea5d OrthancServer/Sources/ServerIndex.cpp --- a/OrthancServer/Sources/ServerIndex.cpp Wed Nov 27 15:00:58 2024 +0100 +++ b/OrthancServer/Sources/ServerIndex.cpp Wed Nov 27 16:02:42 2024 +0100 @@ -266,19 +266,19 @@ } }; - void ServerIndex::UpdateStatisticsThread(ServerIndex* that, - unsigned int threadSleepGranularityMilliseconds) + void ServerIndex::PerformDbHouskeeping(ServerIndex* that, + unsigned int threadSleepGranularityMilliseconds) { - Logging::SetCurrentThreadName("DB-STATS"); + Logging::SetCurrentThreadName("DB-HK"); - static const unsigned int SLEEP_SECONDS = 10; + static const unsigned int SLEEP_SECONDS = 1; if (threadSleepGranularityMilliseconds > 1000) { throw OrthancException(ErrorCode_ParameterOutOfRange); } - LOG(INFO) << "Starting the update statistics thread (sleep = " << SLEEP_SECONDS << " seconds)"; + LOG(INFO) << "Starting the DB Housekeeping thread (sleep = " << SLEEP_SECONDS << " seconds)"; unsigned int count = 0; unsigned int countThreshold = (1000 * SLEEP_SECONDS) / threadSleepGranularityMilliseconds; @@ -290,14 +290,13 @@ if (count >= countThreshold) { - uint64_t diskSize, uncompressedSize, countPatients, countStudies, countSeries, countInstances; - that->GetGlobalStatistics(diskSize, uncompressedSize, countPatients, countStudies, countSeries, countInstances); + that->PerformDbHousekeeping(); count = 0; } } - LOG(INFO) << "Stopping the update statistics thread"; + LOG(INFO) << "Stopping the DB Housekeeping thread"; } void ServerIndex::FlushThread(ServerIndex* that, @@ -377,18 +376,17 @@ } } - // For some DB plugins that implements the UpdateAndGetStatistics function, updating - // the statistics can take quite some time if you have not done it for a long time - // -> make sure they are updated at regular interval - if (GetDatabaseCapabilities().HasUpdateAndGetStatistics()) + // For some DB plugins, some housekeeping might be required at regular interval + // like computing the statistics or update some tables after an upgrade + if (GetDatabaseCapabilities().HasDbHousekeeping()) { if (readOnly) { - LOG(WARNING) << "READ-ONLY SYSTEM: not starting the UpdateStatisticsThread"; + LOG(WARNING) << "READ-ONLY SYSTEM: not starting the DB Housekeeping thread"; } else { - updateStatisticsThread_ = boost::thread(UpdateStatisticsThread, this, threadSleepGranularityMilliseconds); + dbHousekeepingThread_ = boost::thread(PerformDbHouskeeping, this, threadSleepGranularityMilliseconds); } } @@ -425,9 +423,9 @@ flushThread_.join(); } - if (updateStatisticsThread_.joinable()) + if (dbHousekeepingThread_.joinable()) { - updateStatisticsThread_.join(); + dbHousekeepingThread_.join(); } if (unstableResourcesMonitorThread_.joinable()) diff -r 00f650dc07bf -r 1c549458ea5d OrthancServer/Sources/ServerIndex.h --- a/OrthancServer/Sources/ServerIndex.h Wed Nov 27 15:00:58 2024 +0100 +++ b/OrthancServer/Sources/ServerIndex.h Wed Nov 27 16:02:42 2024 +0100 @@ -42,7 +42,7 @@ bool done_; boost::mutex monitoringMutex_; boost::thread flushThread_; - boost::thread updateStatisticsThread_; + boost::thread dbHousekeepingThread_; boost::thread unstableResourcesMonitorThread_; LeastRecentlyUsedIndex, UnstableResourcePayload> unstableResources_; @@ -55,8 +55,8 @@ static void FlushThread(ServerIndex* that, unsigned int threadSleep); - static void UpdateStatisticsThread(ServerIndex* that, - unsigned int threadSleep); + static void PerformDbHouskeeping(ServerIndex* that, + unsigned int threadSleep); static void UnstableResourcesMonitorThread(ServerIndex* that, unsigned int threadSleep);