resque-stats 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in resque-stats.gemspec
4
+ gemspec
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,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
@@ -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
@@ -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