girl_friday 0.9.5 → 0.9.6
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +1 -1
- data/History.md +7 -0
- data/README.md +12 -15
- data/TODO.md +1 -1
- data/girl_friday.gemspec +3 -2
- data/lib/girl_friday.rb +4 -4
- data/lib/girl_friday/persistence.rb +10 -3
- data/lib/girl_friday/version.rb +1 -1
- data/lib/girl_friday/work_queue.rb +5 -2
- data/test/helper.rb +1 -1
- data/test/test_girl_friday_queue.rb +12 -69
- metadata +29 -26
data/.travis.yml
CHANGED
data/History.md
CHANGED
data/README.md
CHANGED
@@ -1,11 +1,7 @@
|
|
1
1
|
girl\_friday
|
2
2
|
====================
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
> The term Man Friday has become an idiom, still in mainstream usage, to describe an especially faithful servant or
|
7
|
-
> one's best servant or right-hand man. The female equivalent is Girl Friday. The title of the movie His Girl Friday
|
8
|
-
> alludes to it and may have popularized it.
|
4
|
+
[![Travis-CI build status](https://secure.travis-ci.org/mperham/girl_friday.png)](http://travis-ci.org/mperham/girl\_friday)
|
9
5
|
|
10
6
|
girl\_friday is a Ruby library for performing asynchronous tasks. Often times you don't want to block a web response by performing some task, like sending an email, so you can just use this gem to perform it in the background. It works with any Ruby application, including Rails 3 applications.
|
11
7
|
|
@@ -37,22 +33,16 @@ In your Rails app, create a `config/initializers/girl_friday.rb` which defines y
|
|
37
33
|
ImageCrawler.process(msg)
|
38
34
|
end
|
39
35
|
|
40
|
-
SCRAPE_QUEUE = GirlFriday::WorkQueue.new(:scrape_sites, :size => 4, :store => GirlFriday::Store::Redis, :store_config => [{ :host => 'host' }]) do |msg|
|
41
|
-
Page.scrape(msg)
|
42
|
-
end
|
43
|
-
|
44
|
-
TRANSCODE_QUEUE = GirlFriday::WorkQueue.new(:scrape_sites, :size => 4, :store => GirlFriday::Store::Redis, :store_config => [{ :redis => $redis }]) do |msg|
|
45
|
-
VideoProcessor.transcode(msg)
|
46
|
-
end
|
47
|
-
|
48
36
|
:size is the number of workers to spin up and defaults to 5. Keep in mind, ActiveRecord defaults to a connection pool size of 5 so if your workers are accessing the database you'll want to ensure that the connection pool is large enough by modifying `config/database.yml`.
|
49
37
|
|
50
|
-
|
38
|
+
In order to use the Redis backend, you must use a connection pool to share a set of Redis connections with
|
51
39
|
other threads and GirlFriday queues using the `connection\_pool` gem:
|
52
40
|
|
53
41
|
require 'connection_pool'
|
42
|
+
|
54
43
|
redis_pool = ConnectionPool.new(:size => 5, :timeout => 5) { Redis.new }
|
55
|
-
|
44
|
+
|
45
|
+
CLEAN_FILTER_QUEUE = GirlFriday::WorkQueue.new(:clean_filter, :store => GirlFriday::Store::Redis, :store_config => [{ :pool => redis_pool}]) do |msg|
|
56
46
|
Filter.clean(msg)
|
57
47
|
end
|
58
48
|
|
@@ -71,11 +61,18 @@ have no reference to them. Make sure you call `WorkQueue#shutdown` if you are
|
|
71
61
|
dynamically creating them so you don't leak memory. `GirlFriday.shutdown!` will shut down all
|
72
62
|
running queues in the process.
|
73
63
|
|
64
|
+
|
74
65
|
More Detail
|
75
66
|
--------------------
|
76
67
|
|
77
68
|
Please see the [girl\_friday wiki](https://github.com/mperham/girl_friday/wiki) for more detail and advanced options and tuning. You'll find details on queue persistence with Redis, implementing clean shutdown, querying runtime metrics and SO MUCH MORE!
|
78
69
|
|
70
|
+
From wikipedia:
|
71
|
+
|
72
|
+
> The term Man Friday has become an idiom, still in mainstream usage, to describe an especially faithful servant or
|
73
|
+
> one's best servant or right-hand man. The female equivalent is Girl Friday. The title of the movie His Girl Friday
|
74
|
+
> alludes to it and may have popularized it.
|
75
|
+
|
79
76
|
|
80
77
|
Thanks
|
81
78
|
--------------------
|
data/TODO.md
CHANGED
data/girl_friday.gemspec
CHANGED
@@ -16,6 +16,7 @@ Gem::Specification.new do |s|
|
|
16
16
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
17
17
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
18
18
|
s.require_paths = ["lib"]
|
19
|
-
s.
|
20
|
-
s.add_development_dependency '
|
19
|
+
s.add_dependency 'connection_pool', '~> 0.1.0'
|
20
|
+
s.add_development_dependency 'sinatra', '~> 1.0'
|
21
|
+
s.add_development_dependency 'rake'
|
21
22
|
end
|
data/lib/girl_friday.rb
CHANGED
@@ -17,10 +17,10 @@ require 'girl_friday/batch'
|
|
17
17
|
|
18
18
|
module GirlFriday
|
19
19
|
|
20
|
-
|
20
|
+
@lock = Mutex.new
|
21
21
|
|
22
22
|
def self.add_queue(ref)
|
23
|
-
|
23
|
+
@lock.synchronize do
|
24
24
|
@queues ||= []
|
25
25
|
@queues.reject! { |q| !q.weakref_alive? }
|
26
26
|
@queues << ref
|
@@ -28,7 +28,7 @@ module GirlFriday
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def self.remove_queue(ref)
|
31
|
-
|
31
|
+
@lock.synchronize do
|
32
32
|
@queues.delete ref
|
33
33
|
end
|
34
34
|
end
|
@@ -81,7 +81,7 @@ module GirlFriday
|
|
81
81
|
end
|
82
82
|
|
83
83
|
m.synchronize do
|
84
|
-
var.wait(m, timeout)
|
84
|
+
var.wait(m, timeout) if count != 0
|
85
85
|
end
|
86
86
|
end
|
87
87
|
count
|
@@ -23,17 +23,20 @@ module GirlFriday
|
|
23
23
|
class Redis
|
24
24
|
def initialize(name, options)
|
25
25
|
@opts = options
|
26
|
+
unless @opts[:pool]
|
27
|
+
raise ArgumentError, "you must pass in a :pool"
|
28
|
+
end
|
26
29
|
@key = "girl_friday-#{name}-#{environment}"
|
27
30
|
end
|
28
31
|
|
29
32
|
def push(work)
|
30
33
|
val = Marshal.dump(work)
|
31
|
-
redis.rpush(@key, val)
|
34
|
+
redis{ |r| r.rpush(@key, val) }
|
32
35
|
end
|
33
36
|
alias_method :<<, :push
|
34
37
|
|
35
38
|
def pop
|
36
|
-
val = redis.lpop(@key)
|
39
|
+
val = redis{ |r| r.lpop(@key) }
|
37
40
|
Marshal.load(val) if val
|
38
41
|
end
|
39
42
|
|
@@ -48,8 +51,12 @@ module GirlFriday
|
|
48
51
|
end
|
49
52
|
|
50
53
|
def redis
|
51
|
-
@redis ||=
|
54
|
+
@redis ||= @opts.delete(:pool)
|
55
|
+
@redis.with do |pooled|
|
56
|
+
yield pooled
|
57
|
+
end
|
52
58
|
end
|
53
59
|
end
|
60
|
+
|
54
61
|
end
|
55
62
|
end
|
data/lib/girl_friday/version.rb
CHANGED
@@ -15,9 +15,10 @@ module GirlFriday
|
|
15
15
|
|
16
16
|
@shutdown = false
|
17
17
|
@busy_workers = []
|
18
|
+
@ready_workers = nil
|
18
19
|
@created_at = Time.now.to_i
|
19
20
|
@total_processed = @total_errors = @total_queued = 0
|
20
|
-
@persister = (options[:store] || Store::InMemory).new(name, (options[:store_config] ||
|
21
|
+
@persister = (options[:store] || Store::InMemory).new(name, (options[:store_config] || {}))
|
21
22
|
@weakref = WeakRef.new(self)
|
22
23
|
start
|
23
24
|
GirlFriday.add_queue @weakref
|
@@ -49,7 +50,7 @@ module GirlFriday
|
|
49
50
|
{ @name => {
|
50
51
|
:pid => $$,
|
51
52
|
:pool_size => @size,
|
52
|
-
:ready => ready_workers.size,
|
53
|
+
:ready => @ready_workers ? @ready_workers.size : 0,
|
53
54
|
:busy => @busy_workers.size,
|
54
55
|
:backlog => @persister.size,
|
55
56
|
:total_queued => @total_queued,
|
@@ -127,8 +128,10 @@ module GirlFriday
|
|
127
128
|
|
128
129
|
def start
|
129
130
|
@supervisor = Actor.spawn do
|
131
|
+
Thread.current[:label] = "#{name}-supervisor"
|
130
132
|
supervisor = Actor.current
|
131
133
|
@work_loop = Proc.new do
|
134
|
+
Thread.current[:label] = "#{name}-worker"
|
132
135
|
while !@shutdown do
|
133
136
|
work = Actor.receive
|
134
137
|
if !@shutdown
|
data/test/helper.rb
CHANGED
@@ -6,7 +6,7 @@ at_exit do
|
|
6
6
|
Thread.list.each do |thread|
|
7
7
|
next if thread.status == 'run'
|
8
8
|
puts "WARNING: lingering threads found. All threads should be shutdown and garbage collected."
|
9
|
-
p [thread, thread[
|
9
|
+
p [thread, thread[:label], thread.object_id]
|
10
10
|
# puts thread.backtrace.join("\n")
|
11
11
|
end
|
12
12
|
end
|
@@ -87,44 +87,12 @@ class TestGirlFriday < MiniTest::Unit::TestCase
|
|
87
87
|
assert(metrics[:total_processed] > 0)
|
88
88
|
end
|
89
89
|
|
90
|
-
def
|
91
|
-
begin
|
92
|
-
require 'redis'
|
93
|
-
redis = Redis.new
|
94
|
-
redis.flushdb
|
95
|
-
rescue LoadError
|
96
|
-
return puts "Skipping redis test, 'redis' gem not found: #{$!.message}"
|
97
|
-
rescue Errno::ECONNREFUSED
|
98
|
-
return puts 'Skipping redis test, not running locally'
|
99
|
-
end
|
100
|
-
|
101
|
-
mutex = Mutex.new
|
102
|
-
total = 100
|
103
|
-
count = 0
|
104
|
-
incr = Proc.new do
|
105
|
-
mutex.synchronize do
|
106
|
-
count += 1
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
async_test(1.0) do |cb|
|
111
|
-
queue = GirlFriday::WorkQueue.new('redis', :size => 2, :store => GirlFriday::Store::Redis) do |msg|
|
112
|
-
incr.call
|
113
|
-
queue.shutdown do
|
114
|
-
cb.call
|
115
|
-
end if count == total
|
116
|
-
end
|
117
|
-
total.times do
|
118
|
-
queue.push(:text => 'foo')
|
119
|
-
end
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
|
-
def test_should_persist_with_redis_instance
|
90
|
+
def test_should_persist_with_redis_connection_pool
|
124
91
|
begin
|
125
92
|
require 'redis'
|
126
|
-
|
127
|
-
|
93
|
+
require 'connection_pool'
|
94
|
+
pool = ConnectionPool.new(:size => 5, :timeout => 2){ Redis.new }
|
95
|
+
pool.flushdb
|
128
96
|
rescue LoadError
|
129
97
|
return puts "Skipping redis test, 'redis' gem not found: #{$!.message}"
|
130
98
|
rescue Errno::ECONNREFUSED
|
@@ -140,8 +108,8 @@ class TestGirlFriday < MiniTest::Unit::TestCase
|
|
140
108
|
end
|
141
109
|
end
|
142
110
|
|
143
|
-
async_test(
|
144
|
-
queue = GirlFriday::WorkQueue.new('redis-
|
111
|
+
async_test(2.0) do |cb|
|
112
|
+
queue = GirlFriday::WorkQueue.new('redis-pool', :size => 2, :store => GirlFriday::Store::Redis, :store_config => { :pool => pool }) do |msg|
|
145
113
|
incr.call
|
146
114
|
queue.shutdown do
|
147
115
|
cb.call
|
@@ -153,36 +121,10 @@ class TestGirlFriday < MiniTest::Unit::TestCase
|
|
153
121
|
end
|
154
122
|
end
|
155
123
|
|
156
|
-
def
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
redis = ConnectionPool.new(:size => 5, :timeout => 5){ Redis.new }
|
161
|
-
redis.flushdb
|
162
|
-
rescue LoadError
|
163
|
-
return puts "Skipping redis test, 'redis' gem not found: #{$!.message}"
|
164
|
-
rescue Errno::ECONNREFUSED
|
165
|
-
return puts 'Skipping redis test, not running locally'
|
166
|
-
end
|
167
|
-
|
168
|
-
mutex = Mutex.new
|
169
|
-
total = 100
|
170
|
-
count = 0
|
171
|
-
incr = Proc.new do
|
172
|
-
mutex.synchronize do
|
173
|
-
count += 1
|
174
|
-
end
|
175
|
-
end
|
176
|
-
|
177
|
-
async_test(1.0) do |cb|
|
178
|
-
queue = GirlFriday::WorkQueue.new('redis-pool', :size => 2, :store => GirlFriday::Store::Redis, :store_config => [{ :redis => redis }]) do |msg|
|
179
|
-
incr.call
|
180
|
-
queue.shutdown do
|
181
|
-
cb.call
|
182
|
-
end if count == total
|
183
|
-
end
|
184
|
-
total.times do
|
185
|
-
queue.push(:text => 'foo')
|
124
|
+
def test_should_raise_if_no_store_config_passed_in_for_redis_backend
|
125
|
+
assert_raises(ArgumentError) do
|
126
|
+
GirlFriday::WorkQueue.new('raise-test', :store => GirlFriday::Store::Redis) do |msg|
|
127
|
+
# doing work
|
186
128
|
end
|
187
129
|
end
|
188
130
|
end
|
@@ -221,7 +163,8 @@ class TestGirlFriday < MiniTest::Unit::TestCase
|
|
221
163
|
cb.call
|
222
164
|
end
|
223
165
|
end
|
224
|
-
|
166
|
+
assert queue.instance_variable_defined?(:@ready_workers)
|
167
|
+
assert_nil queue.instance_variable_get(:@ready_workers)
|
225
168
|
# don't instantiate the worker threads until we actually put
|
226
169
|
# work onto the queue.
|
227
170
|
queue << 'empty msg'
|
metadata
CHANGED
@@ -1,39 +1,49 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: girl_friday
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
|
4
|
+
version: 0.9.6
|
5
|
+
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Mike Perham
|
9
|
-
autorequire:
|
9
|
+
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2011-09-
|
13
|
-
default_executable:
|
12
|
+
date: 2011-09-26 00:00:00.000000000Z
|
14
13
|
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: connection_pool
|
16
|
+
requirement: &22271180 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 0.1.0
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *22271180
|
15
25
|
- !ruby/object:Gem::Dependency
|
16
26
|
name: sinatra
|
17
|
-
|
27
|
+
requirement: &22270680 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
18
29
|
requirements:
|
19
30
|
- - ~>
|
20
31
|
- !ruby/object:Gem::Version
|
21
32
|
version: '1.0'
|
22
|
-
none: false
|
23
|
-
requirement: *2086
|
24
|
-
prerelease: false
|
25
33
|
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *22270680
|
26
36
|
- !ruby/object:Gem::Dependency
|
27
37
|
name: rake
|
28
|
-
|
38
|
+
requirement: &22270300 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
29
40
|
requirements:
|
30
41
|
- - ! '>='
|
31
42
|
- !ruby/object:Gem::Version
|
32
43
|
version: '0'
|
33
|
-
none: false
|
34
|
-
requirement: *2104
|
35
|
-
prerelease: false
|
36
44
|
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *22270300
|
37
47
|
description: Background processing, simplified
|
38
48
|
email:
|
39
49
|
- mperham@gmail.com
|
@@ -73,35 +83,28 @@ files:
|
|
73
83
|
- test/test_girl_friday.rb
|
74
84
|
- test/test_girl_friday_immediately.rb
|
75
85
|
- test/test_girl_friday_queue.rb
|
76
|
-
has_rdoc: true
|
77
86
|
homepage: http://github.com/mperham/girl_friday
|
78
87
|
licenses: []
|
79
|
-
post_install_message:
|
88
|
+
post_install_message:
|
80
89
|
rdoc_options: []
|
81
90
|
require_paths:
|
82
91
|
- lib
|
83
92
|
required_ruby_version: !ruby/object:Gem::Requirement
|
93
|
+
none: false
|
84
94
|
requirements:
|
85
95
|
- - ! '>='
|
86
96
|
- !ruby/object:Gem::Version
|
87
97
|
version: '0'
|
88
|
-
none: false
|
89
98
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
99
|
+
none: false
|
90
100
|
requirements:
|
91
101
|
- - ! '>='
|
92
102
|
- !ruby/object:Gem::Version
|
93
103
|
version: '0'
|
94
|
-
none: false
|
95
104
|
requirements: []
|
96
105
|
rubyforge_project: girl_friday
|
97
|
-
rubygems_version: 1.
|
98
|
-
signing_key:
|
106
|
+
rubygems_version: 1.8.6
|
107
|
+
signing_key:
|
99
108
|
specification_version: 3
|
100
109
|
summary: Background processing, simplified
|
101
|
-
test_files:
|
102
|
-
- test/helper.rb
|
103
|
-
- test/test_batch.rb
|
104
|
-
- test/test_girl_friday.rb
|
105
|
-
- test/test_girl_friday_immediately.rb
|
106
|
-
- test/test_girl_friday_queue.rb
|
107
|
-
...
|
110
|
+
test_files: []
|