resque-job_history 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4014d5408bda6f84a8e9779ebc7d1fe26d7f1a31
4
+ data.tar.gz: 080997bc6cb436dfd7efc0cb505ceb5d707e0f80
5
+ SHA512:
6
+ metadata.gz: f6826cd3babf4b41a3cb40d3abd32fa6eaca66b0aed7a1190c97855476e386082b7dfc5a16c77ee5455d6bb1963714153e76599191b87c22b5fc478297926a5b
7
+ data.tar.gz: 5a279b25e700dcfda034517029f6b587224e6050a48e98c850778700bfd77bcbe4de87830f60a0d2707c59b03f861fb957c0a3c0b6f273263862b5a36088fd66
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2016 RealNobody
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
+ NONINFRINGEMENT. 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.rdoc ADDED
@@ -0,0 +1,3 @@
1
+ = ResqueJobHistory
2
+
3
+ This project rocks and uses MIT-LICENSE.
data/Rakefile ADDED
@@ -0,0 +1,28 @@
1
+ begin
2
+ require "bundler/setup"
3
+ rescue LoadError
4
+ puts "You must `gem install bundler` and `bundle install` to run rake tasks"
5
+ end
6
+
7
+ require "rdoc/task"
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = "rdoc"
11
+ rdoc.title = "ResqueJobHistory"
12
+ rdoc.options << "--line-numbers"
13
+ rdoc.rdoc_files.include("README.rdoc")
14
+ rdoc.rdoc_files.include("lib/**/*.rb")
15
+ end
16
+
17
+ Bundler::GemHelper.install_tasks
18
+
19
+ require "rake/testtask"
20
+
21
+ Rake::TestTask.new(:test) do |t|
22
+ t.libs << "lib"
23
+ t.libs << "test"
24
+ t.pattern = "test/**/*_test.rb"
25
+ t.verbose = false
26
+ end
27
+
28
+ task default: :test
@@ -0,0 +1,7 @@
1
+ require "resque"
2
+ require File.expand_path(File.join("resque", "plugins", "job_history", "history_base"), File.dirname(__FILE__))
3
+ require File.expand_path(File.join("resque", "plugins", "job_history", "history_list"), File.dirname(__FILE__))
4
+ require File.expand_path(File.join("resque", "plugins", "job_history", "job_list"), File.dirname(__FILE__))
5
+ require File.expand_path(File.join("resque", "plugins", "job_history", "job"), File.dirname(__FILE__))
6
+ require File.expand_path(File.join("resque", "plugins", "job_history", "cleaner"), File.dirname(__FILE__))
7
+ require File.expand_path(File.join("resque", "plugins", "job_history"), File.dirname(__FILE__))
@@ -0,0 +1,189 @@
1
+ require "resque"
2
+ require "resque/server"
3
+ require "resque-job_history"
4
+ require "action_view/helpers/date_helper"
5
+
6
+ module Resque
7
+ # Extends Resque Web Based UI.
8
+ # Structure has been borrowed from ResqueHistory.
9
+ module JobHistoryServer
10
+ include ActionView::Helpers::DateHelper
11
+
12
+ class << self
13
+ def erb_path(filename)
14
+ File.join(File.dirname(__FILE__), "server", "views", filename)
15
+ end
16
+
17
+ def public_path(filename)
18
+ File.join(File.dirname(__FILE__), "server", "public", filename)
19
+ end
20
+
21
+ def included(base)
22
+ add_page_views(base)
23
+ add_button_callbacks(base)
24
+ add_static_files(base)
25
+ end
26
+
27
+ private
28
+
29
+ def add_page_views(base)
30
+ job_history(base)
31
+ class_details(base)
32
+ job_details(base)
33
+ end
34
+
35
+ def job_history(base)
36
+ job_history_params(base)
37
+
38
+ base.class_eval do
39
+ get "/job history" do
40
+ set_job_history_params
41
+
42
+ erb File.read(Resque::JobHistoryServer.erb_path("job_history.erb"))
43
+ end
44
+ end
45
+ end
46
+
47
+ def job_history_params(base)
48
+ base.class_eval do
49
+ def set_job_history_params
50
+ @sort_by = params[:sort] || "class_name"
51
+ @sort_order = params[:order] || "asc"
52
+ @page_num = (params[:page_num] || 1).to_i
53
+ @page_size = (params[:page_size] || Resque::Plugins::JobHistory::HistoryBase::PAGE_SIZE).to_i
54
+ end
55
+ end
56
+ end
57
+
58
+ def class_details(base)
59
+ class_details_params(base)
60
+ running_page_params(base)
61
+ finished_page_params(base)
62
+
63
+ base.class_eval do
64
+ get "/job history/job_class_details" do
65
+ set_class_details_params
66
+
67
+ erb File.read(Resque::JobHistoryServer.erb_path("job_class_details.erb"))
68
+ end
69
+ end
70
+ end
71
+
72
+ def class_details_params(base)
73
+ base.class_eval do
74
+ def set_class_details_params
75
+ @job_class_name = params[:class_name]
76
+ set_running_page_params
77
+ set_finished_page_params
78
+ end
79
+ end
80
+ end
81
+
82
+ def running_page_params(base)
83
+ base.class_eval do
84
+ def set_running_page_params
85
+ @running_page_num = (params[:running_page_num] || 1).to_i
86
+ @running_page_size = (params[:running_page_size] ||
87
+ Resque::Plugins::JobHistory::HistoryBase::PAGE_SIZE).to_i
88
+ end
89
+ end
90
+ end
91
+
92
+ def finished_page_params(base)
93
+ base.class_eval do
94
+ def set_finished_page_params
95
+ @finished_page_num = (params[:finished_page_num] || 1).to_i
96
+ @finished_page_size = (params[:finished_page_size] ||
97
+ Resque::Plugins::JobHistory::HistoryBase::PAGE_SIZE).to_i
98
+ end
99
+ end
100
+ end
101
+
102
+ def job_details(base)
103
+ base.class_eval do
104
+ get "/job history/job_details" do
105
+ @job_class_name = params[:class_name]
106
+ @job_id = params[:job_id]
107
+
108
+ erb File.read(Resque::JobHistoryServer.erb_path("job_details.erb"))
109
+ end
110
+ end
111
+ end
112
+
113
+ def add_static_files(base)
114
+ base.class_eval do
115
+ get %r{job_history/public/([a-z_]+\.[a-z]+)} do
116
+ send_file Resque::JobHistoryServer.public_path(params[:captures].first)
117
+ end
118
+ end
119
+ end
120
+
121
+ def add_button_callbacks(base)
122
+ purge_all(base)
123
+ purge_class(base)
124
+ retry_job(base)
125
+ delete_job(base)
126
+ cancel_job(base)
127
+ end
128
+
129
+ def cancel_job(base)
130
+ base.class_eval do
131
+ post "/job history/cancel_job" do
132
+ Resque::Plugins::JobHistory::Job.new(params[:class_name], params[:job_id]).cancel
133
+
134
+ redirect u("job history/job_details?#{{ class_name: params[:class_name],
135
+ job_id: params[:job_id] }.to_param}")
136
+ end
137
+ end
138
+ end
139
+
140
+ def delete_job(base)
141
+ base.class_eval do
142
+ post "/job history/delete_job" do
143
+ Resque::Plugins::JobHistory::Job.new(params[:class_name], params[:job_id]).purge
144
+
145
+ redirect u("job history/job_class_details?#{{ class_name: params[:class_name] }.to_param}")
146
+ end
147
+ end
148
+ end
149
+
150
+ def retry_job(base)
151
+ base.class_eval do
152
+ post "/job history/retry_job" do
153
+ Resque::Plugins::JobHistory::Job.new(params[:class_name], params[:job_id]).retry
154
+
155
+ redirect u("job history/job_class_details?#{{ class_name: params[:class_name] }.to_param}")
156
+ end
157
+ end
158
+ end
159
+
160
+ def purge_class(base)
161
+ base.class_eval do
162
+ post "/job history/purge_class" do
163
+ Resque::Plugins::JobHistory::Cleaner.purge_class(params[:class_name])
164
+
165
+ redirect u("job history")
166
+ end
167
+ end
168
+ end
169
+
170
+ def purge_all(base)
171
+ base.class_eval do
172
+ post "/job history/purge_all" do
173
+ Resque::Plugins::JobHistory::Cleaner.purge_all_jobs
174
+
175
+ redirect u("job history")
176
+ end
177
+ end
178
+ end
179
+ end
180
+
181
+ Resque::Server.tabs << "Job History"
182
+ end
183
+ end
184
+
185
+ Resque.extend Resque::JobHistoryServer
186
+
187
+ Resque::Server.class_eval do
188
+ include Resque::JobHistoryServer
189
+ end
@@ -0,0 +1,63 @@
1
+ require "active_support/concern"
2
+
3
+ module Resque
4
+ module Plugins
5
+ # Include in a Resque Job to keep a history of the execution of the job.
6
+ # Every job keeps its own independent history so that you can see when an individual job was run.
7
+ module JobHistory
8
+ extend ActiveSupport::Concern
9
+
10
+ # Redis mapp:
11
+ # job_history - a set of all of the class names of all jobs
12
+ # job_history.<class_name>.max_jobs - The maximum number of jobs that have run for this class.
13
+ # job_history.<class_name>.total_finished_jobs - The maximum number of jobs that have run for
14
+ # this class.
15
+ # job_history.<class_name>.running_jobs - a list of the IDs for all running jobs in the order
16
+ # they were started.
17
+ # job_history.<class_name>.finished_jobs - a list of rhe IDs for all finished jobs in the
18
+ # order they completed.
19
+ # job_history.<class_name>.<job_id> - a hash of values detailing the job
20
+ # start_time
21
+ # args - JSON encoded array of encoded args
22
+ # end_time
23
+ # error
24
+ MAX_JOB_HISTORY = 200
25
+
26
+ # The class methods added to the job class that is being enqueued and whose history is to be
27
+ # recorded.
28
+ module ClassMethods
29
+ attr_reader :running_job
30
+
31
+ def before_perform_job_history(*args)
32
+ running_job.cancel if running_job
33
+
34
+ @running_job = Resque::Plugins::JobHistory::Job.new(name, SecureRandom.uuid)
35
+
36
+ running_job.start(*args)
37
+ end
38
+
39
+ def after_perform_job_history(*_args)
40
+ running_job.try(:finish)
41
+ @running_job = nil
42
+ end
43
+
44
+ def on_failure_job_history(exception, *_args)
45
+ running_job.try(:failed, exception)
46
+ @running_job = nil
47
+ end
48
+
49
+ def job_history_len
50
+ @job_history_len || MAX_JOB_HISTORY
51
+ end
52
+
53
+ def purge_age
54
+ @purge_jobs_after || 24.hours
55
+ end
56
+
57
+ def page_size
58
+ @page_size || Resque::Plugins::JobHistory::HistoryBase::PAGE_SIZE
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,92 @@
1
+ module Resque
2
+ module Plugins
3
+ module JobHistory
4
+ # JobHistory cleanup functions to allow the user to cleanup Redis for histories.
5
+ class Cleaner
6
+ class << self
7
+ def clean_all_old_running_jobs
8
+ job_classes.each do |class_name|
9
+ Resque::Plugins::JobHistory::Job.new(class_name, "").clean_old_running_jobs
10
+ end
11
+ end
12
+
13
+ def fixup_all_keys
14
+ job_classes.each do |class_name|
15
+ fixup_job_keys class_name
16
+ end
17
+ end
18
+
19
+ def fixup_job_keys(class_name)
20
+ keys = job_keys(class_name)
21
+ keys -= Resque::Plugins::JobHistory::HistoryList.new(class_name, "running").job_ids
22
+ keys -= Resque::Plugins::JobHistory::HistoryList.new(class_name, "finished").job_ids
23
+
24
+ keys.each do |stranded_key|
25
+ del_key(stranded_key, "Stranded job key deleted")
26
+ end
27
+ end
28
+
29
+ def purge_all_jobs
30
+ job_classes.each do |class_name|
31
+ purge_class class_name
32
+ end
33
+
34
+ del_key(Resque::Plugins::JobHistory::HistoryBase.new("").job_history_key,
35
+ "Purging job_history_key")
36
+ end
37
+
38
+ def purge_invalid_jobs
39
+ viewer_klass.job_classes.each do |class_name|
40
+ next if Resque::Plugins::JobHistory::HistoryBase.new(class_name).class_name_valid?
41
+
42
+ purge_class(class_name)
43
+ end
44
+ end
45
+
46
+ def purge_class(class_name)
47
+ job_keys(class_name).each do |job_key|
48
+ del_key(job_key, "Purging job key")
49
+ end
50
+
51
+ job_support_keys(class_name).each do |support_key|
52
+ del_key(support_key, "Purging #{support_key}")
53
+ end
54
+ end
55
+
56
+ private
57
+
58
+ def job_classes
59
+ Resque::Plugins::JobHistory::JobList.new.job_classes
60
+ end
61
+
62
+ def job_keys(class_name)
63
+ history_base = Resque::Plugins::JobHistory::HistoryBase.new(class_name)
64
+
65
+ history_base.redis.keys("#{history_base.job_history_base_key}.*") - job_support_keys(history_base)
66
+ end
67
+
68
+ def job_support_keys(history_base)
69
+ if history_base.is_a?(String)
70
+ history_base = Resque::Plugins::JobHistory::HistoryBase.new(history_base)
71
+ end
72
+
73
+ ["#{history_base.job_history_base_key}.running_jobs",
74
+ "#{history_base.job_history_base_key}.total_running_jobs",
75
+ "#{history_base.job_history_base_key}.finished_jobs",
76
+ "#{history_base.job_history_base_key}.total_finished_jobs",
77
+ "#{history_base.job_history_base_key}.max_jobs"]
78
+ end
79
+
80
+ def redis
81
+ @redis ||= Resque::Plugins::JobHistory::HistoryBase.new("").redis
82
+ end
83
+
84
+ def del_key(key, message)
85
+ Resque.logger.warn("#{message} - #{key}")
86
+ redis.del key
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,61 @@
1
+ module Resque
2
+ module Plugins
3
+ module JobHistory
4
+ # A base class for job history classes which provides a base key and a few common functions.
5
+ class HistoryBase
6
+ attr_accessor :class_name
7
+
8
+ NAME_SPACE = "Resque::Plugins::ResqueJobHistory".freeze
9
+ PAGE_SIZE = 25
10
+
11
+ def initialize(class_name)
12
+ @class_name = class_name
13
+ end
14
+
15
+ def redis
16
+ @redis ||= Redis::Namespace.new(NAME_SPACE, redis: Resque.redis)
17
+ end
18
+
19
+ def job_history_key
20
+ "job_history"
21
+ end
22
+
23
+ def job_history_base_key
24
+ "#{job_history_key}.#{class_name}"
25
+ end
26
+
27
+ def running_list
28
+ @running_list = HistoryList.new(class_name, "running")
29
+ end
30
+
31
+ def finished_list
32
+ @finished_list = HistoryList.new(class_name, "finished")
33
+ end
34
+
35
+ def class_name_valid?
36
+ described_class.present?
37
+ end
38
+
39
+ private
40
+
41
+ def described_class
42
+ class_name.constantize
43
+ rescue StandardError
44
+ nil
45
+ end
46
+
47
+ def class_purge_age
48
+ described_class.try(:purge_age) || 24.hours
49
+ end
50
+
51
+ def class_history_len
52
+ described_class.try(:job_history_len) || MAX_JOB_HISTORY
53
+ end
54
+
55
+ def class_page_size
56
+ described_class.try(:page_size) || Resque::Plugins::JobHistory::HistoryBase::PAGE_SIZE
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end