Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deadlocks and "Too many connections" #60

Open
MiloszKrajewski opened this issue May 10, 2019 · 1 comment
Open

Deadlocks and "Too many connections" #60

MiloszKrajewski opened this issue May 10, 2019 · 1 comment

Comments

@MiloszKrajewski
Copy link

MiloszKrajewski commented May 10, 2019

This issue is strictly related to #56 and #57 but this time I have some answers.

Symptoms

When scheduling tasks from many threads MySQL may trow deadlock exception (on inserts I guess). For some reason though it leads to connection leaks and quickly we reach the point when connection pool is exhausted and whole process grinds to a halt.

Reproduction

It can be reproduced with this code:

using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

// ReSharper disable IdentifierTypo
// ReSharper disable StringLiteralTypo

namespace Hangfire.MySql.Deadlock
{
  class Program
  {
    private static int TID => Thread.CurrentThread.ManagedThreadId;
    private static DateTime Now => DateTime.Now;

    static void Main(string[] args)
    {
      // create database hangfire collate utf8_bin;
      var (db, uid, pwd) = ("hangfire", "mysql", "mysql");

      var connectionString = $"Server=localhost;Database={db};Uid={uid};Pwd={pwd}";
      var storage = new MySqlStorage(connectionString, new MySqlStorageOptions());
      var client = new BackgroundJobClient(storage);

      using (new BackgroundJobServer(storage))
      {
        var cancel = new CancellationTokenSource();

        Task Run() => Task.Factory.StartNew(
          () => Loop(cancel.Token, client, TimeSpan.Zero),
          TaskCreationOptions.LongRunning);

        var loops = Task.WhenAll(Run(), Run(), Run(), Run());

        Console.WriteLine("Press <enter> to stop...");
        Console.ReadLine();

        cancel.Cancel();
        loops.Wait(CancellationToken.None);
      }
    }

    private static void Loop(
      CancellationToken cancel, 
      BackgroundJobClient client, 
      TimeSpan interval)
    {
      Console.WriteLine($"Loop started @ [{TID}]");
      while (!cancel.IsCancellationRequested)
      {
        Thread.Sleep(interval);
        var now = Now;
        Console.WriteLine($">>> [{TID}] {now:s}");

        try
        {
          client.Schedule(() => Execute(now), now.AddSeconds(10));
        }
        catch (Exception e)
        {
          Console.WriteLine($"{e.GetType().Name}: {e.Message}\n{e.StackTrace}");
        }
      }
    }

    public static void Execute(DateTime then) => 
      Console.WriteLine($"<<< [{TID}] {then:s} ({Now.Subtract(then).TotalSeconds}s)");
  }
}

It schedules some simple task using four threads at "full" speed (interval = 0).
From time to time it throws deadlock exception and at some point just stops. It seems like it exhaust all connections in connection pool. It wakes up from time to time to enqueue 1 or 2 tasks but hangs after that. I was thinking that it reuses some connections released by GC, but this is just hypothesis.

Workaround

Adding a lock (mutex/semaphore) around adding a task so there are no two client.Schedule(...); executing at the same time. Seems like fixes deadlock problem but also keeps number of connections at reasonable level.
Although, I would still expect problem with multiple processes.

@tluyben
Copy link
Contributor

tluyben commented Mar 14, 2020

You mention on your github https://github.com/MiloszKrajewski/Hangfire.Storage.MySql that you kept close to the original so changes can be merged back? Can you merge them back? Now that the license here is LGPL maybe you would consider merging back in this version which more people seem to use?

Also, did you have permission from the author to change the license to MIT? I don't care but lawyers ask; they are very panicky about anything that could legally be considered GPL in hindsight...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants