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

Deadlock when reusing redis connection #18

Open
stephankaag opened this issue Dec 10, 2013 · 14 comments
Open

Deadlock when reusing redis connection #18

stephankaag opened this issue Dec 10, 2013 · 14 comments

Comments

@stephankaag
Copy link

Works:

  i = 0
  threads = [1, 2].collect do
    Thread.new do
      Redis::Semaphore.new("foo", :redis => Redis.new(:db => 15)).lock do
        i += 1
      end
    end
  end
  threads.each &:join
  i.should == 2

Deadlocks:

  i = 0
  threads = [1, 2].collect do
    Thread.new do
      Redis::Semaphore.new("foo", :redis => @redis).lock do
        i += 1
      end
    end
  end
  threads.each &:join
  i.should == 2
@dv
Copy link
Owner

dv commented Dec 13, 2013

Interesting, good catch! I think we should not allow a single Redis client to be used in different threads like this, because I'm guessing the BLPOP command we're using (which iirc freezes the connection) is not thread-safe.

I'll think about it how to best solve this.

@felipeclopes
Copy link

I was trying to lock some of my Sidekiq tasks using redis-semaphore, but I still did not find a way to make it works, it either or goes to deadlock or don't block at all.

Is there any work around for this? I was using different connections to do the lock, but apparently it doesn't lock then.

@dv
Copy link
Owner

dv commented Mar 7, 2014

@felipeclopes I would suggest using different connections, it should work. Can you give an example where you don't get a lock, with different connections?

The deadlock that happens if you initialize two Semaphores with the same Redis client is unavoidable since the Redis client is blocked while locked.

@felipeclopes
Copy link

I tried to use with different connections but it didn't blocked on the .lock command. The only way to make it work, was to use the Sidekiq connection, as suggested on this blog post.

Are you suggesting a solution like this one on a StackOverflow question?

Thank you for your support.

@dv
Copy link
Owner

dv commented Mar 11, 2014

@felipeclopes if it didn't block when using different connections, then that's a bug in redis-semaphore, since that should definitely work (it's the reason I made it :) ). Could you give some example code that didn't work for you?

It's become obvious that this is a use-case that I should cover, so I'll think about how to make redis-semaphore thread-safe. I'll probably encapsulate the locking logic in a ruby mutex.

@ixti
Copy link

ixti commented Mar 26, 2014

You can try my "way" of dealing with Sidekiq and redis-semaphore: http://ixti.net/development/ruby/2014/03/26/share-sidekiq-s-redis-pool-with-other-part-of-your-app.html

@stephankaag
Copy link
Author

Looks good to me. @dv ?

@dv
Copy link
Owner

dv commented Jun 15, 2015

Thanks for reminding me @stephankaag, I'll find some free time this week and work through the open issues on this gem.

@kimrgrey
Copy link

Yeah, using of the same redis connection in, for example, different sidekiq workers is not good idea. It will lead to the endless lock of all threads. Think, it's better to clarify this fact in README.

@dv
Copy link
Owner

dv commented Apr 17, 2016

I agree, due to the blocking nature of some usecases of redis-semaphore, reusing redis clients should be discouraged. Since even adding a Ruby mutex to the blocking code in redis-semaphore would not cover all possible "abuses"*, for now I won't add code to support this use-case.

I'll add a README part that explains it and warns against it. Also, it's still possible as long as you yourself take care

*: It would only cover the use case where the redis client is only used by redis-semaphore. As soon as other code also uses it the Ruby mutex will be useless.

@jeremyhaile
Copy link

Is this issue (and the README) out-of-date? The redis-rb gem is threadsafe now and the original "failing" test case works fine for me when testing with the same redis connection.

@jeremyhaile
Copy link

Never mind...I got it to lock up now...

@atomical
Copy link

atomical commented May 3, 2017

Is the solution is to open a new redis connection in each thread?

bbonamin added a commit to bbonamin/sidekiq-rate-limiter that referenced this issue Sep 5, 2018
The redis-semaphore gem advises [against reusing the redis connection across threads](dv/redis-semaphore#18) because it causes a Deadlock. We actually experience this and the solution was to initialize a new redis connection when checking for the semaphore.

I additionally added a staleness check to avoid locking everything forever should the semaphore hang for some other reason.
@jcavalieri
Copy link

I'm seeing deadlocks even with new connections. Is there any reason why this library couldn't use a Mutex per semaphore key? I'm experimenting with an LRU cache of mutexes.

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

8 participants