belated 0.3.2 → 0.4.2

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: 995560f345bb7644aa19035b27a68d43a1210ee36c21d13e21cc430b76d9fded
4
- data.tar.gz: 103441a7e60e79319c65bacbedfb4dc337224c41c0e9560225f3b2421e219473
3
+ metadata.gz: 33b9abbd449108f850f65e0afaa92929c780b03856e9e151efb6cc33b84021d5
4
+ data.tar.gz: eb9a7142602bae2572454c227d057adf9f38397c1638dcc6f66f2cd16019b766
5
5
  SHA512:
6
- metadata.gz: ecc27c208d2fdb26c93ee39ebba7ada11236e9f564565d69258dfa6381a407c924e076365329f088ca4071955fd0eaeb8e9e1a1119f77d824473987590fee670
7
- data.tar.gz: 57db27785351cbba33b6c704f877eacf4343d95a02267411e79bf63f1efadb49a67430342d145898a8571318bc61eafdd756bc2618388603122a0733bad7a3ed
6
+ metadata.gz: c50c1dfe80dceee431785ec150a0a5b6a7c220e18fc8c6a05069637d474d848719c5bfd05f0c0f4125e379348296ebb92823e71b985e76cccffe9e3080349822
7
+ data.tar.gz: 357053481de792ea44b832eade91895d5257074e3381333ddb58aaa48c20d08b6d02d6e84b57664218108ae2927d8247db7229f1a1fa91483a745a3202031a0d
data/.gitignore CHANGED
@@ -14,3 +14,4 @@
14
14
  /dummy/log/
15
15
  /dummy/log/test.log
16
16
  belated_dump
17
+ stackprof*
data/.rubocop.yml CHANGED
@@ -41,5 +41,8 @@ Metrics/BlockLength:
41
41
  Metrics/ClassLength:
42
42
  Max: 200
43
43
 
44
+ Metrics/MethodLength:
45
+ Max: 15
46
+
44
47
  Security/YAMLLoad:
45
48
  Enabled: false
data/CHANGELOG.md CHANGED
@@ -1,6 +1,20 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.4.2] - 2021-08-05
3
4
 
5
+ - Client also handles no connection, now it saves jobs to a bank and adds them to the queue once it has a connection.
6
+ ## [0.4.1] - 2021-08-05
7
+
8
+ - Now handles saving future jobs too! So if you have a job enqueued for tomorrow, and restart Belated, it should still be enqueued.
9
+
10
+ ## [0.4.0] - 2021-08-03
11
+
12
+ - Now you can enqueue jobs to be done at a later time. Just pass an `at:` keyword param to the client.
13
+ - Does not save the jobs when you quit.
14
+
15
+ ## [0.3.3] - 2021-08-01
16
+
17
+ - Shutdown trapped signal thread, make sure :shutdown is not recorded as a job.
4
18
 
5
19
  ## [0.3.2] - 2021-07-31
6
20
 
data/Gemfile CHANGED
@@ -11,5 +11,8 @@ gem 'rspec', '~> 3.0'
11
11
 
12
12
  gem 'rubocop', '~> 1.7'
13
13
 
14
+ gem 'database_cleaner-active_record'
14
15
  gem 'rails', '>= 6.1.3'
16
+ gem 'rspec-rails'
15
17
  gem 'sqlite3'
18
+ gem 'stackprof'
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- belated (0.3.2)
4
+ belated (0.4.2)
5
5
  drb
6
6
  dry-configurable
7
7
 
@@ -72,6 +72,10 @@ GEM
72
72
  byebug (11.1.3)
73
73
  concurrent-ruby (1.1.9)
74
74
  crass (1.0.6)
75
+ database_cleaner-active_record (2.0.1)
76
+ activerecord (>= 5.a)
77
+ database_cleaner-core (~> 2.0.0)
78
+ database_cleaner-core (2.0.1)
75
79
  diff-lcs (1.4.4)
76
80
  drb (2.0.4)
77
81
  dry-configurable (0.12.1)
@@ -145,6 +149,14 @@ GEM
145
149
  rspec-mocks (3.10.2)
146
150
  diff-lcs (>= 1.2.0, < 2.0)
147
151
  rspec-support (~> 3.10.0)
