locker 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: