changeset 6110:e08274ea166e attach-custom-data

queue size + OrthancPlugins::Queue
author Alain Mazy <am@orthanc.team>
date Thu, 15 May 2025 18:49:47 +0200
parents 370479295564
children 91dde382f780
files OrthancServer/Plugins/Engine/OrthancPluginDatabase.cpp OrthancServer/Plugins/Engine/OrthancPluginDatabaseV3.cpp OrthancServer/Plugins/Engine/OrthancPluginDatabaseV4.cpp OrthancServer/Plugins/Engine/OrthancPlugins.cpp OrthancServer/Plugins/Engine/OrthancPlugins.h OrthancServer/Plugins/Include/orthanc/OrthancCPlugin.h OrthancServer/Plugins/Include/orthanc/OrthancDatabasePlugin.proto OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.h OrthancServer/Sources/Database/IDatabaseWrapper.h OrthancServer/Sources/Database/SQLiteDatabaseWrapper.cpp OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp OrthancServer/Sources/Database/StatelessDatabaseOperations.h OrthancServer/Sources/OrthancRestApi/OrthancRestSystem.cpp
diffstat 14 files changed, 269 insertions(+), 67 deletions(-) [+]
line wrap: on
line diff
--- a/OrthancServer/Plugins/Engine/OrthancPluginDatabase.cpp	Thu May 15 16:01:57 2025 +0200
+++ b/OrthancServer/Plugins/Engine/OrthancPluginDatabase.cpp	Thu May 15 18:49:47 2025 +0200
@@ -1492,6 +1492,12 @@
       throw OrthancException(ErrorCode_InternalError);  // Not supported
     }
 
+    virtual void GetQueueSize(uint64_t& size,
+                              const std::string& queueId) ORTHANC_OVERRIDE
+    {
+      throw OrthancException(ErrorCode_InternalError);  // Not supported
+    }
+
     virtual bool GetAttachment(FileInfo& attachment,
                                int64_t& revision,
                                const std::string& attachmentUuid) ORTHANC_OVERRIDE
--- a/OrthancServer/Plugins/Engine/OrthancPluginDatabaseV3.cpp	Thu May 15 16:01:57 2025 +0200
+++ b/OrthancServer/Plugins/Engine/OrthancPluginDatabaseV3.cpp	Thu May 15 18:49:47 2025 +0200
@@ -1104,6 +1104,12 @@
       throw OrthancException(ErrorCode_InternalError);  // Not supported
     }
 
+    virtual void GetQueueSize(uint64_t& size,
+                              const std::string& queueId) ORTHANC_OVERRIDE
+    {
+      throw OrthancException(ErrorCode_InternalError);  // Not supported
+    }
+
     virtual bool GetAttachment(FileInfo& attachment,
                                int64_t& revision,
                                const std::string& attachmentUuid) ORTHANC_OVERRIDE
--- a/OrthancServer/Plugins/Engine/OrthancPluginDatabaseV4.cpp	Thu May 15 16:01:57 2025 +0200
+++ b/OrthancServer/Plugins/Engine/OrthancPluginDatabaseV4.cpp	Thu May 15 18:49:47 2025 +0200
@@ -1853,14 +1853,20 @@
     virtual void EnqueueValue(const std::string& queueId,
                               const std::string& value) ORTHANC_OVERRIDE
     {
-      throw OrthancException(ErrorCode_InternalError);  // Not supported
+      throw OrthancException(ErrorCode_InternalError);  // TODO_ATTACH_CUSTOM_DATA
     }
 
     virtual bool DequeueValue(std::string& value,
                               const std::string& queueId,
                               QueueOrigin origin) ORTHANC_OVERRIDE
     {
-      throw OrthancException(ErrorCode_InternalError);  // Not supported
+      throw OrthancException(ErrorCode_InternalError);  // TODO_ATTACH_CUSTOM_DATA
+    }
+
+    virtual void GetQueueSize(uint64_t& size,
+                              const std::string& queueId) ORTHANC_OVERRIDE
+    {
+      throw OrthancException(ErrorCode_InternalError);  // TODO_ATTACH_CUSTOM_DATA
     }
 
   };
@@ -1953,8 +1959,8 @@
       dbCapabilities_.SetMeasureLatency(systemInfo.has_measure_latency());
       dbCapabilities_.SetHasExtendedChanges(systemInfo.has_extended_changes());
       dbCapabilities_.SetHasFindSupport(systemInfo.supports_find());
-      dbCapabilities_.SetHasKeyValueStore(systemInfo.has_key_value_store());
-      dbCapabilities_.SetHasQueue(systemInfo.has_queue());
+      dbCapabilities_.SetKeyValueStoresSupport(systemInfo.supports_key_value_stores());
+      dbCapabilities_.SetQueuesSupport(systemInfo.supports_queues());
     }
 
     open_ = true;
--- a/OrthancServer/Plugins/Engine/OrthancPlugins.cpp	Thu May 15 16:01:57 2025 +0200
+++ b/OrthancServer/Plugins/Engine/OrthancPlugins.cpp	Thu May 15 18:49:47 2025 +0200
@@ -4722,11 +4722,11 @@
     lock.GetContext().GetIndex().UpdateAttachmentCustomData(parameters.attachmentUuid, customData);
   }
 
-  bool OrthancPlugins::HasKeyValueStore()
+  bool OrthancPlugins::HasKeyValueStoresSupport()
   {
     PImpl::ServerContextReference lock(*pimpl_);
 
-    return lock.GetContext().GetIndex().HasKeyValueStore();
+    return lock.GetContext().GetIndex().HasKeyValueStoresSupport();
   }
 
   void OrthancPlugins::ApplyStoreKeyValue(const _OrthancPluginStoreKeyValue& parameters)
@@ -4766,11 +4766,11 @@
     CopyStringList(*(parameters.keys), keys);
   }
 
-  bool OrthancPlugins::HasQueue()
+  bool OrthancPlugins::HasQueuesSupport()
   {
     PImpl::ServerContextReference lock(*pimpl_);
 
-    return lock.GetContext().GetIndex().HasQueue();
+    return lock.GetContext().GetIndex().HasQueuesSupport();
   }
 
   void OrthancPlugins::ApplyEnqueueValue(const _OrthancPluginEnqueueValue& parameters)
@@ -4781,7 +4781,7 @@
     lock.GetContext().GetIndex().EnqueueValue(parameters.queueId, value);
   }
 
-  bool OrthancPlugins::ApplyDequeueValue(const _OrthancPluginDequeueValue& parameters)
+  void OrthancPlugins::ApplyDequeueValue(const _OrthancPluginDequeueValue& parameters)
   {
     PImpl::ServerContextReference lock(*pimpl_);
 
@@ -4790,12 +4790,14 @@
     if (lock.GetContext().GetIndex().DequeueValue(value, parameters.queueId, Plugins::Convert(parameters.origin)))
     {
       CopyToMemoryBuffer(*parameters.value, value.size() > 0 ? value.c_str() : NULL, value.size());
-      return true;
-    }
-    else
-    {
-      return false;
-    }
+    }
+  }
+
+  void OrthancPlugins::ApplyGetQueueSize(const _OrthancPluginGetQueueSize& parameters)
+  {
+    PImpl::ServerContextReference lock(*pimpl_);
+
+    lock.GetContext().GetIndex().GetQueueSize(*parameters.size, parameters.queueId);
   }
 
   void OrthancPlugins::ApplyLoadDicomInstance(const _OrthancPluginLoadDicomInstance& params)