152
+ rspec-rails (5.0.1)
153
+ actionpack (>= 5.2)
154
+ activesupport (>= 5.2)
155
+ railties (>= 5.2)
156
+ rspec-core (~> 3.10)
157
+ rspec-expectations (~> 3.10)
158
+ rspec-mocks (~> 3.10)
159
+ rspec-support (~> 3.10)
148
160
  rspec-support (3.10.2)
149
161
  rubocop (1.18.3)
150
162
  parallel (~> 1.10)
@@ -166,6 +178,7 @@ GEM
166
178
  activesupport (>= 4.0)
167
179
  sprockets (>= 3.0.0)
168
180
  sqlite3 (1.4.2)
181
+ stackprof (0.2.16)
169
182
  thor (1.1.0)
170
183
  tzinfo (2.0.4)
171
184
  concurrent-ruby (~> 1.0)
@@ -181,11 +194,14 @@ PLATFORMS
181
194
  DEPENDENCIES
182
195
  belated!
183
196
  byebug
197
+ database_cleaner-active_record
184
198
  rails (>= 6.1.3)
185
199
  rake (~> 13.0)
186
200
  rspec (~> 3.0)
201
+ rspec-rails
187
202
  rubocop (~> 1.7)
188
203
  sqlite3
204
+ stackprof
189
205
 
190
206
  BUNDLED WITH
191
207
  2.2.17
data/README.md CHANGED
@@ -8,25 +8,20 @@ Note that Belated used to be called HardWorker. That name was already in use in
8
8
 
9
9
  It uses dRuby to do the communication! Which is absolute great. No need for Redis or PostgreSQL, just Ruby standard libraries.
10
10
 
11
+ Can be used with or without Rails.
12
+
11
13
  TODO LIST:
12
14
 
13
- - Catch SIGTERM and friends
14
- - Now supports it, partly.
15
+ - Add some checks to the client for proper jobs.
15
16
  - Don't crash on errors (Partially done)
16
- - Make it possible to schedule jobs
17
+ - Have multiple queues?
17
18
  - Maybe support ActiveJob?
18
19
  - Have a web UI
19
20
  - Do some performance testing
21
+ - Deploy a Rails app to production that is using Belated
22
+ and mention it in the readme.
20
23
  - Add a section telling people to use Sidekiq if they can
21
24
 
22
- DONE
23
-
24
- - ~~Marshal the job queue into a file so you don't lose all progress~~
25
- (Ended up using YAML)
26
- - ~~Add a logger~~
27
- - ~~Support Rails~~ (Supported!)
28
- - ~~Parse options from command line, eg. `--workers 10`~~(Done!)
29
-
30
25
  ## Installation
31
26
 
32
27
  Add this line to your application's Gemfile:
@@ -98,6 +93,12 @@ client.perform_belated(job)
98
93
 
99
94
  If you want to pass a job to Belated.
100
95
 
96
+ If you don't want the job to run right away, you can also pass it a keyword param `at:` like so:
97
+
98
+ ```ruby
99
+ client.perform_belated(job, Time.now + 1.month)
100
+ ```
101
+
101
102
  # Settings
102
103
 
103
104
  Configuring Belated:
@@ -113,15 +114,15 @@ end
113
114
 
114
115
  From command line:
115
116
 
116
- $ bundle exec belated --rails=true
117
+ $ bundle exec belated --rails=true
117
118
 
118
119
  Use Rails or not.
119
120
 
120
- $ bundle exec belated --rails_path=/my_rails_project
121
+ $ bundle exec belated --rails_path=/my_rails_project
121
122
 
122
123
  Path to Rails project.
123
124
 
124
- $ bundle exec belated --workers=10
125
+ $ bundle exec belated --workers=10
125
126
 
126
127
  Number of workers.
127
128
 
data/lib/belated.rb CHANGED
@@ -9,6 +9,7 @@ require 'singleton'
9
9
  require 'dry-configurable'
10
10
  require 'belated/client'
11
11
  require 'logger'
12
+ require 'belated/queue'
12
13
 
13
14
  # Belated is a pure Ruby job backend.
14
15
  # It has limited functionality, as it only accepts
@@ -21,8 +22,7 @@ class Belated
21
22
  include Logging
22
23
  include Singleton unless $TESTING
23
24
  URI = 'druby://localhost:8788'
24
- FILE_NAME = 'belated_dump'
25
- @@queue = Queue.new
25
+ @@queue = Belated::Queue.new
26
26
 
27
27
  setting :rails, true
28
28
  setting :rails_path, '.'
