Hi @alainmazy ,
I am using Transfer Accelerator Plugin to transfer dicom from one Orthanc A (acts as Gateway) to another Orthanc B (acts as LongTerm Storage). Whenever stable series event is triggered, Orthanc A sends the dicom series to Orthanc B. Since Modality sends a lot of dicoms to Orthanc A, it cannot properly transfer them to Orthanc B. As a result, Orthanc A quickly became stuck or hung due to many PushTransfer jobs pending/ running in Orthanc A. After analyzing the source, I notice that after Orthanc A sends buckets to Orthanc B, it sends the final “/commit” messages to Orthanc B and waiting for Orthanc B indexing those buckets in its storage. If Orthanc B is busy due to many incoming HTTP requests then the final “/commit” message will become timeout. Hence the transfer is failed. So I recommend that we add a new option that makes the “/commit” route asynchronous in addition to synchronous. In more detail, when receiver process the commit message, it will create a job to process the uploaded buckets and returns immediately to the sender. By that way, sender does not need to wait receiver completely processing its dicom files. I have successfully implemented that initiative. Here is snippet of my code.
ActivePushTransactions.cpp
void ActivePushTransactions::FinalizeTransaction(const std::string& transactionUuid,
bool commit)
{
boost::mutex::scoped_lock lock(mutex_);
Content::iterator found = content_.find(transactionUuid);
if (found == content_.end())
{
throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource);
}
assert(found->second != NULL);
if (commit)
{
if (!PluginContext::GetInstance().IsCommitAsync())
{
found->second->GetDownloadArea()->Commit();
}
else // Submit Job
{
std::string id = OrthancPlugins::OrthancJob::Submit(new OrthancPlugins::UploadDicomJob("UploadDicomJob", found->second->GetDownloadArea().release()), 1);
LOG(INFO) << "ActivePushTransactions::FinalizeTransaction submit jobID=" << id;
}
}
delete found->second;
content_.erase(found);
index_.Invalidate(transactionUuid);
Saola::TransactionDatabase::Instance().DeleteById(transactionUuid);
}
UploadDicomJob.cpp
#include "UploadDicomJob.h"
#include <Compatibility.h> // For std::unique_ptr
#include <SystemToolbox.h>
#include <Logging.h>
#include <boost/filesystem.hpp>
namespace OrthancPlugins
{
UploadDicomJob::UploadDicomJob(const std::string& jobType, DownloadArea* area) :
OrthancJob(jobType)
{
area_.reset(area);
totalInstances_ = area->GetInstancesCount();
totalSize_ = area->GetTotalSize();
Json::Value content;
content["TotalInstances"] = totalInstances_;
content["TotalSizeMB"] = ConvertToMegabytes(totalSize_);
this->UpdateContent(content);
}
OrthancPluginJobStepStatus UploadDicomJob::Step()
{
size_t remaining = this->area_->StepCommit();
Json::Value content;
content["TotalInstances"] = totalInstances_;
content["TotalSizeMB"] = ConvertToMegabytes(totalSize_);
if (remaining > 0)
{
content["UploadedSizeMB"] = ConvertToMegabytes(totalSize_ - this->area_->GetSize());
content["UploadedInstances"] = totalInstances_ - remaining;
this->UpdateProgress(1 - (static_cast<float>(remaining) / static_cast<float>(this->totalInstances_)));
this->UpdateContent(content);
return OrthancPluginJobStepStatus_Continue;
}
content["UploadedSizeMB"] = ConvertToMegabytes(totalSize_);
content["UploadedInstances"] = totalInstances_;
this->UpdateContent(content);
this->UpdateProgress(1.0f);
return OrthancPluginJobStepStatus_Success;
}
void UploadDicomJob::Stop(OrthancPluginJobStopReason reason)
{
LOG(INFO) << "[UploadDicomJob::Stop] reason=" << reason;
}
void UploadDicomJob::Reset()
{
LOG(INFO) << "[UploadDicomJob::Reset]";
}
}