belated 0.3.3 → 0.4.3

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: 639e202c5feb0ecb2af5bcd7f22815128f670f36c2aa793ce5bc0c107477a92f
4
- data.tar.gz: 65864414d040a9b1cd3e92897a97b5f596683c42cb6941c5cdbaee731502a622
3
+ metadata.gz: 598cfda479ae3ec79564557a4f111ff5600c255bdf3cc8b3e03194b0b0b6ce86
4
+ data.tar.gz: f6b07150c218bf9754f5d40625e7e5d26673f87bf4d8a1baa4ef87814edbbc09
5
5
  SHA512:
6
- metadata.gz: bda697be3405328560b19afbf9a90cc8cac0a8487e627ba0d6ee02ef2bbd6bf933cf3235fa312a1f817224ade6837da390d6794742a5110b255ca6052cafb838
7
- data.tar.gz: da4c2c0b49901ca28aed07eff2d279293c28a3121031500cc4846627d01619fbd24c52b0c382c8c4091d46238df53d827449c7092a85286072a1b74600402313
6
+ metadata.gz: 43ab001fcc721b6750ec2c7d3ee00f32ab58ff9909268bb9dda92d7096a5cca85782fbd8f3502295cc07086ac6ef18c056124f55ef4a706b607d2aab3c667ef8
7
+ data.tar.gz: c83cec5063743c8ed84a28a1b5995b7b3d7deeade1f6f352d81d1dca5b1531231dc46678f14e1ad6795662d8105dfc957ca81f7868996b310bc8b4128132e0c9
data/.gitignore CHANGED
@@ -14,3 +14,4 @@
14
14
  /dummy/log/
15
15
  /dummy/log/test.log
16
16
  belated_dump
17
+ stackprof*
data/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.4.3] - 2021-08-06
4
+
5
+ - Client now starts the banker thread to execute jobs that were enqueued when there was no connection to Belated only if necessary.
6
+ ## [0.4.2] - 2021-08-05
7
+
8
+ - Client also handles no connection, now it saves jobs to a bank and adds them to the queue once it has a connection.
9
+ ## [0.4.1] - 2021-08-05
10
+
11
+ - Now handles saving future jobs too! So if you have a job enqueued for tomorrow, and restart Belated, it should still be enqueued.
12
+
13
+ ## [0.4.0] - 2021-08-03
14
+
15
+ - Now you can enqueue jobs to be done at a later time. Just pass an `at:` keyword param to the client.
16
+ - Does not save the jobs when you quit.
17
+
3
18
  ## [0.3.3] - 2021-08-01
4
19
 
5
20
  - Shutdown trapped signal thread, make sure :shutdown is not recorded as a job.
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.3)
4
+ belated (0.4.3)
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
@@ -4,29 +4,26 @@
4
4
 
5
5
  This is Belated, a new Ruby backend job library! It supports running procs and classes in the background. To deal with restarts, it uses YAML to load the queue into a file, which it then calls at startup to find the previous jobs.
6
6
 
7
+ Belated uses the Ruby Queue class, so it's First In, First Out (FIFO).
8
+
7
9
  Note that Belated used to be called HardWorker. That name was already in use in Sidekiq documentation and a bit too generic anyway.
8
10
 
9
11
  It uses dRuby to do the communication! Which is absolute great. No need for Redis or PostgreSQL, just Ruby standard libraries.
10
12
 
13
+ Can be used with or without Rails.
14
+
11
15
  TODO LIST:
12
16
 
13
- - Catch SIGTERM and friends
14
- - Now supports it, partly.
17
+ - Add some checks to the client for proper jobs.
15
18
  - Don't crash on errors (Partially done)
16
- - Make it possible to schedule jobs
19
+ - Have multiple queues?
17
20
  - Maybe support ActiveJob?
18
21
  - Have a web UI
19
22
  - Do some performance testing
23
+ - Deploy a Rails app to production that is using Belated
24
+ and mention it in the readme.
20
25
  - Add a section telling people to use Sidekiq if they can
21
26
 
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
27
  ## Installation
31
28
 
32
29
  Add this line to your application's Gemfile:
@@ -98,6 +95,14 @@ client.perform_belated(job)
98
95
 
99
96
  If you want to pass a job to Belated.
100
97
 
98
+ If you don't want the job to run right away, you can also pass it a keyword param `at:` like so:
99
+
100
+ ```ruby
101
+ client.perform_belated(job, Time.now + 1.month)
102
+ ```
103
+
104
+ Note that you probably want to memoize the client, as it always creates a 'banker thread' now if you have no connection. Maybe even use it as a global!(`$client`)
105
+
101
106
  # Settings
102
107
 
103
108
  Configuring Belated:
@@ -113,15 +118,15 @@ end
113
118
 
114
119
  From command line:
115
120
 
116
- $ bundle exec belated --rails=true
121
+ $ bundle exec belated --rails=true
117
122
 
118
123
  Use Rails or not.
119
124
 
120
- $ bundle exec belated --rails_path=/my_rails_project
125
+ $ bundle exec belated --rails_path=/my_rails_project
121
126
 
122
127
  Path to Rails project.
123
128
 
124
- $ bundle exec belated --workers=10
129
+ $ bundle exec belated --workers=10
125
130
 
126
131
  Number of workers.
127
132
 
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,7 @@ 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
- unless (klass = @@queue.pop).instance_of?(Proc) || klass == :shutdown
111
- class_array << klass
112
- end
113
- end
114
- pp File.open(FILE_NAME, 'wb') { |f| f.write(YAML.dump(class_array)) }
109
+ @@queue.save_jobs
115
110
  exit unless $TESTING
116
111
  end
117
112
 
@@ -1,16 +1,48 @@
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
- # @bank =
8
16
  DRb.start_service
17
+ self.bank = Thread::Queue.new
9
18
  self.queue = DRbObject.new_with_uri(server_uri)
10
19
  end
11
20
 
12
- def perform(job)
13
- queue.push(job)
21
+ # Thread in charge of handling the bank queue.
22
+ # You probably want to memoize the client in order to avoid
23
+ # having many threads in the sleep state.
24
+ # @return [void]
25
+ def start_banker_thread
26
+ self.banker_thread = Thread.new do
27
+ loop do
28
+ job, at = bank.pop
29
+
30
+ perform(job, at: at)
31
+ end
32
+ end
33
+ end
34
+
35
+ # The method that pushes the jobs to the queue.
36
+ # If there is no connection, it pushes the job to the bank.
37
+ # @param job [Object] - The the job to be pushed.
38
+ # @param at [Date] - The time at which the job should be executed.
39
+ # @return [Object] - The job that was pushed.
40
+ def perform(job, at: nil)
41
+ queue.push(job, at: at)
42
+ rescue DRb::DRbConnError
43
+ bank.push([job, at])
44
+ start_banker_thread if banker_thread.nil?
45
+ # banker_thread.wakeup if banker_thread.status == 'sleep'
14
46
  end
15
47
  alias perform_belated perform
16
48
  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.3'
4
+ VERSION = '0.4.3'
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.3
4
+ version: 0.4.3
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-08-01 00:00:00.000000000 Z
11
+ date: 2021-08-06 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