@@ -36,17 +36,17 @@ class Belated
36
36
  # Aliased for testing purposes.
37
37
  # This is only run from the bin file.
38
38
  def start
39
- boot_app && load_jobs
39
+ boot_app && @@queue.load_jobs
40
40
  @worker_list = []
41
- Belated.config.workers.times do |_i|
42
- @worker_list << Thread.new { Worker.new }
41
+ Belated.config.workers.times do |i|
42
+ @worker_list << Thread.new { Worker.new(number: i.next) }
43
43
  end
44
44
  return unless Belated.config.connect
45
45
 
46
46
  connect!
47
47
  banner_and_info
48
48
  trap_signals
49
- DRb.thread.join
49
+ enqueue_future_jobs
50
50
  end
51
51
  alias initialize start
52
52
 
@@ -55,7 +55,6 @@ class Belated
55
55
  DRb.start_service(URI, @@queue, verbose: true)
56
56
  rescue DRb::DRbConnError, Errno::EADDRINUSE
57
57
  Belated.logger.error 'Could not connect to DRb server.'
58
- retry
59
58
  end
60
59
 
61
60
  def trap_signals
@@ -65,7 +64,7 @@ class Belated
65
64
  @@queue.push(:shutdown)
66
65
  end
67
66
  Thread.new { stop_workers }
68
- sleep 0.1 until @@queue.empty?
67
+ sleep 0.1 until @@queue.empty? || $TESTING
69
68
  exit
70
69
  end
71
70
  end
@@ -84,20 +83,22 @@ class Belated
84
83
  Belated.config.rails
85
84
  end
86
85
 
87
- def load_jobs
88
- log "reloading... if file exists #{File.exist?(Belated::FILE_NAME)}"
89
- return unless File.exist?(Belated::FILE_NAME)
90
-
91
- jobs = YAML.load(File.binread(FILE_NAME))
92
- jobs.each do |job|
93
- @@queue.push(job)
86
+ def enqueue_future_jobs
87
+ log 'starting future jobs thread'
88
+ loop do
89
+ @@queue.future_jobs.each_with_index do |job, i|
90
+ if job[:at] <= Time.now.utc
91
+ log @@queue.future_jobs.delete_at(i)
92
+ @@queue.push(job[:klass])
93
+ end
94
+ end
95
+ sleep 0.01
94
96
  end
95
- File.delete(Belated::FILE_NAME)
96
97
  end
97
98
 
98
99
  def reload
99
100
  log 'reloading...'
100
- load_jobs
101
+ @@queue.load_jobs
101
102
  end
102
103
 
103
104
  def stop_workers
@@ -105,13 +106,8 @@ class Belated
105
106
  sleep 0.1 if worker.alive?
106
107
  Thread.kill(worker)
107
108
  end
108
- class_array = []
109
- @@queue.size.times do |_i|
110
- next if (klass = @@queue.pop).instance_of?(Proc)
111
-
112
- class_array << klass
113
- end
114
- pp File.open(FILE_NAME, 'wb') { |f| f.write(YAML.dump(class_array)) }
109
+ @@queue.save_jobs
110
+ exit unless $TESTING
115
111
  end
116
112
 
117
113
  def banner
@@ -1,16 +1,41 @@
1
1
  class Belated
2
+ # The client class is responsible for managing the connection to the
3
+ # DRb server. If it has no connection, it adds the jobs to a bank queue.
4
+ # You can enqueue jobs to be processed by the server.
5
+ # Example:
6
+ # client = Belated::Client.new
7
+ # client.enqueue(JubJub.new, at: Time.now + 5.seconds)
2
8
  class Client
3
- attr_accessor :queue
9
+ attr_accessor :queue, :bank, :banker_thread
4
10
 
11
+ # Starts up the client.
12
+ # Connects to the queue through DRb.
13
+ # @return [void]
5
14
  def initialize
6
15
  server_uri = Belated::URI
7
16
  # @bank =
8
17
  DRb.start_service
18
+ self.bank = Thread::Queue.new
9
19
  self.queue = DRbObject.new_with_uri(server_uri)
20
+ self.banker_thread = Thread.new do
21
+ loop do
22
+ sleep 0.05
23
+ next unless (job, at = bank.pop)
24
+
25
+ perform(job, at: at)
26
+ end
27
+ end
10
28
  end
11
29
 
