delayed_job_master 3.0.1 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bde4178a3a916bd11ef509370761d7776d1678001b58640d898e7406083038a5
4
- data.tar.gz: 1b96fa607eeefd616fdaf7c8984249417ebbe1034545ed0f3b5188d1f8108a90
3
+ metadata.gz: 6683f368856e5c22a45b1a58bc11073761ee2214d28b520d5006b3a3c5d573e2
4
+ data.tar.gz: 19b671f335157e6a4a3ff4ea0c2dde4a0fdb9029f9e8cda013a4d8c27d4b1923
5
5
  SHA512:
6
- metadata.gz: 4814327c73cad53727c33b0397a27268f2ef82473eecbeadb5ae7c009fe9e6e0fa14ebc7b2e225677b5d94f4c009d82f2bde10fe646fc5432520423b0cd61b0f
7
- data.tar.gz: a10fa10a78e642b209969bed0e0ac0098cae344e2a64970ad2e7cecdf4ac743a4c9537457de5d96112afc1b7a77c07bfcc6b9282164968cecfd34dae889820a0
6
+ metadata.gz: f7e5076e40a1637698209c5980d877fa1f5e25b1bf1913e22944ac9ed2667f9877236716e0c4d15cf2427bf6aecdfa919cc75a2163c1e1ab5901c16f6329b4c1
7
+ data.tar.gz: f723aaf03678034ac3309f05bb8f5aa88f780aa9b6bc5e829bc2f127d9b249026546409b33354df3de81860ff005cc16e7771a10591989436525de0da736343e
data/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 3.1.0
4
+
5
+ * Add timer feature.
6
+
3
7
  ## 3.0.1
4
8
 
5
9
  * Fix title of forked process.
data/README.md CHANGED
@@ -48,7 +48,7 @@ working_directory Dir.pwd
48
48
  monitor_interval 5
49
49
 
50
50
  # polling interval for new jobs (in seconds)
51
- polling_interval 5
51
+ polling_interval 30
52
52
 
53
53
  # path to pid file
54
54
  pid_file "#{Dir.pwd}/tmp/pids/delayed_job_master.pid"
@@ -22,7 +22,7 @@ module Delayed
22
22
  @log_file = "#{@working_directory}/log/delayed_job_master.log"
23
23
  @log_level = :info
24
24
  @monitor_interval = 5
25
- @polling_interval = 5
25
+ @polling_interval = 30
26
26
  @databases = []
27
27
  CALLBACK_CONFIGS.each do |name|
28
28
  send("#{name}=", [])
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Delayed
4
+ module Master
5
+ class Job
6
+ attr_accessor :database, :setting, :id, :run_at
7
+
8
+ def initialize(attrs = {})
9
+ attrs.each do |k, v|
10
+ send("#{k}=", v)
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -16,7 +16,9 @@ module Delayed
16
16
  @databases = master.databases
17
17
  @callbacks = master.callbacks
18
18
  @queues = @databases.map { |database| [database, Queue.new] }.to_h
19
- @threads = []
19
+ @threads = SafeArray.new
20
+ @timer_threads = SafeArray.new
21
+ @job_finder = JobFinder.new(master)
20
22
  end
21
23
 
22
24
  def start
@@ -53,6 +55,15 @@ module Delayed
53
55
  end
54
56
  end
55
57
 
58
+ def start_timer_thread(database, run_at)
59
+ @timer_threads << Thread.new(database, run_at) do |database, run_at|
60
+ interval = run_at.to_f - Time.zone.now.to_f
61
+ sleep interval if interval > 0
62
+ schedule(database)
63
+ @timer_threads.delete(Thread.current)
64
+ end
65
+ end
66
+
56
67
  def stop
57
68
  @databases.each do |database|
58
69
  queue = @queues[database]
@@ -70,56 +81,60 @@ module Delayed
70
81
 
71
82
  def wait
72
83
  @threads.each(&:join)
84
+ @timer_threads.each(&:join)
73
85
  end
74
86
 
75
87
  def shutdown
76
88
  @threads.each(&:kill)
89
+ @timer_threads.each(&:kill)
77
90
  end
78
91
 
79
92
  private
80
93
 
81
94
  def check(database)
82
- free_settings = detect_free_settings(database)
83
- return if free_settings.blank?
84
-
85
95
  @master.logger.debug { "checking jobs @#{database.spec_name}..." }
86
- settings = check_jobs(database, free_settings)
87
- if settings.present?
88
- @master.logger.info { "found jobs for #{settings.uniq.map(&:worker_info).join(', ')}" }
89
- fork_workers(database, settings)
90
- end
96
+ check_jobs(database)
97
+ check_recent_jobs(database)
91
98
  rescue => e
92
99
  @master.logger.warn { "#{e.class}: #{e.message}" }
93
100
  @master.logger.debug { e.backtrace.join("\n") }
94
101
  end
95
102
 
96
- def detect_free_settings(database)
97
- @config.worker_settings.each_with_object([]) do |setting, array|
98
- used_count = @master.workers.count { |worker| worker.setting.queues == setting.queues }
99
- free_count = setting.max_processes - used_count
100
- array << [setting, free_count] if free_count > 0
103
+ def check_jobs(database)
104
+ jobs = find_jobs(database)
105
+ jobs.each do |job|
106
+ @master.logger.info { "found jobs @#{job.database.spec_name} for #{job.setting.worker_info}" }
107
+ fork_worker(job.database, job.setting)
101
108
  end
