9 Commits

Author SHA1 Message Date
Nick Elser
d8f8350d1c check timeout at the entry of the loop 2015-04-12 15:57:32 -07:00
Nick Elser
1668756a48 update changelog 2015-04-12 15:45:36 -07:00
Nick Elser
74e9e3de75 add build status 2015-04-12 15:41:31 -07:00
Nick Elser
75aad64c08 add minitest dependency 2015-04-12 15:35:37 -07:00
Nick Elser
2eb56a8eaa more gemspec updates 2015-04-12 15:34:05 -07:00
Nick Elser
d260160618 make test the default rake command 2015-04-12 15:31:19 -07:00
Nick Elser
57fad16e4b add license 2015-04-12 15:30:12 -07:00
Nick Elser
30639cae72 use msgpack for efficiency 2015-04-12 15:28:53 -07:00
Nick Elser
89061170ea start servers for travis 2015-04-12 15:28:35 -07:00
11 changed files with 83 additions and 47 deletions

View File

@@ -1,3 +1,6 @@
language: ruby language: ruby
rvm: rvm:
- 2.2.0 - 2.2.0
services:
- memcached
- redis-server

View File

@@ -1,3 +1,8 @@
## 0.1.1
- Use [MessagePack](https://github.com/msgpack/msgpack-ruby) for semaphore serialization.
## 0.1.0 ## 0.1.0
- First release - First release.

22
LICENSE.txt Normal file
View File

@@ -0,0 +1,22 @@
Copyright (c) 2015 Nick Elser
MIT License
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -1,4 +1,4 @@
# Suo # Suo [![Build Status](https://travis-ci.org/nickelser/suo.png?branch=master)](https://travis-ci.org/nickelser/suo)
:lock: Distributed semaphores using Memcached or Redis in Ruby. :lock: Distributed semaphores using Memcached or Redis in Ruby.

View File

@@ -1,7 +1,8 @@
require "bundler/gem_tasks" require "bundler/gem_tasks"
require "rake/testtask" require "rake/testtask"
task default: :test
Rake::TestTask.new do |t| Rake::TestTask.new do |t|
t.libs << "test" t.libs << "test"
t.pattern = "test/*_test.rb" t.pattern = "test/**/*_test.rb"
end end

View File

@@ -82,14 +82,15 @@ module Suo
private private
def serialize_locks(locks) def serialize_locks(locks)
locks.map { |time, token| [time.to_f, token].join(":") }.join(",") MessagePack.pack(locks.map { |time, token| [time.to_f, token] })
end end
def deserialize_locks(str) def deserialize_locks(val)
str.split(",").map do |s| MessagePack.unpack(val).map do |time, token|
time, token = s.split(":", 2) [Time.at(time), token]
[Time.at(time.to_f), token]
end end
rescue EOFError => _
[]
end end
def clear_expired_locks(locks, options) def clear_expired_locks(locks, options)

View File

@@ -16,7 +16,12 @@ module Suo
begin begin
start = Time.now.to_f start = Time.now.to_f
options[:retry_count].times do |i| options[:retry_count].times do
if options[:retry_timeout]
now = Time.now.to_f
break if now - start > options[:retry_timeout]
end
val, cas = client.get_cas(key) val, cas = client.get_cas(key)
# no key has been set yet; we could simply set it, but would lead to race conditions on the initial setting # no key has been set yet; we could simply set it, but would lead to race conditions on the initial setting
@@ -38,11 +43,6 @@ module Suo
end end
end end
if options[:retry_timeout]
now = Time.now.to_f
break if now - start > options[:retry_timeout]
end
sleep(rand(options[:retry_delay] * 1000).to_f / 1000) sleep(rand(options[:retry_delay] * 1000).to_f / 1000)
end end
rescue => _ rescue => _
@@ -60,6 +60,11 @@ module Suo
start = Time.now.to_f start = Time.now.to_f
options[:retry_count].times do options[:retry_count].times do
if options[:retry_timeout]
now = Time.now.to_f
break if now - start > options[:retry_timeout]
end
val, cas = client.get_cas(key) val, cas = client.get_cas(key)
# much like with initial set - ensure the key is here # much like with initial set - ensure the key is here
@@ -76,11 +81,6 @@ module Suo
break if client.set_cas(key, newval, cas) break if client.set_cas(key, newval, cas)
if options[:retry_timeout]
now = Time.now.to_f
break if now - start > options[:retry_timeout]
end
sleep(rand(options[:retry_delay] * 1000).to_f / 1000) sleep(rand(options[:retry_delay] * 1000).to_f / 1000)
end end
rescue => _ rescue => _
@@ -98,6 +98,11 @@ module Suo
start = Time.now.to_f start = Time.now.to_f
options[:retry_count].times do options[:retry_count].times do
if options[:retry_timeout]
now = Time.now.to_f
break if now - start > options[:retry_timeout]
end
val, cas = client.get_cas(key) val, cas = client.get_cas(key)
break if val.nil? # lock has expired totally break if val.nil? # lock has expired totally
@@ -114,11 +119,6 @@ module Suo
# another client cleared a token in the interim - try again! # another client cleared a token in the interim - try again!
if options[:retry_timeout]
now = Time.now.to_f
break if now - start > options[:retry_timeout]
end
sleep(rand(options[:retry_delay] * 1000).to_f / 1000) sleep(rand(options[:retry_delay] * 1000).to_f / 1000)
end end
rescue => boom # rubocop:disable Lint/HandleExceptions rescue => boom # rubocop:disable Lint/HandleExceptions

View File

@@ -17,6 +17,11 @@ module Suo
start = Time.now.to_f start = Time.now.to_f
options[:retry_count].times do options[:retry_count].times do
if options[:retry_timeout]
now = Time.now.to_f
break if now - start > options[:retry_timeout]
end
client.watch(key) do client.watch(key) do
begin begin
val = client.get(key) val = client.get(key)
@@ -41,15 +46,9 @@ module Suo
break if acquisition_token break if acquisition_token
if options[:retry_timeout]
now = Time.now.to_f
break if now - start > options[:retry_timeout]
end
sleep(rand(options[:retry_delay] * 1000).to_f / 1000) sleep(rand(options[:retry_delay] * 1000).to_f / 1000)
end end
rescue => boom rescue => _
raise boom
raise Suo::Client::FailedToAcquireLock raise Suo::Client::FailedToAcquireLock
end end
@@ -65,6 +64,11 @@ module Suo
start = Time.now.to_f start = Time.now.to_f
options[:retry_count].times do options[:retry_count].times do
if options[:retry_timeout]
now = Time.now.to_f
break if now - start > options[:retry_timeout]
end
client.watch(key) do client.watch(key) do
begin begin
val = client.get(key) val = client.get(key)
@@ -87,11 +91,6 @@ module Suo
break if refreshed break if refreshed
if options[:retry_timeout]
now = Time.now.to_f
break if now - start > options[:retry_timeout]
end
sleep(rand(options[:retry_delay] * 1000).to_f / 1000) sleep(rand(options[:retry_delay] * 1000).to_f / 1000)
end end
rescue => _ rescue => _
@@ -111,6 +110,11 @@ module Suo
options[:retry_count].times do options[:retry_count].times do
cleared = false cleared = false
if options[:retry_timeout]
now = Time.now.to_f
break if now - start > options[:retry_timeout]
end
client.watch(key) do client.watch(key) do
begin begin
val = client.get(key) val = client.get(key)
@@ -144,11 +148,6 @@ module Suo
break if cleared break if cleared
if options[:retry_timeout]
now = Time.now.to_f
break if now - start > options[:retry_timeout]
end
sleep(rand(options[:retry_delay] * 1000).to_f / 1000) sleep(rand(options[:retry_delay] * 1000).to_f / 1000)
end end
rescue => boom # rubocop:disable Lint/HandleExceptions rescue => boom # rubocop:disable Lint/HandleExceptions

View File

@@ -6,6 +6,8 @@ require "dalli/cas/client"
require "redis" require "redis"
require "msgpack"
require "suo/client/errors" require "suo/client/errors"
require "suo/client/base" require "suo/client/base"
require "suo/client/memcached" require "suo/client/memcached"

View File

@@ -1,3 +1,3 @@
module Suo module Suo
VERSION = "0.1.0" VERSION = "0.1.1"
end end

View File

@@ -1,7 +1,7 @@
# coding: utf-8 # coding: utf-8
lib = File.expand_path('../lib', __FILE__) lib = File.expand_path("../lib", __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'suo/version' require "suo/version"
Gem::Specification.new do |spec| Gem::Specification.new do |spec|
spec.name = "suo" spec.name = "suo"
@@ -10,12 +10,14 @@ Gem::Specification.new do |spec|
spec.email = ["nick.elser@gmail.com"] spec.email = ["nick.elser@gmail.com"]
spec.summary = %q(Distributed semaphores using Memcached or Redis.) spec.summary = %q(Distributed semaphores using Memcached or Redis.)
# spec.description = %q{TODO: Long description} spec.description = %q(Distributed semaphores using Memcached or Redis.)
spec.homepage = "https://github.com/nickelser/suo" spec.homepage = "https://github.com/nickelser/suo"
spec.license = "MIT"
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } spec.files = `git ls-files -z`.split("\x0")
spec.bindir = "bin" spec.bindir = "bin"
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
spec.require_paths = ["lib"] spec.require_paths = ["lib"]
spec.add_dependency "dalli" spec.add_dependency "dalli"
@@ -25,4 +27,5 @@ Gem::Specification.new do |spec|
spec.add_development_dependency "bundler", "~> 1.5" spec.add_development_dependency "bundler", "~> 1.5"
spec.add_development_dependency "rake", "~> 10.0" spec.add_development_dependency "rake", "~> 10.0"
spec.add_development_dependency "rubocop", "~> 0.30.0" spec.add_development_dependency "rubocop", "~> 0.30.0"
spec.add_development_dependency "minitest", "~> 5.5.0"
end end