rabbit_jobs 0.0.4 → 0.0.5

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
@@ -1,6 +1,6 @@
1
1
  # RabbitJobs
2
2
 
3
- TODO: Write a gem description
3
+ Schedule jobs on rabbitmq and run them in background.
4
4
 
5
5
  ## Installation
6
6
 
@@ -18,7 +18,7 @@ Or install it yourself as:
18
18
 
19
19
  ## Usage
20
20
 
21
- TODO: Write usage instructions here
21
+ TODO: Write usage instructions here.
22
22
 
23
23
  ## Contributing
24
24
 
@@ -14,7 +14,6 @@ RabbitJobs.configure do |c|
14
14
  end
15
15
 
16
16
  puts JSON.pretty_generate(RabbitJobs.config.to_hash)
17
-
18
17
  puts JSON.pretty_generate(RabbitJobs.config.queues)
19
18
 
20
19
  class MyJob < RabbitJobs::Job
data/lib/rabbit_jobs.rb CHANGED
@@ -10,6 +10,7 @@ require 'rabbit_jobs/logger'
10
10
  require 'rabbit_jobs/job'
11
11
  require 'rabbit_jobs/publisher'
12
12
  require 'rabbit_jobs/worker'
13
+ require 'rabbit_jobs/scheduler'
13
14
 
14
15
  module RabbitJobs
15
16
  extend self
@@ -21,4 +22,8 @@ module RabbitJobs
21
22
  def publish_to(routing_key, klass, opts = {}, *params)
22
23
  RabbitJobs::Publisher.publish_to(routing_key, klass, opts, *params)
23
24
  end
25
+ end
26
+
27
+ module RJ
28
+ include RabbitJobs
24
29
  end
@@ -4,7 +4,6 @@ module RabbitJobs
4
4
  module AmqpHelpers
5
5
 
6
6
  # Calls given block with initialized amqp
7
-
8
7
  def amqp_with_exchange(&block)
9
8
  raise ArgumentError unless block
10
9
 
@@ -23,6 +22,23 @@ module RabbitJobs
23
22
  end
24
23
  end
25
24
 
25
+ def em_amqp_with_exchange(&block)
26
+ raise ArgumentError unless block
27
+
28
+ connection = AMQP.connect(host: RabbitJobs.config.host)
29
+ channel = AMQP::Channel.new(connection)
30
+
31
+ channel.on_error do |ch, channel_close|
32
+ puts "Channel-level error: #{channel_close.reply_text}, shutting down..."
33
+ connection.close { EM.stop }
34
+ end
35
+
36
+ exchange = channel.direct(RabbitJobs.config[:exchange], RabbitJobs.config[:exchange_params])
37
+
38
+ # go work
39
+ block.call(connection, exchange)
40
+ end
41
+
26
42
  def amqp_with_queue(routing_key, &block)
27
43
 
28
44
  raise ArgumentError unless routing_key && block
@@ -21,7 +21,7 @@ module RabbitJobs::Job
21
21
  def run_perform
22
22
  if @child_pid = fork
23
23
  srand # Reseeding
24
- log "Forked #{@child_pid} at #{Time.now} to process #{self.class}.perform(#{ params.map(&:inspect).join(', ') })"
24
+ RabbitJobs::Logger.log "Forked #{@child_pid} at #{Time.now} to process #{self.class}.perform(#{ params.map(&:inspect).join(', ') })"
25
25
  Process.wait(@child_pid)
26
26
  yield if block_given?
27
27
  else
@@ -21,7 +21,11 @@ module RabbitJobs
21
21
  job = klass.new(*params)
22
22
  job.opts = opts
23
23
 
24
- publish_job_to(routing_key, job)
24
+ if defined?(EM) && EM.reactor_running?
25
+ em_publish_job_to(routing_key, job)
26
+ else
27
+ publish_job_to(routing_key, job)
28
+ end
25
29
  end
26
30
 
27
31
  def publish_job_to(routing_key, job)
@@ -33,7 +37,24 @@ module RabbitJobs
33
37
 
34
38
  payload = job.payload
35
39
  exchange.publish(job.payload, Configuration::DEFAULT_MESSAGE_PARAMS.merge({routing_key: routing_key})) {
36
- connection.close { EM.stop }
40
+ connection.close {
41
+ EM.stop
42
+ }
43
+ }
44
+ end
45
+ end
46
+
47
+ def em_publish_job_to(routing_key, job)
48
+ em_amqp_with_exchange do |connection, exchange|
49
+
50
+ queue = make_queue(exchange, routing_key)
51
+
52
+ job.opts['created_at'] = Time.now.to_s
53
+
54
+ payload = job.payload
55
+ exchange.publish(job.payload, Configuration::DEFAULT_MESSAGE_PARAMS.merge({routing_key: routing_key})) {
56
+ connection.close {
57
+ }
37
58
  }
38
59
  end
39
60
  end
@@ -0,0 +1,160 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ require 'rufus/scheduler'
4
+ require 'thwait'
5
+ require 'yaml'
6
+
7
+ module RabbitJobs
8
+ class Scheduler
9
+ include AmqpHelpers
10
+ include Logger
11
+
12
+ attr_accessor :pidfile, :background, :schedule
13
+
14
+ def load_default_schedule
15
+ if defined?(Rails)
16
+ file = Rails.root.join('config/schedule.yml')
17
+ if file.file?
18
+ @schedule = YAML.load_file(file)
19
+ end
20
+ end
21
+ end
22
+
23
+ # Pulls the schedule from Resque.schedule and loads it into the
24
+ # rufus scheduler instance
25
+ def load_schedule!
26
+ @schedule ||= load_default_schedule
27
+
28
+ raise "You should setup a schedule or place it in config/schedule.yml" unless schedule
29
+
30
+ schedule.each do |name, config|
31
+ # If rails_env is set in the config, enforce ENV['RAILS_ENV'] as
32
+ # required for the jobs to be scheduled. If rails_env is missing, the
33
+ # job should be scheduled regardless of what ENV['RAILS_ENV'] is set
34
+ # to.
35
+ if config['rails_env'].nil? || rails_env_matches?(config)
36
+ log! "Scheduling #{name} "
37
+ interval_defined = false
38
+ interval_types = %w{cron every}
39
+ interval_types.each do |interval_type|
40
+ if !config[interval_type].nil? && config[interval_type].length > 0
41
+ rufus_scheduler.send(interval_type, config[interval_type]) do
42
+ log! "queueing #{config['class']} (#{name})"
43
+ publish_from_config(config)
44
+ end
45
+ interval_defined = true
46
+ break
47
+ end
48
+ end
49
+ unless interval_defined
50
+ log! "no #{interval_types.join(' / ')} found for #{config['class']} (#{name}) - skipping"
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+ # Returns true if the given schedule config hash matches the current ENV['RAILS_ENV']
57
+ def rails_env_matches?(config)
58
+ config['rails_env'] && ENV['RAILS_ENV'] && config['rails_env'].gsub(/\s/,'').split(',').include?(ENV['RAILS_ENV'])
59
+ end
60
+
61
+ # Publish a job based on a config hash
62
+ def publish_from_config(config)
63
+ args = config['args'] || config[:args] || []
64
+ klass_name = config['class'] || config[:class]
65
+ params = args.is_a?(Hash) ? [args] : Array(args)
66
+ queue = config['queue'] || config[:queue] || RabbitJobs.config.routing_keys.first
67
+
68
+ log "publishing #{config}"
69
+ RabbitJobs.publish_to(queue, klass_name.constantize, nil, *params)
70
+ rescue
71
+ log! "Failed to enqueue #{klass_name}:\n #{$!}\n params = #{params.inspect}"
72
+ end
73
+
74
+ def rufus_scheduler
75
+ @rufus_scheduler ||= Rufus::Scheduler.start_new
76
+ end
77
+
78
+ # Stops old rufus scheduler and creates a new one. Returns the new
79
+ # rufus scheduler
80
+ def clear_schedule!
81
+ rufus_scheduler.stop
82
+ @rufus_scheduler = nil
83
+ rufus_scheduler
84
+ end
85
+
86
+ # Subscribes to channel and working on jobs
87
+ def work(time = 0)
88
+ startup
89
+
90
+ processed_count = 0
91
+ amqp_with_exchange do |connection, exchange|
92
+ load_schedule!
93
+
94
+ check_shutdown = Proc.new {
95
+ if @shutdown
96
+ connection.close {
97
+ File.delete(self.pidfile) if self.pidfile
98
+ EM.stop { exit! }
99
+ }
100
+ end
101
+ }
102
+
103
+ if time > 0
104
+ # for debugging
105
+ EM.add_timer(time) do
106
+ log "Stopping scheduler..."
107
+ self.shutdown
108
+ end
109
+ end
110
+
111
+ log "Scheduler started."
112
+
113
+ EM.add_periodic_timer(1) do
114
+ check_shutdown.call
115
+ end
116
+ end
117
+ end
118
+
119
+ def shutdown
120
+ log "Stopping scheduler..."
121
+ @shutdown = true
122
+ end
123
+
124
+ def startup
125
+ # prune_dead_workers
126
+
127
+ Process.daemon(true) if self.background
128
+
129
+ if self.pidfile
130
+ File.open(self.pidfile, 'w') { |f| f << Process.pid }
131
+ end
132
+
133
+ # Fix buffering so we can `rake rj:work > resque.log` and
134
+ # get output from the child in there.
135
+ $stdout.sync = true
136
+
137
+ @shutdown = false
138
+
139
+ Signal.trap('TERM') { shutdown }
140
+ Signal.trap('INT') { shutdown! }
141
+ end
142
+
143
+ def shutdown!
144
+ shutdown
145
+ kill_child
146
+ end
147
+
148
+ def kill_child
149
+ if @job && @job.child_pid
150
+ # log! "Killing child at #{@child}"
151
+ if Kernel.system("ps -o pid,state -p #{@job.child_pid}")
152
+ Process.kill("KILL", @job.child_pid) rescue nil
153
+ else
154
+ # log! "Child #{@child} not found, restarting."
155
+ # shutdown
156
+ end
157
+ end
158
+ end
159
+ end
160
+ end
@@ -1,11 +1,13 @@
1
1
  # require 'resque/tasks'
2
2
  # will give you the resque tasks
3
3
 
4
+ require 'rabbit_jobs'
5
+
4
6
  namespace :rj do
5
7
  task :setup
6
8
 
7
9
  desc "Start a Rabbit Jobs worker"
8
- task :work => [ :preload, :setup ] do
10
+ task :worker => [ :preload, :setup ] do
9
11
  require 'rabbit_jobs'
10
12
 
11
13
  queues = (ENV['QUEUES'] || ENV['QUEUE']).to_s.split(',')
@@ -18,10 +20,23 @@ namespace :rj do
18
20
  # worker.very_verbose = ENV['VVERBOSE']
19
21
  end
20
22
 
21
- # worker.log "Starting worker #{worker.pid}"
22
- # worker.verbose = true
23
- worker.work 10
24
- # worker.work(ENV['INTERVAL'] || 5) # interval, will block
23
+ worker.work
24
+ end
25
+
26
+ desc "Start a Rabbit Jobs scheduler"
27
+ task :scheduler => [ :preload, :setup ] do
28
+
29
+ queues = (ENV['SCHEDULE']).to_s.split(',')
30
+
31
+ begin
32
+ scheduler = RabbitJobs::Scheduler.new(*queues)
33
+ scheduler.pidfile = ENV['PIDFILE']
34
+ scheduler.background = %w(yes true).include? ENV['BACKGROUND']
35
+ RabbitJobs::Logger.verbose = true if ENV['VERBOSE']
36
+ # worker.very_verbose = ENV['VVERBOSE']
37
+ end
38
+
39
+ scheduler.work
25
40
  end
26
41
 
27
42
  desc "Start multiple Resque workers. Should only be used in dev mode."
@@ -30,7 +45,7 @@ namespace :rj do
30
45
 
31
46
  ENV['COUNT'].to_i.times do
32
47
  threads << Thread.new do
33
- system "rake resque:work"
48
+ system "rake resque:worker"
34
49
  end
35
50
  end
36
51
 
@@ -41,7 +56,8 @@ namespace :rj do
41
56
  task :preload => :setup do
42
57
  if defined?(Rails) && Rails.respond_to?(:application)
43
58
  # Rails 3
44
- Rails.application.eager_load!
59
+ # Rails.application.eager_load!
60
+ Rails.application.require_environment!
45
61
  elsif defined?(Rails::Initializer)
46
62
  # Rails 2.3
47
63
  $rails_rake_task = false
@@ -1,3 +1,3 @@
1
1
  module RabbitJobs
2
- VERSION = "0.0.4"
2
+ VERSION = "0.0.5"
3
3
  end
@@ -1,3 +1,3 @@
1
-
2
1
  $LOAD_PATH.unshift File.dirname(__FILE__) + '/../../lib'
2
+
3
3
  require 'rabbit_jobs/tasks'
data/rabbit_jobs.gemspec CHANGED
@@ -19,4 +19,5 @@ Gem::Specification.new do |gem|
19
19
 
20
20
  gem.add_dependency "amqp", "~> 0.9"
21
21
  gem.add_dependency "rake"
22
+ gem.add_dependency "rufus-scheduler", "~> 2.0"
22
23
  end
@@ -0,0 +1,13 @@
1
+ do_a_job:
2
+ cron: "* * * * *" # run every minute
3
+ queue: queue_name
4
+ class: MyJobClass
5
+ args: "string to be passed to MyJobClass.perform"
6
+ description: "Description is not supported yet"
7
+
8
+ another_job:
9
+ every: 1h # run every 1 hour
10
+ queue: queue_name
11
+ class: MyJobClass
12
+ args: "string to be passed to MyJobClass.perform"
13
+ description: "Description is not supported yet"
@@ -0,0 +1,17 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'spec_helper'
3
+
4
+ describe RabbitJobs::Scheduler do
5
+ it 'should start with config.yml' do
6
+ scheduler = RabbitJobs::Scheduler.new
7
+ scheduler.schedule = YAML.load_file(File.expand_path('../../fixtures/schedule.yml', __FILE__))
8
+
9
+ scheduler.pidfile = '/tmp/rj_scheduler.pid'
10
+ scheduler.background = false
11
+
12
+ scheduler.work(10) # work for 1 second
13
+
14
+ puts "messages queued: " + RabbitJobs::Publisher.purge_queue('default').to_s
15
+ RabbitJobs::Publisher.purge_queue('default').should == 0
16
+ end
17
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rabbit_jobs
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-01-31 00:00:00.000000000 Z
12
+ date: 2012-02-01 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: amqp
16
- requirement: &70340051105620 !ruby/object:Gem::Requirement
16
+ requirement: &70230061017880 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '0.9'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70340051105620
24
+ version_requirements: *70230061017880
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rake
27
- requirement: &70340051105000 !ruby/object:Gem::Requirement
27
+ requirement: &70230061016460 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,7 +32,18 @@ dependencies:
32
32
  version: '0'
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *70340051105000
35
+ version_requirements: *70230061016460
36
+ - !ruby/object:Gem::Dependency
37
+ name: rufus-scheduler
38
+ requirement: &70230061008760 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ version: '2.0'
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *70230061008760
36
47
  description: Background jobs on RabbitMQ
37
48
  email:
38
49
  - lazureykis@gmail.com
@@ -54,6 +65,7 @@ files:
54
65
  - lib/rabbit_jobs/job.rb
55
66
  - lib/rabbit_jobs/logger.rb
56
67
  - lib/rabbit_jobs/publisher.rb
68
+ - lib/rabbit_jobs/scheduler.rb
57
69
  - lib/rabbit_jobs/tasks.rb
58
70
  - lib/rabbit_jobs/version.rb
59
71
  - lib/rabbit_jobs/worker.rb
@@ -61,7 +73,9 @@ files:
61
73
  - rabbit_jobs.gemspec
62
74
  - spec/fixtures/config.yml
63
75
  - spec/fixtures/jobs.rb
76
+ - spec/fixtures/schedule.yml
64
77
  - spec/integration/publisher_spec.rb
78
+ - spec/integration/scheduler_spec.rb
65
79
  - spec/integration/worker_spec.rb
66
80
  - spec/spec_helper.rb
67
81
  - spec/unit/configuration_spec.rb
@@ -89,14 +103,16 @@ required_rubygems_version: !ruby/object:Gem::Requirement
89
103
  version: '0'
90
104
  requirements: []
91
105
  rubyforge_project:
92
- rubygems_version: 1.8.15
106
+ rubygems_version: 1.8.10
93
107
  signing_key:
94
108
  specification_version: 3
95
109
  summary: Background jobs on RabbitMQ
96
110
  test_files:
97
111
  - spec/fixtures/config.yml
98
112
  - spec/fixtures/jobs.rb
113
+ - spec/fixtures/schedule.yml
99
114
  - spec/integration/publisher_spec.rb
115
+ - spec/integration/scheduler_spec.rb
100
116
  - spec/integration/worker_spec.rb
101
117
  - spec/spec_helper.rb
102
118
  - spec/unit/configuration_spec.rb