changeset 4613:2684544ff03c db-changes

maximum number of database retries for writer collisions is now set by the plugins
author Sebastien Jodogne <s.jodogne@gmail.com>
date Thu, 08 Apr 2021 10:46:12 +0200
parents 4982733a4e39
children 67d112ef680f
files OrthancServer/Plugins/Engine/OrthancPlugins.cpp OrthancServer/Plugins/Engine/OrthancPlugins.h OrthancServer/Plugins/Include/orthanc/OrthancCDatabasePlugin.h OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp OrthancServer/Sources/Database/StatelessDatabaseOperations.h OrthancServer/Sources/main.cpp
diffstat 6 files changed, 46 insertions(+), 9 deletions(-) [+]
line wrap: on
line diff
--- 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<OrthancPluginDatabaseV3>  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_;
+  }
 }
--- 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;
   };
 }
 
--- 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(&params, 0, sizeof(params));
     params.backend = backend;
     params.backendSize = sizeof(OrthancPluginDatabaseBackendV3);
+    params.maxDatabaseRetries = maxDatabaseRetries;
     params.database = database;
 
     return context->InvokeService(context, _OrthancPluginService_RegisterDatabaseBackendV3, &params);
--- 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<boost::shared_mutex> 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<boost::shared_mutex> lock(mutex_);
+
     if (factory == NULL)
     {
       throw OrthancException(ErrorCode_NullPointer);
@@ -696,6 +702,13 @@
   }
     
 
+  void StatelessDatabaseOperations::SetMaxDatabaseRetries(unsigned int maxRetries)
+  {
+    boost::unique_lock<boost::shared_mutex> lock(mutex_);
+    maxRetries_ = maxRetries;
+  }
+  
+
   void StatelessDatabaseOperations::Apply(IReadOnlyOperations& operations)
   {
     ApplyInternal(&operations, NULL);
--- 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 <boost/shared_ptr.hpp>
+#include <boost/thread/shared_mutex.hpp>
 
 
 namespace Orthanc
@@ -408,10 +409,13 @@
     class Transaction;
 
     IDatabaseWrapper&                            db_;
+    boost::shared_ptr<MainDicomTagsRegistry>     mainDicomTagsRegistry_;  // "shared_ptr" because of PImpl
+    bool                                         hasFlushToDisk_;
+
+    // Mutex to protect the configuration options
+    boost::shared_mutex                          mutex_;
     std::unique_ptr<ITransactionContextFactory>  factory_;
     unsigned int                                 maxRetries_;
-    boost::shared_ptr<MainDicomTagsRegistry>     mainDicomTagsRegistry_;  // "shared_ptr" because of PImpl
-    bool                                         hasFlushToDisk_;
 
     void NormalizeLookup(std::vector<DatabaseConstraint>& 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
--- 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
     }