belated 0.4.2 → 0.5.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: 33b9abbd449108f850f65e0afaa92929c780b03856e9e151efb6cc33b84021d5
4
- data.tar.gz: eb9a7142602bae2572454c227d057adf9f38397c1638dcc6f66f2cd16019b766
3
+ metadata.gz: 5824acbe80af6b8b64a09ab2a0ee62d204504f99edee65d426ab05c737d52da0
4
+ data.tar.gz: ed3ff585ac66e715e55afd143bcf63fa534bd3434c9f6e9b5d4e0960fa47d9c8
5
5
  SHA512:
6
- metadata.gz: c50c1dfe80dceee431785ec150a0a5b6a7c220e18fc8c6a05069637d474d848719c5bfd05f0c0f4125e379348296ebb92823e71b985e76cccffe9e3080349822
7
- data.tar.gz: 357053481de792ea44b832eade91895d5257074e3381333ddb58aaa48c20d08b6d02d6e84b57664218108ae2927d8247db7229f1a1fa91483a745a3202031a0d
6
+ metadata.gz: 7d42b2530bd69a3a523383d98ccceb7ef76406bcf4c20791d93231b31004a6c43a208781308ede75fb614c84281bac3a97394646df9f0225a09de244030ff485
7
+ data.tar.gz: 62a9d873677daba7273ba182c2c0470f9ef9188d8a370d35fa94a7a45676abdbb4a26898299b886e06496f5bab85e9f740276e62f9dd9ee507f8ceb891605080
data/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.5.1] - 2021-08-12
4
+
5
+ - Requiring byebug in bin file. 😮
6
+
7
+ ## [0.5.0] - 2021-08-011
8
+
9
+ - Job retries! The jobs now have ids, so you can follow the job and it's retries from the log.
10
+ - Quite a lot has changed internally, so if you were not using the Belated::Queue class to enqueue your jobs, you will need to update your code.
11
+
12
+ ## [0.4.4] - 2021-08-07
13
+
14
+ - Now if you pass something with a syntax error in it as a job, it should not bring down the whole app!
15
+
16
+ ## [0.4.3] - 2021-08-06
17
+
18
+ - Client now starts the banker thread to execute jobs that were enqueued when there was no connection to Belated only if necessary.
3
19
  ## [0.4.2] - 2021-08-05
4
20
 
5
21
  - Client also handles no connection, now it saves jobs to a bank and adds them to the queue once it has a connection.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- belated (0.4.2)
4
+ belated (0.5.1)
5
5
  drb
6
6
  dry-configurable
7
7
 
data/README.md CHANGED
@@ -2,24 +2,31 @@
2
2
 
3
3
  [![CodeFactor](https://www.codefactor.io/repository/github/sampokuokkanen/belated/badge)](https://www.codefactor.io/repository/github/sampokuokkanen/belated) [![Gem Version](https://badge.fury.io/rb/belated.svg)](https://badge.fury.io/rb/belated)
4
4
 
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.
5
+ This is Belated, a new Ruby backend job library! It supports running procs, lambdas 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. There is no way in Ruby to save procs or lambdas to a file, so they are discarded when the process restarts.
6
+
7
+ Belated uses the Ruby Queue class, so it's First In, First Out (FIFO).
6
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
+ Note that currently the timezone is hardcoded to UTC.
14
+
11
15
  Can be used with or without Rails.
12
16
 
13
17
  TODO LIST:
14
18
 
19
+ - Don't use class instance variables.
20
+ - Make port configurable.
21
+ - Don't hardcode timezone.
15
22
  - Add some checks to the client for proper jobs.
16
- - Don't crash on errors (Partially done)
17
23
  - Have multiple queues?
18
24
  - Maybe support ActiveJob?
19
- - Have a web UI
20
- - Do some performance testing
25
+ - Have a web UI.
26
+ - Have a job history
27
+ - Do some performance testing.
21
28
  - Deploy a Rails app to production that is using Belated
22
- and mention it in the readme.
29
+ and mention it in the readme. (Capistrano support?)
23
30
  - Add a section telling people to use Sidekiq if they can
24
31
 
25
32
  ## Installation
@@ -48,15 +55,6 @@ Then, in another program, connect to Belated and give it a job to do.
48
55
  Sample below:
49
56
 
50
57
  ```ruby
51
- class DummyWorker
52
- attr_accessor :queue
53
-
54
- def initialize
55
- server_uri = Belated::URI
56
- self.queue = DRbObject.new_with_uri(server_uri)
57
- end
58
- end
59
-
60
58
  class DumDum
61
59
  # classes need to have a perform method
62
60
  def perform
@@ -64,11 +62,14 @@ class DumDum
64
62
  end
65
63
  end
66
64
 
67
- # Need to start dRuby on the client side
68
- DRb.start_service
69
- dummy = DummyWorker.new
70
- dummy.queue.push(proc { 2 / 1 })
71
- dummy.queue.push(DumDum.new)
65
+ client = Belated::Client.new
66
+ client.perform_belated(proc { 2 / 1 })
67
+ client.perform_belated(DumDum.new)
68
+ # client.perform, client.perform_later are also good
69
+ # if you want to do something later:
70
+ client.perform_belated(DumDum.new, at: Time.now + 5 * 60)
71
+ # max retries:
72
+ client.perform_belated(DumDum.new, max_retries: 3) # default 5
72
73
  ```
73
74
 
74
75
  Belated runs on localhost, port 8788.
@@ -88,6 +89,7 @@ and you can use the client!
88
89
  Call
89
90
 
90
91
  ```ruby
92
+ job = proc { 2 / 1 }
91
93
  client.perform_belated(job)
92
94
  ```
93
95
 
@@ -99,6 +101,8 @@ If you don't want the job to run right away, you can also pass it a keyword para
99
101
  client.perform_belated(job, Time.now + 1.month)
100
102
  ```
101
103
 
104
+ Note that you probably want to memoize the client, as it always creates a 'banker thread' now if you have no connection and there is the overhead of connecting to dRuby. Maybe even use it as a global!(`$client`)
105
+
102
106
  # Settings
103
107
 
104
108
  Configuring Belated:
data/bin/belated CHANGED
@@ -5,7 +5,6 @@ $TESTING = false
5
5
  $LOAD_PATH.unshift File.expand_path("#{File.dirname(__FILE__)}/../lib")
6
6
  require 'belated'
7
7
  require 'optparse'
8
- require 'byebug'
9
8
 
10
9
  OptionParser.new { |opts|
11
10
  opts.banner = 'Usage: belated [options]'
data/lib/belated.rb CHANGED
@@ -1,15 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'belated/logging'
3
4
  require_relative 'belated/version'
4
5
  require_relative 'belated/worker'
5
- require_relative 'belated/logging'
6
+ require 'belated/client'
7
+ require 'belated/job_wrapper'
8
+ require 'belated/queue'
6
9
  require 'drb'
7
- require 'yaml'
8
- require 'singleton'
9
10
  require 'dry-configurable'
10
- require 'belated/client'
11
11
  require 'logger'
12
- require 'belated/queue'
12
+ require 'singleton'
13
+ require 'yaml'
13
14
 
14
15
  # Belated is a pure Ruby job backend.
15
16
  # It has limited functionality, as it only accepts
@@ -64,7 +65,11 @@ class Belated
64
65
  @@queue.push(:shutdown)
65
66
  end
66
67
  Thread.new { stop_workers }
67
- sleep 0.1 until @@queue.empty? || $TESTING
68
+ # Max 30 seconds to shutdown
69
+ timeout = 0
70
+ until (timeout += 0.1) >= 30 || @@queue.empty? || $TESTING
71
+ sleep 0.1
72
+ end
68
73
  exit
69
74
  end
70
75
  end
@@ -87,9 +92,9 @@ class Belated
87
92
  log 'starting future jobs thread'
88
93
  loop do
89
94
  @@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])
95
+ if job.at <= Time.now.utc
96
+ log "Deleting #{@@queue.future_jobs.delete_at(i)} from future jobs"
97
+ @@queue.push(job)
93
98
  end
94
99
  end
95
100
  sleep 0.01
@@ -151,12 +156,16 @@ class Belated
151
156
  @@queue.clear
152
157
  end
153
158
 
159
+ def self.fetch_job
160
+ @@queue.pop
161
+ end
162
+
154
163
  def job_list
155
164
  @@queue
156
165
  end
157
166
 
158
- def self.fetch_job
159
- @@queue.pop
167
+ def self.job_list
168
+ @@queue
160
169
  end
161
170
 
162
171
  class Error < StandardError; end
@@ -1,3 +1,4 @@
1
+ require 'belated/job_wrapper'
1
2
  class Belated
2
3
  # The client class is responsible for managing the connection to the
3
4
  # DRb server. If it has no connection, it adds the jobs to a bank queue.
@@ -13,16 +14,21 @@ class Belated
13
14
  # @return [void]
14
15
  def initialize
15
16
  server_uri = Belated::URI
16
- # @bank =
17
17
  DRb.start_service
18
18
  self.bank = Thread::Queue.new
19
19
  self.queue = DRbObject.new_with_uri(server_uri)
20
+ end
21
+
22
+ # Thread in charge of handling the bank queue.
23
+ # You probably want to memoize the client in order to avoid
24
+ # having many threads in the sleep state.
25
+ # @return [void]
26
+ def start_banker_thread
20
27
  self.banker_thread = Thread.new do
21
28
  loop do
22
- sleep 0.05
23
- next unless (job, at = bank.pop)
29
+ job = bank.pop
24
30
 
25
- perform(job, at: at)
31
+ perform(job)
26
32
  end
27
33
  end
28
34
  end
@@ -31,11 +37,20 @@ class Belated
31
37
  # If there is no connection, it pushes the job to the bank.
32
38
  # @param job [Object] - The the job to be pushed.
33
39
  # @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)
40
+ # @param max_retries [Integer] - Times the job should be retried if it fails.
41
+ # @return [JobWrapper] - The job wrapper for the queue.
42
+ def perform(job, at: nil, max_retries: 5)
43
+ job_wrapper = if job.is_a?(JobWrapper)
44
+ job
45
+ else
46
+ JobWrapper.new(job: job, at: at, max_retries: max_retries)
47
+ end
48
+ pp queue.push(job_wrapper)
49
+ job_wrapper
37
50
  rescue DRb::DRbConnError
38
- bank.push([job, at])
51
+ bank.push(job_wrapper)
52
+ start_banker_thread if banker_thread.nil?
53
+ banker_thread.wakeup if banker_thread.status == 'sleep'
39
54
  end
40
55
  alias perform_belated perform
41
56
  alias perform_later perform
@@ -0,0 +1,44 @@
1
+ require 'securerandom'
2
+ require_relative 'logging'
3
+
4
+ class Belated
5
+ class JobWrapper
6
+ include Logging
7
+ attr_accessor :retries, :max_retries, :id, :job, :at
8
+
9
+ def initialize(job:, max_retries: 5, at: nil)
10
+ self.retries = 0
11
+ self.max_retries = max_retries
12
+ self.id = SecureRandom.uuid
13
+ self.job = job
14
+ self.at = at
15
+ end
16
+
17
+ # rubocop:disable Lint/RescueException
18
+ def perform
19
+ if job.respond_to?(:call)
20
+ job.call
21
+ else
22
+ job.perform
23
+ end
24
+ rescue Exception => e
25
+ case e.class
26
+ when Interrupt, SignalException
27
+ raise e
28
+ else
29
+ retry_job
30
+ "Error while executing job, #{e.inspect}. Retry #{retries} of #{max_retries}"
31
+ end
32
+ end
33
+ # rubocop:enable Lint/RescueException
34
+
35
+ def retry_job
36
+ self.retries += 1
37
+ return if retries > max_retries
38
+
39
+ self.at = Time.now.utc + (retries.next**4)
40
+ log "Job #{id} failed, retrying at #{at}"
41
+ Belated.job_list.push(self)
42
+ end
43
+ end
44
+ end
data/lib/belated/queue.rb CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'belated/job'
4
4
  require 'belated/logging'
5
+ require 'belated/job_wrapper'
5
6
  class Belated
6
7
  class Queue
7
8
  include Logging
@@ -14,11 +15,11 @@ class Belated
14
15
  self.future_jobs = future_jobs
15
16
  end
16
17
 
17
- def push(job, at: nil)
18
- if at.nil?
18
+ def push(job)
19
+ if job.at.nil? || job.at <= Time.now.utc
19
20
  @queue.push(job)
20
21
  else
21
- @future_jobs << Job.new(job, at)
22
+ @future_jobs << job
22
23
  end
23
24
  end
24
25
 
@@ -45,7 +46,7 @@ class Belated
45
46
 
46
47
  jobs = YAML.load(File.binread(FILE_NAME))
47
48
  jobs.each do |job|
48
- if job.is_a?(Job)
49
+ if job.at && job.at > Time.now.utc
49
50
  future_jobs.push(job)
50
51
  else
51
52
  @queue.push(job)
@@ -73,7 +74,7 @@ class Belated
73
74
  private
74
75
 
75
76
  def proc_or_shutdown?(job)
76
- job.instance_of?(Proc) || job == :shutdown
77
+ job.job.instance_of?(Proc) || job == :shutdown
77
78
  end
78
79
  end
79
80
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Belated
4
- VERSION = '0.4.2'
4
+ VERSION = '0.5.1'
5
5
  end
@@ -18,18 +18,9 @@ class Belated
18
18
 
19
19
  break if job == :shutdown
20
20
 
21
- log call_job(job)
22
- end
23
- end
24
-
25
- def call_job(job)
26
- if job.respond_to?(:call)
27
- job.call
28
- else
21
+ log "Worker #{@number} got job: #{job.inspect}"
29
22
  job.perform
30
23
  end
31
- rescue StandardError => e
32
- e.inspect
33
24
  end
34
25
  end
35
26
  end
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.4.2
4
+ version: 0.5.1
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-05 00:00:00.000000000 Z
11
+ date: 2021-08-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: drb
@@ -81,6 +81,7 @@ files:
81
81
  - lib/belated.rb
82
82
  - lib/belated/client.rb
83
83
  - lib/belated/job.rb
84
+ - lib/belated/job_wrapper.rb
84
85
  - lib/belated/logging.rb
85
86
  - lib/belated/queue.rb
86
87
  - lib/belated/rails.rb