From fdb0b7f9d5b6aa823bd3a09e96600b2bf30aef60 Mon Sep 17 00:00:00 2001 From: Ian Remillard Date: Fri, 28 Sep 2018 12:43:52 -0700 Subject: [PATCH 1/5] adds lock ttl and lock_release_removes_key --- lib/suo/client/base.rb | 11 +++++++++-- lib/suo/client/memcached.rb | 6 +++++- lib/suo/client/redis.rb | 6 +++++- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/lib/suo/client/base.rb b/lib/suo/client/base.rb index 7f37355..3427a20 100644 --- a/lib/suo/client/base.rb +++ b/lib/suo/client/base.rb @@ -5,7 +5,9 @@ module Suo acquisition_timeout: 0.1, acquisition_delay: 0.01, stale_lock_expiration: 3600, - resources: 1 + resources: 1, + lock_release_removes_key: false, + ttl: nil, }.freeze BLANK_STR = "".freeze @@ -81,7 +83,12 @@ module Suo acquisition_lock = remove_lock(cleared_locks, token) break unless acquisition_lock - break if set(serialize_locks(cleared_locks), cas) + + if @options[:lock_release_removes_key] && cleared_locks.empty? + break if clear + else + break if set(serialize_locks(cleared_locks), cas) + end end rescue LockClientError => _ # rubocop:disable Lint/HandleExceptions # ignore - assume success due to optimistic locking diff --git a/lib/suo/client/memcached.rb b/lib/suo/client/memcached.rb index 83b37b6..3e15d75 100644 --- a/lib/suo/client/memcached.rb +++ b/lib/suo/client/memcached.rb @@ -17,7 +17,11 @@ module Suo end def set(newval, cas) - @client.set_cas(@key, newval, cas) + if @options[:ttl] + @client.set_cas(@key, newval, cas, {ttl: @options[:ttl]}) + else + @client.set_cas(@key, newval, cas) + end end def initial_set(val = BLANK_STR) diff --git a/lib/suo/client/redis.rb b/lib/suo/client/redis.rb index b13119e..ff736e5 100644 --- a/lib/suo/client/redis.rb +++ b/lib/suo/client/redis.rb @@ -20,7 +20,11 @@ module Suo def set(newval, _) ret = @client.multi do |multi| - multi.set(@key, newval) + if @options[:ttl] + multi.setex(@key, @options[:ttl], newval) + else + multi.set(@key, newval) + end end ret && ret[0] == OK_STR From aa4da5d739097a94749d155c80ba54e950153d5e Mon Sep 17 00:00:00 2001 From: Ian Remillard Date: Fri, 28 Sep 2018 13:19:01 -0700 Subject: [PATCH 2/5] update docs for new options --- README.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/README.md b/README.md index 4f2420c..69a04bc 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,22 @@ suo.lock do |token| end ``` +### Time To Live + +```ruby +Suo::Client::Redis.new("bar_resource", ttl: 10) #ttl in seconds +``` + +All locks are expired and the lock key is removed after the specified `ttl` time runs out. This would be the time since any resource for a given key was locked or unlocked. + +### Lock Release Removes Key + +```ruby +Suo::Client::Redis.new("bar_resource", lock_release_removes_key: true) +``` + +Normally, a key representing a set of resource locks is persisted indefinitely even when no resources are currently locked. When `lock_release_removes_key` is set to `true`, the key is removed when the last resource lock is released. This also means that when another lock is acquired the key has to be recreated. + ## TODO - more race condition tests From 6be3a5bdda5a376673e99243f84c5547cd20db92 Mon Sep 17 00:00:00 2001 From: Ian Remillard Date: Fri, 28 Sep 2018 16:43:44 -0700 Subject: [PATCH 3/5] edits to docs --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 69a04bc..7136f54 100644 --- a/README.md +++ b/README.md @@ -78,7 +78,7 @@ end Suo::Client::Redis.new("bar_resource", ttl: 10) #ttl in seconds ``` -All locks are expired and the lock key is removed after the specified `ttl` time runs out. This would be the time since any resource for a given key was locked or unlocked. +All locks are expired and the lock key is removed after the specified `ttl` time runs out. This is counted as the time since any resource for a given key was locked or unlocked. ### Lock Release Removes Key @@ -86,7 +86,7 @@ All locks are expired and the lock key is removed after the specified `ttl` time Suo::Client::Redis.new("bar_resource", lock_release_removes_key: true) ``` -Normally, a key representing a set of resource locks is persisted indefinitely even when no resources are currently locked. When `lock_release_removes_key` is set to `true`, the key is removed when the last resource lock is released. This also means that when another lock is acquired the key has to be recreated. +By default, a key representing a set of resource locks is persisted indefinitely even when no resources are currently locked. When `lock_release_removes_key` is set to `true`, the key is removed when the last resource lock is released. This also means that when another lock is acquired the key has to be recreated. ## TODO - more race condition tests From 1022a6f9d36476c756143179c9c2416be7aad025 Mon Sep 17 00:00:00 2001 From: Ian Remillard Date: Mon, 1 Oct 2018 10:35:30 -0700 Subject: [PATCH 4/5] move to expire only when all locks are released --- README.md | 11 ++--------- lib/suo/client/base.rb | 12 +++--------- lib/suo/client/memcached.rb | 6 +++--- lib/suo/client/redis.rb | 4 ++-- 4 files changed, 10 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 7136f54..4a3923f 100644 --- a/README.md +++ b/README.md @@ -75,18 +75,11 @@ end ### Time To Live ```ruby -Suo::Client::Redis.new("bar_resource", ttl: 10) #ttl in seconds +Suo::Client::Redis.new("bar_resource", ttl: 60) #ttl in seconds ``` -All locks are expired and the lock key is removed after the specified `ttl` time runs out. This is counted as the time since any resource for a given key was locked or unlocked. +A key representing a set of lockable resources is removed once the last resource lock is released and the `ttl` time runs out. When another lock is acquired and the key has been removed the key has to be recreated. -### Lock Release Removes Key - -```ruby -Suo::Client::Redis.new("bar_resource", lock_release_removes_key: true) -``` - -By default, a key representing a set of resource locks is persisted indefinitely even when no resources are currently locked. When `lock_release_removes_key` is set to `true`, the key is removed when the last resource lock is released. This also means that when another lock is acquired the key has to be recreated. ## TODO - more race condition tests diff --git a/lib/suo/client/base.rb b/lib/suo/client/base.rb index 3427a20..c439575 100644 --- a/lib/suo/client/base.rb +++ b/lib/suo/client/base.rb @@ -6,8 +6,7 @@ module Suo acquisition_delay: 0.01, stale_lock_expiration: 3600, resources: 1, - lock_release_removes_key: false, - ttl: nil, + ttl: 60, }.freeze BLANK_STR = "".freeze @@ -66,7 +65,7 @@ module Suo refresh_lock(cleared_locks, token) - break if set(serialize_locks(cleared_locks), cas) + break if set(serialize_locks(cleared_locks), cas, expire: cleared_locks.empty?) end end @@ -83,12 +82,7 @@ module Suo acquisition_lock = remove_lock(cleared_locks, token) break unless acquisition_lock - - if @options[:lock_release_removes_key] && cleared_locks.empty? - break if clear - else - break if set(serialize_locks(cleared_locks), cas) - end + break if set(serialize_locks(cleared_locks), cas, expire: cleared_locks.empty?) end rescue LockClientError => _ # rubocop:disable Lint/HandleExceptions # ignore - assume success due to optimistic locking diff --git a/lib/suo/client/memcached.rb b/lib/suo/client/memcached.rb index 3e15d75..2aabc8a 100644 --- a/lib/suo/client/memcached.rb +++ b/lib/suo/client/memcached.rb @@ -16,9 +16,9 @@ module Suo @client.get_cas(@key) end - def set(newval, cas) - if @options[:ttl] - @client.set_cas(@key, newval, cas, {ttl: @options[:ttl]}) + def set(newval, cas, expire:) + if expire + @client.set_cas(@key, newval, cas, @options[:ttl]) else @client.set_cas(@key, newval, cas) end diff --git a/lib/suo/client/redis.rb b/lib/suo/client/redis.rb index ff736e5..98861f9 100644 --- a/lib/suo/client/redis.rb +++ b/lib/suo/client/redis.rb @@ -18,9 +18,9 @@ module Suo [@client.get(@key), nil] end - def set(newval, _) + def set(newval, _, expire:) ret = @client.multi do |multi| - if @options[:ttl] + if expire multi.setex(@key, @options[:ttl], newval) else multi.set(@key, newval) From ca46f5f3694c29199cb22fcd3b414353923c519d Mon Sep 17 00:00:00 2001 From: Ian Remillard Date: Mon, 1 Oct 2018 10:48:50 -0700 Subject: [PATCH 5/5] add default for expire on set --- lib/suo/client/memcached.rb | 2 +- lib/suo/client/redis.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/suo/client/memcached.rb b/lib/suo/client/memcached.rb index 2aabc8a..09f5aa7 100644 --- a/lib/suo/client/memcached.rb +++ b/lib/suo/client/memcached.rb @@ -16,7 +16,7 @@ module Suo @client.get_cas(@key) end - def set(newval, cas, expire:) + def set(newval, cas, expire: false) if expire @client.set_cas(@key, newval, cas, @options[:ttl]) else diff --git a/lib/suo/client/redis.rb b/lib/suo/client/redis.rb index 98861f9..7bd740a 100644 --- a/lib/suo/client/redis.rb +++ b/lib/suo/client/redis.rb @@ -18,7 +18,7 @@ module Suo [@client.get(@key), nil] end - def set(newval, _, expire:) + def set(newval, _, expire: false) ret = @client.multi do |multi| if expire multi.setex(@key, @options[:ttl], newval)