locker 0.0.3 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -4,3 +4,4 @@ Gemfile.lock
4
4
  pkg/*
5
5
  /spec/database.yml
6
6
  .rvmrc
7
+ /gemfiles/*.lock
data/.travis.yml ADDED
@@ -0,0 +1,15 @@
1
+ language: ruby
2
+ gemfile:
3
+ - gemfiles/rails2.gemfile
4
+ - gemfiles/rails3.gemfile
5
+ rvm:
6
+ - 1.8.7
7
+ - ree
8
+ - 1.9.2
9
+ - 1.9.3
10
+ - 2.0.0
11
+ - jruby-18mode
12
+ - jruby-19mode
13
+ - rbx-18mode
14
+ - rbx-19mode
15
+ script: bundle exec rspec
data/Gemfile CHANGED
@@ -1,3 +1,3 @@
1
- source "http://rubygems.org"
1
+ source "https://rubygems.org"
2
2
 
3
3
  gemspec
data/README.md CHANGED
@@ -1,9 +1,19 @@
1
- # Locker
1
+ # Locker [![Build Status](https://travis-ci.org/zencoder/locker.png)](https://travis-ci.org/zencoder/locker)
2
2
 
3
3
  Locker is a locking mechanism for limiting the concurrency of ruby code using the database.
4
4
 
5
5
  Locker is dependent on Postgres and the ActiveRecord (>= 2.3.14) 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 2.3.x+:
10
+
11
+ script/generate migration add_sequence_to_locks sequence:bigint
12
+
13
+ In Rails 3.x+:
14
+
15
+ script/rails generate migration add_sequence_to_locks sequence:bigint
16
+
7
17
  ## The Basics
8
18
 
9
19
  In its simplest form it can be used as follows:
@@ -137,6 +147,14 @@ Locker.run("some-unique-key", :model => SomeOtherLockModel) do
137
147
  end
138
148
  ```
139
149
 
150
+ If you need to know how many times the lock has been acquired, this is available as well.
151
+
152
+ ```ruby
153
+ Locker.run("some-unique-key") do |sequence|
154
+ # sequence is the number of times the lock has been acquired
155
+ end
156
+ ```
157
+
140
158
  ## A Common pattern
141
159
 
142
160
  In our use we've settled on a common pattern, one that lets us distribute the load of our processes between our application and/or utility servers while making sure we have no single point of failure. This means that no single server going down (except the database) will stop the code from executing. Continuing from the code above, we'll use the example of the RSS/Atom feed checker, `FeedChecker.check_for_new_feeds`. To improve on the previous examples, we'll make the code rotate among our servers, so over a long enough time period each server will have spent an equal amount of time running the task.
@@ -0,0 +1,15 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem "activerecord", ">=2.3.14", "<3"
4
+
5
+ platforms :jruby do
6
+ gem 'activerecord-jdbcpostgresql-adapter'
7
+ end
8
+
9
+ platforms :ruby do
10
+ gem "pg"
11
+ end
12
+
13
+ group :development, :test do
14
+ gem "rspec"
15
+ end
@@ -0,0 +1,15 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem "activerecord", ">=3.2", "<4"
4
+
5
+ platforms :jruby do
6
+ gem 'activerecord-jdbcpostgresql-adapter'
7
+ end
8
+
9
+ platforms :ruby do
10
+ gem "pg"
11
+ end
12
+
13
+ group :development, :test do
14
+ gem "rspec"
15
+ end
@@ -1,9 +1,10 @@
1
1
  class Create<%= plural_name.camelize %> < ActiveRecord::Migration
2
- <%- if ActiveRecord::VERSION::MAJOR == 3 && ActiveRecord::VERSION::MINOR == 1 -%>
2
+ <%- if ActiveRecord::VERSION::MAJOR > 3 || ActiveRecord::VERSION::MAJOR == 3 && ActiveRecord::VERSION::MINOR >= 1 -%>
3
3
  def change
4
4
  create_table :<%= plural_name %> do |t|
5
5
  t.string :locked_by
6
6
  t.string :key
7
+ t.integer :sequence, :default => 0, :limit => 8
7
8
  t.datetime :locked_at
8
9
  t.datetime :locked_until
9
10
  end
@@ -15,6 +16,7 @@ class Create<%= plural_name.camelize %> < ActiveRecord::Migration
15
16
  create_table :<%= plural_name %> do |t|
16
17
  t.string :locked_by
17
18
  t.string :key
19
+ t.integer :sequence, :default => 0, :limit => 8
18
20
  t.datetime :locked_at
19
21
  t.datetime :locked_until
20
22
  end
data/lib/locker/locker.rb CHANGED
@@ -1,10 +1,9 @@
1
+ require "socket"
2
+ require "securerandom"
3
+
1
4
  class Locker
2
5
  class LockStolen < StandardError; end
3
6
 
4
- if !defined?(SecureRandom)
5
- SecureRandom = ActiveSupport::SecureRandom
6
- end
7
-
8
7
  attr_accessor :identifier, :key, :renew_every, :lock_for, :model, :locked, :blocking
9
8
 
10
9
  class << self
@@ -48,7 +47,7 @@ class Locker
48
47
  end
49
48
  end
50
49
 
51
- block.call
50
+ block.call(sequence)
52
51
  ensure
53
52
  renewer.exit rescue nil
54
53
  release if @locked
@@ -61,7 +60,7 @@ class Locker
61
60
  end
62
61
 
63
62
  def get
64
- @locked = update_all(["locked_by = ?, locked_at = clock_timestamp() at time zone 'UTC', locked_until = clock_timestamp() at time zone 'UTC' + #{lock_interval}", @identifier],
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],
65
64
  ["key = ? AND (locked_by IS NULL OR locked_by = ? OR locked_until < clock_timestamp() at time zone 'UTC')", @key, @identifier])
66
65
  end
67
66
 
@@ -75,6 +74,15 @@ class Locker
75
74
  @locked
76
75
  end
77
76
 
77
+ def sequence
78
+ if @sequence
79
+ @sequence
80
+ else
81
+ record = model.find_by_key_and_locked_by(@key, @identifier)
82
+ @sequence = record && record.sequence
83
+ end
84
+ end
85
+
78
86
  private
79
87
 
80
88
  def lock_interval
@@ -1,3 +1,3 @@
1
1
  class Locker
2
- VERSION = "0.0.3"
2
+ VERSION = "0.1.0"
3
3
  end
data/locker.gemspec CHANGED
@@ -19,5 +19,4 @@ Gem::Specification.new do |s|
19
19
  s.add_dependency "activerecord", ">=2.3.14"
20
20
  s.add_development_dependency "pg"
21
21
  s.add_development_dependency "rspec"
22
- s.add_development_dependency "autotest"
23
22
  end
@@ -85,6 +85,25 @@ describe Locker do
85
85
  end
86
86
  end.to raise_error(Locker::LockStolen)
87
87
  end
88
+
89
+ it "should call the passed in block when the lock is aquired" do
90
+ value = nil
91
+ Locker.run("foo") do
92
+ value = 1
93
+ end
94
+
95
+ expect(value).to eq(1)
96
+ end
97
+
98
+ it "should pass in a sequence number representing the number of times the lock has been locked" do
99
+ (1..10).each do |i|
100
+ value = nil
101
+ Locker.run("foo") do |sequence|
102
+ value = sequence
103
+ end
104
+ expect(value).to eq(i)
105
+ end
106
+ end
88
107
  end
89
108
 
90
109
  describe "blocking" do
data/spec/spec_helper.rb CHANGED
@@ -10,7 +10,19 @@ require 'locker'
10
10
  ActiveRecord::Base.time_zone_aware_attributes = true
11
11
  ActiveRecord::Base.default_timezone = "UTC"
12
12
 
13
- config = YAML.load_file(File.join(File.dirname(__FILE__), 'database.yml'))
13
+ if File.exist?(File.join(File.dirname(__FILE__), 'database.yml'))
14
+ config = YAML.load_file(File.join(File.dirname(__FILE__), 'database.yml'))
15
+ else
16
+ puts "database.yml did not exist, using defaults"
17
+ config = { "reconnect" => false,
18
+ "database" => "locker_test",
19
+ "adapter" => "postgresql",
20
+ "password" => nil,
21
+ "user" => "postgres",
22
+ "encoding" => "utf8",
23
+ "pool" => 5 }
24
+ end
25
+
14
26
  begin
15
27
  ActiveRecord::Base.establish_connection(config.merge('database' => 'postgres', 'schema_search_path' => 'public'))
16
28
  ActiveRecord::Base.connection.create_database(config['database'], config.merge("encoding" => config['encoding'] || ENV['CHARSET'] || 'utf8'))
@@ -24,6 +36,7 @@ ActiveRecord::Base.connection.execute("DROP TABLE IF EXISTS locks")
24
36
  ActiveRecord::Base.connection.create_table(:locks) do |t|
25
37
  t.string :locked_by
26
38
  t.string :key
39
+ t.integer :sequence, :default => 0
27
40
  t.datetime :locked_at
28
41
  t.datetime :locked_until
29
42
  end
@@ -42,6 +55,14 @@ class FakeLock
42
55
  fake_locks[key]
43
56
  end
44
57
 
58
+ def self.find_by_key_and_locked_by(key, locked_by)
59
+ lock = fake_locks[key]
60
+
61
+ if lock && lock.locked_by == locked_by
62
+ lock
63
+ end
64
+ end
65
+
45
66
  def self.create(attributes={})
46
67
  fake_locks[attributes[:key]] = new(attributes)
47
68
  true
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: locker
3
3
  version: !ruby/object:Gem::Version
4
- hash: 25
4
+ hash: 27
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
+ - 1
8
9
  - 0
9
- - 3
10
- version: 0.0.3
10
+ version: 0.1.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Nathan Sutton
@@ -16,7 +16,7 @@ autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
18
 
19
- date: 2011-09-02 00:00:00 Z
19
+ date: 2013-04-04 00:00:00 Z
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
22
22
  name: activerecord
@@ -62,20 +62,6 @@ dependencies:
62
62
  version: "0"
63
63
  type: :development
64
64
  version_requirements: *id003
65
- - !ruby/object:Gem::Dependency
66
- name: autotest
67
- prerelease: false
68
- requirement: &id004 !ruby/object:Gem::Requirement
69
- none: false
70
- requirements:
71
- - - ">="
72
- - !ruby/object:Gem::Version
73
- hash: 3
74
- segments:
75
- - 0
76
- version: "0"
77
- type: :development
78
- version_requirements: *id004
79
65
  description: Locker is a locking mechanism for limiting the concurrency of ruby code using the database. It presently only works with PostgreSQL.
80
66
  email:
81
67
  - nate@zencoder.com
@@ -88,11 +74,14 @@ extra_rdoc_files: []
88
74
 
89
75
  files:
90
76
  - .gitignore
77
+ - .travis.yml
91
78
  - Gemfile
92
79
  - LICENSE
93
80
  - README.md
94
81
  - Rakefile
95
82
  - autotest/discover.rb
83
+ - gemfiles/rails2.gemfile
84
+ - gemfiles/rails3.gemfile
96
85
  - generators/locker/USAGE
97
86
  - generators/locker/locker_generator.rb
98
87
  - generators/locker/templates/migration.rb
@@ -136,10 +125,11 @@ required_rubygems_version: !ruby/object:Gem::Requirement
136
125
  requirements: []
137
126
 
138
127
  rubyforge_project: locker
139
- rubygems_version: 1.8.6
128
+ rubygems_version: 1.8.25
140
129
  signing_key:
141
130
  specification_version: 3
142
131
  summary: Locker is a locking mechanism for limiting the concurrency of ruby code using the database.
143
132
  test_files:
144
133
  - spec/locker/locker_spec.rb
145
134
  - spec/spec_helper.rb
135
+ has_rdoc: