delayed_job_groups_mongoid 0.2.2
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/Gemfile +18 -0
- data/README.markdown +74 -0
- data/delayed_job_groups_mongoid.gemspec +39 -0
- data/lib/delayed_job_groups.rb +7 -0
- data/lib/delayed_job_groups/init.rb +3 -0
- data/lib/delayed_job_groups/job_groups.rb +29 -0
- data/lib/delayed_job_groups/mongoid_groups.rb +41 -0
- data/spec/delayed_job_groups_spec.rb +107 -0
- data/spec/spec_helper.rb +41 -0
- metadata +76 -0
data/Gemfile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
source :rubygems
|
2
|
+
|
3
|
+
gem 'rails'
|
4
|
+
gem 'delayed_job', '2.1.1'
|
5
|
+
gem 'delayed_job_mongoid', '1.0.0'
|
6
|
+
gem 'mongoid', '2.0.0.beta.20'
|
7
|
+
gem 'bson_ext'
|
8
|
+
|
9
|
+
|
10
|
+
group :test do
|
11
|
+
gem 'rspec-rails'
|
12
|
+
gem 'sqlite3-ruby'
|
13
|
+
gem 'autotest'
|
14
|
+
gem 'autotest-fsevent'
|
15
|
+
gem 'autotest-growl'
|
16
|
+
gem 'database_cleaner'
|
17
|
+
gem 'ruby-debug'
|
18
|
+
end
|
data/README.markdown
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
delayed job groups
|
2
|
+
==================
|
3
|
+
|
4
|
+
Adds grouping to jobs. Only 1 job in each group will be executed regardless of the number of workers.
|
5
|
+
|
6
|
+
|
7
|
+
Requires
|
8
|
+
--------
|
9
|
+
|
10
|
+
* rails 3
|
11
|
+
* Latest version of delayed_job from github - current release(v2.1.0.pre2) does not support enqueue hooks
|
12
|
+
* active_record backend - other backends aren't supported
|
13
|
+
|
14
|
+
|
15
|
+
Install
|
16
|
+
-------
|
17
|
+
|
18
|
+
# Gemfile
|
19
|
+
gem 'delayed_job_groups'
|
20
|
+
|
21
|
+
Add migration to add lock_group column
|
22
|
+
|
23
|
+
# db/migrate/xxx_add_lock_group_to_delayed_jobs
|
24
|
+
class AddLockGroupToDelayedJobs < ActiveRecord::Migration
|
25
|
+
def self.up
|
26
|
+
add_column :delayed_jobs, :lock_group, :string
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.down
|
30
|
+
remove_column :delayed_jobs, :lock_group
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
Usage
|
36
|
+
-----
|
37
|
+
|
38
|
+
Job groups are strings. You need to specify what the job_group should be in a block. Delayed job will only perform 1 job from each group at a time.
|
39
|
+
|
40
|
+
### Job groups for standard jobs ###
|
41
|
+
|
42
|
+
class ResizeImageJob < Struct.new(:format)
|
43
|
+
job_group{ |resize_image_job| resize_image_job.format }
|
44
|
+
|
45
|
+
def perform
|
46
|
+
resize_to format
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
### Job groups when using delay() ###
|
52
|
+
|
53
|
+
class Person < ActiveRecord::Base
|
54
|
+
job_group{ |person| person.role }
|
55
|
+
|
56
|
+
def send_welcome
|
57
|
+
...
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
Person.create(:role => "admin").delay.send_welcome
|
62
|
+
|
63
|
+
### Job groups when methods are declared asynchonous ###
|
64
|
+
|
65
|
+
class Person < ActiveRecord::Base
|
66
|
+
job_group{ |person| person.role }
|
67
|
+
|
68
|
+
def send_welcome
|
69
|
+
...
|
70
|
+
end
|
71
|
+
handle_asynchronously :send_welcome
|
72
|
+
end
|
73
|
+
|
74
|
+
Person.create(:role => "admin").send_welcome
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile.rb, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{delayed_job_groups_mongoid}
|
8
|
+
s.version = "0.2.2"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["opsb"]
|
12
|
+
s.date = %q{2010-11-04}
|
13
|
+
s.description = %q{Adds job groups to delayed_job}
|
14
|
+
s.email = %q{oliver@opsb.co.uk}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"README.markdown"
|
17
|
+
]
|
18
|
+
s.files = Dir.glob("lib/**/*") + %w(Gemfile delayed_job_groups_mongoid.gemspec README.markdown)
|
19
|
+
s.homepage = %q{http://github.com/opsb/delayed_job_groups}
|
20
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
21
|
+
s.require_paths = ["lib"]
|
22
|
+
s.rubygems_version = %q{1.3.7}
|
23
|
+
s.summary = %q{Adds job groups to delayed_job}
|
24
|
+
s.test_files = [
|
25
|
+
"spec/delayed_job_groups_spec.rb",
|
26
|
+
"spec/spec_helper.rb"
|
27
|
+
]
|
28
|
+
|
29
|
+
if s.respond_to? :specification_version then
|
30
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
31
|
+
s.specification_version = 3
|
32
|
+
|
33
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
34
|
+
else
|
35
|
+
end
|
36
|
+
else
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module JobGroups
|
2
|
+
|
3
|
+
def self.included(base)
|
4
|
+
base.extend(ClassMethods)
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def job_group(&block)
|
9
|
+
@lock_group_factory = block
|
10
|
+
end
|
11
|
+
|
12
|
+
def has_job_groups?
|
13
|
+
!!@lock_group_factory
|
14
|
+
end
|
15
|
+
|
16
|
+
def lock_group(payload)
|
17
|
+
@lock_group_factory.call(payload)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def enqueue(job)
|
22
|
+
payload = job.payload_object
|
23
|
+
target = payload.class == ::Delayed::PerformableMethod ? payload.object : payload
|
24
|
+
if target.class.has_job_groups?
|
25
|
+
job.lock_group = target.class.lock_group(target)
|
26
|
+
job.save
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'delayed_job'
|
2
|
+
|
3
|
+
|
4
|
+
module Delayed
|
5
|
+
class Backend::Mongoid::Job
|
6
|
+
field :lock_group
|
7
|
+
scope :in_unlocked_group, lambda{ where(:lock_group.nin => self.locked_groups) }
|
8
|
+
|
9
|
+
def self.locked_groups
|
10
|
+
Delayed::Job.only(:lock_group).where(:locked_by.ne => nil).map{|grouping| grouping[:lock_group]}
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.find_available(worker_name, limit = 5, max_run_time = Worker.max_run_time)
|
14
|
+
right_now = db_time_now
|
15
|
+
|
16
|
+
conditions = {:run_at => {"$lte" => right_now}, :failed_at => nil}
|
17
|
+
(conditions[:priority] ||= {})['$gte'] = Worker.min_priority.to_i if Worker.min_priority
|
18
|
+
(conditions[:priority] ||= {})['$lte'] = Worker.max_priority.to_i if Worker.max_priority
|
19
|
+
|
20
|
+
|
21
|
+
where = "this.locked_at == null || this.locked_at < #{make_date(right_now - max_run_time)}"
|
22
|
+
results = self.in_unlocked_group.where(conditions.merge(:locked_by => worker_name)).limit(-limit).order_by([['priority', 1], ['run_at', 1]]).to_a
|
23
|
+
results += self.in_unlocked_group.where(conditions.merge('$where' => where)).limit(-limit+results.size).order_by([['priority', 1], ['run_at', 1]]).to_a if results.size < limit
|
24
|
+
results
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
__END__
|
31
|
+
|
32
|
+
scope :in_unlocked_group, lambda{
|
33
|
+
delayed_jobs = self.table_name
|
34
|
+
unlocked_groups_select = "select distinct #{delayed_jobs}.lock_group from #{delayed_jobs} where #{delayed_jobs}.locked_by is not null"
|
35
|
+
where("#{delayed_jobs}.lock_group not in (#{unlocked_groups_select}) or #{delayed_jobs}.lock_group is null")
|
36
|
+
}
|
37
|
+
scope :orig_ready_to_run, scopes[:ready_to_run]
|
38
|
+
scope :ready_to_run, lambda{ |*args|
|
39
|
+
orig_ready_to_run(*args).
|
40
|
+
in_unlocked_group
|
41
|
+
}
|
@@ -0,0 +1,107 @@
|
|
1
|
+
require 'spec/spec_helper'
|
2
|
+
|
3
|
+
|
4
|
+
class GroupedJob
|
5
|
+
include Mongoid::Document
|
6
|
+
include JobGroups
|
7
|
+
field :repository
|
8
|
+
|
9
|
+
job_group{ |simple_job| simple_job.repository }
|
10
|
+
|
11
|
+
def perform
|
12
|
+
puts "running grouped job"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class SimpleJob
|
17
|
+
include Mongoid::Document
|
18
|
+
include JobGroups
|
19
|
+
|
20
|
+
def perform
|
21
|
+
puts "running simple job"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class User
|
26
|
+
include Mongoid::Document
|
27
|
+
include JobGroups
|
28
|
+
|
29
|
+
job_group{ |user| user.role }
|
30
|
+
def send_welcome_email
|
31
|
+
puts "thanking user"
|
32
|
+
end
|
33
|
+
handle_asynchronously :send_welcome_email
|
34
|
+
def expensive_operation
|
35
|
+
puts "working hard"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe Delayed::Job do
|
40
|
+
MAX_RUN_TIME = 4000 # seconds? TBC
|
41
|
+
WORKER = 'name_of_worker'
|
42
|
+
|
43
|
+
context "a job" do
|
44
|
+
before do
|
45
|
+
Delayed::Job.enqueue GroupedJob.new(:repository => "repo1")
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should have a job group" do
|
49
|
+
Delayed::Job.first.lock_group.should == "repo1"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context "with 2 jobs in the same group, one locked, one unlocked and 1 job in a different group" do
|
54
|
+
before do
|
55
|
+
group_a_job1 = GroupedJob.new(:repository => "repo1")
|
56
|
+
group_a_job2 = GroupedJob.new(:repository => "repo1")
|
57
|
+
@group_b_job1 = GroupedJob.new(:repository => "repo2")
|
58
|
+
|
59
|
+
[group_a_job1, @group_b_job1].each{ |job| Delayed::Job.enqueue job }
|
60
|
+
queued_job = Delayed::Job.enqueue group_a_job2
|
61
|
+
queued_job.lock_exclusively!(MAX_RUN_TIME, WORKER)
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should find no unlocked jobs" do
|
65
|
+
Delayed::Job.find_available(WORKER, MAX_RUN_TIME).count.should == 1
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context "job with no group" do
|
70
|
+
before do
|
71
|
+
Delayed::Job.enqueue SimpleJob.new
|
72
|
+
end
|
73
|
+
it "should still be queuable" do
|
74
|
+
Delayed::Job.find_available(WORKER, MAX_RUN_TIME).count.should == 1
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
context "with 2 jobs in the same group, from methods declared as asynchronous, one unlocked and 1 job in a different group" do
|
79
|
+
before do
|
80
|
+
2.times do
|
81
|
+
User.create(:role => "admin").send_welcome_email
|
82
|
+
end
|
83
|
+
1.times{ Delayed::Job.enqueue GroupedJob.new(:repository => "repo2") }
|
84
|
+
Delayed::Job.first.lock_exclusively!(MAX_RUN_TIME, WORKER)
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should find no jobs that are ready to run" do
|
89
|
+
Delayed::Job.find_available(WORKER, MAX_RUN_TIME).count.should == 1
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
context "with 2 jobs in the same group, from delay() calls, one unlocked and 1 job in a different group" do
|
94
|
+
before do
|
95
|
+
2.times do
|
96
|
+
User.create(:role => "admin").delay.expensive_operation
|
97
|
+
end
|
98
|
+
1.times{ Delayed::Job.enqueue GroupedJob.new(:repository => "repo2") }
|
99
|
+
Delayed::Job.first.lock_exclusively!(MAX_RUN_TIME, WORKER)
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
it "should find no jobs that are ready to run" do
|
104
|
+
Delayed::Job.find_available(WORKER, MAX_RUN_TIME).count.should == 1
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__) + '/../lib')
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'logger'
|
6
|
+
require 'mongoid'
|
7
|
+
|
8
|
+
|
9
|
+
|
10
|
+
ENV['RAILS_ENV'] = 'test'
|
11
|
+
require 'rails'
|
12
|
+
|
13
|
+
Mongoid.configure do |config|
|
14
|
+
name = "delayed_job_groups_mongoid"
|
15
|
+
host = "localhost"
|
16
|
+
config.master = Mongo::Connection.new.db(name)
|
17
|
+
config.slaves = [
|
18
|
+
Mongo::Connection.new(host, 27017, :slave_ok => true).db(name)
|
19
|
+
]
|
20
|
+
config.persist_in_safe_mode = false
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
require 'rspec'
|
25
|
+
require 'delayed_job'
|
26
|
+
|
27
|
+
Delayed::Worker.logger = Logger.new('/tmp/dj.log')
|
28
|
+
|
29
|
+
Delayed::Worker.backend = :mongoid
|
30
|
+
|
31
|
+
|
32
|
+
require 'delayed_job_groups/init.rb'
|
33
|
+
RSpec.configure do |config|
|
34
|
+
require 'database_cleaner'
|
35
|
+
DatabaseCleaner.strategy = :truncation
|
36
|
+
config.after(:each) do
|
37
|
+
DatabaseCleaner.clean
|
38
|
+
end
|
39
|
+
config.filter_run :focus => true
|
40
|
+
config.run_all_when_everything_filtered = true
|
41
|
+
end
|
metadata
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: delayed_job_groups_mongoid
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 19
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 2
|
9
|
+
- 2
|
10
|
+
version: 0.2.2
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- opsb
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-11-04 00:00:00 +00:00
|
19
|
+
default_executable:
|
20
|
+
dependencies: []
|
21
|
+
|
22
|
+
description: Adds job groups to delayed_job
|
23
|
+
email: oliver@opsb.co.uk
|
24
|
+
executables: []
|
25
|
+
|
26
|
+
extensions: []
|
27
|
+
|
28
|
+
extra_rdoc_files:
|
29
|
+
- README.markdown
|
30
|
+
files:
|
31
|
+
- lib/delayed_job_groups/init.rb
|
32
|
+
- lib/delayed_job_groups/job_groups.rb
|
33
|
+
- lib/delayed_job_groups/mongoid_groups.rb
|
34
|
+
- lib/delayed_job_groups.rb
|
35
|
+
- Gemfile
|
36
|
+
- delayed_job_groups_mongoid.gemspec
|
37
|
+
- README.markdown
|
38
|
+
- spec/delayed_job_groups_spec.rb
|
39
|
+
- spec/spec_helper.rb
|
40
|
+
has_rdoc: true
|
41
|
+
homepage: http://github.com/opsb/delayed_job_groups
|
42
|
+
licenses: []
|
43
|
+
|
44
|
+
post_install_message:
|
45
|
+
rdoc_options:
|
46
|
+
- --charset=UTF-8
|
47
|
+
require_paths:
|
48
|
+
- lib
|
49
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
hash: 3
|
55
|
+
segments:
|
56
|
+
- 0
|
57
|
+
version: "0"
|
58
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
59
|
+
none: false
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
hash: 3
|
64
|
+
segments:
|
65
|
+
- 0
|
66
|
+
version: "0"
|
67
|
+
requirements: []
|
68
|
+
|
69
|
+
rubyforge_project:
|
70
|
+
rubygems_version: 1.3.7
|
71
|
+
signing_key:
|
72
|
+
specification_version: 3
|
73
|
+
summary: Adds job groups to delayed_job
|
74
|
+
test_files:
|
75
|
+
- spec/delayed_job_groups_spec.rb
|
76
|
+
- spec/spec_helper.rb
|