locker 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
- ---
2
- SHA512:
3
- metadata.gz: b1f742b2a4f11f79b4f47ba505a8f08bc0014932806e1af33e267a580acdcae3583641ff4ed00d7ce627680ab384c3bfa044a3eabddcaf0740f631c2ed93893b
4
- data.tar.gz: 093dfdf74134fd108e7dd9450b37f80544678ab3662bcb75fd25bc54899d5cafad2d11075f7df64253fab6fae0ef166eeaa1988547c9c118ddba1dfde2e65e30
5
- SHA1:
6
- metadata.gz: 4d31db0d4478e7101d795093e417f9189b8ceaf1
7
- data.tar.gz: b92edff7f04603bfca7b4c6a87be37d3a6f9250a
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8d4fe6495ead44c7d486b9b92e2e3a1e426f544c
4
+ data.tar.gz: 992771712a5be9b554ca67b55c89f7817b3dcd63
5
+ SHA512:
6
+ metadata.gz: a8118488900768eb4350649fd1e03b0ad96dcc16777975163f914b46e6b89ba25a226189e8e4ad864f4b60197d7adccd709402c04384b5ee1885771f455e63c7
7
+ data.tar.gz: b225d2c0d59c66ceaf30a194e76b4a58606bac879adf83e74b85cd6f60561630f1def4ae9fdd6c47f5dbc67596356f81909dca3b7d9c761fd2d13ed60f6144b6
@@ -0,0 +1 @@
1
+ locker
@@ -0,0 +1 @@
1
+ ruby-2.2.1
@@ -2,13 +2,7 @@ language: ruby
2
2
  gemfile:
3
3
  - gemfiles/rails3.gemfile
4
4
  rvm:
5
- - 1.8.7
6
- - ree
7
- - 1.9.2
8
- - 1.9.3
9
- - 2.0.0
10
- - jruby-18mode
5
+ - 2.1.5
6
+ - 2.2.1
11
7
  - jruby-19mode
12
- - rbx-18mode
13
- - rbx-19mode
14
8
  script: bundle exec rspec
data/Gemfile CHANGED
@@ -1,3 +1,8 @@
1
1
  source "https://rubygems.org"
2
2
 
3
- gemspec
3
+ gem "activerecord", ">=3.2", "<5"
4
+
5
+ group :development, :test do
6
+ gem "pg"
7
+ gem "rspec", "~> 3.2.0"
8
+ end
data/README.md CHANGED
@@ -4,17 +4,6 @@ Locker is a locking mechanism for limiting the concurrency of ruby code using th
4
4
 
5
5
  Locker is dependent on Postgres and the ActiveRecord (>= 3.2.0) gem.
6
6
 
7
- **NOTE:** In the next minor version (0.1.0), the generated Locker migration has changed to include a bigint field named `sequence` with a default of zero. Since I'm pretty sure Zencoder is the only one using this gem at the moment, I opted to not include an upgrade path. If you really must, however:
8
-
9
- In Rails 3.x+:
10
-
11
- script/rails generate migration add_sequence_to_locks sequence:bigint
12
-
13
- Then add a line that changes the default of the column to zero and updates the existing records:
14
-
15
- change_column_default :locks, :sequence, 0
16
- execute "UPDATE locks SET sequence = 0"
17
-
18
7
  ## Supported Rubies
19
8
 
