Run one job queue in the same time on multiple servers

Viewed 304

I have a real scenario with two servers. I have one queue and I want to run only one job at the same time on one of two servers. But it looks like both servers are trying to handle this job. It is possible?

My code doesn’t work. The requirement is if one server runs a job, another one should not put on the queue but forget. In a real scenario, I need to distribute this across multiple servers.

Possible storages can be only Redis and PostgreSQL.

Any ideas on how to resolve this?

using System.Threading;
using System.Threading.Tasks;
using Hangfire;
using Hangfire.Logging;
using Hangfire.Logging.LogProviders;
using Hangfire.MemoryStorage;
using Hangfire.MemoryStorage.Database;
using Hangfire.PostgreSql;
using Hangfire.Pro.Redis;

namespace HangfireLockQueue
{
    public class Program
    {
        static void Main(string[] args)
        {
            // 1.memory
            //JobStorage storage = new MemoryStorageFake(new MemoryStorageOptions());

            // 2.redis
            //JobStorage storage = new RedisStorage("redis:6379,ssl = false", new RedisStorageOptions
            //{
            //    Prefix = "HangfireRedis-Local:"
            //});

            // 3.postgres
            var connectionString = "Host=postgres;Database=scheduler;Username=test;Password=<>;";
            JobStorage storage = new PostgreSqlStorage(connectionString, new PostgreSqlStorageOptions
            {
                InvisibilityTimeout = TimeSpan.FromMinutes(5),
                QueuePollInterval = TimeSpan.FromMilliseconds(200),
                DistributedLockTimeout = TimeSpan.FromSeconds(10),
            });

            JobStorage.Current = storage;
            Console.WriteLine(JobStorage.Current);

            LogProvider.SetCurrentLogProvider(new ColouredConsoleLogProvider());

            RunServer1(storage);
            RunServer2(storage);

            Console.ReadLine();
        }

        private static void RunServer1(JobStorage storage)
        {
            Task.Factory.StartNew(() => RunServer1Client1Queue(storage));
        }

        private static void RunServer1Client1Queue(JobStorage storage)
        {
            var serverOptions = new BackgroundJobServerOptions
            {
                ShutdownTimeout = TimeSpan.FromMinutes(5),
                ServerName = $"{Environment.MachineName}1.{Guid.NewGuid()}",
                Queues = new[] { "queue1" },
                WorkerCount = 1
            };

            using (new BackgroundJobServer(serverOptions, storage))
            {
                Log("Hangfire Server 1 started. Press any key to exit...");

                RecurringJob.AddOrUpdate(
                    Guid.NewGuid().ToString(),
                    () => JobThree(),
                    "* * * * *",
                    queue: "queue1");

                Console.ReadKey();
            }
        }

        private static void RunServer2(JobStorage storage)
        {
            Task.Factory.StartNew(() => RunServer2Client1Queue(storage));
        }

        private static void RunServer2Client1Queue(JobStorage storage)
        {
            Thread.Sleep(2000);
            var serverOptions = new BackgroundJobServerOptions
            {
                ShutdownTimeout = TimeSpan.FromMinutes(5),
                ServerName = $"{Environment.MachineName}2.{Guid.NewGuid()}",
                Queues = new[] { "queue1" },
                WorkerCount = 1
            };

            using (new BackgroundJobServer(serverOptions, storage))
            {
                Log("Hangfire Server 2 started. Press any key to exit...");

                RecurringJob.AddOrUpdate(
                    Guid.NewGuid().ToString(),
                    () => JobThree(),
                    "* * * * *",
                    queue: "queue1");

                Console.ReadKey();
            }
        }

        public static void JobThree()
        {
            // implement guard if not working
            Thread.Sleep(3000);
            Log($"JobThree, current time:{DateTime.Now.ToString()}");
        }

        public static void Log(string msg, LogLevel level = LogLevel.Info)
        {
            LogProvider.GetLogger("Main").Log(level, () => { return msg; });
        }
    }
}
0 Answers
Related