12
- def perform(job)
13
- queue.push(job)
30
+ # The method that pushes the jobs to the queue.
31
+ # If there is no connection, it pushes the job to the bank.
32
+ # @param job [Object] - The the job to be pushed.
33
+ # @param at [Date] - The time at which the job should be executed.
34
+ # @return [Object] - The job that was pushed.
35
+ def perform(job, at: nil)
36
+ queue.push(job, at: at)
37
+ rescue DRb::DRbConnError
38
+ bank.push([job, at])
14
39
  end
15
40
  alias perform_belated perform
16
41
  alias perform_later perform
@@ -0,0 +1 @@
1
+ Job = Struct.new(:klass, :at)
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'belated/job'
4
+ require 'belated/logging'
5
+ class Belated
6
+ class Queue
7
+ include Logging
8
+ attr_accessor :future_jobs
9
+
10
+ FILE_NAME = 'belated_dump'
11
+
12
+ def initialize(queue: Thread::Queue.new, future_jobs: [])
13
+ @queue = queue
14
+ self.future_jobs = future_jobs
15
+ end
16
+
17
+ def push(job, at: nil)
18
+ if at.nil?
19
+ @queue.push(job)
20
+ else
21
+ @future_jobs << Job.new(job, at)
22
+ end
23
+ end
24
+
25
+ def pop
26
+ @queue.pop
27
+ end
28
+
29
+ def clear
30
+ @queue.clear
31
+ self.future_jobs = []
32
+ end
33
+
34
+ def empty?
35
+ @queue.empty?
36
+ end
37
+
38
+ def length
39
+ @queue.length
40
+ end
41
+
42
+ def load_jobs
43
+ log "reloading... if file exists #{File.exist?(FILE_NAME)}"
44
+ return unless File.exist?(FILE_NAME)
45
+
46
+ jobs = YAML.load(File.binread(FILE_NAME))
47
+ jobs.each do |job|
48
+ if job.is_a?(Job)
49
+ future_jobs.push(job)
50
+ else
51
+ @queue.push(job)
52
+ end
53
+ end
54
+ File.delete(FILE_NAME)
55
+ end
56
+
57
+ def save_jobs
58
+ class_array = []
59
+ @queue.length.times do |_i|
60
+ unless proc_or_shutdown?(klass = @queue.pop)
61
+ class_array << klass
62
+ end
63
+ end
64
+ future_jobs.each do |_job|
65
+ unless proc_or_shutdown?(klass = future_jobs.pop)
66
+ class_array << klass
67
+ end
68
+ end
69
+
70
+ pp File.open(FILE_NAME, 'wb') { |f| f.write(YAML.dump(class_array)) }
71
+ end
72
+
73
+ private
74
+
75
+ def proc_or_shutdown?(job)
76
+ job.instance_of?(Proc) || job == :shutdown
77
+ end
78
+ end
79
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Belated
4
- VERSION = '0.3.2'
4
+ VERSION = '0.4.2'
5
5
  end
@@ -1,22 +1,24 @@
1
1
  require_relative 'logging'
2
2
  class Belated
3
3
  # The worker class that actually gets the jobs from the queue
4
- # and calls them. Expects the jobs to be procs.
4
+ # and calls them. Expects the jobs to be procs or
5
+ # classes that have a perform method.
5
6
  class Worker
6
7
  include Logging
7
8
 
8
- def initialize
9
+ def initialize(number: 1)
10
+ @number = number
9
11
  start_working
10
12
  end
11
13
 
12
14
  def start_working
13
15
  loop do
16
+ log "Worker #{@number} fetching jobs!"
14
17
  next unless (job = Belated.fetch_job)
15
18
 
16
19
  break if job == :shutdown
17
20
 
18
21
  log call_job(job)
19
- log 'fetching jobs...'
20
22
  end
21
23
  end
22
24
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: belated
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.2
4
+ version: 0.4.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sampo Kuokkanen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-07-31 00:00:00.000000000 Z
11
+ date: 2021-08-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: drb
@@ -80,7 +80,9 @@ files:
80
80
  - bin/setup
81
81
  - lib/belated.rb
82
82
  - lib/belated/client.rb
83
+ - lib/belated/job.rb
83
84
  - lib/belated/logging.rb
85
+ - lib/belated/queue.rb
84
86
  - lib/belated/rails.rb
85
87
  - lib/belated/version.rb
86
88
  - lib/belated/worker.rb