# HG changeset patch # User Sebastien Jodogne # Date 1617871572 -7200 # Node ID 2684544ff03ce41f4cc51507d1cfe0345abfe2ec # Parent 4982733a4e39cac8e4255f422a06bfccda0b19c8 maximum number of database retries for writer collisions is now set by the plugins diff -r 4982733a4e39 -r 2684544ff03c OrthancServer/Plugins/Engine/OrthancPlugins.cpp --- a/OrthancServer/Plugins/Engine/OrthancPlugins.cpp Wed Apr 07 10:41:39 2021 +0200 +++ b/OrthancServer/Plugins/Engine/OrthancPlugins.cpp Thu Apr 08 10:46:12 2021 +0200 @@ -1212,6 +1212,7 @@ std::unique_ptr databaseV3_; // New in Orthanc 1.9.2 PluginsErrorDictionary dictionary_; std::string databaseServerIdentifier_; // New in Orthanc 1.9.2 + unsigned int maxDatabaseRetries_; // New in Orthanc 1.9.2 PImpl(const std::string& databaseServerIdentifier) : context_(NULL), @@ -1219,7 +1220,8 @@ worklistCallback_(NULL), argc_(1), argv_(NULL), - databaseServerIdentifier_(databaseServerIdentifier) + databaseServerIdentifier_(databaseServerIdentifier), + maxDatabaseRetries_(0) { memset(&moveCallbacks_, 0, sizeof(moveCallbacks_)); } @@ -5087,6 +5089,7 @@ { pimpl_->databaseV3_.reset(new OrthancPluginDatabaseV3(plugin, GetErrorDictionary(), p.backend, p.backendSize, p.database, pimpl_->databaseServerIdentifier_)); + pimpl_->maxDatabaseRetries_ = p.maxDatabaseRetries; } else { @@ -5754,4 +5757,11 @@ boost::recursive_mutex::scoped_lock lock(pimpl_->invokeServiceMutex_); return (pimpl_->authorizationTokens_.find(token) != pimpl_->authorizationTokens_.end()); } + + + unsigned int OrthancPlugins::GetMaxDatabaseRetries() const + { + boost::recursive_mutex::scoped_lock lock(pimpl_->invokeServiceMutex_); + return pimpl_->maxDatabaseRetries_; + } } diff -r 4982733a4e39 -r 2684544ff03c OrthancServer/Plugins/Engine/OrthancPlugins.h --- a/OrthancServer/Plugins/Engine/OrthancPlugins.h Wed Apr 07 10:41:39 2021 +0200 +++ b/OrthancServer/Plugins/Engine/OrthancPlugins.h Thu Apr 08 10:46:12 2021 +0200 @@ -389,6 +389,8 @@ // New in Orthanc 1.8.1 (cf. "OrthancPluginGenerateRestApiAuthorizationToken()") bool IsValidAuthorizationToken(const std::string& token) const; + + unsigned int GetMaxDatabaseRetries() const; }; } diff -r 4982733a4e39 -r 2684544ff03c OrthancServer/Plugins/Include/orthanc/OrthancCDatabasePlugin.h --- a/OrthancServer/Plugins/Include/orthanc/OrthancCDatabasePlugin.h Wed Apr 07 10:41:39 2021 +0200 +++ b/OrthancServer/Plugins/Include/orthanc/OrthancCDatabasePlugin.h Thu Apr 08 10:46:12 2021 +0200 @@ -1320,6 +1320,7 @@ { const OrthancPluginDatabaseBackendV3* backend; uint32_t backendSize; + uint32_t maxDatabaseRetries; void* database; } _OrthancPluginRegisterDatabaseBackendV3; @@ -1328,6 +1329,7 @@ OrthancPluginContext* context, const OrthancPluginDatabaseBackendV3* backend, uint32_t backendSize, + uint32_t maxDatabaseRetries, /* To handle "OrthancPluginErrorCode_DatabaseCannotSerialize" */ void* database) { _OrthancPluginRegisterDatabaseBackendV3 params; @@ -1340,6 +1342,7 @@ memset(¶ms, 0, sizeof(params)); params.backend = backend; params.backendSize = sizeof(OrthancPluginDatabaseBackendV3); + params.maxDatabaseRetries = maxDatabaseRetries; params.database = database; return context->InvokeService(context, _OrthancPluginService_RegisterDatabaseBackendV3, ¶ms); diff -r 4982733a4e39 -r 2684544ff03c OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp --- a/OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp Wed Apr 07 10:41:39 2021 +0200 +++ b/OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp Thu Apr 08 10:46:12 2021 +0200 @@ -588,6 +588,8 @@ void StatelessDatabaseOperations::ApplyInternal(IReadOnlyOperations* readOperations, IReadWriteOperations* writeOperations) { + boost::shared_lock lock(mutex_); // To protect "factory_" and "maxRetries_" + if ((readOperations == NULL && writeOperations == NULL) || (readOperations != NULL && writeOperations != NULL)) { @@ -599,7 +601,7 @@ throw OrthancException(ErrorCode_BadSequenceOfCalls, "No transaction context was provided"); } - unsigned int count = 0; + unsigned int attempt = 0; for (;;) { @@ -638,14 +640,16 @@ { if (e.GetErrorCode() == ErrorCode_DatabaseCannotSerialize) { - if (count >= maxRetries_) + if (attempt >= maxRetries_) { throw; } else { - count++; - boost::this_thread::sleep(boost::posix_time::milliseconds(100 * count)); + attempt++; + + // The "rand()" adds some jitter to de-synchronize writers + boost::this_thread::sleep(boost::posix_time::milliseconds(50 * attempt + 5 * (rand() % 10))); } } else @@ -659,9 +663,9 @@ StatelessDatabaseOperations::StatelessDatabaseOperations(IDatabaseWrapper& db) : db_(db), - maxRetries_(10), mainDicomTagsRegistry_(new MainDicomTagsRegistry), - hasFlushToDisk_(db.HasFlushToDisk()) + hasFlushToDisk_(db.HasFlushToDisk()), + maxRetries_(0) { } @@ -681,6 +685,8 @@ void StatelessDatabaseOperations::SetTransactionContextFactory(ITransactionContextFactory* factory) { + boost::unique_lock lock(mutex_); + if (factory == NULL) { throw OrthancException(ErrorCode_NullPointer); @@ -696,6 +702,13 @@ } + void StatelessDatabaseOperations::SetMaxDatabaseRetries(unsigned int maxRetries) + { + boost::unique_lock lock(mutex_); + maxRetries_ = maxRetries; + } + + void StatelessDatabaseOperations::Apply(IReadOnlyOperations& operations) { ApplyInternal(&operations, NULL); diff -r 4982733a4e39 -r 2684544ff03c OrthancServer/Sources/Database/StatelessDatabaseOperations.h --- a/OrthancServer/Sources/Database/StatelessDatabaseOperations.h Wed Apr 07 10:41:39 2021 +0200 +++ b/OrthancServer/Sources/Database/StatelessDatabaseOperations.h Thu Apr 08 10:46:12 2021 +0200 @@ -39,6 +39,7 @@ #include "../DicomInstanceOrigin.h" #include +#include namespace Orthanc @@ -408,10 +409,13 @@ class Transaction; IDatabaseWrapper& db_; + boost::shared_ptr mainDicomTagsRegistry_; // "shared_ptr" because of PImpl + bool hasFlushToDisk_; + + // Mutex to protect the configuration options + boost::shared_mutex mutex_; std::unique_ptr factory_; unsigned int maxRetries_; - boost::shared_ptr mainDicomTagsRegistry_; // "shared_ptr" because of PImpl - bool hasFlushToDisk_; void NormalizeLookup(std::vector& target, const DatabaseLookup& source, @@ -428,6 +432,10 @@ explicit StatelessDatabaseOperations(IDatabaseWrapper& database); void SetTransactionContextFactory(ITransactionContextFactory* factory /* takes ownership */); + + // Only used to handle "ErrorCode_DatabaseCannotSerialize" in the + // case of collision between multiple writers + void SetMaxDatabaseRetries(unsigned int maxRetries); // It is assumed that "GetDatabaseVersion()" can run out of a // database transaction diff -r 4982733a4e39 -r 2684544ff03c OrthancServer/Sources/main.cpp --- a/OrthancServer/Sources/main.cpp Wed Apr 07 10:41:39 2021 +0200 +++ b/OrthancServer/Sources/main.cpp Thu Apr 08 10:46:12 2021 +0200 @@ -1354,6 +1354,7 @@ { plugins_->SetServerContext(context_); context_.SetPlugins(*plugins_); + context_.GetIndex().SetMaxDatabaseRetries(plugins_->GetMaxDatabaseRetries()); } #endif }