102
109
  end
103
110
 
104
- def check_jobs(database, settings)
105
- finder = JobFinder.new(database.model)
111
+ def find_jobs(database)
112
+ free_settings = count_free_settings
113
+ free_settings.map do |setting, free_count|
114
+ @job_finder.ready_jobs(database, setting, free_count)
115
+ end.flatten
116
+ end
106
117
 
107
- settings.each_with_object([]) do |(setting, free_count), array|
108
- job_ids = finder.call(setting, free_count)
109
- if job_ids.size > 0
110
- [free_count, job_ids.size].min.times do
111
- array << setting
112
- end
113
- end
114
- end
118
+ def count_free_settings
119
+ @config.worker_settings.map do |setting|
120
+ used_count = @master.workers.count { |worker| worker.setting == setting }
121
+ free_count = setting.max_processes - used_count
122
+ [setting, free_count] if free_count > 0
123
+ end.compact.to_h
124
+ end
125
+
126
+ def fork_worker(database, setting)
127
+ worker = Worker.new(database: database, setting: setting)
128
+ Forker.new(@master).call(worker)
129
+ @master.workers << worker
130
+ @master.monitoring.schedule(worker)
115
131
  end
116
132
 
117
- def fork_workers(database, settings)
118
- settings.each do |setting|
119
- worker = Worker.new(database: database, setting: setting)
120
- Forker.new(@master).call(worker)
121
- @master.workers << worker
122
- @master.monitoring.schedule(worker)
133
+ def check_recent_jobs(database)
134
+ jobs = @job_finder.recent_jobs(database)
135
+ jobs.each do |job|
136
+ @master.logger.info { "set timer to #{job.run_at.iso8601(6)} @#{job.database.spec_name}" }
137
+ start_timer_thread(job.database, job.run_at)
123
138
  end
124
139
  end
125
140
  end
@@ -1,31 +1,49 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'job'
4
+
3
5
  # JobFinder runs SQL query which is almost same as delayed_job_active_record.
4
6
  # See https://github.com/collectiveidea/delayed_job_active_record/blob/master/lib/delayed/backend/active_record.rb
5
7
  module Delayed
6
8
  module Master
7
9
  class JobFinder
8
- def initialize(model)
9
- @model = model
10
+ def initialize(master)
11
+ @config = master.config
10
12
  end
11
13
 
12
- def call(setting, limit)
13
- scope(setting).limit(limit).pluck(:id)
14
+ def ready_jobs(database, setting, limit)
15
+ ready_scope(database, setting).limit(limit).pluck(:id, :run_at).map do |id, run_at|
16
+ Job.new(database: database, setting: setting, id: id, run_at: run_at)
17
+ end
14
18
  end
15
19
 
16
- def count(setting)
17
- scope(setting).count
20
+ def recent_jobs(database)
21
+ recent_scope(database).order(:run_at).limit(1).pluck(:id, :run_at).map do |id, run_at|
22
+ Job.new(database: database, id: id, run_at: run_at)
23
+ end
18
24
  end
19
25
 
20
26
  private
21
27
 
22
- def scope(setting)
23
- @model.ready_to_run(nil, setting.max_run_time || Delayed::Worker::DEFAULT_MAX_RUN_TIME).tap do |jobs|
28
+ def ready_scope(database, setting)
29
+ model = database.model
30
+ model.where("(run_at <= ? AND (locked_at IS NULL OR locked_at < ?)) AND failed_at IS NULL",
31
+ model.db_time_now,
32
+ model.db_time_now - (setting.max_run_time || Delayed::Worker::DEFAULT_MAX_RUN_TIME)
33
+ ).tap do |jobs|
24
34
  jobs.where!("priority >= ?", setting.min_priority) if setting.min_priority
25
35
  jobs.where!("priority <= ?", setting.max_priority) if setting.max_priority
26
36
  jobs.where!(queue: setting.queues) if setting.queues.present?
27
37
  end
28
38
  end
39
+
40
+ def recent_scope(database)
41
+ model = database.model
42
+ model.where("run_at > ? AND run_at < ? AND locked_at IS NULL AND failed_at IS NULL",
43
+ model.db_time_now,
44
+ model.db_time_now + @config.polling_interval,
45
+ )
46
+ end
29
47
  end
30
48
  end
31
49
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DelayedJobMaster
4
- VERSION = "3.0.1"
4
+ VERSION = "3.1.0"
5
5
  end
@@ -5,7 +5,7 @@ working_directory Dir.pwd
5
5
  monitor_interval 5
6
6
 
7
7
  # polling interval for new jobs (in seconds)
8
- polling_interval 5
8
+ polling_interval 30
9
9
 
10
10
  # path to pid file
11
11
  pid_file "#{Dir.pwd}/tmp/pids/delayed_job_master.pid"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: delayed_job_master
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.1
4
+ version: 3.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yoshikazu Kaneta
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-05-29 00:00:00.000000000 Z
11
+ date: 2023-06-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: delayed_job
@@ -192,6 +192,7 @@ files:
192
192
  - lib/delayed/master/database.rb
193
193
  - lib/delayed/master/file_reopener.rb
194
194
  - lib/delayed/master/forker.rb
195
+ - lib/delayed/master/job.rb
195
196
  - lib/delayed/master/job_checker.rb
196
197
  - lib/delayed/master/job_finder.rb
197
198
  - lib/delayed/master/job_listener.rb