We’ve been recently running rest APIs on active-active server pairs (docker containers running on pairs of VMs on separate hosts) with postgres-BDR (multi-master bidirectional replication) for fault-tolerant storage and a pair of fault-tolerant HAProxy instances for incoming request routing. This is a robust setup which provides zero downtime during rolling updates or hardware maintenance or failure.
However, clustering scheduled jobs (i.e. ensuring that scheduled jobs execute exactly once) becomes a problem in this configuration. Multi-master replicated databases avoid a single point of failure but they are not suitable for use with database-based clustered schedulers like Quartz, so we needed to consider other options. There are complex clustered job schedulers, but we wanted to keep it simple and use Linux crond for scheduling. We finally settled on using keepalived to maintain a single master across the cluster and schedule the cron jobs identically on all servers, using a small if_master.sh script to ensure that a crontab entry only runs if the server is currently the keepalived master. The crontab entries then look like:
0 * * * * ./if_master.sh "./command.sh"
if_master.sh checks if the server is currently keepalived master (by looking for the floating ip address)
#!/bin/bash FLOATING_IP_ADDRESS=x.x.x.x if (ip addr show | grep "$FLOATING_IP_ADDRESS") then eval $1 fi
and only executing the command if the server is currently master (the same crontabs execute on the other servers, but do nothing if the server is not the keepalive master).
This was the simplest solution we could find which allows us to keep using cron and configure crontabs on all servers identically.
Pingback: An API-first approach to COVID-19 contact-tracing – blog.armstrongconsulting.com