mongoid-locker 0.2.0 → 0.2.1
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.
- data/.travis.yml +1 -0
- data/CHANGELOG.md +6 -1
- data/Gemfile.lock +36 -33
- data/README.md +1 -1
- data/Rakefile +1 -0
- data/VERSION +1 -1
- data/demo/README.md +10 -0
- data/demo/config/mongoid.yml +6 -0
- data/demo/instagram.graffle +1012 -0
- data/demo/instagram.png +0 -0
- data/demo/showoff.css +16 -0
- data/demo/showoff.md +159 -0
- data/lib/mongoid/locker.rb +2 -0
- data/lib/mongoid/locker/wrapper.rb +9 -7
- data/mongoid-locker.gemspec +8 -2
- data/spec/database.yml +1 -0
- data/spec/mongoid-locker_spec.rb +18 -2
- data/spec/spec_helper.rb +6 -1
- metadata +9 -3
data/demo/instagram.png
ADDED
Binary file
|
data/demo/showoff.css
ADDED
data/demo/showoff.md
ADDED
@@ -0,0 +1,159 @@
|
|
1
|
+
!SLIDE
|
2
|
+
|
3
|
+
# Mongoid-Locker
|
4
|
+
|
5
|
+
[github.com/afeld/mongoid-locker](https://github.com/afeld/mongoid-locker)
|
6
|
+
|
7
|
+
## Aidan Feldman, [Jux.com](https://jux.com)
|
8
|
+
|
9
|
+
!SLIDE
|
10
|
+
|
11
|
+

|
12
|
+
|
13
|
+
!SLIDE
|
14
|
+
|
15
|
+
# Jux needed a queue.
|
16
|
+
|
17
|
+
* Distributed, but synchronized
|
18
|
+
* No add'l DB
|
19
|
+
* No add'l hassle (managing workers, etc.)
|
20
|
+
|
21
|
+
!SLIDE
|
22
|
+
|
23
|
+
* Thread
|
24
|
+
- \+ Distributed
|
25
|
+
- – Not synchronized
|
26
|
+
* Many workers
|
27
|
+
- \+ Distributed
|
28
|
+
- – Not synchronized
|
29
|
+
* One worker
|
30
|
+
- \+ Synchronized
|
31
|
+
- – Not distributed
|
32
|
+
- – Single POF
|
33
|
+
|
34
|
+
!SLIDE
|
35
|
+
|
36
|
+
@@@ ruby
|
37
|
+
require 'rubygems'
|
38
|
+
require 'mongoid-locker'
|
39
|
+
Mongoid.load!('config/mongoid.yml', :development)
|
40
|
+
|
41
|
+
|
42
|
+
class User
|
43
|
+
include Mongoid::Document
|
44
|
+
# include Mongoid::Locker
|
45
|
+
|
46
|
+
field :balance, type: Float
|
47
|
+
end
|
48
|
+
|
49
|
+
# cleanup
|
50
|
+
User.destroy_all
|
51
|
+
|
52
|
+
bob = User.create!(balance: 100.00)
|
53
|
+
|
54
|
+
!SLIDE
|
55
|
+
|
56
|
+
# Atomic Operations
|
57
|
+
|
58
|
+
@@@ ruby
|
59
|
+
class User
|
60
|
+
def purchase(amount)
|
61
|
+
if amount > self.balance
|
62
|
+
raise "Can't have negative balance!"
|
63
|
+
else
|
64
|
+
# deduct *atomically*
|
65
|
+
self.inc(:balance, -1 * amount)
|
66
|
+
# a.k.a.
|
67
|
+
# db.users.update({_id: ...}, {$inc: {balance: ...}})
|
68
|
+
|
69
|
+
puts "cha-ching!"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
bob.purchase(5.10) #=> "cha-ching!"
|
75
|
+
bob.purchase(110.53) #=> "Can't have negative balance!"
|
76
|
+
|
77
|
+
!SLIDE
|
78
|
+
|
79
|
+
# All fine, right?
|
80
|
+
|
81
|
+
!SLIDE
|
82
|
+
|
83
|
+
@@@ ruby
|
84
|
+
class User
|
85
|
+
def purchase(amount)
|
86
|
+
if amount > self.balance
|
87
|
+
raise "Can't have negative balance!"
|
88
|
+
else
|
89
|
+
# artificial delay
|
90
|
+
print 'has enough money...waiting for ENTER > '
|
91
|
+
gets
|
92
|
+
|
93
|
+
self.inc(:balance, -1 * amount)
|
94
|
+
puts "cha-ching!"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
!SLIDE
|
100
|
+
|
101
|
+
@@@ ruby
|
102
|
+
# shell 1
|
103
|
+
bob.purchase(10.00)
|
104
|
+
|
105
|
+
# shell 2
|
106
|
+
also_bob = User.first
|
107
|
+
also_bob.purchase(95.00)
|
108
|
+
|
109
|
+
!SLIDE
|
110
|
+
|
111
|
+
# oops.
|
112
|
+
|
113
|
+
!SLIDE
|
114
|
+
|
115
|
+
@@@ ruby
|
116
|
+
class User
|
117
|
+
# add doc-level locking
|
118
|
+
include Mongoid::Locker
|
119
|
+
timeout_lock_after 20
|
120
|
+
|
121
|
+
def purchase(amount)
|
122
|
+
# only one at a time
|
123
|
+
self.with_lock(wait: true) do
|
124
|
+
# after the `wait`, will have updated `balance`
|
125
|
+
|
126
|
+
if amount > self.balance
|
127
|
+
raise "Can't have negative balance!"
|
128
|
+
else
|
129
|
+
print 'has enough money...waiting for ENTER > '
|
130
|
+
gets
|
131
|
+
|
132
|
+
self.inc(:balance, -1 * amount)
|
133
|
+
puts "cha-ching!"
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
!SLIDE
|
140
|
+
|
141
|
+
# Summary
|
142
|
+
|
143
|
+
* Easy document-level locking
|
144
|
+
* Useful for queueing or pseudo-transactions
|
145
|
+
* No additional dependencies
|
146
|
+
|
147
|
+
!SLIDE
|
148
|
+
|
149
|
+
# Fin.
|
150
|
+
|
151
|
+
[afeld/mongoid-locker](https://github.com/afeld/mongoid-locker)
|
152
|
+
|
153
|
+
----------------
|
154
|
+
|
155
|
+
## Aidan Feldman, [Jux.com](https://jux.com)
|
156
|
+
|
157
|
+
[@aidanfeldman](https://twitter.com/aidanfeldman)
|
158
|
+
|
159
|
+
[afeld.me](http://afeld.me)
|
data/lib/mongoid/locker.rb
CHANGED
@@ -2,7 +2,7 @@ module Mongoid
|
|
2
2
|
module Locker
|
3
3
|
# Normalizes queries between Mongoid 2 and 3.
|
4
4
|
module Wrapper
|
5
|
-
IS_OLD_MONGOID = Mongoid::VERSION.start_with? '2'
|
5
|
+
IS_OLD_MONGOID = Mongoid::VERSION.start_with? '2.'
|
6
6
|
|
7
7
|
# Update the document for the provided Class matching the provided query with the provided setter.
|
8
8
|
#
|
@@ -11,12 +11,14 @@ module Mongoid
|
|
11
11
|
# @param [Hash] The Mongoid setter
|
12
12
|
# @return [Boolean] true if the document was successfully updated, false otherwise
|
13
13
|
def self.update klass, query, setter
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
14
|
+
error_obj =
|
15
|
+
if IS_OLD_MONGOID
|
16
|
+
klass.collection.update(query, setter, :safe => true)
|
17
|
+
else
|
18
|
+
klass.with(:safe => true).collection.find(query).update(setter)
|
19
|
+
end
|
20
|
+
|
21
|
+
error_obj['n'] == 1
|
20
22
|
end
|
21
23
|
|
22
24
|
# Determine whether the provided document is locked in the database or not.
|
data/mongoid-locker.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "mongoid-locker"
|
8
|
-
s.version = "0.2.
|
8
|
+
s.version = "0.2.1"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Aidan Feldman"]
|
12
|
-
s.date = "2012-
|
12
|
+
s.date = "2012-11-28"
|
13
13
|
s.description = "Allows multiple processes to operate on individual documents in MongoDB while ensuring that only one can act at a time."
|
14
14
|
s.email = "aidan.feldman@gmail.com"
|
15
15
|
s.extra_rdoc_files = [
|
@@ -29,6 +29,12 @@ Gem::Specification.new do |s|
|
|
29
29
|
"README.md",
|
30
30
|
"Rakefile",
|
31
31
|
"VERSION",
|
32
|
+
"demo/README.md",
|
33
|
+
"demo/config/mongoid.yml",
|
34
|
+
"demo/instagram.graffle",
|
35
|
+
"demo/instagram.png",
|
36
|
+
"demo/showoff.css",
|
37
|
+
"demo/showoff.md",
|
32
38
|
"gemfiles/mongoid2.gemfile",
|
33
39
|
"gemfiles/mongoid3.gemfile",
|
34
40
|
"lib/mongoid-locker.rb",
|
data/spec/database.yml
CHANGED
data/spec/mongoid-locker_spec.rb
CHANGED
@@ -14,7 +14,7 @@ describe Mongoid::Locker do
|
|
14
14
|
field :account_balance, :type => Integer # easier to test than Float
|
15
15
|
end
|
16
16
|
|
17
|
-
@user = User.create!
|
17
|
+
@user = User.create! :account_balance => 20
|
18
18
|
end
|
19
19
|
|
20
20
|
after do
|
@@ -94,7 +94,7 @@ describe Mongoid::Locker do
|
|
94
94
|
end
|
95
95
|
|
96
96
|
@user.account_balance.should eq(10)
|
97
|
-
User.first.account_balance.should
|
97
|
+
User.first.account_balance.should eq(20)
|
98
98
|
end
|
99
99
|
|
100
100
|
it "should handle errors gracefully" do
|
@@ -153,6 +153,22 @@ describe Mongoid::Locker do
|
|
153
153
|
end
|
154
154
|
end
|
155
155
|
|
156
|
+
it "should reload the document if it needs to wait for a lock" do
|
157
|
+
User.timeout_lock_after 1
|
158
|
+
|
159
|
+
@user.with_lock do
|
160
|
+
user_dup = User.first
|
161
|
+
|
162
|
+
@user.account_balance = 10
|
163
|
+
@user.save!
|
164
|
+
|
165
|
+
user_dup.account_balance.should eq(20)
|
166
|
+
user_dup.with_lock :wait => true do
|
167
|
+
user_dup.account_balance.should eq(10)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
156
172
|
it "should succeed for subclasses" do
|
157
173
|
class Admin < User
|
158
174
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -11,5 +11,10 @@ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
|
11
11
|
|
12
12
|
RSpec.configure do |config|
|
13
13
|
Mongoid.load! File.join(File.dirname(__FILE__), 'database.yml')
|
14
|
-
|
14
|
+
|
15
|
+
# use to check the query conditions
|
16
|
+
if ENV['LOG']
|
17
|
+
Mongoid.logger.level = Logger::DEBUG
|
18
|
+
Moped.logger.level = Logger::DEBUG if defined? Moped
|
19
|
+
end
|
15
20
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mongoid-locker
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-11-28 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: mongoid
|
@@ -166,6 +166,12 @@ files:
|
|
166
166
|
- README.md
|
167
167
|
- Rakefile
|
168
168
|
- VERSION
|
169
|
+
- demo/README.md
|
170
|
+
- demo/config/mongoid.yml
|
171
|
+
- demo/instagram.graffle
|
172
|
+
- demo/instagram.png
|
173
|
+
- demo/showoff.css
|
174
|
+
- demo/showoff.md
|
169
175
|
- gemfiles/mongoid2.gemfile
|
170
176
|
- gemfiles/mongoid3.gemfile
|
171
177
|
- lib/mongoid-locker.rb
|
@@ -190,7 +196,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
190
196
|
version: '0'
|
191
197
|
segments:
|
192
198
|
- 0
|
193
|
-
hash:
|
199
|
+
hash: -4115924438345576931
|
194
200
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
195
201
|
none: false
|
196
202
|
requirements:
|