@@ -5892,7 +5894,7 @@
 
       case _OrthancPluginService_StoreKeyValue:
       {
-        if (!HasKeyValueStore())
+        if (!HasKeyValueStoresSupport())
         {
           LOG(ERROR) << "The DB engine does not support Key Value Store";
           return false;
@@ -5906,7 +5908,7 @@
 
       case _OrthancPluginService_DeleteKeyValue:
       {
-        if (!HasKeyValueStore())
+        if (!HasKeyValueStoresSupport())
         {
           LOG(ERROR) << "The DB engine does not support Key Value Store";
           return false;
@@ -5920,7 +5922,7 @@
 
       case _OrthancPluginService_GetKeyValue:
       {
-        if (!HasKeyValueStore())
+        if (!HasKeyValueStoresSupport())
         {
           LOG(ERROR) << "The DB engine does not support Key Value Store";
           return false;
@@ -5934,7 +5936,7 @@
 
       case _OrthancPluginService_ListKeys:
       {
-        if (!HasKeyValueStore())
+        if (!HasKeyValueStoresSupport())
         {
           LOG(ERROR) << "The DB engine does not support Key Value Store";
           return false;
@@ -5948,7 +5950,7 @@
 
       case _OrthancPluginService_EnqueueValue:
       {
-        if (!HasQueue())
+        if (!HasQueuesSupport())
         {
           LOG(ERROR) << "The DB engine does not support Queues";
           return false;
@@ -5962,7 +5964,7 @@
 
       case _OrthancPluginService_DequeueValue:
       {
-        if (!HasQueue())
+        if (!HasQueuesSupport())
         {
           LOG(ERROR) << "The DB engine does not support Queues";
           return false;
@@ -5970,14 +5972,22 @@
 
         const _OrthancPluginDequeueValue& p =
           *reinterpret_cast<const _OrthancPluginDequeueValue*>(parameters);
-        if (ApplyDequeueValue(p))
-        {
-          return true;
-        }
-        else
-        {
-          throw OrthancException(ErrorCode_UnknownResource);
-        }
+        ApplyDequeueValue(p);
+        return true;
+      }
+
+      case _OrthancPluginService_GetQueueSize:
+      {
+        if (!HasQueuesSupport())
+        {
+          LOG(ERROR) << "The DB engine does not support Queues";
+          return false;
+        }
+
+        const _OrthancPluginGetQueueSize& p =
+          *reinterpret_cast<const _OrthancPluginGetQueueSize*>(parameters);
+        ApplyGetQueueSize(p);
+        return true;
       }
 
       default:
--- a/OrthancServer/Plugins/Engine/OrthancPlugins.h	Thu May 15 16:01:57 2025 +0200
+++ b/OrthancServer/Plugins/Engine/OrthancPlugins.h	Thu May 15 18:49:47 2025 +0200
@@ -229,7 +229,7 @@
 
     void ApplyUpdateAttachmentCustomData(const _OrthancPluginUpdateAttachmentCustomData& parameters);
 
-    bool HasKeyValueStore();
+    bool HasKeyValueStoresSupport();
 
     void ApplyStoreKeyValue(const _OrthancPluginStoreKeyValue& parameters);
 
@@ -239,11 +239,13 @@
 
     void ApplyListKeys(const _OrthancPluginListKeys& parameters);
 
-    bool HasQueue();
+    bool HasQueuesSupport();
 
     void ApplyEnqueueValue(const _OrthancPluginEnqueueValue& parameters);
 
-    bool ApplyDequeueValue(const _OrthancPluginDequeueValue& parameters);
+    void ApplyDequeueValue(const _OrthancPluginDequeueValue& parameters);
+
+    void ApplyGetQueueSize(const _OrthancPluginGetQueueSize& parameters);
 
     void ComputeHash(_OrthancPluginService service,
                      const void* parameters);
--- a/OrthancServer/Plugins/Include/orthanc/OrthancCPlugin.h	Thu May 15 16:01:57 2025 +0200
+++ b/OrthancServer/Plugins/Include/orthanc/OrthancCPlugin.h	Thu May 15 18:49:47 2025 +0200
@@ -476,8 +476,9 @@
     _OrthancPluginService_ListKeys = 50,                            /* New in Orthanc 1.12.99 */
     _OrthancPluginService_EnqueueValue = 51,                        /* New in Orthanc 1.12.99 */
     _OrthancPluginService_DequeueValue = 52,                        /* New in Orthanc 1.12.99 */
-    _OrthancPluginService_GetAttachmentCustomData = 53,             /* New in Orthanc 1.12.99 */
-    _OrthancPluginService_UpdateAttachmentCustomData = 54,          /* New in Orthanc 1.12.99 */
+    _OrthancPluginService_GetQueueSize = 53,                        /* New in Orthanc 1.12.99 */
+    _OrthancPluginService_GetAttachmentCustomData = 54,             /* New in Orthanc 1.12.99 */
+    _OrthancPluginService_UpdateAttachmentCustomData = 55,          /* New in Orthanc 1.12.99 */
 
 
     /* Registration of callbacks */
@@ -10060,7 +10061,7 @@
    * @brief Dequeue a value from a queue.
    *
    * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
-   * @param queueId A unique identifier identifying both the plugin and the store
+   * @param queueId A unique identifier identifying both the plugin and the queue
    * @param origin The extremity of the queue the value is dequeue from (back for LIFO or front for FIFO)
    * @param value The value retrieved from the queue
    **/
@@ -10078,6 +10079,31 @@
     return context->InvokeService(context, _OrthancPluginService_DequeueValue, &params);
   }
 
+  typedef struct
+  {
+    const char*                   queueId;
+    uint64_t*                     size;
+  } _OrthancPluginGetQueueSize;
+  
+  /**
+   * @brief Get the number of elements in a queue.
+   *
+   * @param context The Orthanc plugin context, as received by OrthancPluginInitialize().
+   * @param queueId A unique identifier identifying both the plugin and the queue
+   * @param size The number of elements in the queue
+   **/
+  ORTHANC_PLUGIN_INLINE OrthancPluginErrorCode OrthancPluginGetQueueSize(
+    OrthancPluginContext*         context,
+    const char*                   queueId, /* in */
+    uint64_t*                     size /* out */)
+  {
+    _OrthancPluginGetQueueSize params;
+    params.queueId = queueId;
+    params.size = size;
+
+    return context->InvokeService(context, _OrthancPluginService_GetQueueSize, &params);
+  }
+
 #ifdef  __cplusplus
 }
 #endif
--- a/OrthancServer/Plugins/Include/orthanc/OrthancDatabasePlugin.proto	Thu May 15 16:01:57 2025 +0200
+++ b/OrthancServer/Plugins/Include/orthanc/OrthancDatabasePlugin.proto	Thu May 15 18:49:47 2025 +0200
@@ -172,8 +172,8 @@
     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_key_value_store = 10;  // New in Orthanc 1.12.99
-    bool has_queue = 11;            // New in Orthanc 1.12.99
+    bool supports_key_value_stores = 10;  // New in Orthanc 1.12.99
+    bool supports_queues = 11;            // New in Orthanc 1.12.99
   }
 }
 
--- a/OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp	Thu May 15 16:01:57 2025 +0200
+++ b/OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.cpp	Thu May 15 18:49:47 2025 +0200
@@ -4387,7 +4387,78 @@
       return limit == 0 || (jsonKeys.size() < limit);
     }
     
+#if HAS_ORTHANC_EXCEPTION == 1
     throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "Unable to list keys");
+#else
+    ORTHANC_PLUGINS_LOG_ERROR("Unable to list keys");
+    ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
+#endif
+  }
+
+  Queue::Queue(const std::string& queueId)
+  : queueId_(queueId)
+  {
+  }
+
+  void Queue::PushBack(const std::string& value)
+  {
+    OrthancPluginEnqueueValue(OrthancPlugins::GetGlobalContext(),
+                              queueId_.c_str(),
+                              value.c_str(),
+                              value.size());
+  }
+
+  bool Queue::PopInternal(std::string& value, OrthancPluginQueueOrigin origin)
+  {
+    OrthancPlugins::MemoryBuffer valueBuffer;
+    OrthancPluginErrorCode ret = OrthancPluginDequeueValue(OrthancPlugins::GetGlobalContext(),
+                                                           queueId_.c_str(),
+                                                           origin,
+                                                           *valueBuffer);
+
+    if (ret == OrthancPluginErrorCode_Success)
+    {
+      if (!valueBuffer.IsEmpty())
+      {
+        value.assign(valueBuffer.GetData(), valueBuffer.GetSize());
+        return true;
+      }
+      else
+      {
+        return false;
+      }
+    }
+    
+    return false;
+  }
+
+  bool Queue::PopBack(std::string& value)
+  {
+    return PopInternal(value, OrthancPluginQueueOrigin_Back);
+  }
+
+  bool Queue::PopFront(std::string& value)
+  {
+    return PopInternal(value, OrthancPluginQueueOrigin_Front);
+  }
+
+  uint64_t Queue::GetSize()
+  {
+    uint64_t size = 0;
+    OrthancPluginErrorCode ret = OrthancPluginGetQueueSize(OrthancPlugins::GetGlobalContext(),
+                                                           queueId_.c_str(),
+                                                           &size);
+    if (ret != OrthancPluginErrorCode_Success)
+    {
+#if HAS_ORTHANC_EXCEPTION == 1
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "Unable get queue size");
+#else
+      ORTHANC_PLUGINS_LOG_ERROR("Unable get queue size");
+      ORTHANC_PLUGINS_THROW_EXCEPTION(InternalError);
+#endif
+    }
+
+    return size;
   }
 #endif
 
--- a/OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.h	Thu May 15 16:01:57 2025 +0200
+++ b/OrthancServer/Plugins/Samples/Common/OrthancPluginCppWrapper.h	Thu May 15 18:49:47 2025 +0200
@@ -1623,6 +1623,26 @@
 
     bool GetAllKeys(std::list<std::string>& keys, uint64_t since, uint64_t limit);
   };
+
+  class Queue : public boost::noncopyable
+  {
+  private:
+    std::string queueId_;
+
+    bool PopInternal(std::string& value, OrthancPluginQueueOrigin origin);
+
+  public:
+    Queue(const std::string& queueId);
+
+    void PushBack(const std::string& value);
+
+    bool PopFront(std::string& value);
+
+    bool PopBack(std::string& value);
+
+    uint64_t GetSize();
+  };
+
 #endif
 
 }
--- a/OrthancServer/Sources/Database/IDatabaseWrapper.h	Thu May 15 16:01:57 2025 +0200
+++ b/OrthancServer/Sources/Database/IDatabaseWrapper.h	Thu May 15 18:49:47 2025 +0200
@@ -57,8 +57,8 @@
       bool hasFindSupport_;
       bool hasExtendedChanges_;
       bool hasAttachmentCustomDataSupport_;
-      bool hasKeyValueStore_;
-      bool hasQueue_;
+      bool hasKeyValueStoresSupport_;
+      bool hasQueuesSupport_;
 
     public:
       Capabilities() :
@@ -71,8 +71,8 @@
         hasFindSupport_(false),
         hasExtendedChanges_(false),
         hasAttachmentCustomDataSupport_(false),
-        hasKeyValueStore_(false),
-        hasQueue_(false)
+        hasKeyValueStoresSupport_(false),
+        hasQueuesSupport_(false)
       {
       }
 
@@ -166,24 +166,24 @@
         return hasFindSupport_;
       }
 
-      void SetHasKeyValueStore(bool value)
+      void SetKeyValueStoresSupport(bool value)
       {
-        hasKeyValueStore_ = value;
+        hasKeyValueStoresSupport_ = value;
       }
 
-      bool HasKeyValueStore() const
+      bool HasKeyValueStoresSupport() const
       {
-        return hasKeyValueStore_;
+        return hasKeyValueStoresSupport_;
       }
 
-      void SetHasQueue(bool value)
+      void SetQueuesSupport(bool value)
       {
-        hasQueue_ = value;
+        hasQueuesSupport_ = value;
       }
 
-      bool HasQueue() const
+      bool HasQueuesSupport() const
       {
-        return hasQueue_;
+        return hasQueuesSupport_;
       }
     };
 
@@ -463,6 +463,9 @@
                                 const std::string& queueId,
                                 QueueOrigin origin) = 0;
 
+      virtual void GetQueueSize(uint64_t& size,
+                                const std::string& queueId) = 0;
+
     };
 
 
--- a/OrthancServer/Sources/Database/SQLiteDatabaseWrapper.cpp	Thu May 15 16:01:57 2025 +0200
+++ b/OrthancServer/Sources/Database/SQLiteDatabaseWrapper.cpp	Thu May 15 18:49:47 2025 +0200
@@ -2222,10 +2222,10 @@
       switch (origin)
       {
         case QueueOrigin_Front:
-          s.reset(new SQLite::Statement(db_, SQLITE_FROM_HERE, "SELECT id, value FROM KeyValueStores WHERE queueId=? ORDER BY id ASC LIMIT 1"));
+          s.reset(new SQLite::Statement(db_, SQLITE_FROM_HERE, "SELECT id, value FROM Queues WHERE queueId=? ORDER BY id ASC LIMIT 1"));
           break;
         case QueueOrigin_Back:
-          s.reset(new SQLite::Statement(db_, SQLITE_FROM_HERE, "SELECT id, value FROM KeyValueStores WHERE queueId=? ORDER BY id DESC LIMIT 1"));
+          s.reset(new SQLite::Statement(db_, SQLITE_FROM_HERE, "SELECT id, value FROM Queues WHERE queueId=? ORDER BY id DESC LIMIT 1"));
           break;
         default:
           throw OrthancException(ErrorCode_InternalError);
@@ -2251,6 +2251,15 @@
       }    
     }
 
+    // New in Orthanc 1.12.99
+    virtual void GetQueueSize(uint64_t& size,
+                              const std::string& queueId) ORTHANC_OVERRIDE
+    {
+      SQLite::Statement s(db_, SQLITE_FROM_HERE, "SELECT COUNT(*) FROM Queues WHERE queueId=?");
+      s.BindString(0, queueId);
+      s.Step();
+      size = s.ColumnInt64(0);
+    }
 
   };
 
@@ -2451,8 +2460,8 @@
     dbCapabilities_.SetLabelsSupport(true);
     dbCapabilities_.SetHasExtendedChanges(true);
     dbCapabilities_.SetHasFindSupport(HasIntegratedFind());
-    dbCapabilities_.SetHasKeyValueStore(true);
-    dbCapabilities_.SetHasQueue(true);
+    dbCapabilities_.SetKeyValueStoresSupport(true);
+    dbCapabilities_.SetQueuesSupport(true);
     db_.Open(path);
   }
 
@@ -2467,8 +2476,8 @@
     dbCapabilities_.SetLabelsSupport(true);
     dbCapabilities_.SetHasExtendedChanges(true);
     dbCapabilities_.SetHasFindSupport(HasIntegratedFind());
-    dbCapabilities_.SetHasKeyValueStore(true);
-    dbCapabilities_.SetHasQueue(true);
+    dbCapabilities_.SetKeyValueStoresSupport(true);
+    dbCapabilities_.SetQueuesSupport(true);
     db_.OpenInMemory();
   }
 
@@ -2671,10 +2680,27 @@
 
   }
 
+  // class RaiiTransactionLogger
+  // {
+  //   TransactionType type_;
+  //   public:
+  //     RaiiTransactionLogger(TransactionType type)
+  //     : type_(type)
+  //     {
+  //       LOG(INFO) << "IN  " << (type_ == TransactionType_ReadOnly ? "RO" : "RW");
+  //     }
+  //     ~RaiiTransactionLogger()
+  //     {
+  //     LOG(INFO) << "OUT " << (type_ == TransactionType_ReadOnly ? "RO" : "RW");
+  //     }
+
+  // };
 
   IDatabaseWrapper::ITransaction* SQLiteDatabaseWrapper::StartTransaction(TransactionType type,
                                                                           IDatabaseListener& listener)
   {
+    // RaiiTransactionLogger logger(type);
+
     switch (type)
     {
       case TransactionType_ReadOnly:
--- a/OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp	Thu May 15 16:01:57 2025 +0200
+++ b/OrthancServer/Sources/Database/StatelessDatabaseOperations.cpp	Thu May 15 18:49:47 2025 +0200
@@ -3200,16 +3200,16 @@
     return db_.GetDatabaseCapabilities().HasFindSupport();
   }
 
-  bool StatelessDatabaseOperations::HasKeyValueStore()
+  bool StatelessDatabaseOperations::HasKeyValueStoresSupport()
   {
     boost::shared_lock<boost::shared_mutex> lock(mutex_);
-    return db_.GetDatabaseCapabilities().HasKeyValueStore();
+    return db_.GetDatabaseCapabilities().HasKeyValueStoresSupport();
   }
 
-  bool StatelessDatabaseOperations::HasQueue()
+  bool StatelessDatabaseOperations::HasQueuesSupport()
   {
     boost::shared_lock<boost::shared_mutex> lock(mutex_);
-    return db_.GetDatabaseCapabilities().HasQueue();
+    return db_.GetDatabaseCapabilities().HasQueuesSupport();
   }
 
   void StatelessDatabaseOperations::ExecuteCount(uint64_t& count,
@@ -3510,6 +3510,25 @@
     return operations.HasFound();
   }
 
+  void StatelessDatabaseOperations::GetQueueSize(uint64_t& size,
+                                                 const std::string& queueId)
+  {
+    class Operations : public ReadOnlyOperationsT2<uint64_t&, const std::string& >
+    {
+    public:
+
+      virtual void ApplyTuple(ReadOnlyTransaction& transaction,
+                              const Tuple& tuple) ORTHANC_OVERRIDE
+      {
+        transaction.GetQueueSize(tuple.get<0>(), tuple.get<1>());
+      }
+    };
+
+    Operations operations;
+    operations.Apply(*this, size, queueId);
+  }
+
+
   bool StatelessDatabaseOperations::GetAttachment(FileInfo& attachment,
                                                   int64_t& revision,
                                                   const std::string& attachmentUuid)
--- a/OrthancServer/Sources/Database/StatelessDatabaseOperations.h	Thu May 15 16:01:57 2025 +0200
+++ b/OrthancServer/Sources/Database/StatelessDatabaseOperations.h	Thu May 15 18:49:47 2025 +0200
@@ -308,6 +308,12 @@
         return transaction_.GetKeyValue(value, storeId, key);
       }
 
