Locking for Multiple Nodes the Easy Way: GCS
It happens to all of us. We develop stateless applications that can scale horizontally without any effort. Find out more!
Join the DZone community and get the full member experience.Join For Free
It happens to all of us. We develop stateless applications that can scale horizontally without any effort.
However, sometimes cases arise where you need to achieve some type of coordination.
You can go really advanced on this one. For example, you can use a framework like Akka and it’s cluster capabilities. Or you can go really simple like rolling a mechanism on your own as long as it gives you the results needed. On another note, you can just have different node groups based on the work you need them to do. The options and the solutions can change based on the problem.
If your problem can go with a simple option, one way to do so, provided you use Google Cloud Storage, is to use its lock capabilities.
Imagine for example a scenario of 4 nodes, they do scale dynamically but each time a new node registers you want to change its actions by acquiring a unique configuration that does not collide with a configuration another node might have received.
The strategy can be to use a file on Google Cloud Storage for locking and a file that acts as a centralized configuration registry.
The lock file is nothing more than a file on cloud storage which shall be created and deleted. What will give us lock abilities is the option on GCS to create a file only if it not exists.
Thus a process from one node will try to create the `lock` file, this action would be equivalent to obtaining the lock.
Once the process is done will delete the file, this action would be equivalent to releasing the lock.
Other processes in the meantime will try to create the file (acquire the lock) and fail (file already exists) because other processes have created the file.
Meanwhile, the process that has successfully created the file (acquired the lock) will change the centralized configuration registry and once done will delete the file (release the lock).
So let’s start with the lock object.
As you can see the write specifies to write an object only if it does not exist. This operation behind the scenes is using the x-goog-if-generation-match header which is used for concurrency.
Thus one node will be able to acquire the lock and change the configuration files.
Afterward, it can delete the lock. If an exception is raised probably the operation fails and the lock is already acquired.
To make the example more complete let’s make the configuration file. The configuration file would be a simple JSON file for keymap actions.
It is simple config util backed by GCS. Eventually, it can be changed and put the lock operating inside the addProperty operation, it’s up to the user and the code. For this blog, we shall just acquire the lock change the configuration, and release the lock.
Our main class will look like this.
Now let’s go for some multithreading. Ten threads will try to put values, it is expected that they have some failure.
Obviously, 10 threads are ok to display the capabilities. During the thread initialization and execution, some threads will try to acquire the lock simultaneously and one will fails, while other threads will be late and will fail and wait until the lock is available.
In the end, what is expected is for all of them to have their values added to the configuration.
That’s it. If your problems have a simple nature this approach might do the trick. Obviously, you can use the HTTP API instead of the SDK. You can find the code on Github.
Published at DZone with permission of Emmanouil Gkatziouras, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.