creeper 0.0.3 → 0.0.4

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/README.md CHANGED
@@ -2,14 +2,18 @@
2
2
 
3
3
  Can be used as an in place drop in for stalker but it is multi threaded so you can easily do more without using more memory
4
4
 
5
+ *Note*: Creeper requires Ruby 1.9
6
+
5
7
  Creeper - an improvement on Stalker
6
- ==========================================
8
+ ===================================
7
9
 
8
10
  The big difference is how you "work" jobs
9
11
 
10
- all you need is a thread count arguement :)
12
+ all you need is a runner (thread) count argument :)
11
13
 
12
- Creeper.work(<jobs>, <thread_count>)
14
+ ```
15
+ Creeper.work([<job>, ...], <runner_count>)
16
+ ```
13
17
 
14
18
  [Beanstalkd](http://kr.github.com/beanstalkd/) is a fast, lightweight queueing backend inspired by mmemcached.
15
19
 
@@ -18,62 +22,81 @@ Queueing jobs
18
22
 
19
23
  From anywhere in your app:
20
24
 
21
- require 'creeper'
25
+ ```ruby
26
+ require 'creeper'
22
27
 
23
- Creeper.enqueue('email.send', :to => 'joe@example.com')
24
- Creeper.enqueue('post.cleanup.all')
25
- Creeper.enqueue('post.cleanup', :id => post.id)
28
+ Creeper.enqueue('email.send', :to => 'joe@example.com')
29
+ Creeper.enqueue('post.cleanup.all')
30
+ Creeper.enqueue('post.cleanup', :id => post.id)
31
+ ```
26
32
 
27
33
  Working jobs
28
34
  ------------
29
35
 
30
36
  In a standalone file, typically jobs.rb or worker.rb:
31
37
 
32
- require 'creeper'
33
- include Creeper
38
+ ```ruby
39
+ require 'creeper'
40
+ include Creeper::Creep
41
+
42
+ job 'email.send' do |args|
43
+ Pony.send(:to => args['to'], :subject => "Hello there")
44
+ end
45
+
46
+ job 'post.cleanup.all' do |args|
47
+ Post.all.each do |post|
48
+ enqueue('post.cleanup', :id => post.id)
49
+ end
50
+ end
51
+
52
+ job 'post.cleanup' do |args|
53
+ Post.find(args['id']).cleanup
54
+ end
34
55
 
35
- job 'email.send' do |args|
36
- Pony.send(:to => args['to'], :subject => "Hello there")
37
- end
56
+ # All of these Creeper.work calls are equivalent:
38
57
 
39
- job 'post.cleanup.all' do |args|
40
- Post.all.each do |post|
41
- enqueue('post.cleanup', :id => post.id)
42
- end
43
- end
58
+ Creeper.work(:all, 3) # work all jobs, 3 threads
59
+ Creeper.work(nil, 3) # same as previous line
60
+ Creeper.work([ 'email.send', 'post.cleanup.all', 'post.cleanup' ], 3) # same as previous line
44
61
 
45
- job 'post.cleanup' do |args|
46
- Post.find(args['id']).cleanup
47
- end
48
-
49
- Creeper.work(<jobs>, <thread_count>)
62
+ # Here we work just one job:
63
+ Creeper.work('email.send', 5) # work 'email.send', 5 threads
64
+ ```
50
65
 
51
66
  Running
52
67
  -------
53
68
 
54
69
  First, make sure you have Beanstalkd installed and running:
55
70
 
56
- $ sudo brew install beanstalkd
57
- $ beanstalkd
71
+ ```bash
72
+ $ sudo brew install beanstalkd
73
+ $ beanstalkd
74
+ ```
58
75
 
59
76
  Creeper:
60
77
 
61
- $ sudo gem install creeper
78
+ ```bash
79
+ $ sudo gem install creeper
80
+ ```
62
81
 
63
82
  Error Handling
64
83
  -------------
65
84
 
66
85
  If you include an `error` block in your jobs definition, that block will be invoked when a worker encounters an error. You might use this to report errors to an external monitoring service:
67
86
 
68
- error do |e, job, args|
69
- Exceptional.handle(e)
70
- end
87
+ ```ruby
88
+ error do |e, job, args|
89
+ Exceptional.handle(e)
90
+ end
91
+ ```
71
92
 
72
93
  Before filter
73
94
  -------------
74
95
 
75
96
  If you wish to run a block of code prior to any job:
76
97
 
77
- before do |job|
78
- puts "About to work #{job}"
79
- end
98
+ ```ruby
99
+ before do |job|
100
+ puts "About to work #{job}"
101
+ end
102
+ ```
data/bin/creeper ADDED
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift(File.dirname(__FILE__) + '/../lib') unless $LOAD_PATH.include?(File.dirname(__FILE__) + '/../lib')
4
+
5
+ require 'creeper/launcher'
6
+ require 'optparse'
7
+
8
+ options = { runner_count: 1 }
9
+
10
+ op = OptionParser.new("", 24, ' ') do |opts|
11
+ cmd = File.basename($0)
12
+ opts.banner = "Usage: #{cmd} [ruby options] [#{cmd} options]"
13
+
14
+ opts.separator "Ruby options:"
15
+
16
+ opts.on("-d", "--debug", "set debugging flags (set $DEBUG to true)") do
17
+ $DEBUG = true
18
+ end
19
+
20
+ opts.separator "#{cmd} options:"
21
+
22
+ opts.on("-D", "--daemonize", "run daemonized in the background") do |d|
23
+ options[:daemonize] = !!d
24
+ end
25
+
26
+ opts.on("-j", "--job-file FILE", "Creeper-specific job file") do |f|
27
+ options[:job_file] = f
28
+ end
29
+
30
+ opts.on("-r", "--runner-count NUMBER", "Threads to run (default: #{options[:runner_count]})") do |r|
31
+ options[:runner_count] = (r.to_i rescue 1)
32
+ end
33
+
34
+ opts.separator "Common options:"
35
+
36
+ opts.on_tail("-h", "--help", "Show this message") do
37
+ puts opts.to_s.gsub(/^.*DEPRECATED.*$/s, '')
38
+ exit
39
+ end
40
+
41
+ opts.on_tail("-v", "--version", "Show version") do
42
+ puts "#{cmd} v#{Creeper::VERSION}"
43
+ exit
44
+ end
45
+
46
+ opts.parse! ARGV
47
+
48
+ end
49
+
50
+ unless options[:job_file]
51
+ $stderr.puts "ERROR: job file required", ''
52
+ puts op.to_s.gsub(/^.*DEPRECATED.*$/s, '')
53
+ exit 1
54
+ end
55
+
56
+ if $DEBUG
57
+ require 'pp'
58
+ pp(options)
59
+ end
60
+
61
+ Creeper::Launcher.launch!(options) if options[:daemonize]
62
+ Creeper.new(options).start.join
data/creeper.gemspec CHANGED
@@ -16,6 +16,7 @@ Gem::Specification.new do |gem|
16
16
  gem.version = Creeper::VERSION
17
17
 
18
18
  gem.add_development_dependency 'pry'
19
+ gem.add_development_dependency 'rake'
19
20
  gem.add_development_dependency 'rspec'
20
21
  # gem.add_development_dependency 'stalker'
21
22
 
data/examples/jack.rb ADDED
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env creeper -j
2
+ #
3
+ # There are 2 ways to run this file:
4
+ # creeper -j jack.rb
5
+ # OR
6
+ # ./jack.rb
7
+ #
8
+ # If you remove the shebang, it can also be run with:
9
+ # ruby jack.rb
10
+
11
+ $LOAD_PATH.unshift(File.dirname(__FILE__) + '/../lib') unless $LOAD_PATH.include?(File.dirname(__FILE__) + '/../lib')
12
+
13
+ require 'creeper'
14
+
15
+ include Creeper::Creep
16
+
17
+ job('jack.work') do |*args|
18
+ Creeper.logger.info "[JACK.WORK] #{$0.inspect} #{args.inspect}"
19
+ end
20
+
21
+ Creeper.work(:all, 1) unless defined?(Creeper::Launcher)
data/lib/creeper.rb CHANGED
@@ -12,14 +12,10 @@ require 'kgio'
12
12
  require 'logger'
13
13
 
14
14
  require 'creeper/version'
15
+ require 'creeper/creep'
15
16
  require 'creeper/session'
16
17
  require 'creeper/worker'
17
18
 
18
- $stdout.sync = $stderr.sync = true
19
- $stdin.binmode
20
- $stdout.binmode
21
- $stderr.binmode
22
-
23
19
  module Creeper
24
20
 
25
21
  # These hashes map Threads to Workers
@@ -47,9 +43,10 @@ module Creeper
47
43
  end
48
44
 
49
45
  attr_accessor :logger, :error_logger
50
- attr_accessor :jobs, :patience, :runner_count, :soft_quit, :timeout
46
+ attr_accessor :job_file, :jobs, :patience, :ready_pipe, :runner_count, :soft_quit, :timeout
51
47
 
52
48
  extend self
49
+ extend Creeper::Creep
53
50
 
54
51
  ## utilities ##
55
52
 
@@ -74,6 +71,12 @@ module Creeper
74
71
 
75
72
  ### config ###
76
73
 
74
+ def job_file=(job_file)
75
+ (@job_file = job_file).tap do
76
+ require File.expand_path(job_file) if job_file
77
+ end
78
+ end
79
+
77
80
  def patience
78
81
  @patience ||= 60
79
82
  end
@@ -105,6 +108,14 @@ module Creeper
105
108
 
106
109
  ###
107
110
 
111
+ def new(options = {})
112
+ tap do
113
+ options.each do |key, value|
114
+ send("#{key}=", value) if respond_to?("#{key}=")
115
+ end
116
+ end
117
+ end
118
+
108
119
  def work(jobs = nil, runner_count = 1)
109
120
  self.jobs, self.runner_count = jobs, runner_count
110
121
 
@@ -134,6 +145,10 @@ module Creeper
134
145
 
135
146
  reset_proc_name
136
147
  logger.info "creeper process ready"
148
+ if ready_pipe
149
+ ready_pipe.syswrite($$.to_s)
150
+ ready_pipe = ready_pipe.close rescue nil
151
+ end
137
152
  begin
138
153
  reap_all_runners
139
154
  case SIG_QUEUE.shift
@@ -228,7 +243,6 @@ module Creeper
228
243
  GRAVEYARD[thread] = worker
229
244
  else
230
245
  logger.debug "creeper [murder] => hard quit" if $DEBUG
231
- worker.session.disconnect
232
246
  thread.kill
233
247
  thread.join
234
248
  end
@@ -268,6 +282,10 @@ module Creeper
268
282
  end
269
283
 
270
284
  def proc_name(tag)
285
+ if defined?(Navy) and defined?($officer)
286
+ Creeper::START_CTX.merge!(Navy::Admiral::START_CTX)
287
+ tag = "(#{$officer.captain.label}) officer[#{$officer.number}] #{tag}"
288
+ end
271
289
  $0 = ([
272
290
  File.basename(Creeper::START_CTX[0]),
273
291
  tag
@@ -276,31 +294,4 @@ module Creeper
276
294
 
277
295
  ##
278
296
 
279
- public
280
-
281
- def clear!
282
- @default_session.disconnect if @default_session
283
- @default_session = nil
284
- end
285
-
286
- def default_session
287
- @default_session ||= Creeper::Session.new
288
- end
289
-
290
- def enqueue(job, args = {}, opts = {})
291
- default_session.enqueue(job, args, opts)
292
- end
293
-
294
- def job(name, &block)
295
- default_session.job(name, &block)
296
- end
297
-
298
- def before(&block)
299
- default_session.before(&block)
300
- end
301
-
302
- def error(&block)
303
- default_session.error(&block)
304
- end
305
-
306
297
  end
@@ -0,0 +1,31 @@
1
+ module Creeper
2
+ module Creep
3
+
4
+ def clear!
5
+ Creeper.default_session.disconnect if Creeper.instance_variable_defined?(:@default_session) and Creeper.instance_variable_get(:@default_session)
6
+ Creeper.instance_variable_set(:@default_session, nil)
7
+ end
8
+
9
+ def default_session
10
+ return Creeper.instance_variable_get(:@default_session) if Creeper.instance_variable_defined?(:@default_session)
11
+ Creeper.instance_variable_set(:@default_session, Creeper::Session.new)
12
+ end
13
+
14
+ def enqueue(job, args = {}, opts = {})
15
+ default_session.enqueue(job, args, opts)
16
+ end
17
+
18
+ def job(name, &block)
19
+ default_session.job(name, &block)
20
+ end
21
+
22
+ def before(&block)
23
+ default_session.before(&block)
24
+ end
25
+
26
+ def error(&block)
27
+ default_session.error(&block)
28
+ end
29
+
30
+ end
31
+ end
@@ -0,0 +1,44 @@
1
+ $stdout.sync = $stderr.sync = true
2
+ $stdin.binmode
3
+ $stdout.binmode
4
+ $stderr.binmode
5
+
6
+ require 'creeper'
7
+
8
+ module Creeper
9
+ module Launcher
10
+
11
+ extend self
12
+
13
+ def launch!(options)
14
+ $stdin.reopen("/dev/null")
15
+
16
+ # grandparent - reads pipe, exits when master is ready
17
+ # \_ parent - exits immediately ASAP
18
+ # \_ creeper master - writes to pipe when ready
19
+
20
+ rd, wr = IO.pipe
21
+ grandparent = $$
22
+ if fork
23
+ wr.close # grandparent does not write
24
+ else
25
+ rd.close # creeper master does not read
26
+ Process.setsid
27
+ exit if fork # parent dies now
28
+ end
29
+
30
+ if grandparent == $$
31
+ # this will block until Creeper.join runs (or it dies)
32
+ creeper_pid = (rd.readpartial(16) rescue nil).to_i
33
+ unless creeper_pid > 1
34
+ warn "creeper failed to start, check stderr log for details"
35
+ exit!(1)
36
+ end
37
+ exit 0
38
+ else # creeper master process
39
+ options[:ready_pipe] = wr
40
+ end
41
+ end
42
+
43
+ end
44
+ end
@@ -1,3 +1,3 @@
1
1
  module Creeper
2
- VERSION = "0.0.3"
2
+ VERSION = "0.0.4"
3
3
  end
@@ -68,7 +68,9 @@ module Creeper
68
68
  def prepare
69
69
  raise NoJobsDefined if session.handlers.empty?
70
70
 
71
- jobs = options[:jobs] || session.all_jobs
71
+ jobs = session.all_jobs if options[:jobs] == :all
72
+ jobs ||= options[:jobs] ? [*options[:jobs]] : session.all_jobs
73
+ jobs = session.all_jobs if jobs.empty?
72
74
 
73
75
  jobs.each do |job|
74
76
  raise(NoSuchJob, job) unless session.handlers.has_key?(job)
@@ -107,16 +109,17 @@ module Creeper
107
109
  end
108
110
 
109
111
  job.delete
110
- log_job_end(name)
112
+ log_job_end(name, args)
111
113
  rescue Beanstalk::NotConnected => e
112
114
  failed_connection(e)
113
115
  rescue SystemExit
114
- puts "FART"
116
+
115
117
  raise
116
118
  rescue => e
117
119
  log_error exception_message(e)
118
120
  job.bury rescue nil
119
- log_job_end(name, 'failed') if @job_begun
121
+ args ||= []
122
+ log_job_end(name, args, 'failed') if @job_begun
120
123
  if session.error_handler
121
124
  if session.error_handler.arity == 1
122
125
  session.error_handler.call(e)
@@ -133,7 +136,7 @@ module Creeper
133
136
  end
134
137
 
135
138
  def stop
136
- logger.info "worker dying: #{Thread.current.inspect}"
139
+ logger.info "worker dying: (current=#{Thread.current.inspect}#{@thread.inspect}"
137
140
  session.disconnect
138
141
  @thread.kill
139
142
  end
@@ -150,23 +153,28 @@ module Creeper
150
153
 
151
154
  def log_job_begin(name, args)
152
155
  @working = true
153
- args_flat = unless args.empty?
154
- '(' + args.inject([]) do |accum, (key,value)|
155
- accum << "#{key}=#{value}"
156
- end.join(' ') + ')'
157
- else
158
- ''
159
- end
156
+ args_flat = flatten_args(args)
160
157
 
161
158
  log [ "Working", name, args_flat ].join(' ')
162
159
  @job_begun = Time.now
163
160
  end
164
161
 
165
- def log_job_end(name, failed=false)
162
+ def log_job_end(name, args, failed=false)
166
163
  @working = false
167
164
  ellapsed = Time.now - @job_begun
168
165
  ms = (ellapsed.to_f * 1000).to_i
169
- log "Finished #{name} in #{ms}ms #{failed ? ' (failed)' : ''}"
166
+ args_flat = flatten_args(args)
167
+ log [ "Finished #{name} in #{ms}ms #{failed ? ' (failed)' : ''}", args_flat ].join(' ')
168
+ end
169
+
170
+ def flatten_args(args)
171
+ unless args.empty?
172
+ '(' + args.inject([]) do |accum, (key,value)|
173
+ accum << "#{key}=#{value}"
174
+ end.join(' ') + ')'
175
+ else
176
+ ''
177
+ end
170
178
  end
171
179
 
172
180
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: creeper
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
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-05-15 00:00:00.000000000 Z
12
+ date: 2012-05-25 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: pry
@@ -27,6 +27,22 @@ dependencies:
27
27
  - - ! '>='
28
28
  - !ruby/object:Gem::Version
29
29
  version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
30
46
  - !ruby/object:Gem::Dependency
31
47
  name: rspec
32
48
  requirement: !ruby/object:Gem::Requirement
@@ -78,7 +94,8 @@ dependencies:
78
94
  description: Stalker with threads
79
95
  email:
80
96
  - lyondhill@gmail.com
81
- executables: []
97
+ executables:
98
+ - creeper
82
99
  extensions: []
83
100
  extra_rdoc_files: []
84
101
  files:
@@ -88,8 +105,12 @@ files:
88
105
  - LICENSE
89
106
  - README.md
90
107
  - Rakefile
108
+ - bin/creeper
91
109
  - creeper.gemspec
110
+ - examples/jack.rb
92
111
  - lib/creeper.rb
112
+ - lib/creeper/creep.rb
113
+ - lib/creeper/launcher.rb
93
114
  - lib/creeper/session.rb
94
115
  - lib/creeper/version.rb
95
116
  - lib/creeper/worker.rb
@@ -109,15 +130,21 @@ required_ruby_version: !ruby/object:Gem::Requirement
109
130
  - - ! '>='
110
131
  - !ruby/object:Gem::Version
111
132
  version: '0'
133
+ segments:
134
+ - 0
135
+ hash: -2251731633976259583
112
136
  required_rubygems_version: !ruby/object:Gem::Requirement
113
137
  none: false
114
138
  requirements:
115
139
  - - ! '>='
116
140
  - !ruby/object:Gem::Version
117
141
  version: '0'
142
+ segments:
143
+ - 0
144
+ hash: -2251731633976259583
118
145
  requirements: []
119
146
  rubyforge_project:
120
- rubygems_version: 1.8.20
147
+ rubygems_version: 1.8.23
121
148
  signing_key:
122
149
  specification_version: 3
123
150
  summary: A better solution for io bound jobs, same as stalker in functionality but
@@ -127,4 +154,3 @@ test_files:
127
154
  - spec/lib/creeper/worker_spec.rb
128
155
  - spec/lib/creeper_spec.rb
129
156
  - spec/spec_helper.rb
130
- has_rdoc: