sidekiq-web_custom 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.circleci/config.yml +109 -0
- data/.gitignore +18 -0
- data/.rspec +3 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Dockerfile +18 -0
- data/Gemfile +14 -0
- data/Gemfile.lock +182 -0
- data/LICENSE.txt +21 -0
- data/Makefile +40 -0
- data/README.md +92 -0
- data/Rakefile +8 -0
- data/bin/all_workers +69 -0
- data/bin/bundle +114 -0
- data/bin/publish +40 -0
- data/bin/publish_git +38 -0
- data/bin/publish_ruby_gems +22 -0
- data/bin/rails +18 -0
- data/bin/sidekiq +29 -0
- data/bin/sidekiqmon +29 -0
- data/docker-compose.yml +43 -0
- data/dummy_rails/.rspec +4 -0
- data/dummy_rails/app/assets/config/manifest.js +0 -0
- data/dummy_rails/app/controllers/application_controller.rb +2 -0
- data/dummy_rails/app/helpers/application_helper.rb +2 -0
- data/dummy_rails/app/views/layouts/application.html.erb +15 -0
- data/dummy_rails/app/views/layouts/mailer.html.erb +13 -0
- data/dummy_rails/app/workers/exclude_hard_worker.rb +15 -0
- data/dummy_rails/app/workers/exclude_lazy_worker.rb +11 -0
- data/dummy_rails/app/workers/exclude_random_raise_worker.rb +11 -0
- data/dummy_rails/app/workers/hard_worker.rb +15 -0
- data/dummy_rails/app/workers/lazy_worker.rb +10 -0
- data/dummy_rails/app/workers/random_raise_worker.rb +12 -0
- data/dummy_rails/app/workers/taco_worker.rb +12 -0
- data/dummy_rails/app/workers/tostada_worker.rb +11 -0
- data/dummy_rails/bin/rspec +5 -0
- data/dummy_rails/config.ru +5 -0
- data/dummy_rails/config/application.rb +20 -0
- data/dummy_rails/config/boot.rb +5 -0
- data/dummy_rails/config/environment.rb +5 -0
- data/dummy_rails/config/environments/development.rb +52 -0
- data/dummy_rails/config/environments/production.rb +88 -0
- data/dummy_rails/config/environments/test.rb +40 -0
- data/dummy_rails/config/initializers/assets.rb +14 -0
- data/dummy_rails/config/initializers/cookies_serializer.rb +5 -0
- data/dummy_rails/config/initializers/filter_parameter_logging.rb +4 -0
- data/dummy_rails/config/initializers/sidekiq-web_custom.rb +3 -0
- data/dummy_rails/config/initializers/wrap_parameters.rb +14 -0
- data/dummy_rails/config/locales/en.yml +33 -0
- data/dummy_rails/config/puma.rb +37 -0
- data/dummy_rails/config/routes.rb +5 -0
- data/dummy_rails/config/secrets.yml +5 -0
- data/dummy_rails/config/spring.rb +5 -0
- data/dummy_rails/config/storage.yml +34 -0
- data/dummy_rails/package.json +5 -0
- data/dummy_rails/spec/spec_helper.rb +50 -0
- data/lib/load_random_workers.rb +25 -0
- data/lib/sidekiq/views/actions/queues/delete.erb +12 -0
- data/lib/sidekiq/views/actions/queues/drain_queue.erb +4 -0
- data/lib/sidekiq/views/actions/queues/pause.erb +13 -0
- data/lib/sidekiq/views/actions/retries/delete.erb +5 -0
- data/lib/sidekiq/views/actions/retries/execute.erb +7 -0
- data/lib/sidekiq/views/actions/scheduled/delete.erb +5 -0
- data/lib/sidekiq/views/actions/scheduled/execute.erb +7 -0
- data/lib/sidekiq/views/queues.erb +64 -0
- data/lib/sidekiq/views/retries.erb +123 -0
- data/lib/sidekiq/views/scheduled.erb +97 -0
- data/lib/sidekiq/web_custom.rb +95 -0
- data/lib/sidekiq/web_custom/configuration.rb +112 -0
- data/lib/sidekiq/web_custom/job.rb +11 -0
- data/lib/sidekiq/web_custom/processor.rb +82 -0
- data/lib/sidekiq/web_custom/queue.rb +12 -0
- data/lib/sidekiq/web_custom/timeout.rb +64 -0
- data/lib/sidekiq/web_custom/version.rb +14 -0
- data/lib/sidekiq/web_custom/web_action.rb +38 -0
- data/lib/sidekiq/web_custom/web_app.rb +46 -0
- data/sidekiq-web_custom.gemspec +37 -0
- metadata +151 -0
@@ -0,0 +1,97 @@
|
|
1
|
+
<header class="row">
|
2
|
+
<div class="col-sm-5">
|
3
|
+
<h3><%= t('ScheduledJobs') %></h3>
|
4
|
+
</div>
|
5
|
+
<% if @scheduled.size > 0 && @total_size > @count %>
|
6
|
+
<div class="col-sm-4">
|
7
|
+
<%= erb :_paging, locals: { url: "#{root_path}scheduled" } %>
|
8
|
+
</div>
|
9
|
+
<% end %>
|
10
|
+
<%= filtering('scheduled') %>
|
11
|
+
</header>
|
12
|
+
|
13
|
+
<% if @scheduled.size > 0 %>
|
14
|
+
|
15
|
+
<form action="<%= root_path %>scheduled" method="post">
|
16
|
+
<%= csrf_tag %>
|
17
|
+
<div class="table_container">
|
18
|
+
<table class="table table-striped table-bordered">
|
19
|
+
<thead>
|
20
|
+
<tr>
|
21
|
+
<th class="checkbox-column">
|
22
|
+
<input type="checkbox" class="check_all" />
|
23
|
+
</th>
|
24
|
+
<th><%= t('When') %></th>
|
25
|
+
<th><%= t('Queue') %></th>
|
26
|
+
<th><%= t('Job') %></th>
|
27
|
+
<th><%= t('Arguments') %></th>
|
28
|
+
<th><%= t('Actions') %></th>
|
29
|
+
</tr>
|
30
|
+
</thead>
|
31
|
+
<% @scheduled.each do |entry| %>
|
32
|
+
<tr>
|
33
|
+
<td>
|
34
|
+
<input type='checkbox' name='key[]' value='<%= job_params(entry.item, entry.score) %>' />
|
35
|
+
</td>
|
36
|
+
<td>
|
37
|
+
<a href="<%= root_path %>scheduled/<%= job_params(entry.item, entry.score) %>"><%= relative_time(entry.at) %></a>
|
38
|
+
</td>
|
39
|
+
<td>
|
40
|
+
<a href="<%= root_path %>queues/<%= entry.queue %>"><%= entry.queue %></a>
|
41
|
+
</td>
|
42
|
+
<td>
|
43
|
+
<%= entry.display_class %>
|
44
|
+
<%= display_tags(entry, "scheduled") %>
|
45
|
+
</td>
|
46
|
+
<td>
|
47
|
+
<div class="args"><%= display_args(entry.display_args) %></div>
|
48
|
+
</td>
|
49
|
+
<td>
|
50
|
+
__sidekiq_web_custom_replacement__
|
51
|
+
</td>
|
52
|
+
</tr>
|
53
|
+
<% end %>
|
54
|
+
</table>
|
55
|
+
</div>
|
56
|
+
<input class="btn btn-danger pull-right flip" type="submit" name="delete" value="<%= t('Delete') %>" />
|
57
|
+
<input class="btn btn-danger pull-right flip" type="submit" name="add_to_queue" value="<%= t('AddToQueue') %>" />
|
58
|
+
</form>
|
59
|
+
<% else %>
|
60
|
+
<div class="alert alert-success"><%= t('NoScheduledFound') %></div>
|
61
|
+
<% end %>
|
62
|
+
|
63
|
+
<div id="_sidekiq_web_block_screen_div_" style='display: none'>
|
64
|
+
<div id="_sidekiq_web_block_screen_text_"></div>
|
65
|
+
</div>
|
66
|
+
|
67
|
+
<script type="text/javascript">
|
68
|
+
function _sidekiq_web_block_screen(text) {
|
69
|
+
document.getElementById('_sidekiq_web_block_screen_text_').innerHTML = text
|
70
|
+
document.getElementById('_sidekiq_web_block_screen_div_').style.display = "block";
|
71
|
+
}
|
72
|
+
</script>
|
73
|
+
|
74
|
+
<style>
|
75
|
+
#_sidekiq_web_block_screen_div_ {
|
76
|
+
position: fixed;
|
77
|
+
display: none;
|
78
|
+
width: 100%;
|
79
|
+
height: 100%;
|
80
|
+
top: 0;
|
81
|
+
left: 0;
|
82
|
+
right: 0;
|
83
|
+
bottom: 0;
|
84
|
+
background-color: rgba(0,0,0,0.8);
|
85
|
+
z-index: 2;
|
86
|
+
}
|
87
|
+
|
88
|
+
#_sidekiq_web_block_screen_text_{
|
89
|
+
position: absolute;
|
90
|
+
top: 50%;
|
91
|
+
left: 50%;
|
92
|
+
font-size: 50px;
|
93
|
+
color: white;
|
94
|
+
transform: translate(-50%,-50%);
|
95
|
+
-ms-transform: translate(-50%,-50%);
|
96
|
+
}
|
97
|
+
</style>
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'byebug'
|
4
|
+
require_relative 'web_custom/version'
|
5
|
+
|
6
|
+
# require sidekiq/web first to ensure web_Action will prepend correctly
|
7
|
+
require 'sidekiq/web'
|
8
|
+
require 'sidekiq/web_custom/configuration'
|
9
|
+
require 'sidekiq/web_custom/web_action'
|
10
|
+
require 'sidekiq/web_custom/queue'
|
11
|
+
require 'sidekiq/web_custom/job'
|
12
|
+
require 'sidekiq/web_custom/web_app'
|
13
|
+
|
14
|
+
module Sidekiq
|
15
|
+
module WebCustom
|
16
|
+
class Error < StandardError; end
|
17
|
+
class ArgumentError < Error; end
|
18
|
+
class FileNotFound < Error; end
|
19
|
+
class StopExecution < Error; end
|
20
|
+
class ExecutionTimeExceeded < Error; end
|
21
|
+
class ConfigurationEstablished < Error; end
|
22
|
+
|
23
|
+
BREAK_BIT = '__sidekiq-web_custom-breakbit__'
|
24
|
+
|
25
|
+
def self.default_available_actions_mapping
|
26
|
+
@available_actions_mapping ||= begin
|
27
|
+
temp = {}
|
28
|
+
Dir["#{actions_root}/**/*.erb"].map do |erb_path|
|
29
|
+
base_path = File.basename(erb_path).split('.')[0]
|
30
|
+
second_half = erb_path.split(actions_root)[1]
|
31
|
+
action_type = second_half.split(base_path)[0]
|
32
|
+
action_type = action_type.delete('/').to_sym
|
33
|
+
temp[action_type] ||= {}
|
34
|
+
temp[action_type][base_path.to_sym] = erb_path
|
35
|
+
end
|
36
|
+
temp
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.default_local_erb_mapping
|
41
|
+
@local_erb_mapping ||= Dir["#{local_erbs_root}/*.erb"].map do |erb_path|
|
42
|
+
[File.basename(erb_path).split('.')[0].to_sym, erb_path]
|
43
|
+
end.to_h
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.actions_root
|
47
|
+
@actions_root ||= "#{local_erbs_root}/actions"
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.local_erbs_root
|
51
|
+
@local_erbs_root ||= "#{root_path}/views"
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.root_path
|
55
|
+
@root_path ||= File.dirname(__FILE__)
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.config
|
59
|
+
@config ||= Configuration.new.tap do |t|
|
60
|
+
t.merge(base: :actions, params: default_available_actions_mapping)
|
61
|
+
t.merge(base: :local_erbs, params: default_local_erb_mapping)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.configure
|
66
|
+
yield config if block_given?
|
67
|
+
|
68
|
+
config.validate!
|
69
|
+
__inject_dependencies
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.local_erb_mapping
|
73
|
+
config.local_erbs
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.reset!
|
77
|
+
@config = nil
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def self.__inject_dependencies
|
83
|
+
return if @__already_called
|
84
|
+
|
85
|
+
@__already_called = true
|
86
|
+
::Sidekiq::WebAction.prepend WebAction
|
87
|
+
::Sidekiq::Queue.prepend Queue
|
88
|
+
::Sidekiq::Job.prepend Job
|
89
|
+
::Sidekiq::Web.register WebApp
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# dependent the error classes loaded on boot, requie after code is loaded
|
95
|
+
require 'sidekiq/web_custom/timeout'
|
@@ -0,0 +1,112 @@
|
|
1
|
+
module Sidekiq
|
2
|
+
module WebCustom
|
3
|
+
class Configuration
|
4
|
+
|
5
|
+
ALLOWED_BASED = [ACTIONS = :actions, LOCAL_ERBS = :local_erbs]
|
6
|
+
INTEGERS = [:drain_rate, :max_execution_time, :warn_execution_time]
|
7
|
+
|
8
|
+
DEFAULT_DRAIN_RATE = 10
|
9
|
+
DEFAULT_EXEC_TIME = 6
|
10
|
+
DEFAULT_WARN_TIME = 5
|
11
|
+
|
12
|
+
attr_reader *ALLOWED_BASED
|
13
|
+
attr_reader *INTEGERS
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
ALLOWED_BASED.each do |var|
|
17
|
+
instance_variable_set(:"@#{var}", {})
|
18
|
+
end
|
19
|
+
|
20
|
+
@drain_rate = DEFAULT_DRAIN_RATE
|
21
|
+
@max_execution_time = DEFAULT_EXEC_TIME
|
22
|
+
@warn_execution_time = DEFAULT_WARN_TIME
|
23
|
+
end
|
24
|
+
|
25
|
+
INTEGERS.each do |int|
|
26
|
+
self.define_method("#{int}=") do |val|
|
27
|
+
unless _allow_write_block?
|
28
|
+
raise Sidekiq::WebCustom::ConfigurationEstablished, "Unable to assign [#{int}]. Assignment must happen on boot"
|
29
|
+
end
|
30
|
+
|
31
|
+
unless [Integer, Float].include?(val.class)
|
32
|
+
raise Sidekiq::WebCustom::ArgumentError, "Expected #{int} to be an integer or float"
|
33
|
+
end
|
34
|
+
|
35
|
+
instance_variable_set(:"@#{int}", val)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def merge(base:, params:, action_type: nil)
|
40
|
+
unless _allow_write_block?
|
41
|
+
raise Sidekiq::WebCustom::ConfigurationEstablished, "Unable to assign base [#{base}]. Assignment must happen on boot"
|
42
|
+
end
|
43
|
+
raise Sidekiq::WebCustom::ArgumentError, "Unexpected base: #{base}" unless ALLOWED_BASED.include?(base)
|
44
|
+
raise Sidekiq::WebCustom::ArgumentError, "Expected object for #{base} to be a Hash" unless params.is_a?(Hash)
|
45
|
+
|
46
|
+
value = instance_variable_get(:"@#{base}")
|
47
|
+
value =
|
48
|
+
if action_type && base == ACTIONS
|
49
|
+
value[action_type.to_sym] ||= {}
|
50
|
+
value[action_type.to_sym].merge!(params)
|
51
|
+
value
|
52
|
+
else
|
53
|
+
value.merge(params)
|
54
|
+
end
|
55
|
+
instance_variable_set(:"@#{base}", value)
|
56
|
+
end
|
57
|
+
|
58
|
+
def validate!
|
59
|
+
ALLOWED_BASED.each do |key|
|
60
|
+
value = instance_variable_get(:"@#{key}")
|
61
|
+
|
62
|
+
_validate!(value, key)
|
63
|
+
end
|
64
|
+
|
65
|
+
unless @warn_execution_time <= @max_execution_time
|
66
|
+
raise Sidekiq::WebCustom::ArgumentError, "Expected warn_execution_time to be less than max_execution_time"
|
67
|
+
end
|
68
|
+
|
69
|
+
unless actions.keys.all? { |k| local_erbs.keys.include?(k) }
|
70
|
+
raise Sidekiq::WebCustom::ArgumentError, "Unexpected actions keys#{actions.keys} -- Expected to be part of #{local_erbs.keys}"
|
71
|
+
end
|
72
|
+
|
73
|
+
define_convenienve_methods!
|
74
|
+
_unset_allow_write!
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def _allow_write_block?
|
80
|
+
true
|
81
|
+
end
|
82
|
+
|
83
|
+
def _unset_allow_write!
|
84
|
+
define_singleton_method('_allow_write_block?') do
|
85
|
+
false
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def define_convenienve_methods!
|
90
|
+
actions.keys.each do |key|
|
91
|
+
define_singleton_method("actions_for_#{key.to_s}") do
|
92
|
+
actions[key]
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def _validate!(params, key, add: [])
|
98
|
+
params.each do |k, file|
|
99
|
+
return _validate!(file, k, add: add << key) if file.is_a? Hash
|
100
|
+
passed = [add, k].flatten.compact.join(':')
|
101
|
+
|
102
|
+
|
103
|
+
next if File.exist?(file)
|
104
|
+
|
105
|
+
raise Sidekiq::WebCustom::FileNotFound,
|
106
|
+
"#{key}.merge passed #{passed}: #{file}.\n" \
|
107
|
+
"The absolute file path does not exist."
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'sidekiq/processor'
|
2
|
+
|
3
|
+
module Sidekiq
|
4
|
+
module WebCustom
|
5
|
+
class Processor < ::Sidekiq::Processor
|
6
|
+
|
7
|
+
def self.execute(max:, queue:, options: Sidekiq.options)
|
8
|
+
__processor__(queue: queue, options: options).__execute(max: max)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.execute_job(job:, options: Sidekiq.options)
|
12
|
+
__processor__(queue: job.queue, options: options).__execute_job(job: job)
|
13
|
+
rescue StandardError => _
|
14
|
+
false # error gets loggged downstream
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.__processor__(queue:, options: Sidekiq.options)
|
18
|
+
options_temp = options.clone
|
19
|
+
queue = queue.is_a?(String) ? Sidekiq::Queue.new(queue) : queue
|
20
|
+
|
21
|
+
options_temp[:queues] = [queue.name]
|
22
|
+
klass = options_temp[:fetch]&.class || BasicFetch
|
23
|
+
options_temp[:fetch] = klass.new(options_temp)
|
24
|
+
new(manager: nil, options: options_temp, queue: queue)
|
25
|
+
end
|
26
|
+
|
27
|
+
def initialize(manager:, options:, queue:)
|
28
|
+
@__queue = queue
|
29
|
+
@__basic_fetch = options[:fetch].class == BasicFetch
|
30
|
+
|
31
|
+
super(manager, options)
|
32
|
+
end
|
33
|
+
|
34
|
+
def __execute_job(job:)
|
35
|
+
queue_name = "queue:#{job.queue}"
|
36
|
+
work_unit = Sidekiq::BasicFetch::UnitOfWork.new(queue_name, job.item.to_json)
|
37
|
+
begin
|
38
|
+
Sidekiq.logger.info "Manually processing individual work unit for #{work_unit.queue_name}"
|
39
|
+
process(work_unit)
|
40
|
+
rescue StandardError => e
|
41
|
+
Sidekiq.logger.error "Manually processed work unit failed with #{e.message}. Work unit will not be dequeued"
|
42
|
+
raise e
|
43
|
+
end
|
44
|
+
|
45
|
+
begin
|
46
|
+
job.delete
|
47
|
+
Sidekiq.logger.info { "Manually processed work unit sucessfully dequeued." }
|
48
|
+
rescue StandardError => e
|
49
|
+
Sidekiq.logger.fatal "Manually processed work unit failed to be dequeued. #{e.message}."
|
50
|
+
raise e
|
51
|
+
end
|
52
|
+
|
53
|
+
true
|
54
|
+
end
|
55
|
+
|
56
|
+
def __execute(max:)
|
57
|
+
count = 0
|
58
|
+
max.times do
|
59
|
+
break if @__queue.size <= 0
|
60
|
+
|
61
|
+
if Thread.current[Sidekiq::WebCustom::BREAK_BIT]
|
62
|
+
Sidekiq.logger.warn "Yikes -- Break bit has been set. Attempting to return in time. Completed #{count} of attempted #{max}"
|
63
|
+
break
|
64
|
+
end
|
65
|
+
|
66
|
+
Sidekiq.logger.info { "Manually processing next item in queue:[#{@__queue.name}]" }
|
67
|
+
process_one
|
68
|
+
count += 1
|
69
|
+
|
70
|
+
end
|
71
|
+
count
|
72
|
+
rescue Exception => ex
|
73
|
+
if @job && @__basic_fetch
|
74
|
+
Sidekiq.logger.fatal "Processor Execution interrupted. Lost Job #{@job.job}"
|
75
|
+
end
|
76
|
+
Sidekiq.logger.warn "Manual execution has terminated. Received error [#{ex.message}]"
|
77
|
+
return count
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Sidekiq
|
2
|
+
module WebCustom
|
3
|
+
module Timeout
|
4
|
+
module_function
|
5
|
+
DEFAULT_EXCEPTION = Sidekiq::WebCustom::ExecutionTimeExceeded
|
6
|
+
PROC = Proc.new do |warn_sec, timeout_sec, proc, exception, message, debug, &block|
|
7
|
+
puts "at: PROC; begining" if debug
|
8
|
+
|
9
|
+
sec_to_raise = timeout_sec - warn_sec
|
10
|
+
begin
|
11
|
+
from = debug ? "from #{caller_locations(1, 1)[0]}" : nil
|
12
|
+
x = Thread.current
|
13
|
+
# do everything in a new thread
|
14
|
+
y = Thread.start {
|
15
|
+
puts "at: PROC; second thread; about to start" if debug
|
16
|
+
Thread.current.name = from
|
17
|
+
# block for warning in new thread
|
18
|
+
begin
|
19
|
+
puts "at: PROC; second thread; starting warn time for #{warn_sec}'s " if debug
|
20
|
+
sleep warn_sec
|
21
|
+
rescue => e
|
22
|
+
x.raise e
|
23
|
+
else
|
24
|
+
# yield back during warning time so downstream can do some prep work
|
25
|
+
puts "at: PROC; second thread; trying to warn in main thread" if debug
|
26
|
+
proc.call(x, warn_sec)
|
27
|
+
end
|
28
|
+
|
29
|
+
# block additional seconds to raise for
|
30
|
+
begin
|
31
|
+
puts "at: PROC; second thread; starting violent exection time for #{sec_to_raise}'s " if debug
|
32
|
+
sleep sec_to_raise
|
33
|
+
rescue => e
|
34
|
+
puts "at: PROC; second thread; Error occured" if debug
|
35
|
+
x.raise e
|
36
|
+
else
|
37
|
+
puts "at: PROC; second thread; trying to raise in main thread" if debug
|
38
|
+
x.raise exception, message
|
39
|
+
end
|
40
|
+
}
|
41
|
+
puts "at: PROC; second thread; fully spooled" if debug
|
42
|
+
# after thread starts, yield back to calle function with max timout
|
43
|
+
block.call(timeout_sec)
|
44
|
+
ensure
|
45
|
+
if y
|
46
|
+
puts "at: PROC; Second thread still exists. Returned in time" if debug
|
47
|
+
y.kill
|
48
|
+
y.join
|
49
|
+
else
|
50
|
+
puts "at: PROC; Second thread no longer exists. Failed to return in time" if debug
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def timeout(warn:, timeout:, proc: ->(_, _) {}, exception: DEFAULT_EXCEPTION, message: nil, debug: false, &block)
|
56
|
+
raise Sidekiq::WebCustom::ArgumentError, 'Block not given' unless block_given?
|
57
|
+
|
58
|
+
puts "at: timeout; valid bock given" if debug
|
59
|
+
message ||= "Execution exceeded #{timeout} seconds." if debug
|
60
|
+
PROC.call(warn, timeout, proc, exception, message, debug, &block)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|