20
9
  See [the travis configuration file](https://github.com/zencoder/locker/blob/master/.travis.yml) for which ruby versions we support.
@@ -32,8 +32,6 @@ class Locker
32
32
  def run(&block)
33
33
  connection = ActiveRecord::Base.connection_pool.checkout
34
34
  connection.transaction :requires_new => true do
35
- ensure_unlocked(connection)
36
-
37
35
  while !get(connection) && @blocking
38
36
  sleep 0.5
39
37
  end
@@ -57,7 +55,7 @@ class Locker
57
55
 
58
56
  block.call
59
57
  ensure
60
- release(connection) if @locked
58
+ @locked = false
61
59
  # Using a mutex to synchronize so that we're sure we're not
62
60
  # executing a query when we kill the thread.
63
61
  mutex.synchronize{}
@@ -81,15 +79,6 @@ class Locker
81
79
  @locked = successful_result?(result)
82
80
  end
83
81
 
84
- def release(connection)
85
- result = exec_query(connection, "SELECT pg_advisory_unlock(#{connection.quote(@lockspace)}, #{connection.quote(@crc)})")
86
- successful_result?(result)
87
- end
88
-
89
- def ensure_unlocked(connection)
90
- while release(connection); end
91
- end
92
-
93
82
  def check(connection, thread)
94
83
  if !connection.active?
95
84
  @locked = false
@@ -108,9 +97,7 @@ class Locker
108
97
  end
109
98
 
110
99
  def exec_query(connection, query)
111
- silence_stderr do
112
- connection.exec_query(query, "Locker::Advisory")
113
- end
100
+ connection.exec_query(query, "Locker::Advisory")
114
101
  end
115
102
 
116
103
  end
@@ -60,16 +60,21 @@ class Locker
60
60
  end
61
61
 
62
62
  def get
63
- @locked = update_all(["locked_by = ?, locked_at = clock_timestamp() at time zone 'UTC', locked_until = clock_timestamp() at time zone 'UTC' + #{lock_interval}, sequence = sequence + 1", @identifier],
64
- ["key = ? AND (locked_by IS NULL OR locked_by = ? OR locked_until < clock_timestamp() at time zone 'UTC')", @key, @identifier])
63
+ @locked = updated?(model.
64
+ where(["key = ? AND (locked_by IS NULL OR locked_by = ? OR locked_until < clock_timestamp() at time zone 'UTC')", @key, @identifier]).
65
+ update_all(["locked_by = ?, locked_at = clock_timestamp() at time zone 'UTC', locked_until = clock_timestamp() at time zone 'UTC' + #{lock_interval}, sequence = sequence + 1", @identifier]))
65
66
  end
66
67
 
67
68
  def release
68
- @locked = update_all(["locked_by = NULL"],["key = ? and locked_by = ?", @key, @identifier])
69
+ @locked = updated?(model.
70
+ where(["key = ? and locked_by = ?", @key, @identifier]).
71
+ update_all(["locked_by = NULL"]))
69
72
  end
70
73
 
71
74
  def renew(thread=Thread.current)
72
- @locked = update_all(["locked_until = clock_timestamp() at time zone 'UTC' + #{lock_interval}"], ["key = ? and locked_by = ?", @key, @identifier])
75
+ @locked = updated?(model.
76
+ where(["key = ? and locked_by = ?", @key, @identifier]).
77
+ update_all(["locked_until = clock_timestamp() at time zone 'UTC' + #{lock_interval}"]))
73
78
  thread.raise LockStolen unless @locked
74
79
  @locked
75
80
  end
@@ -96,8 +101,8 @@ private
96
101
  end
97
102
 
98
103
  # Returns a boolean. True if it updates any rows, false if it didn't.
99
- def update_all(*args)
100
- model.update_all(*args) > 0
104
+ def updated?(rows_updated)
105
+ rows_updated > 0
101
106
  end
102
107
 
103
108
  end
@@ -1,3 +1,3 @@
1
1
  class Locker
2
- VERSION = "0.2.1"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -16,7 +16,7 @@ Gem::Specification.new do |s|
16
16
  s.test_files = `git ls-files -- spec/*`.split("\n")
17
17
  s.require_paths = ["lib"]
18
18
 
19
- s.add_dependency "activerecord", ">=3.2", "<4"
20
- s.add_development_dependency "pg"
21
- s.add_development_dependency "rspec"
19
+ s.add_dependency "activerecord", ">=3.2", "<5"
20
+ s.add_development_dependency "pg", "~> 0"
21
+ s.add_development_dependency "rspec", "~> 3.2"
22
22
  end
@@ -5,17 +5,17 @@ describe Locker::Advisory do
5
5
  describe "initialization" do
6
6
  it "should have default values" do
7
7
  advisory = Locker::Advisory.new("foo")
8
- advisory.key.should == "foo"
9
- advisory.crc.should == -1938594527
10
- advisory.lockspace.should == 1
11
- advisory.blocking.should be_false
12
- advisory.locked.should be_false
8
+ expect(advisory.key).to eq("foo")
9
+ expect(advisory.crc).to eq(-1938594527)
10
+ expect(advisory.lockspace).to eq(1)
11
+ expect(advisory.blocking).to be false
12
+ expect(advisory.locked).to be false
13
13
  end
14
14
 
15
15
  it "should have some overridable values" do
16
16
  advisory = Locker::Advisory.new("foo", :lockspace => 2, :blocking => true)
17
- advisory.lockspace.should == 2
18
- advisory.blocking.should be_true
17
+ expect(advisory.lockspace).to eq(2)
18
+ expect(advisory.blocking).to be true
19
19
  end
20
20
 
21
21
  it "should validate key" do
@@ -33,24 +33,26 @@ describe Locker::Advisory do
33
33
  lock1 = false
34
34
  lock2 = false
35
35
 
36
- t = Thread.new do
36
+ t1 = Thread.new do
37
37
  Locker::Advisory.run("foo") do
38
38
  lock1 = true
39
- sleep 1
39
+ sleep 2
40
40
  end
41
41
  end
42
42
 
43
- Thread.pass
44
-
45
- lock2_result = Locker::Advisory.run("foo") do
46
- lock2 = true
43
+ t2 = Thread.new do
44
+ sleep 1
45
+ Locker::Advisory.run("foo") do
46
+ lock2 = true
47
+ end
47
48
  end
48
49
 
49
- lock1_result = t.join
50
- lock1.should be_true
51
- lock2.should be_false
52
- lock1_result.should be_true
53
- lock2_result.should be_false
50
+ lock1_result = t1.value
51
+ lock2_result = t2.value
52
+ expect(lock1).to be true
53
+ expect(lock2).to be false
54
+ expect(lock1_result).to be true
55
+ expect(lock2_result).to be false
54
56
  end
55
57
 
56
58
  it "should release locks after the block is finished" do
@@ -65,10 +67,10 @@ describe Locker::Advisory do
65
67
  lock2 = true
66
68
  end
67
69
 
68
- lock1.should be_true
69
- lock2.should be_true
70
- lock1_result.should be_true
71
- lock2_result.should be_true
70
+ expect(lock1).to be true
71
+ expect(lock2).to be true
72
+ expect(lock1_result).to be true
73
+ expect(lock2_result).to be true
72
74
  end
73
75
  end
74
76
 
@@ -9,30 +9,30 @@ describe Locker do
9
9
  describe "initialization" do
10
10
  it "should have default values" do
11
11
  locker = Locker.new("foo")
12
- locker.key.should == "foo"
13
- locker.renew_every.should == 10
14
- locker.lock_for.should == 30
15
- locker.model.should == Lock
16
- locker.identifier.should match(/^#{Regexp.escape("host:#{Socket.gethostname} pid:#{Process.pid}")} guid:[a-f0-9]+$/)
17
- locker.blocking.should be_false
18
- locker.locked.should be_false
12
+ expect(locker.key).to eq("foo")
13
+ expect(locker.renew_every).to eq(10)
14
+ expect(locker.lock_for).to eq(30)
15
+ expect(locker.model).to eq(Lock)
16
+ expect(locker.identifier).to match(/^#{Regexp.escape("host:#{Socket.gethostname} pid:#{Process.pid}")} guid:[a-f0-9]+$/)
17
+ expect(locker.blocking).to be false
18
+ expect(locker.locked).to be false
19
19
  end
20
20
 
21
21
  it "should have some overridable values" do
22
22
  locker = Locker.new("bar", :renew_every => 20.seconds, :lock_for => 1.minute, :blocking => true, :model => FakeLock)
23
- locker.key.should == "bar"
24
- locker.renew_every.should == 20
25
- locker.lock_for.should == 60
26
- locker.model.should == FakeLock
27
- locker.identifier.should match(/^#{Regexp.escape("host:#{Socket.gethostname} pid:#{Process.pid}")} guid:[a-f0-9]+$/)
28
- locker.blocking.should be_true
29
- locker.locked.should be_false
23
+ expect(locker.key).to eq("bar")
24
+ expect(locker.renew_every).to eq(20)
25
+ expect(locker.lock_for).to eq(60)
26
+ expect(locker.model).to eq(FakeLock)
27
+ expect(locker.identifier).to match(/^#{Regexp.escape("host:#{Socket.gethostname} pid:#{Process.pid}")} guid:[a-f0-9]+$/)
28
+ expect(locker.blocking).to be true
29
+ expect(locker.locked).to be false
30
30
  end
31
31
 
32
32
  it "should ensure that the key exists" do
33
- Lock.find_by_key("baz").should be_nil
33
+ expect(Lock.find_by_key("baz")).to be_nil
34
34
  Locker.new("baz", :renew_every => 20.seconds, :lock_for => 1.minute, :blocking => true)
35
- Lock.find_by_key("baz").should_not be_nil
35
+ expect(Lock.find_by_key("baz")).to_not be_nil
36
36
  Locker.new("baz", :renew_every => 20.seconds, :lock_for => 1.minute, :blocking => true)
37
37
  end
38
38
 
@@ -48,30 +48,31 @@ describe Locker do
48
48
  describe "locking" do
49
49
  it "should lock a record" do
50
50
  locker = Locker.new("foo")
51
- locker.get.should be_true
51
+ expect(locker.get).to be true
52
+
52
53
  lock = Lock.find_by_key("foo")
53
- lock.locked_until.should be <= (Time.now.utc + locker.lock_for)
54
- lock.locked_by.should == locker.identifier
55
- lock.locked_at.should be < Time.now.utc
54
+ expect(lock.locked_until).to be <= (Time.now.utc + locker.lock_for)
55
+ expect(lock.locked_by).to eq(locker.identifier)
56
+ expect(lock.locked_at).to be < Time.now.utc
56
57
  end
57
58
 
58
59
  it "should renew a lock" do
59
60
  locker = Locker.new("foo")
60
- locker.get.should be_true
61
+ expect(locker.get).to be true
61
62
  lock = Lock.find_by_key("foo")
62
- lock.locked_until.should be <= (Time.now.utc + locker.lock_for)
63
- lock.locked_by.should == locker.identifier
64
- lock.locked_at.should be < Time.now.utc
65
- locker.renew.should be_true
63
+ expect(lock.locked_until).to be <= (Time.now.utc + locker.lock_for)
64
+ expect(lock.locked_by).to eq(locker.identifier)
65
+ expect(lock.locked_at).to be < Time.now.utc
66
+ expect(locker.renew).to be true
66
67
  lock = Lock.find_by_key("foo")
67
- lock.locked_until.should be <= (Time.now.utc + locker.lock_for)
68
- lock.locked_by.should == locker.identifier
69
- lock.locked_at.should be < Time.now.utc
68
+ expect(lock.locked_until).to be <= (Time.now.utc + locker.lock_for)
69
+ expect(lock.locked_by).to eq(locker.identifier)
70
+ expect(lock.locked_at).to be < Time.now.utc
70
71
  end
71
72
 
72
73
  it "should raise when someone steals the lock" do
73
74
  locker = Locker.new("foo")
74
- locker.get.should be_true
75
+ expect(locker.get).to be true
75
76
  lock = Lock.find_by_key("foo")
76
77
  lock.update_attribute(:locked_by, "someone else")
77
78
  expect{ locker.renew }.to raise_error(Locker::LockStolen)
@@ -91,7 +92,6 @@ describe Locker do
91
92
  Locker.run("foo") do
92
93
  value = 1
93
94
  end
94
-
95
95
  expect(value).to eq(1)
96
96
  end
97
97
 
@@ -118,7 +118,7 @@ describe Locker do
118
118
  @second_locker.run(true){"something innocuous"}
119
119
  end_time = Time.now.to_f
120
120
  time_ran = (end_time - start_time)
121
- time_ran.should be >= 0.6, "Oops, time was #{end_time-start_time} seconds"
121
+ expect(time_ran).to be >= 0.6, "Oops, time was #{end_time-start_time} seconds"
122
122
  end
123
123
  end
124
124
 
@@ -130,17 +130,17 @@ describe Locker do
130
130
  end
131
131
 
132
132
  it "should return false when we can't obtain the lock" do
133
- @second_locker.run{raise "SHOULD NOT RUN KTHXBAI"}.should be_false
134
- @first_locker.run{ "something" }.should be_true
133
+ expect(@second_locker.run{raise "SHOULD NOT RUN KTHXBAI"}).to be false
134
+ expect(@first_locker.run{ "something" }).to be true
135
135
  end
136
136
 
137
137
  it "should take less than half a second to fail" do
138
138
  start_time = Time.now.to_f
139
139
  return_value = @second_locker.run{raise "SHOULD NOT RUN KTHXBAI"}
140
140
  end_time = Time.now.to_f
141
- return_value.should be_false
141
+ expect(return_value).to be false
142
142
  run_time = (end_time - start_time)
143
- run_time.should be < 0.5
143
+ expect(run_time).to be < 0.5
144
144
  end
145
145
  end
146
146
 
@@ -8,7 +8,7 @@ require 'active_record'
8
8
  require 'locker'
9
9
 
10
10
  ActiveRecord::Base.time_zone_aware_attributes = true
11
- ActiveRecord::Base.default_timezone = "UTC"
11
+ ActiveRecord::Base.default_timezone = :utc
12
12
 
13
13
  if File.exist?(File.join(File.dirname(__FILE__), 'database.yml'))
14
14
  config = YAML.load_file(File.join(File.dirname(__FILE__), 'database.yml'))
@@ -77,11 +77,19 @@ end
77
77
 
78
78
  RSpec.configure do |c|
79
79
  c.before do
80
- ActiveRecord::Base.connection.increment_open_transactions
81
- ActiveRecord::Base.connection.begin_db_transaction
80
+ if ActiveRecord::VERSION::MAJOR < 4
81
+ ActiveRecord::Base.connection.increment_open_transactions
82
+ ActiveRecord::Base.connection.begin_db_transaction
83
+ else
84
+ ActiveRecord::Base.connection.begin_transaction
85
+ end
82
86
  end
83
87
  c.after do
84
- ActiveRecord::Base.connection.rollback_db_transaction
85
- ActiveRecord::Base.connection.decrement_open_transactions
88
+ if ActiveRecord::VERSION::MAJOR < 4
89
+ ActiveRecord::Base.connection.rollback_db_transaction
90
+ ActiveRecord::Base.connection.decrement_open_transactions
91
+ else
92
+ ActiveRecord::Base.connection.rollback_transaction
93
+ end
86
94
  end
87
95
  end
metadata CHANGED
@@ -1,62 +1,77 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: locker
3
- version: !ruby/object:Gem::Version
4
- version: 0.2.1
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.0
5
5
  platform: ruby
6
- authors:
6
+ authors:
7
7
  - Nathan Sutton
8
8
  - Justin Greer
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
-
13
- date: 2014-05-10 00:00:00 Z
14
- dependencies:
15
- - !ruby/object:Gem::Dependency
12
+ date: 2015-04-03 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
16
15
  name: activerecord
17
- prerelease: false
18
- requirement: &id001 !ruby/object:Gem::Requirement
19
- requirements:
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
20
18
  - - ">="
21
- - !ruby/object:Gem::Version
22
- version: "3.2"
23
- - - <
24
- - !ruby/object:Gem::Version
25
- version: "4"
19
+ - !ruby/object:Gem::Version
20
+ version: '3.2'
21
+ - - "<"
22
+ - !ruby/object:Gem::Version
23
+ version: '5'
26
24
  type: :runtime
27
- version_requirements: *id001
28
- - !ruby/object:Gem::Dependency
29
- name: pg
30
25
  prerelease: false
31
- requirement: &id002 !ruby/object:Gem::Requirement
32
- requirements:
33
- - &id003
34
- - ">="
35
- - !ruby/object:Gem::Version
36
- version: "0"
26
+ version_requirements: !ruby/object:Gem::Requirement
27
+ requirements:
28
+ - - ">="
29
+ - !ruby/object:Gem::Version
30
+ version: '3.2'
31
+ - - "<"
32
+ - !ruby/object:Gem::Version
33
+ version: '5'
34
+ - !ruby/object:Gem::Dependency
35
+ name: pg
36
+ requirement: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
37
41
  type: :development
38
- version_requirements: *id002
39
- - !ruby/object:Gem::Dependency
40
- name: rspec
41
42
  prerelease: false
42
- requirement: &id004 !ruby/object:Gem::Requirement
43
- requirements:
44
- - *id003
43
+ version_requirements: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ - !ruby/object:Gem::Dependency
49
+ name: rspec
50
+ requirement: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.2'
45
55
  type: :development
46
- version_requirements: *id004
47
- description: Locker is a locking mechanism for limiting the concurrency of ruby code using the database. It presently only works with PostgreSQL.
48
- email:
56
+ prerelease: false
57
+ version_requirements: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.2'
62
+ description: Locker is a locking mechanism for limiting the concurrency of ruby code
63
+ using the database. It presently only works with PostgreSQL.
64
+ email:
49
65
  - nate@zencoder.com
50
66
  - justin@zencoder.com
51
67
  executables: []
52
-
53
68
  extensions: []
54
-
55
69
  extra_rdoc_files: []
56
-
57
- files:
58
- - .gitignore
59
- - .travis.yml
70
+ files:
71
+ - ".gitignore"
72
+ - ".ruby-gemset"
73
+ - ".ruby-version"
74
+ - ".travis.yml"
60
75
  - CHANGELOG.md
61
76
  - Gemfile
62
77
  - LICENSE
@@ -81,29 +96,29 @@ files:
81
96
  - spec/spec_helper.rb
82
97
  homepage:
83
98
  licenses: []
84
-
85
99
  metadata: {}
86
-
87
100
  post_install_message:
88
101
  rdoc_options: []
89
-
90
- require_paths:
102
+ require_paths:
91
103
  - lib
92
- required_ruby_version: !ruby/object:Gem::Requirement
93
- requirements:
94
- - *id003
95
- required_rubygems_version: !ruby/object:Gem::Requirement
96
- requirements:
97
- - *id003
104
+ required_ruby_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ required_rubygems_version: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ version: '0'
98
114
  requirements: []
99
-
100
115
  rubyforge_project: locker
101
- rubygems_version: 2.0.14
116
+ rubygems_version: 2.4.6
102
117
  signing_key:
103
118
  specification_version: 4
104
- summary: Locker is a locking mechanism for limiting the concurrency of ruby code using the database.
105
- test_files:
119
+ summary: Locker is a locking mechanism for limiting the concurrency of ruby code using
120
+ the database.
121
+ test_files:
106
122
  - spec/locker/advisory_spec.rb
107
123
  - spec/locker/locker_spec.rb
108
124
  - spec/spec_helper.rb
109
- has_rdoc: