Implementing load balancing on gateways for head CT Dicom images
Problem Statement
A Head CT is quite complicated when compared to an X-Ray part because CT consists of a series of images coming from the scanning modality. To correctly process the CT we have to be sure that all the images part of the series are being sent to the same modality(in our case, it’s our gateway). Say if half of the images are sent to modality A and the remaining half to modality B we cannot accurately process the CT and we duplicate a single study of the patient. The idea is to process studies using multiple gateways and place a load balancer ahead of gateways which takes care of load-balancing on the gateways.
Logic behind the Load Balancer
The approach we took here is by using Orthanc Server Side
scripting in lua
. Orthanc provides various call-back functions in lua to implement custom features. We here are leveraging Orthanc to build a load balancer.
Refer this before going ahead in this blog.
We will be using this callback provided in lua to implement a load-balancer
The above callback OnStoredInstance
sends the instanceID
to the modality named sample
and deletes the instance successfully forwarded to the mentioned modality.
This is what we exactly needed to build a load balancer. The ability to send to the modality of our choice(load-balancers choice) is the need of the hour and we have it. The next thing that bothers is how do you decide where to send a certain study. I guess you might have heard of terms like hashing and buckets. The basic idea is to hash a unique identifier for a study and allot the study the bucket_number basing on the hash and the number of buckets we chose to sort them into.
Below here is the lua script(function A, B, C combined in hash.lua) which does all the work for us.
Here, the unique identifier for the study is StudyInstanceUID
and it comes from the tags parameter.
We must be able to make a hash out of this StudyInstanceUID
. It was pretty hard to find an in-built hash function in lua. Hence there was a need for creating a custom hash that is uniformly random.
say uid = "1.2.2.12.12667.35356677546776445.44447887346356547"
is our string which has to be hashed and note the string always consists of digits and .
. Ponder over this, you now are building your own hash function. Be innovative and make sure your hash has to be uniformly random.
Here is my approach, since we only have digits why can’t we just sum them all and create a hash out of it
and sort them into buckets by taking dividing it with a number of buckets
and alloting the study into the bucket numbered remainder
.
This is a very basic hash function but works unless the StudyInstanceUID
generated at the source is biased.
Now we have achieved load-balancer, it’s time to integrate it into our solution.
Integrating load balancer with the gateways
We use docker-compose to do it seamlessly by running them as different services.
In the function GetModalitybyBucket
we are actually returning the modality name which must be present in the DicomModalities config
of orthanc.json
The generalised form of an entry in DicomModalities is:
"modalityname": ["aetname","IPaddress","portnumber"]
Instead of IP address we provide the service name as we are inside the docker, containers can be recognized with their service names.
Explanation : Say ,the function SendToModality()
picks modality dcmio2
to send the study. From the above, we know the dcmio2
modality is our docker service gateway2
. Thus, gateway2
receives the study based on our hash function and the number of buckets.
I will keep this space updated with any deployment issues or with some better-performing hash functions. We are soon planning to make our gateway open-source for collaborations.
Thank you for reading the blog!! Have a great day.