Redis lock implementation The 2019 Stack Overflow Developer Survey Results Are InRead-write...

Can I have a signal generator on while it's not connected?

Why are there uneven bright areas in this photo of black hole?

Why can't devices on different VLANs, but on the same subnet, communicate?

What could be the right powersource for 15 seconds lifespan disposable giant chainsaw?

"as much details as you can remember"

Falsification in Math vs Science

Inverse Relationship Between Precision and Recall

How do PCB vias affect signal quality?

How to charge AirPods to keep battery healthy?

Why was M87 targeted for the Event Horizon Telescope instead of Sagittarius A*?

How come people say “Would of”?

What to do when moving next to a bird sanctuary with a loosely-domesticated cat?

Pokemon Turn Based battle (Python)

What do these terms in Caesar's Gallic Wars mean?

Likelihood that a superbug or lethal virus could come from a landfill

Is an up-to-date browser secure on an out-of-date OS?

Why “相同意思的词” is called “同义词” instead of "同意词"?

What is the meaning of Triage in Cybersec world?

Is it okay to consider publishing in my first year of PhD?

What is the motivation for a law requiring 2 parties to consent for recording a conversation

How can I define good in a religion that claims no moral authority?

How to display lines in a file like ls displays files in a directory?

Star Trek - X-shaped Item on Regula/Orbital Office Starbases

How to type a long/em dash `—`



Redis lock implementation



The 2019 Stack Overflow Developer Survey Results Are InRead-write lock implementation for Ruby, new versionChat logger using RedisSpin-lock implementationRedis Message Queue (Cloud)Token generating library on RedisSilly redis implementationRedis protocol implementation by KotlinRedis lookup with a default valueRedis lua - game leaderboard “overview”Reader Writer Lock Implementation





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty{ margin-bottom:0;
}







1












$begingroup$


Considering I'm not implementing distributed lock mechanism, is this code correct and clear?



class RedisLock
class NotAcquired < StandardError; end

def initialize(redis)
@redis = redis
end

def lock(key, expiration_ms)
val = SecureRandom.random_number(100000000000000000).to_s

if @redis.set(key, val, nx: true, px: expiration_ms)
yield
unlock(key, val)
true
else
false
end
end

def lock!(*args, &block)
unless lock(*args, &block)
raise NotAcquired.new("Could not acquire lock with #{args}")
end
end

def unlock(key, val)
check_and_delete = <<-LUA
if redis.call('get', KEYS[1]) == KEYS[2] then
redis.call('del', KEYS[1])
end
LUA

@redis.eval(check_and_delete, [key, val])
end
end


RedisLock.new(Redis.current).lock!('key', 10000) { do_something }









share|improve this question











$endgroup$




bumped to the homepage by Community 9 mins ago


This question has answers that may be good or bad; the system has marked it active so that they can be reviewed.






















    1












    $begingroup$


    Considering I'm not implementing distributed lock mechanism, is this code correct and clear?



    class RedisLock
    class NotAcquired < StandardError; end

    def initialize(redis)
    @redis = redis
    end

    def lock(key, expiration_ms)
    val = SecureRandom.random_number(100000000000000000).to_s

    if @redis.set(key, val, nx: true, px: expiration_ms)
    yield
    unlock(key, val)
    true
    else
    false
    end
    end

    def lock!(*args, &block)
    unless lock(*args, &block)
    raise NotAcquired.new("Could not acquire lock with #{args}")
    end
    end

    def unlock(key, val)
    check_and_delete = <<-LUA
    if redis.call('get', KEYS[1]) == KEYS[2] then
    redis.call('del', KEYS[1])
    end
    LUA

    @redis.eval(check_and_delete, [key, val])
    end
    end


    RedisLock.new(Redis.current).lock!('key', 10000) { do_something }









    share|improve this question











    $endgroup$




    bumped to the homepage by Community 9 mins ago


    This question has answers that may be good or bad; the system has marked it active so that they can be reviewed.


















      1












      1








      1





      $begingroup$


      Considering I'm not implementing distributed lock mechanism, is this code correct and clear?



      class RedisLock
      class NotAcquired < StandardError; end

      def initialize(redis)
      @redis = redis
      end

      def lock(key, expiration_ms)
      val = SecureRandom.random_number(100000000000000000).to_s

      if @redis.set(key, val, nx: true, px: expiration_ms)
      yield
      unlock(key, val)
      true
      else
      false
      end
      end

      def lock!(*args, &block)
      unless lock(*args, &block)
      raise NotAcquired.new("Could not acquire lock with #{args}")
      end
      end

      def unlock(key, val)
      check_and_delete = <<-LUA
      if redis.call('get', KEYS[1]) == KEYS[2] then
      redis.call('del', KEYS[1])
      end
      LUA

      @redis.eval(check_and_delete, [key, val])
      end
      end


      RedisLock.new(Redis.current).lock!('key', 10000) { do_something }









      share|improve this question











      $endgroup$




      Considering I'm not implementing distributed lock mechanism, is this code correct and clear?



      class RedisLock
      class NotAcquired < StandardError; end

      def initialize(redis)
      @redis = redis
      end

      def lock(key, expiration_ms)
      val = SecureRandom.random_number(100000000000000000).to_s

      if @redis.set(key, val, nx: true, px: expiration_ms)
      yield
      unlock(key, val)
      true
      else
      false
      end
      end

      def lock!(*args, &block)
      unless lock(*args, &block)
      raise NotAcquired.new("Could not acquire lock with #{args}")
      end
      end

      def unlock(key, val)
      check_and_delete = <<-LUA
      if redis.call('get', KEYS[1]) == KEYS[2] then
      redis.call('del', KEYS[1])
      end
      LUA

      @redis.eval(check_and_delete, [key, val])
      end
      end


      RedisLock.new(Redis.current).lock!('key', 10000) { do_something }






      ruby locking redis






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Mar 22 '17 at 12:42









      200_success

      131k17157422




      131k17157422










      asked Feb 9 '17 at 18:26









      cutalioncutalion

      1645




      1645





      bumped to the homepage by Community 9 mins ago


      This question has answers that may be good or bad; the system has marked it active so that they can be reviewed.







      bumped to the homepage by Community 9 mins ago


      This question has answers that may be good or bad; the system has marked it active so that they can be reviewed.
























          1 Answer
          1






          active

          oldest

          votes


















          0












          $begingroup$

          Here's a stab at clarifying the intent without changing any of the functionality. I didn't try to run it or anything, so don't hold me to syntax.



          # handles locking and unlocking redis for a particular operation
          class RedisLock
          # thrown when a lock cannot be acquired
          class NotAcquired < StandardError; end

          # since the redis key and ms are used in pretty much every method,
          # might as well go ahead and pass them in the constructor.
          # set defaults here if there are defaults that make sense.
          # make a yardoc comment and describe what these all are.
          def initialize(redis: Redis.current, nx: true, redis_key:, expiration_ms:)
          @redis = redis
          @redis_key = redis_key
          @expiration_ms = expiration_ms
          @nx = nx
          end

          # call it with_lock instead of lock
          # to make it more apparent it accepts a block
          def with_lock!
          raise(NotAcquired, "Could not acquire lock with #{args}") unless lock
          yield
          unlock(redis_key, random_lock_number)
          true # do you really need to return true here?
          end

          def with_lock
          with_lock!
          rescue NotAcquired
          false
          end

          private

          # set instance variables in the initializer, but never call them directly.
          attr_reader :redis, :redis_key, :expiration_ms, :nx

          def lock
          redis.set(redis_key, random_lock_number, nx: nx, px: expiration_ms)
          end

          # number is a little decieving here when you call `.to_s` at the end.
          # can you use SecureRandom.uuid instead?
          def random_lock_number
          @random_lock_number ||= SecureRandom.random_number(100_000_000_000_000_000).to_s
          end

          # no need for this to be exposed publicly.
          # calling eval directly is usually a bad idea.
          def unlock
          redis.eval(redis_check_and_delete, [redis_key, random_lock_number])
          end

          def redis_check_and_delete
          <<-LUA
          if redis.call('get', KEYS[1]) == KEYS[2] then
          redis.call('del', KEYS[1])
          end
          LUA
          end
          end


          And to call it:



          RedisLock.new(redis_key: 'key', expiration_ms: 10000).with_lock! { do_something }





          share|improve this answer











          $endgroup$













          • $begingroup$
            Hi @Chris! Thanks you for the answer! Though I cannot agree with the suggested improvements. The general concern about it is that the new object cannot be re-used with another key. Moreover it even cannot be re-used with the same key. I also have minor concerns (like nx in parameters makes no sense), but this is not so important after the first point.
            $endgroup$
            – cutalion
            Feb 20 '17 at 12:56










          • $begingroup$
            object instantiation is free. You have no need to to re-use the single object. the Redis connection is already passed in. you've even named the class RedisLock and not something like RedisLockFactory. The major takeaway here, if you care to take away anything is that your methods are doing too much internally. If you're passing arguments into every single method, that's a code smell. also usually the ! version of a method is called by the non-bang version instead of the other way around. good luck.
            $endgroup$
            – Chris Drappier
            Feb 20 '17 at 16:42












          Your Answer





          StackExchange.ifUsing("editor", function () {
          return StackExchange.using("mathjaxEditing", function () {
          StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
          StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
          });
          });
          }, "mathjax-editing");

          StackExchange.ifUsing("editor", function () {
          StackExchange.using("externalEditor", function () {
          StackExchange.using("snippets", function () {
          StackExchange.snippets.init();
          });
          });
          }, "code-snippets");

          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "196"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          autoActivateHeartbeat: false,
          convertImagesToLinks: false,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: null,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });














          draft saved

          draft discarded


















          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f154936%2fredis-lock-implementation%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown

























          1 Answer
          1






          active

          oldest

          votes








          1 Answer
          1






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes









          0












          $begingroup$

          Here's a stab at clarifying the intent without changing any of the functionality. I didn't try to run it or anything, so don't hold me to syntax.



          # handles locking and unlocking redis for a particular operation
          class RedisLock
          # thrown when a lock cannot be acquired
          class NotAcquired < StandardError; end

          # since the redis key and ms are used in pretty much every method,
          # might as well go ahead and pass them in the constructor.
          # set defaults here if there are defaults that make sense.
          # make a yardoc comment and describe what these all are.
          def initialize(redis: Redis.current, nx: true, redis_key:, expiration_ms:)
          @redis = redis
          @redis_key = redis_key
          @expiration_ms = expiration_ms
          @nx = nx
          end

          # call it with_lock instead of lock
          # to make it more apparent it accepts a block
          def with_lock!
          raise(NotAcquired, "Could not acquire lock with #{args}") unless lock
          yield
          unlock(redis_key, random_lock_number)
          true # do you really need to return true here?
          end

          def with_lock
          with_lock!
          rescue NotAcquired
          false
          end

          private

          # set instance variables in the initializer, but never call them directly.
          attr_reader :redis, :redis_key, :expiration_ms, :nx

          def lock
          redis.set(redis_key, random_lock_number, nx: nx, px: expiration_ms)
          end

          # number is a little decieving here when you call `.to_s` at the end.
          # can you use SecureRandom.uuid instead?
          def random_lock_number
          @random_lock_number ||= SecureRandom.random_number(100_000_000_000_000_000).to_s
          end

          # no need for this to be exposed publicly.
          # calling eval directly is usually a bad idea.
          def unlock
          redis.eval(redis_check_and_delete, [redis_key, random_lock_number])
          end

          def redis_check_and_delete
          <<-LUA
          if redis.call('get', KEYS[1]) == KEYS[2] then
          redis.call('del', KEYS[1])
          end
          LUA
          end
          end


          And to call it:



          RedisLock.new(redis_key: 'key', expiration_ms: 10000).with_lock! { do_something }





          share|improve this answer











          $endgroup$













          • $begingroup$
            Hi @Chris! Thanks you for the answer! Though I cannot agree with the suggested improvements. The general concern about it is that the new object cannot be re-used with another key. Moreover it even cannot be re-used with the same key. I also have minor concerns (like nx in parameters makes no sense), but this is not so important after the first point.
            $endgroup$
            – cutalion
            Feb 20 '17 at 12:56










          • $begingroup$
            object instantiation is free. You have no need to to re-use the single object. the Redis connection is already passed in. you've even named the class RedisLock and not something like RedisLockFactory. The major takeaway here, if you care to take away anything is that your methods are doing too much internally. If you're passing arguments into every single method, that's a code smell. also usually the ! version of a method is called by the non-bang version instead of the other way around. good luck.
            $endgroup$
            – Chris Drappier
            Feb 20 '17 at 16:42
















          0












          $begingroup$

          Here's a stab at clarifying the intent without changing any of the functionality. I didn't try to run it or anything, so don't hold me to syntax.



          # handles locking and unlocking redis for a particular operation
          class RedisLock
          # thrown when a lock cannot be acquired
          class NotAcquired < StandardError; end

          # since the redis key and ms are used in pretty much every method,
          # might as well go ahead and pass them in the constructor.
          # set defaults here if there are defaults that make sense.
          # make a yardoc comment and describe what these all are.
          def initialize(redis: Redis.current, nx: true, redis_key:, expiration_ms:)
          @redis = redis
          @redis_key = redis_key
          @expiration_ms = expiration_ms
          @nx = nx
          end

          # call it with_lock instead of lock
          # to make it more apparent it accepts a block
          def with_lock!
          raise(NotAcquired, "Could not acquire lock with #{args}") unless lock
          yield
          unlock(redis_key, random_lock_number)
          true # do you really need to return true here?
          end

          def with_lock
          with_lock!
          rescue NotAcquired
          false
          end

          private

          # set instance variables in the initializer, but never call them directly.
          attr_reader :redis, :redis_key, :expiration_ms, :nx

          def lock
          redis.set(redis_key, random_lock_number, nx: nx, px: expiration_ms)
          end

          # number is a little decieving here when you call `.to_s` at the end.
          # can you use SecureRandom.uuid instead?
          def random_lock_number
          @random_lock_number ||= SecureRandom.random_number(100_000_000_000_000_000).to_s
          end

          # no need for this to be exposed publicly.
          # calling eval directly is usually a bad idea.
          def unlock
          redis.eval(redis_check_and_delete, [redis_key, random_lock_number])
          end

          def redis_check_and_delete
          <<-LUA
          if redis.call('get', KEYS[1]) == KEYS[2] then
          redis.call('del', KEYS[1])
          end
          LUA
          end
          end


          And to call it:



          RedisLock.new(redis_key: 'key', expiration_ms: 10000).with_lock! { do_something }





          share|improve this answer











          $endgroup$













          • $begingroup$
            Hi @Chris! Thanks you for the answer! Though I cannot agree with the suggested improvements. The general concern about it is that the new object cannot be re-used with another key. Moreover it even cannot be re-used with the same key. I also have minor concerns (like nx in parameters makes no sense), but this is not so important after the first point.
            $endgroup$
            – cutalion
            Feb 20 '17 at 12:56










          • $begingroup$
            object instantiation is free. You have no need to to re-use the single object. the Redis connection is already passed in. you've even named the class RedisLock and not something like RedisLockFactory. The major takeaway here, if you care to take away anything is that your methods are doing too much internally. If you're passing arguments into every single method, that's a code smell. also usually the ! version of a method is called by the non-bang version instead of the other way around. good luck.
            $endgroup$
            – Chris Drappier
            Feb 20 '17 at 16:42














          0












          0








          0





          $begingroup$

          Here's a stab at clarifying the intent without changing any of the functionality. I didn't try to run it or anything, so don't hold me to syntax.



          # handles locking and unlocking redis for a particular operation
          class RedisLock
          # thrown when a lock cannot be acquired
          class NotAcquired < StandardError; end

          # since the redis key and ms are used in pretty much every method,
          # might as well go ahead and pass them in the constructor.
          # set defaults here if there are defaults that make sense.
          # make a yardoc comment and describe what these all are.
          def initialize(redis: Redis.current, nx: true, redis_key:, expiration_ms:)
          @redis = redis
          @redis_key = redis_key
          @expiration_ms = expiration_ms
          @nx = nx
          end

          # call it with_lock instead of lock
          # to make it more apparent it accepts a block
          def with_lock!
          raise(NotAcquired, "Could not acquire lock with #{args}") unless lock
          yield
          unlock(redis_key, random_lock_number)
          true # do you really need to return true here?
          end

          def with_lock
          with_lock!
          rescue NotAcquired
          false
          end

          private

          # set instance variables in the initializer, but never call them directly.
          attr_reader :redis, :redis_key, :expiration_ms, :nx

          def lock
          redis.set(redis_key, random_lock_number, nx: nx, px: expiration_ms)
          end

          # number is a little decieving here when you call `.to_s` at the end.
          # can you use SecureRandom.uuid instead?
          def random_lock_number
          @random_lock_number ||= SecureRandom.random_number(100_000_000_000_000_000).to_s
          end

          # no need for this to be exposed publicly.
          # calling eval directly is usually a bad idea.
          def unlock
          redis.eval(redis_check_and_delete, [redis_key, random_lock_number])
          end

          def redis_check_and_delete
          <<-LUA
          if redis.call('get', KEYS[1]) == KEYS[2] then
          redis.call('del', KEYS[1])
          end
          LUA
          end
          end


          And to call it:



          RedisLock.new(redis_key: 'key', expiration_ms: 10000).with_lock! { do_something }





          share|improve this answer











          $endgroup$



          Here's a stab at clarifying the intent without changing any of the functionality. I didn't try to run it or anything, so don't hold me to syntax.



          # handles locking and unlocking redis for a particular operation
          class RedisLock
          # thrown when a lock cannot be acquired
          class NotAcquired < StandardError; end

          # since the redis key and ms are used in pretty much every method,
          # might as well go ahead and pass them in the constructor.
          # set defaults here if there are defaults that make sense.
          # make a yardoc comment and describe what these all are.
          def initialize(redis: Redis.current, nx: true, redis_key:, expiration_ms:)
          @redis = redis
          @redis_key = redis_key
          @expiration_ms = expiration_ms
          @nx = nx
          end

          # call it with_lock instead of lock
          # to make it more apparent it accepts a block
          def with_lock!
          raise(NotAcquired, "Could not acquire lock with #{args}") unless lock
          yield
          unlock(redis_key, random_lock_number)
          true # do you really need to return true here?
          end

          def with_lock
          with_lock!
          rescue NotAcquired
          false
          end

          private

          # set instance variables in the initializer, but never call them directly.
          attr_reader :redis, :redis_key, :expiration_ms, :nx

          def lock
          redis.set(redis_key, random_lock_number, nx: nx, px: expiration_ms)
          end

          # number is a little decieving here when you call `.to_s` at the end.
          # can you use SecureRandom.uuid instead?
          def random_lock_number
          @random_lock_number ||= SecureRandom.random_number(100_000_000_000_000_000).to_s
          end

          # no need for this to be exposed publicly.
          # calling eval directly is usually a bad idea.
          def unlock
          redis.eval(redis_check_and_delete, [redis_key, random_lock_number])
          end

          def redis_check_and_delete
          <<-LUA
          if redis.call('get', KEYS[1]) == KEYS[2] then
          redis.call('del', KEYS[1])
          end
          LUA
          end
          end


          And to call it:



          RedisLock.new(redis_key: 'key', expiration_ms: 10000).with_lock! { do_something }






          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited Feb 20 '17 at 7:55

























          answered Feb 20 '17 at 7:50









          Chris DrappierChris Drappier

          255129




          255129












          • $begingroup$
            Hi @Chris! Thanks you for the answer! Though I cannot agree with the suggested improvements. The general concern about it is that the new object cannot be re-used with another key. Moreover it even cannot be re-used with the same key. I also have minor concerns (like nx in parameters makes no sense), but this is not so important after the first point.
            $endgroup$
            – cutalion
            Feb 20 '17 at 12:56










          • $begingroup$
            object instantiation is free. You have no need to to re-use the single object. the Redis connection is already passed in. you've even named the class RedisLock and not something like RedisLockFactory. The major takeaway here, if you care to take away anything is that your methods are doing too much internally. If you're passing arguments into every single method, that's a code smell. also usually the ! version of a method is called by the non-bang version instead of the other way around. good luck.
            $endgroup$
            – Chris Drappier
            Feb 20 '17 at 16:42


















          • $begingroup$
            Hi @Chris! Thanks you for the answer! Though I cannot agree with the suggested improvements. The general concern about it is that the new object cannot be re-used with another key. Moreover it even cannot be re-used with the same key. I also have minor concerns (like nx in parameters makes no sense), but this is not so important after the first point.
            $endgroup$
            – cutalion
            Feb 20 '17 at 12:56










          • $begingroup$
            object instantiation is free. You have no need to to re-use the single object. the Redis connection is already passed in. you've even named the class RedisLock and not something like RedisLockFactory. The major takeaway here, if you care to take away anything is that your methods are doing too much internally. If you're passing arguments into every single method, that's a code smell. also usually the ! version of a method is called by the non-bang version instead of the other way around. good luck.
            $endgroup$
            – Chris Drappier
            Feb 20 '17 at 16:42
















          $begingroup$
          Hi @Chris! Thanks you for the answer! Though I cannot agree with the suggested improvements. The general concern about it is that the new object cannot be re-used with another key. Moreover it even cannot be re-used with the same key. I also have minor concerns (like nx in parameters makes no sense), but this is not so important after the first point.
          $endgroup$
          – cutalion
          Feb 20 '17 at 12:56




          $begingroup$
          Hi @Chris! Thanks you for the answer! Though I cannot agree with the suggested improvements. The general concern about it is that the new object cannot be re-used with another key. Moreover it even cannot be re-used with the same key. I also have minor concerns (like nx in parameters makes no sense), but this is not so important after the first point.
          $endgroup$
          – cutalion
          Feb 20 '17 at 12:56












          $begingroup$
          object instantiation is free. You have no need to to re-use the single object. the Redis connection is already passed in. you've even named the class RedisLock and not something like RedisLockFactory. The major takeaway here, if you care to take away anything is that your methods are doing too much internally. If you're passing arguments into every single method, that's a code smell. also usually the ! version of a method is called by the non-bang version instead of the other way around. good luck.
          $endgroup$
          – Chris Drappier
          Feb 20 '17 at 16:42




          $begingroup$
          object instantiation is free. You have no need to to re-use the single object. the Redis connection is already passed in. you've even named the class RedisLock and not something like RedisLockFactory. The major takeaway here, if you care to take away anything is that your methods are doing too much internally. If you're passing arguments into every single method, that's a code smell. also usually the ! version of a method is called by the non-bang version instead of the other way around. good luck.
          $endgroup$
          – Chris Drappier
          Feb 20 '17 at 16:42


















          draft saved

          draft discarded




















































          Thanks for contributing an answer to Code Review Stack Exchange!


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          Use MathJax to format equations. MathJax reference.


          To learn more, see our tips on writing great answers.




          draft saved


          draft discarded














          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f154936%2fredis-lock-implementation%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          Popular posts from this blog

          is 'sed' thread safeWhat should someone know about using Python scripts in the shell?Nexenta bash script uses...

          How do i solve the “ No module named 'mlxtend' ” issue on Jupyter?

          Pilgersdorf Inhaltsverzeichnis Geografie | Geschichte | Bevölkerungsentwicklung | Politik | Kultur...