+      void GetQueueSize(uint64_t& size,
+                        const std::string& queueId)
+      {
+        return transaction_.GetQueueSize(size, queueId);
+      }
+
       void ListKeys(std::list<std::string>& keys,
                     const std::string& storeId,
                     uint64_t since,
@@ -477,7 +483,6 @@
         return transaction_.DequeueValue(value, queueId, origin);
       }
 
-
       void UpdateAttachmentCustomData(const std::string& attachmentUuid,
                                       const std::string& customData)
       {
@@ -608,9 +613,9 @@
 
     bool HasFindSupport();
 
-    bool HasKeyValueStore();
+    bool HasKeyValueStoresSupport();
 
-    bool HasQueue();
+    bool HasQueuesSupport();
     
     void GetExportedResources(Json::Value& target,
                               int64_t since,
@@ -814,6 +819,8 @@
     bool DequeueValue(std::string& value,
                       const std::string& queueId,
                       QueueOrigin origin);
-
+    
+    void GetQueueSize(uint64_t& size,
+                      const std::string& queueId);
   };
 }
--- a/OrthancServer/Sources/OrthancRestApi/OrthancRestSystem.cpp	Thu May 15 16:01:57 2025 +0200
+++ b/OrthancServer/Sources/OrthancRestApi/OrthancRestSystem.cpp	Thu May 15 18:49:47 2025 +0200
@@ -95,8 +95,8 @@
     static const char* const HAS_LABELS = "HasLabels";
     static const char* const CAPABILITIES = "Capabilities";
     static const char* const HAS_EXTENDED_CHANGES = "HasExtendedChanges";
-    static const char* const HAS_KEY_VALUE_STORE = "HasKeyValueStore";
-    static const char* const HAS_QUEUE = "HasQueue";
+    static const char* const HAS_KEY_VALUE_STORES = "HasKeyValueStores";
+    static const char* const HAS_QUEUES = "HasQueues";
     static const char* const HAS_EXTENDED_FIND = "HasExtendedFind";
     static const char* const READ_ONLY = "ReadOnly";
 
@@ -213,8 +213,8 @@
     result[CAPABILITIES] = Json::objectValue;
     result[CAPABILITIES][HAS_EXTENDED_CHANGES] = OrthancRestApi::GetIndex(call).HasExtendedChanges();
     result[CAPABILITIES][HAS_EXTENDED_FIND] = OrthancRestApi::GetIndex(call).HasFindSupport();
-    result[CAPABILITIES][HAS_KEY_VALUE_STORE] = OrthancRestApi::GetIndex(call).HasKeyValueStore();
-    result[CAPABILITIES][HAS_QUEUE] = OrthancRestApi::GetIndex(call).HasQueue();
+    result[CAPABILITIES][HAS_KEY_VALUE_STORES] = OrthancRestApi::GetIndex(call).HasKeyValueStoresSupport();
+    result[CAPABILITIES][HAS_QUEUES] = OrthancRestApi::GetIndex(call).HasQueuesSupport();
     
     call.GetOutput().AnswerJson(result);
   }