delayed_job_master 3.0.1 → 3.1.1

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: c64ac5ffbb010ea728b8ecd5ab347e7ad4971736572a9ef92b19bfe5454c4a89
4
+ data.tar.gz: 7346a6743488dbe534e2fd7b772510c529350a7e19ec90ad23149898acc138fa
5
5
  SHA512:
6
- metadata.gz: 4814327c73cad53727c33b0397a27268f2ef82473eecbeadb5ae7c009fe9e6e0fa14ebc7b2e225677b5d94f4c009d82f2bde10fe646fc5432520423b0cd61b0f
7
- data.tar.gz: a10fa10a78e642b209969bed0e0ac0098cae344e2a64970ad2e7cecdf4ac743a4c9537457de5d96112afc1b7a77c07bfcc6b9282164968cecfd34dae889820a0
6
+ metadata.gz: f19876c0847934d89437522d51b8afe4d5925af0bbb1157dbfa2190ccd0ae60ac2bde30d591d296cdbde7d08a55309083e089ce67828bb030ac0baa2f152dd58
7
+ data.tar.gz: 28ab1a59dee710808ee8b8e4f4bfb18d172f4845a4aff46693fb66b2a69913671844c079d224c63ec49a23ccf0d5464cf97c5045ab6b030546f744261923c1e0
@@ -4,10 +4,10 @@ on: [push, pull_request]
4
4
 
5
5
  jobs:
6
6
  test:
7
- runs-on: ubuntu-20.04
7
+ runs-on: ubuntu-22.04
8
8
  services:
9
9
  postgres:
10
- image: postgres:9.5
10
+ image: postgres:15
11
11
  env:
12
12
  POSTGRES_USER: postgres
13
13
  POSTGRES_PASSWORD: postgres
@@ -24,8 +24,8 @@ jobs:
24
24
  strategy:
25
25
  fail-fast: false
26
26
  matrix:
27
- ruby: [2.7, '3.0', 3.1, 3.2]
28
- gemfile: ['rails60', 'rails61', 'rails70']
27
+ ruby: [2.7, '3.0', 3.1, 3.2, 3.3]
28
+ gemfile: ['rails60', 'rails61', 'rails70', 'rails71']
29
29
  database: ['postgresql', 'mysql']
30
30
  database_config: ['default', 'multi']
31
31
 
@@ -39,7 +39,7 @@ jobs:
39
39
  BUNDLE_GEMFILE: gemfiles/${{ matrix.gemfile }}.gemfile
40
40
 
41
41
  steps:
42
- - uses: actions/checkout@v3
42
+ - uses: actions/checkout@v4
43
43
  - uses: ruby/setup-ruby@v1
44
44
  with:
45
45
  ruby-version: ${{ matrix.ruby }}
data/.gitignore CHANGED
@@ -6,4 +6,5 @@
6
6
  /spec/reports/
7
7
  /spec/**/log/*.log
8
8
  /spec/**/tmp/*
9
+ /tmp/
9
10
  /vendor/
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 3.1.1
4
+
5
+ * Add `frozen_string_literal: true`.
6
+
7
+ ## 3.1.0
8
+
9
+ * Add timer feature.
10
+
3
11
  ## 3.0.1
4
12
 
5
13
  * 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"
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem "rails", "~> 7.1.0"
4
+
5
+ gemspec path: "../"
@@ -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,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module DelayedJobMaster
2
4
  class Railtie < Rails::Railtie
3
5
  config.after_initialize do
@@ -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.1"
5
5
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_support'
2
4
 
3
5
  require_relative 'delayed_job_master/version'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rails/generators'
2
4
 
3
5
  module DelayedJobMaster
@@ -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.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yoshikazu Kaneta
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-05-29 00:00:00.000000000 Z
11
+ date: 2024-06-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: delayed_job
@@ -184,6 +184,7 @@ files:
184
184
  - gemfiles/rails60.gemfile
185
185
  - gemfiles/rails61.gemfile
186
186
  - gemfiles/rails70.gemfile
187
+ - gemfiles/rails71.gemfile
187
188
  - lib/delayed/master.rb
188
189
  - lib/delayed/master/callbacks.rb
189
190
  - lib/delayed/master/command.rb
@@ -192,6 +193,7 @@ files:
192
193
  - lib/delayed/master/database.rb
193
194
  - lib/delayed/master/file_reopener.rb
194
195
  - lib/delayed/master/forker.rb
196
+ - lib/delayed/master/job.rb
195
197
  - lib/delayed/master/job_checker.rb
196
198
  - lib/delayed/master/job_finder.rb
197
199
  - lib/delayed/master/job_listener.rb
@@ -223,7 +225,7 @@ homepage: https://github.com/kanety/delayed_job_master
223
225
  licenses:
224
226
  - MIT
225
227
  metadata: {}
226
- post_install_message:
228
+ post_install_message:
227
229
  rdoc_options: []
228
230
  require_paths:
229
231
  - lib
@@ -239,7 +241,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
239
241
  version: '0'
240
242
  requirements: []
241
243
  rubygems_version: 3.3.3
242
- signing_key:
244
+ signing_key:
243
245
  specification_version: 4
244
246
  summary: A simple delayed_job master process to control multiple workers
245
247
  test_files: []