delayed_job_master 3.0.1 → 3.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.
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