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
Is it ok to offer lower paid work as a trial period before negotiating for a full-time job?
Button changing its text & action. Good or terrible?
Does adding complexity mean a more secure cipher?
Old scifi movie from the 50s or 60s with men in solid red uniforms who interrogate a spy from the past
How come people say “Would of”?
Accepted by European university, rejected by all American ones I applied to? Possible reasons?
Are spiders unable to hurt humans, especially very small spiders?
Is an up-to-date browser secure on an out-of-date OS?
Can I have a signal generator on while it's not connected?
Getting crown tickets for Statue of Liberty
Is it possible for absolutely everyone to attain enlightenment?
What to do when moving next to a bird sanctuary with a loosely-domesticated cat?
Why couldn't they take pictures of a closer black hole?
How much of the clove should I use when using big garlic heads?
If a sorcerer casts the Banishment spell on a PC while in Avernus, does the PC return to their home plane?
How to obtain a position of last non-zero element
Can you cast a spell on someone in the Ethereal Plane, if you are on the Material Plane and have the True Seeing spell active?
What is the meaning of Triage in Cybersec world?
Why didn't the Event Horizon Telescope team mention Sagittarius A*?
Geography at the pixel level
Why not take a picture of a closer black hole?
What is preventing me from simply constructing a hash that's lower than the current target?
What do hard-Brexiteers want with respect to the Irish border?
For what reasons would an animal species NOT cross a *horizontal* land bridge?
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;
$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
ruby locking redis
$endgroup$
bumped to the homepage by Community♦ 31 mins ago
This question has answers that may be good or bad; the system has marked it active so that they can be reviewed.
add a comment |
$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
ruby locking redis
$endgroup$
bumped to the homepage by Community♦ 31 mins ago
This question has answers that may be good or bad; the system has marked it active so that they can be reviewed.
add a comment |
$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
ruby locking redis
$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
ruby locking redis
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♦ 31 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♦ 31 mins ago
This question has answers that may be good or bad; the system has marked it active so that they can be reviewed.
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
$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
$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 (likenx
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 classRedisLock
and not something likeRedisLockFactory
. 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
add a comment |
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
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e)
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom))
StackExchange.using('gps', function() StackExchange.gps.track('embedded_signup_form.view', location: 'question_page' ); );
$window.unbind('scroll', onScroll);
;
$window.on('scroll', onScroll);
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
$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
$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 (likenx
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 classRedisLock
and not something likeRedisLockFactory
. 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
add a comment |
$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
$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 (likenx
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 classRedisLock
and not something likeRedisLockFactory
. 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
add a comment |
$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
$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
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 (likenx
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 classRedisLock
and not something likeRedisLockFactory
. 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
add a comment |
$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 (likenx
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 classRedisLock
and not something likeRedisLockFactory
. 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
add a comment |
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.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e)
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom))
StackExchange.using('gps', function() StackExchange.gps.track('embedded_signup_form.view', location: 'question_page' ); );
$window.unbind('scroll', onScroll);
;
$window.on('scroll', onScroll);
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e)
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom))
StackExchange.using('gps', function() StackExchange.gps.track('embedded_signup_form.view', location: 'question_page' ); );
$window.unbind('scroll', onScroll);
;
$window.on('scroll', onScroll);
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e)
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom))
StackExchange.using('gps', function() StackExchange.gps.track('embedded_signup_form.view', location: 'question_page' ); );
$window.unbind('scroll', onScroll);
;
$window.on('scroll', onScroll);
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e)
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom))
StackExchange.using('gps', function() StackExchange.gps.track('embedded_signup_form.view', location: 'question_page' ); );
$window.unbind('scroll', onScroll);
;
$window.on('scroll', onScroll);
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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