resque-mongo-groups 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/.bundle/config +2 -0
- data/.gitignore +1 -0
- data/Gemfile +3 -0
- data/HISTORY.md +0 -0
- data/LICENSE +20 -0
- data/README.markdown +0 -0
- data/Rakefile +17 -0
- data/lib/resque_groups.rb +129 -0
- data/lib/resque_groups/server.rb +23 -0
- data/lib/resque_groups/server/views/groups.erb +43 -0
- data/lib/resque_groups/tasks.rb +7 -0
- data/lib/resque_groups/tracked_job.rb +58 -0
- data/lib/resque_groups/version.rb +7 -0
- data/resque-mongo-groups.gemspec +27 -0
- data/tasks/resque_counter.rake +2 -0
- metadata +92 -0
data/.bundle/config
ADDED
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
Gemfile.lock
|
data/Gemfile
ADDED
data/HISTORY.md
ADDED
File without changes
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 Gheorghita Catalin Bordianu
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.markdown
ADDED
File without changes
|
data/Rakefile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
Bundler::GemHelper.install_tasks
|
3
|
+
|
4
|
+
$LOAD_PATH.unshift 'lib'
|
5
|
+
|
6
|
+
Dir['tasks/*.rake'].each do |f|
|
7
|
+
load File.expand_path(f)
|
8
|
+
end
|
9
|
+
|
10
|
+
task :default => :test
|
11
|
+
|
12
|
+
desc "Run tests"
|
13
|
+
task :test do
|
14
|
+
Dir['test/*_test.rb'].each do |f|
|
15
|
+
require File.expand_path(f)
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
gem 'resque-mongo'
|
3
|
+
require 'resque'
|
4
|
+
require 'resque/server'
|
5
|
+
|
6
|
+
module Resque
|
7
|
+
module Plugins
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
require 'resque_groups/tracked_job'
|
12
|
+
require 'resque_groups/server'
|
13
|
+
|
14
|
+
module Resque::Plugins::Groups
|
15
|
+
|
16
|
+
def job_groups
|
17
|
+
self.mongo ||= ENV['MONGO'] || 'localhost:27017'
|
18
|
+
@job_groups ||= @db.collection('job_groups')
|
19
|
+
end
|
20
|
+
|
21
|
+
# Triggered whenever a job belonging to group 'gid' has been enqueued. If there's no entry for that group in the 'job_groups'
|
22
|
+
# collection, create one and record a timestamp. Otherwise, just increment a total count of enqueued jobs for that group
|
23
|
+
def upsert_group(gid)
|
24
|
+
if job_groups.find_one({'_id' => gid}).nil?
|
25
|
+
job_groups.update( {'_id' => gid},
|
26
|
+
{
|
27
|
+
'$inc' => {'total' => 1},
|
28
|
+
'$set' => {'started_at' => Time.now}
|
29
|
+
}, {:upsert => true})
|
30
|
+
else
|
31
|
+
job_groups.update( {'_id' => gid},
|
32
|
+
{
|
33
|
+
'$inc' => {'total' => 1}
|
34
|
+
}, {:upsert => true})
|
35
|
+
end
|
36
|
+
#Resque.print_groups
|
37
|
+
job_groups.count
|
38
|
+
end
|
39
|
+
|
40
|
+
# Increments the 'completed' count of the group with id 'gid'.
|
41
|
+
# Also merge the increment operation with optional custom
|
42
|
+
# atomic operations.
|
43
|
+
def record_performed_job(gid, atomic_op = nil)
|
44
|
+
op = {'$inc' => {'completed' => 1}}
|
45
|
+
if atomic_op
|
46
|
+
op['$inc'].merge!(atomic_op.delete('$inc')) if atomic_op['$inc']
|
47
|
+
op.merge!(atomic_op)
|
48
|
+
end
|
49
|
+
job_groups.update( {'_id' => gid}, op , {:upsert => true})
|
50
|
+
#Resque.print_groups
|
51
|
+
end
|
52
|
+
|
53
|
+
# Increments the 'failed' count of the group with id 'gid'
|
54
|
+
def record_failed_job(gid, exception)
|
55
|
+
job_groups.update( {'_id' => gid},
|
56
|
+
{
|
57
|
+
'$inc' => {'failed' => 1},
|
58
|
+
'$push' => {'exceptions' => exception.inspect}
|
59
|
+
}, {:upsert => true})
|
60
|
+
#Resque.print_groups
|
61
|
+
end
|
62
|
+
|
63
|
+
def record_delayed_job(gid)
|
64
|
+
job_groups.update( {'_id' => gid},
|
65
|
+
{
|
66
|
+
'$inc' => {'delayed' => 1}
|
67
|
+
}, {:upsert => true})
|
68
|
+
#Resque.print_groups
|
69
|
+
end
|
70
|
+
|
71
|
+
def delete_delayed_job(gid)
|
72
|
+
job_groups.update( {'_id' => gid},
|
73
|
+
{
|
74
|
+
'$inc' => {'delayed' => -1}
|
75
|
+
}, {:upsert => true})
|
76
|
+
#Resque.print_groups
|
77
|
+
end
|
78
|
+
|
79
|
+
# Clears all the groups that are being stored in the job_groups collection
|
80
|
+
def reset_groups_queue
|
81
|
+
job_groups.remove
|
82
|
+
end
|
83
|
+
|
84
|
+
# Deletes a group from the job_groups collection
|
85
|
+
def remove_group(gid)
|
86
|
+
job_groups.remove({'_id' => gid})
|
87
|
+
end
|
88
|
+
|
89
|
+
# Returns the stats for a given job group
|
90
|
+
def group_stats(gid)
|
91
|
+
job_groups.find_one({'_id' => gid})
|
92
|
+
end
|
93
|
+
|
94
|
+
def groups_stats(start, count)
|
95
|
+
job_groups.find({}, :skip => start, :limit => count, :sort => ['_id', 1]).to_a
|
96
|
+
end
|
97
|
+
|
98
|
+
def groups_count
|
99
|
+
job_groups.count
|
100
|
+
end
|
101
|
+
|
102
|
+
# Given a set of args, looks through all the args that are Hashes for a :group_id key (the inject line replaces string keys with their symbols)
|
103
|
+
def parse_gid(*args)
|
104
|
+
gid = nil
|
105
|
+
args.flatten.each do |arg|
|
106
|
+
next unless arg.kind_of? Hash
|
107
|
+
sym_arg = arg.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
|
108
|
+
if !sym_arg[:group_id].nil?
|
109
|
+
gid = sym_arg[:group_id]
|
110
|
+
break
|
111
|
+
end
|
112
|
+
end
|
113
|
+
gid
|
114
|
+
end
|
115
|
+
|
116
|
+
def print_groups
|
117
|
+
puts "--------------------"
|
118
|
+
job_groups.find().each do |row|
|
119
|
+
puts row.inspect
|
120
|
+
end
|
121
|
+
puts "--------------------"
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
125
|
+
|
126
|
+
Resque.extend Resque::Plugins::Groups
|
127
|
+
Resque::Server.class_eval do
|
128
|
+
include ResqueGroups::Server
|
129
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
# Extend Resque::Server to add tabs
|
4
|
+
module ResqueGroups
|
5
|
+
|
6
|
+
module Server
|
7
|
+
|
8
|
+
def self.included(base)
|
9
|
+
|
10
|
+
base.class_eval do
|
11
|
+
|
12
|
+
get "/groups" do
|
13
|
+
erb File.read(File.join(File.dirname(__FILE__), 'server/views/groups.erb'))
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
Resque::Server.tabs << 'Groups'
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
<%start = params[:start].to_i %>
|
2
|
+
<%count = params[:count] ? params[:count].to_i : 100 %>
|
3
|
+
|
4
|
+
<% groups = [resque.groups_stats(start, start + count)].flatten %>
|
5
|
+
<% size = resque.groups_count %>
|
6
|
+
<h1>Groups</h1>
|
7
|
+
|
8
|
+
<p class='intro'>
|
9
|
+
This list below contains the groups being processed.
|
10
|
+
</p>
|
11
|
+
|
12
|
+
<p class='sub'>
|
13
|
+
Showing <%= start %> to <%= start + groups.size %> of <b><%= size %></b> groups
|
14
|
+
</p>
|
15
|
+
|
16
|
+
|
17
|
+
<table>
|
18
|
+
<tr>
|
19
|
+
<th>Group</th>
|
20
|
+
<th>Started at</th>
|
21
|
+
<th>Total</th>
|
22
|
+
<th>Delayed</th>
|
23
|
+
<th>Completed</th>
|
24
|
+
<th>Failed</th>
|
25
|
+
<th>Exceptions</th>
|
26
|
+
<th>Custom field</th>
|
27
|
+
</tr>
|
28
|
+
<% groups.each do |group| %>
|
29
|
+
<tr>
|
30
|
+
<td><%= group['_id'] %></td>
|
31
|
+
<td><%= group['started_at'] %></td>
|
32
|
+
<td><%= group['total'] %></td>
|
33
|
+
<td><%= group['delayed'] %></td>
|
34
|
+
<td><%= group['completed'] %></td>
|
35
|
+
<td><%= group['failed'] %></td>
|
36
|
+
<td><%= escape_html(group['exceptions'].inspect) if group['exceptions'] %></td>
|
37
|
+
<td><%= escape_html(group['custom'].inspect) if group['custom'] %></td>
|
38
|
+
</tr>
|
39
|
+
<% end %>
|
40
|
+
</table>
|
41
|
+
|
42
|
+
|
43
|
+
<%= partial :next_more, :start => start, :count => count, :size => size %>
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Resque::Plugins::Groups
|
2
|
+
module TrackedJob
|
3
|
+
|
4
|
+
# The MongoDB atomic operation stored in this variable will be executed
|
5
|
+
# on the group when and if the job is complete.
|
6
|
+
# See http://www.mongodb.org/display/DOCS/Updating for a list of all atomic operations.
|
7
|
+
# e.g.:
|
8
|
+
# {'$inc' => {'custom.size' => 3}}
|
9
|
+
# {'$set' => {'custom.api_problem' => true}}
|
10
|
+
# It's highly recommended to use the "custom" field to store your data, even
|
11
|
+
# if no check is done by the plugin.
|
12
|
+
def atomic_op_on_complete
|
13
|
+
@atomic_op_on_complete
|
14
|
+
end
|
15
|
+
def atomic_op_on_complete=(op)
|
16
|
+
@atomic_op_on_complete = op
|
17
|
+
end
|
18
|
+
|
19
|
+
# resque hook, triggered immediately after a job with *args has been enqueued
|
20
|
+
def after_enqueue_count_job(*args)
|
21
|
+
gid = Resque.parse_gid(*args)
|
22
|
+
raise "The 'group_id' parameter is missing!" if gid.nil?
|
23
|
+
|
24
|
+
# If this was a scheduled/delayed job, decrement the 'delayed' counter
|
25
|
+
Resque.delete_delayed_job(gid) if !is_delayed(args).nil?
|
26
|
+
|
27
|
+
# Update the total count for the group in which the enqueued job belongs
|
28
|
+
Resque.upsert_group(gid)
|
29
|
+
end
|
30
|
+
|
31
|
+
# resque hook, triggered immediately after a job has been performed
|
32
|
+
def after_perform_update_completed(*args)
|
33
|
+
gid = Resque.parse_gid(*args)
|
34
|
+
Resque.record_performed_job(gid, atomic_op_on_complete)
|
35
|
+
end
|
36
|
+
|
37
|
+
# resque hook, triggered if a job raises any exception. uncaught exceptions propagate up to Resque::Failure
|
38
|
+
def on_failure_update_failed(exception, *args)
|
39
|
+
gid = Resque.parse_gid(*args)
|
40
|
+
Resque.record_failed_job(gid, exception)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Determines if this was a delayed job or not, based on the :delayed => true arg
|
44
|
+
def is_delayed(*args)
|
45
|
+
delayed = nil
|
46
|
+
args.flatten.each do |arg|
|
47
|
+
next unless arg.kind_of? Hash
|
48
|
+
sym_arg = arg.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
|
49
|
+
if !sym_arg[:delayed].nil?
|
50
|
+
delayed = sym_arg[:delayed]
|
51
|
+
break
|
52
|
+
end
|
53
|
+
end
|
54
|
+
delayed
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "resque_groups/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "resque-mongo-groups"
|
7
|
+
s.version = Resque::Plugins::Groups::Version
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.author = 'Gheorghita Catalin Bordianu'
|
10
|
+
s.email = ['catalin@silentale.com']
|
11
|
+
s.homepage = "http://github.com/omikronn/resque-mongo-groups"
|
12
|
+
s.summary = "Keeps track of groups of jobs and their completion status"
|
13
|
+
s.description = %q{Provides a way of keeping track of batches of jobs, completion status, start time etc. using a MongoDb collection}
|
14
|
+
|
15
|
+
s.required_rubygems_version = ">= 1.3.6"
|
16
|
+
s.add_development_dependency "bundler", ">= 1.0.0"
|
17
|
+
|
18
|
+
s.rubyforge_project = "resque-mongo-groups"
|
19
|
+
|
20
|
+
s.files = `git ls-files`.split("\n")
|
21
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
22
|
+
s.executables = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact
|
23
|
+
s.require_path = 'lib'
|
24
|
+
|
25
|
+
s.add_runtime_dependency(%q<resque-mongo>, [">= 1.11.0"])
|
26
|
+
|
27
|
+
end
|
metadata
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: resque-mongo-groups
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: "0.5"
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Gheorghita Catalin Bordianu
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2011-03-24 00:00:00 +01:00
|
14
|
+
default_executable:
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: bundler
|
18
|
+
prerelease: false
|
19
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
20
|
+
none: false
|
21
|
+
requirements:
|
22
|
+
- - ">="
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: 1.0.0
|
25
|
+
type: :development
|
26
|
+
version_requirements: *id001
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: resque-mongo
|
29
|
+
prerelease: false
|
30
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
31
|
+
none: false
|
32
|
+
requirements:
|
33
|
+
- - ">="
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: 1.11.0
|
36
|
+
type: :runtime
|
37
|
+
version_requirements: *id002
|
38
|
+
description: Provides a way of keeping track of batches of jobs, completion status, start time etc. using a MongoDb collection
|
39
|
+
email:
|
40
|
+
- catalin@silentale.com
|
41
|
+
executables: []
|
42
|
+
|
43
|
+
extensions: []
|
44
|
+
|
45
|
+
extra_rdoc_files: []
|
46
|
+
|
47
|
+
files:
|
48
|
+
- .bundle/config
|
49
|
+
- .gitignore
|
50
|
+
- Gemfile
|
51
|
+
- HISTORY.md
|
52
|
+
- LICENSE
|
53
|
+
- README.markdown
|
54
|
+
- Rakefile
|
55
|
+
- lib/resque_groups.rb
|
56
|
+
- lib/resque_groups/server.rb
|
57
|
+
- lib/resque_groups/server/views/groups.erb
|
58
|
+
- lib/resque_groups/tasks.rb
|
59
|
+
- lib/resque_groups/tracked_job.rb
|
60
|
+
- lib/resque_groups/version.rb
|
61
|
+
- resque-mongo-groups.gemspec
|
62
|
+
- tasks/resque_counter.rake
|
63
|
+
has_rdoc: true
|
64
|
+
homepage: http://github.com/omikronn/resque-mongo-groups
|
65
|
+
licenses: []
|
66
|
+
|
67
|
+
post_install_message:
|
68
|
+
rdoc_options: []
|
69
|
+
|
70
|
+
require_paths:
|
71
|
+
- lib
|
72
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: "0"
|
78
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
79
|
+
none: false
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: 1.3.6
|
84
|
+
requirements: []
|
85
|
+
|
86
|
+
rubyforge_project: resque-mongo-groups
|
87
|
+
rubygems_version: 1.6.1
|
88
|
+
signing_key:
|
89
|
+
specification_version: 3
|
90
|
+
summary: Keeps track of groups of jobs and their completion status
|
91
|
+
test_files: []
|
92
|
+
|