Simple Mutual Exclusion
Learn how to increase your services' availability through a mutual exclusion mechanism, like the one we'll set up in this tutorial.
Join the DZone community and get the full member experience.
Join For FreeNowadays, for almost all services, we would like to set up at least poor man’s HA. This means we would have more than one node/server/pod at a time. This is great for load balancing and availability purposes. Nevertheless, there’s a simple problem with this setup. What if you want to execute a piece of code in only one node? We can do this via a simple mutual exclusion mechanism. There are various ways to this but I would like to limit this post to achieve mutual exclusion with Java and Spring Framework.
Now, imagine you have a cron job that sends an e-mail to your customers at 9 a.m. every day. You wouldn’t want to send the same e-mails twice, right? If you are using Spring Framework, you can simply do this via Scheduled annotation. A typical code would look like:
@Scheduled(cron = "59 59 8 * * *" /* Every day at 8:59:59am */)
public void sendEmails() {
List<Email> emails = emailDAO.getEmails();
emails.forEach(email -> sendEmail(email));
}
So, we need one node to execute above not the others. It would be great if we can do something like this right?
@Scheduled(cron = "59 59 8 * * *" /* Every day at 8:59:59am */)
@TryLock(name = "emailLock", owner = NODE_NAME, lockFor = TEN_MINUTE)
public void sendEmails() {
List<Email> emails = emailDAO.getEmails();
emails.forEach(email -> sendEmail(email));
}
Thus, I’ve implemented a little library to exactly do this. Currently, it offers synchronization through Postgres and MySQL but one can extend it to other technologies like ZooKeeper, Consul and etcd as well.
By the way, this isn’t just about crons. You can also synchronize over different stuff as well. For instance, you might want to process a message once even if it’s received by multiple nodes.
@Component
class MessageProcessor{
...
@TryLock(name = "messageProcessor", owner = NODE_NAME, lockFor = TEN_MINUTE)
public void processMessage(Message message) {
...
}
}
@Component
class MessageService{
...
@PostConstruct
public void initProcessor(){
while (true) {
Message message = pollMessage();
messageProcessor.processMessage(message);
}
}
}
In consequence, you can implement a simple lock with a timeout with dlock.
Published at DZone with permission of Yusuf Aytaş, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments