resque-stats 0.1.0
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 +4 -0
- data/Gemfile.lock +42 -0
- data/README.md +68 -0
- data/Rakefile +2 -0
- data/lib/resque/plugins/stats.rb +97 -0
- data/resque-stats.gemspec +21 -0
- data/spec/redis-test.conf +30 -0
- data/spec/resque_stats_spec.rb +69 -0
- data/spec/spec_helper.rb +25 -0
- metadata +109 -0
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
resque-stats (0.1.0)
|
5
|
+
resque (~> 1.10.0)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: http://rubygems.org/
|
9
|
+
specs:
|
10
|
+
diff-lcs (1.1.2)
|
11
|
+
json (1.4.6)
|
12
|
+
rack (1.2.1)
|
13
|
+
redis (2.1.1)
|
14
|
+
redis-namespace (0.8.0)
|
15
|
+
redis (< 3.0.0)
|
16
|
+
resque (1.10.0)
|
17
|
+
json (~> 1.4.6)
|
18
|
+
redis-namespace (~> 0.8.0)
|
19
|
+
sinatra (>= 0.9.2)
|
20
|
+
vegas (~> 0.1.2)
|
21
|
+
rspec (2.2.0)
|
22
|
+
rspec-core (~> 2.2)
|
23
|
+
rspec-expectations (~> 2.2)
|
24
|
+
rspec-mocks (~> 2.2)
|
25
|
+
rspec-core (2.2.1)
|
26
|
+
rspec-expectations (2.2.0)
|
27
|
+
diff-lcs (~> 1.1.2)
|
28
|
+
rspec-mocks (2.2.0)
|
29
|
+
sinatra (1.1.0)
|
30
|
+
rack (~> 1.1)
|
31
|
+
tilt (~> 1.1)
|
32
|
+
tilt (1.1)
|
33
|
+
vegas (0.1.8)
|
34
|
+
rack (>= 1.0.0)
|
35
|
+
|
36
|
+
PLATFORMS
|
37
|
+
ruby
|
38
|
+
|
39
|
+
DEPENDENCIES
|
40
|
+
resque (~> 1.10.0)
|
41
|
+
resque-stats!
|
42
|
+
rspec (~> 2.2.0)
|
data/README.md
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
# Resque::Plugins::Stats
|
2
|
+
|
3
|
+
<http://github.com/jrom/resque-stats> by Jordi Romero
|
4
|
+
|
5
|
+
If you want to keep track of the number of executions per
|
6
|
+
Job, extend it with this module.
|
7
|
+
|
8
|
+
## Usage
|
9
|
+
|
10
|
+
require 'resque/plugins/stats'
|
11
|
+
class HardJob
|
12
|
+
extend Resque::Plugins::Stats
|
13
|
+
@queue = :hard_job
|
14
|
+
def self.perform(something)
|
15
|
+
do_work
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
This will keep a historical count of jobs executed *hourly*,
|
20
|
+
*daily* and *monthly*.
|
21
|
+
|
22
|
+
### Hourly
|
23
|
+
|
24
|
+
`HardJob.hourly` will return an array with the count of jobs executed
|
25
|
+
every hour during **today**. Indexes go from 0 to 23 hour.
|
26
|
+
|
27
|
+
### Daily
|
28
|
+
|
29
|
+
`HardJob.daily` will return an array with the count of jobs executed
|
30
|
+
every day during the **current month**. Indexes go from 0 (unused)
|
31
|
+
to current_month.days_in_month (max 31).
|
32
|
+
|
33
|
+
### Monthly
|
34
|
+
|
35
|
+
`HardJob.monthly` will return a hash with the count of jobs executed
|
36
|
+
every month. The key is the year/month pair and the value is the count
|
37
|
+
of executed jobs during that month. The format of the key is "year:month"
|
38
|
+
with 4 digit year and 1/2 digit month.
|
39
|
+
|
40
|
+
## Example
|
41
|
+
|
42
|
+
Resque.enque HardJob, 123
|
43
|
+
# Work this job...
|
44
|
+
Time.now
|
45
|
+
# => Mon Dec 06 00:34:12 +0100 2010
|
46
|
+
HardJob.hourly
|
47
|
+
# => [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
48
|
+
HardJob.daily
|
49
|
+
# => [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
50
|
+
HardJob.monthly
|
51
|
+
# => {"2010:12"=>1}
|
52
|
+
|
53
|
+
## Contributing
|
54
|
+
|
55
|
+
If you want to improve resque-stats
|
56
|
+
|
57
|
+
1. Fork the repo
|
58
|
+
2. Create a topic branch `git checkout -b my_feature`
|
59
|
+
3. Push it! `git push origin my_feature`
|
60
|
+
4. Open a pull request
|
61
|
+
|
62
|
+
Make sure you add specs for your changes and document them.
|
63
|
+
Any contribution will be appreciated, both fixing some typo or
|
64
|
+
adding the coolest feature ever.
|
65
|
+
|
66
|
+
## Issues
|
67
|
+
|
68
|
+
<http://github.com/jrom/resque-stats/issues>
|
data/Rakefile
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
module Resque # :nodoc:
|
2
|
+
module Plugins # :nodoc:
|
3
|
+
# If you want to keep track of the number of executions per
|
4
|
+
# Job, extend it with this module.
|
5
|
+
#
|
6
|
+
# Example:
|
7
|
+
#
|
8
|
+
# require 'resque/plugins/stats'
|
9
|
+
# class HardJob
|
10
|
+
# extend Resque::Plugins::Stats
|
11
|
+
# @queue = :hard_job
|
12
|
+
# def self.perform(something)
|
13
|
+
# do_work
|
14
|
+
# end
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# This will keep a historical count of jobs executed *hourly*,
|
18
|
+
# *daily* and *monthly*.
|
19
|
+
#
|
20
|
+
# Hourly records will only persist for 24 hours, daily will
|
21
|
+
# be available for one month, and monthly counters will be
|
22
|
+
# there forever.
|
23
|
+
module Stats
|
24
|
+
include Resque::Helpers
|
25
|
+
# Hook that will increment the counters after the job has
|
26
|
+
# been executed. Also, will set expiration dates for the
|
27
|
+
# hourly and daily counters.
|
28
|
+
def after_perform_do_stats(*args)
|
29
|
+
Stats.increx hourly_key, 3600 * 24 # 24h
|
30
|
+
Stats.increx daily_key, 3600 * 24 * 31 # 31d
|
31
|
+
redis.incr monthly_key
|
32
|
+
end
|
33
|
+
|
34
|
+
# Returns an array of executed jobs hourly for today.
|
35
|
+
# Indexes go from 0 to 23 hour.
|
36
|
+
def hourly
|
37
|
+
(0..23).collect { |h| redis.get("#{prefix_hourly}:#{Time.now.year}:#{Time.now.month}:#{Time.now.day}:#{h}").to_i }
|
38
|
+
end
|
39
|
+
|
40
|
+
# Returns an array of executed jobs daily for the current month.
|
41
|
+
# Indexes go from 0 (unused) to current_month.days_in_month (max 31).
|
42
|
+
def daily
|
43
|
+
(0..Stats.days_in_month).collect { |d| redis.get("#{prefix_daily}:#{Time.now.year}:#{Time.now.month}:#{d}").to_i }
|
44
|
+
end
|
45
|
+
|
46
|
+
# Returns a hash where the key is the year/month pair and the value
|
47
|
+
# is the count of executed jobs during that month. The format of the
|
48
|
+
# key is "year:month" with 4 digit year and 1/2 digit month.
|
49
|
+
def monthly
|
50
|
+
keys = redis.keys("#{prefix_monthly}:*")
|
51
|
+
keys.zip(redis.mget(keys)).inject({}) { |t,p| t.merge(p.first.sub("#{prefix_monthly}:", '') => p.last.to_i) }
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
def hourly_key # :nodoc:
|
56
|
+
"#{prefix_hourly}:#{Time.now.year}:#{Time.now.month}:#{Time.now.day}:#{Time.now.hour}"
|
57
|
+
end
|
58
|
+
|
59
|
+
def daily_key # :nodoc:
|
60
|
+
"#{prefix_daily}:#{Time.now.year}:#{Time.now.month}:#{Time.now.day}"
|
61
|
+
end
|
62
|
+
|
63
|
+
def monthly_key # :nodoc:
|
64
|
+
"#{prefix_monthly}:#{Time.now.year}:#{Time.now.month}"
|
65
|
+
end
|
66
|
+
|
67
|
+
def prefix # :nodoc:
|
68
|
+
"plugin:stats:#{name}"
|
69
|
+
end
|
70
|
+
|
71
|
+
def prefix_hourly # :nodoc:
|
72
|
+
"#{prefix}:hourly"
|
73
|
+
end
|
74
|
+
|
75
|
+
def prefix_daily # :nodoc:
|
76
|
+
"#{prefix}:daily"
|
77
|
+
end
|
78
|
+
|
79
|
+
def prefix_monthly # :nodoc:
|
80
|
+
"#{prefix}:monthly"
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.days_in_month # :nodoc:
|
84
|
+
month, year = Time.now.month, Time.now.year
|
85
|
+
(Date.new(year,12,31) << 12 - month).day
|
86
|
+
end
|
87
|
+
|
88
|
+
# Increments a key even if it's volatile, by fetching it
|
89
|
+
# first and then setting the incremented value with a new
|
90
|
+
# expiration.
|
91
|
+
def self.increx(key, seconds)
|
92
|
+
tmp = Resque.redis.get key
|
93
|
+
Resque.redis.setex key, seconds, (tmp.to_i + 1)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
$:.push File.expand_path("../lib", __FILE__)
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = "resque-stats"
|
5
|
+
s.version = "0.1.0"
|
6
|
+
s.platform = Gem::Platform::RUBY
|
7
|
+
s.authors = ["Jordi Romero"]
|
8
|
+
s.email = ["jordi@jrom.net"]
|
9
|
+
s.homepage = "http://github.com/jrom/resque-stats"
|
10
|
+
s.summary = %q{Keep track of the number of executions for a Resque job}
|
11
|
+
s.description = %q{If you want to graph the workload created by your different Resque jobs, extend them with this plugin and use the data generated to know exactly the amount of jobs executed.}
|
12
|
+
|
13
|
+
s.files = `git ls-files`.split("\n") - %w(.gitignore .rspec)
|
14
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
15
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
16
|
+
s.require_paths = ["lib"]
|
17
|
+
|
18
|
+
s.add_dependency "resque", "~> 1.10.0"
|
19
|
+
s.add_development_dependency "rspec", "~> 2.2.0"
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
daemonize yes
|
2
|
+
pidfile ./spec/redis.pid
|
3
|
+
port 6378
|
4
|
+
|
5
|
+
timeout 300
|
6
|
+
loglevel debug
|
7
|
+
logfile stdout
|
8
|
+
databases 16
|
9
|
+
|
10
|
+
save 900 1
|
11
|
+
save 300 10
|
12
|
+
save 60 10000
|
13
|
+
rdbcompression yes
|
14
|
+
|
15
|
+
dbfilename dump.rdb
|
16
|
+
dir ./spec/
|
17
|
+
|
18
|
+
appendonly no
|
19
|
+
appendfsync everysec
|
20
|
+
|
21
|
+
vm-enabled no
|
22
|
+
vm-swap-file /tmp/redis.swap
|
23
|
+
vm-max-memory 0
|
24
|
+
vm-page-size 32
|
25
|
+
vm-pages 134217728
|
26
|
+
vm-max-threads 4
|
27
|
+
glueoutputbuf yes
|
28
|
+
hash-max-zipmap-entries 64
|
29
|
+
hash-max-zipmap-value 512
|
30
|
+
activerehashing yes
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
class Job
|
4
|
+
extend Resque::Plugins::Stats
|
5
|
+
@queue = :job
|
6
|
+
def self.perform(*args)
|
7
|
+
# work
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
describe Resque::Plugins::Stats do
|
12
|
+
describe "compliance of Resque Plugins guidelines" do
|
13
|
+
it "should be valid" do
|
14
|
+
lambda{ Resque::Plugin.lint(Resque::Plugins::Stats) }.should_not raise_error
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "initial status" do
|
19
|
+
it "should return an empty hourly counter" do
|
20
|
+
Job.hourly.should == Array.new(24, 0)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should return an empty daily counter" do
|
24
|
+
daily = Job.daily
|
25
|
+
daily.size.should <= 32 # because we start at 0
|
26
|
+
daily.size.should >= 28
|
27
|
+
daily.uniq.should == [0]
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should return an empty monthly counter" do
|
31
|
+
Job.monthly.should == {}
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "counters" do
|
36
|
+
before do
|
37
|
+
Resque.enqueue(Job)
|
38
|
+
Resque::Worker.new(:job).work(0) # Work off the enqueued job
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should increment the hourly counter" do
|
42
|
+
hourly = Job.hourly
|
43
|
+
hourly.size.should == 24
|
44
|
+
hourly[Time.now.hour].should == 1
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should increment the daily counter" do
|
48
|
+
daily = Job.daily
|
49
|
+
daily.size.should >= 28
|
50
|
+
daily.size.should <= 32
|
51
|
+
daily[Time.now.day].should == 1
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should increment the monthly counter" do
|
55
|
+
monthly = Job.monthly
|
56
|
+
monthly.size.should == 1
|
57
|
+
monthly["#{Time.now.year}:#{Time.now.month}"].should == 1
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should increment the counter multiple times" do
|
61
|
+
6.times { Resque.enqueue(Job) }
|
62
|
+
Resque::Worker.new(:job).work(0)
|
63
|
+
Job.hourly[Time.now.hour].should == 7
|
64
|
+
Job.daily[Time.now.day].should == 7
|
65
|
+
Job.monthly["#{Time.now.year}:#{Time.now.month}"].should == 7
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
Bundler.require(:default, :development)
|
2
|
+
require 'lib/resque/plugins/stats'
|
3
|
+
|
4
|
+
RSpec.configure do |config|
|
5
|
+
config.before(:all) do
|
6
|
+
if !system('which redis-server')
|
7
|
+
puts "\ncan't find `redis-server` in your path"
|
8
|
+
abort
|
9
|
+
end
|
10
|
+
`redis-server #{File.dirname(File.expand_path(__FILE__))}/redis-test.conf` # run Redis with local config
|
11
|
+
puts "Starting test redis server: redis-server #{File.dirname(File.expand_path(__FILE__))}/redis-test.conf"
|
12
|
+
Resque.redis = 'localhost:6378' # port specified in redis-test.conf
|
13
|
+
end
|
14
|
+
|
15
|
+
config.before(:each) do
|
16
|
+
Resque.redis.flushall # Drop all keys between examples
|
17
|
+
end
|
18
|
+
|
19
|
+
config.after(:all) do
|
20
|
+
pid = `ps -e -o pid,command | grep [r]edis-test`.split(" ")[0]
|
21
|
+
puts "\nKilling test redis server PID #{pid}..."
|
22
|
+
`rm -f #{File.dirname(File.expand_path(__FILE__))}/dump.rdb` # file specified in redis-test.conf
|
23
|
+
Process.kill("KILL", pid.to_i)
|
24
|
+
end
|
25
|
+
end
|
metadata
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: resque-stats
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 0
|
10
|
+
version: 0.1.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Jordi Romero
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-12-06 00:00:00 +01:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: resque
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 63
|
30
|
+
segments:
|
31
|
+
- 1
|
32
|
+
- 10
|
33
|
+
- 0
|
34
|
+
version: 1.10.0
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: *id001
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: rspec
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
hash: 7
|
46
|
+
segments:
|
47
|
+
- 2
|
48
|
+
- 2
|
49
|
+
- 0
|
50
|
+
version: 2.2.0
|
51
|
+
type: :development
|
52
|
+
version_requirements: *id002
|
53
|
+
description: If you want to graph the workload created by your different Resque jobs, extend them with this plugin and use the data generated to know exactly the amount of jobs executed.
|
54
|
+
email:
|
55
|
+
- jordi@jrom.net
|
56
|
+
executables: []
|
57
|
+
|
58
|
+
extensions: []
|
59
|
+
|
60
|
+
extra_rdoc_files: []
|
61
|
+
|
62
|
+
files:
|
63
|
+
- Gemfile
|
64
|
+
- Gemfile.lock
|
65
|
+
- README.md
|
66
|
+
- Rakefile
|
67
|
+
- lib/resque/plugins/stats.rb
|
68
|
+
- resque-stats.gemspec
|
69
|
+
- spec/redis-test.conf
|
70
|
+
- spec/resque_stats_spec.rb
|
71
|
+
- spec/spec_helper.rb
|
72
|
+
has_rdoc: true
|
73
|
+
homepage: http://github.com/jrom/resque-stats
|
74
|
+
licenses: []
|
75
|
+
|
76
|
+
post_install_message:
|
77
|
+
rdoc_options: []
|
78
|
+
|
79
|
+
require_paths:
|
80
|
+
- lib
|
81
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
82
|
+
none: false
|
83
|
+
requirements:
|
84
|
+
- - ">="
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
hash: 3
|
87
|
+
segments:
|
88
|
+
- 0
|
89
|
+
version: "0"
|
90
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
91
|
+
none: false
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
hash: 3
|
96
|
+
segments:
|
97
|
+
- 0
|
98
|
+
version: "0"
|
99
|
+
requirements: []
|
100
|
+
|
101
|
+
rubyforge_project:
|
102
|
+
rubygems_version: 1.3.7
|
103
|
+
signing_key:
|
104
|
+
specification_version: 3
|
105
|
+
summary: Keep track of the number of executions for a Resque job
|
106
|
+
test_files:
|
107
|
+
- spec/redis-test.conf
|
108
|
+
- spec/resque_stats_spec.rb
|
109
|
+
- spec/spec_helper.rb
|