belated 0.3.2 → 0.4.2

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