delayed_job_master 3.0.1 → 3.1.1

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: 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: []