belated 0.6.1 → 0.6.5

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: 8c9e0e522a8de63f70d61dfeb7d02e8382c957f9e39858a5ce76fc063d4b2beb
4
- data.tar.gz: 5cd83e71ab30ce2ceb5bc357bf53963399ed6b8aae5beae6930eff98a0f79b10
3
+ metadata.gz: b76db91c6812b59cfd2ede6c6e8d5664cde8547de40b0d05345f624e9cba4729
4
+ data.tar.gz: 99e886f91004d6d467db2aa7103e59c0da5bedaa5d2359fd9e91bd69b17a424d
5
5
  SHA512:
6
- metadata.gz: 3cc2687dd4cdbbae82dc205a5c2ea851dd8b6a7f24471b424e8e1aae547a3fb01be063adbf765f92de10fcf886cf5eb10ac23b2c1144fa7c2d0c5cff59acb803
7
- data.tar.gz: 748fd28b0eed08dfb16f221e3519c11c41235cdfd7527fe0ce68c6cdd5b996614c7e61ca6b48f31b66c32f3e20d94dce681be307abbcc616f8df39f6752df3b3
6
+ metadata.gz: a1c095daefe085f5913c363609a3a5b038a0025e133ed0ab16ad131d09a26c4c4d69489b87e5a4939dd6da105b428f6ea9ab186ef38a53bb2ec3605bdbb8086d
7
+ data.tar.gz: 79532774d5169e6781828792b94ef1dd38c0cedbf68b838b95e2c2d64d01d922e0cb9ac093e76524f3c784f9b346bb0e87bda319fb50e1bb1ac8ecd9abd81fbc
data/.rubocop.yml CHANGED
@@ -37,6 +37,9 @@ Style/BlockDelimiters:
37
37
  Style/ModuleFunction:
38
38
  EnforcedStyle: extend_self
39
39
 
40
+ Metrics/AbcSize:
41
+ Max: 17
42
+
40
43
  Metrics/BlockLength:
41
44
  Exclude:
42
45
  - 'spec/**/*.rb'
data/CHANGELOG.md CHANGED
@@ -1,5 +1,30 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.6.5] - 2021-08-23
4
+
5
+ - Timezone used inside Belated is all using the server time now, so it's up to the user to take care of that(using `Time.now` instead of `Time.now.utc`)
6
+ - Possible to configure host and port.
7
+ - No need to call `.start` on the client anymore.
8
+ - Logging some error and warn messages now too, instead of it all being info
9
+
10
+ ## [0.6.4] - 2021-08-22
11
+ - Inline jobs for testing!
12
+ ```ruby
13
+ `belated/testing`
14
+ Belated::Testing.inline!
15
+ ```
16
+ - Very much inspired by how Sidekiq is doing this.
17
+ - Read more in the testing part of README.md
18
+
19
+ ## [0.6.3] - 2021-08-21
20
+
21
+ - Needed to have the hash inside the mutex when going over it; otherwise you still the get can't add key into hash during iteration error. Of course.
22
+
23
+ ## [0.6.2] - 2021-08-20
24
+
25
+ - Use a mutex for the proc_table used by the client. Fixes
26
+ `RuntimeError: can't add a new key into hash during iteration (Most recent call first)`, so starting improving thread safety with this fix.
27
+
3
28
  ## [0.6.1] - 2021-08-20
4
29
 
5
30
  When the client closes and worker has a reference to a proc, a `DRb::ConnError` is raised. Rescueing it and ignoring it.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- belated (0.6.1)
4
+ belated (0.6.5)
5
5
  drb
6
6
  dry-configurable
7
7
  sorted_set
data/README.md CHANGED
@@ -18,11 +18,7 @@ Can be used if you're on a normal instance such as EC2 or Digital Ocean drop. No
18
18
 
19
19
  TODO LIST:
20
20
 
21
- - Improve thread safety.
22
- - Use GDBM for queue storage? That way could maybe get rid of YAML dumping and make things a bit safer. Not ordered though, so maybe keep a list of the jobs as YAML and update it sometimes? Just as backup. Or RocksDB?
23
- - Make DRb port configurable.
24
- - Don't hardcode timezone to UTC.
25
- - Add some checks to the client for proper jobs.
21
+ - Use GDBM for queue storage? That way could maybe get rid of YAML dumping and make things a bit safer. Not ordered though, so maybe keep a list of the jobs as YAML and update it sometimes? Just as backup. Or RocksDB? Would need to be configurable if you don't have something installed.
26
22
  - Maybe support ActiveJob?
27
23
  - Have a web UI.
28
24
  - Have a job history
@@ -87,8 +83,6 @@ Then,
87
83
  ```ruby
88
84
  # Get the client
89
85
  client = Belated::Client.instance
90
- # Start the client, only need to do this once
91
- client.start unless client.started?
92
86
  ```
93
87
 
94
88
  and you can use the client!
@@ -138,8 +132,31 @@ Path to Rails project.
138
132
 
139
133
  $ bundle exec belated --workers=10
140
134
 
135
+ Other available settings:
136
+
137
+ $ bundle exec belated --host=1.1.1.1 --port=1234
138
+ # druby://1.1.1.1:1234
139
+ $ bundle exec belated --env
140
+ # environment
141
+
142
+
141
143
  Number of workers.
142
144
 
145
+ ## Testing
146
+
147
+ When testing, you can require `belated/testing` and then call `Belated::Testing.inline!` to make your jobs perform inline.
148
+
149
+ ```ruby
150
+ `belated/testing`
151
+ c = Belated::Client.instance
152
+ c.perform(proc { 2/ 1}) # Tries to push the job to the drb backend
153
+ # <Belated::JobWrapper:0x00005654bc2db1f0 @at=nil, @completed=false, @id="95e4dc6a-1876-4adf-ae0f-5ae902f5f024", @job=#<Proc:0x00005654bc2db330 (irb):3>, @max_retries=5, @proc_klass=true, @retries=0>
154
+ Belated::Testing.inline! # Sidekiq-inspired, now jobs run inline
155
+ c.perform(proc { 2/ 1}) # Returns 2 right away
156
+ # 2
157
+ Belated::Client.test_mode_off! # Turn off inline job processing
158
+ ```
159
+
143
160
  ## Development
144
161
 
145
162
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
data/bin/belated CHANGED
@@ -28,9 +28,17 @@ OptionParser.new { |opts|
28
28
  Belated.config.env = env
29
29
  end
30
30
 
31
- opts.on('-c=CONNECT', '--connect=CONNECT', 'Start dRuby connection, default true') do |connect|
31
+ opts.on('-c=CONNECT', '--connect=CONNECT', 'Start dRuby connection, default true, use for testing only') do |connect|
32
32
  Belated.config.connect = connect == 'true'
33
33
  end
34
+
35
+ opts.on('-h=HOST', '--host=HOST', 'dRuby host') do |host|
36
+ Belated.config.host = host
37
+ end
38
+
39
+ opts.on('-p=PORT', '--port=PORT', 'dRuby port') do |port|
40
+ Belated.config.port = port
41
+ end
34
42
  }.parse!
35
43
 
36
44
  instance = Belated.instance
data/lib/belated.rb CHANGED
@@ -22,16 +22,18 @@ class Belated
22
22
  extend Dry::Configurable
23
23
  include Logging
24
24
  include Singleton unless $TESTING
25
- URI = 'druby://localhost:8788'
26
25
  @@queue = Belated::Queue.new
27
26
 
28
27
  setting :rails, true
29
28
  setting :rails_path, '.'
30
29
  setting :workers, 1
31
30
  setting :connect, true
32
- setting :environment, 'development'
31
+ setting :environment, 'development', reader: true
33
32
  setting :logger, Logger.new($stdout), reader: true
34
33
  setting :log_level, :info, reader: true
34
+ setting :host, 'localhost', reader: true
35
+ setting :port, '8788', reader: true
36
+ URI = "druby://#{Belated.host}:#{Belated.port}"
35
37
 
36
38
  # Since it's running as a singleton, we need something to start it up.
37
39
  # Aliased for testing purposes.
@@ -55,7 +57,7 @@ class Belated
55
57
  def connect!
56
58
  DRb.start_service(URI, @@queue, verbose: true)
57
59
  rescue DRb::DRbConnError, Errno::EADDRINUSE
58
- Belated.logger.error 'Could not connect to DRb server.'
60
+ error 'Could not connect to DRb server.'
59
61
  end
60
62
 
61
63
  def trap_signals
@@ -96,12 +98,12 @@ class Belated
96
98
  sleep 5
97
99
  next
98
100
  end
99
- if job.at <= Time.now.utc
101
+ if job.at <= Time.now
100
102
  log "Deleting #{@@queue.future_jobs.delete(job)} from future jobs"
101
103
  @@queue.push(job)
102
104
  end
103
105
  rescue DRb::DRbConnError
104
- log 'DRb connection error!!!!!!'
106
+ error 'DRb connection error!!!!!!'
105
107
  log stats
106
108
  end
107
109
  end
@@ -173,8 +175,6 @@ class Belated
173
175
  def self.job_list
174
176
  @@queue
175
177
  end
176
-
177
- class Error < StandardError; end
178
178
  end
179
179
 
180
180
  require 'belated/rails' if defined?(::Rails::Engine)
@@ -1,4 +1,5 @@
1
1
  require 'belated/job_wrapper'
2
+ require 'belated/exceptions'
2
3
  require 'singleton'
3
4
  class Belated
4
5
  # The client class is responsible for managing the connection to the
@@ -16,12 +17,15 @@ class Belated
16
17
  # Connects to the queue through DRb.
17
18
  # @return [void]
18
19
  def start
20
+ return if started?
21
+
19
22
  server_uri = Belated::URI
20
23
  DRb.start_service
21
24
  self.proc_table = {}
22
25
  self.bank = Thread::Queue.new
23
26
  self.queue = DRbObject.new_with_uri(server_uri)
24
27
  @started = true
28
+ @mutex = Mutex.new
25
29
  end
26
30
  alias initialize start
27
31
 
@@ -29,32 +33,41 @@ class Belated
29
33
  @started
30
34
  end
31
35
 
36
+ # Makes it possible to reset the client
37
+ def turn_off
38
+ @started = false
39
+ banker_thread&.kill
40
+ end
41
+
32
42
  # Thread in charge of handling the bank queue.
33
43
  # You probably want to memoize the client in order to avoid
34
44
  # having many threads in the sleep state.
35
45
  # @return [void]
36
46
  def start_banker_thread
37
- self.banker_thread = Thread.new do
47
+ Thread.new do
38
48
  loop do
39
49
  delete_from_table
40
- if bank.empty?
41
- sleep 10
42
- next
43
- end
44
- begin
45
- queue.push(wrapper = bank.pop)
46
- rescue DRb::DRbConnError
47
- bank.push(wrapper)
50
+ sleep 10 and next if bank.empty?
51
+
52
+ until bank.empty?
53
+ begin
54
+ queue.push(wrapper = bank.pop)
55
+ rescue DRb::DRbConnError
56
+ bank.push(wrapper)
57
+ sleep 5
58
+ end
48
59
  end
49
60
  end
50
61
  end
51
62
  end
52
63
 
53
64
  def delete_from_table
54
- return if proc_table.empty?
65
+ return if proc_table.length < 25
55
66
 
56
- proc_table.select { |_k, v| v.completed }.each do |key, _value|
57
- proc_table.delete(key)
67
+ @mutex.synchronize do
68
+ proc_table.select { |_k, v| v.completed }.each do |key, _value|
69
+ proc_table.delete(key)
70
+ end
58
71
  end
59
72
  end
60
73
 
@@ -65,16 +78,14 @@ class Belated
65
78
  # @param max_retries [Integer] - Times the job should be retried if it fails.
66
79
  # @return [JobWrapper] - The job wrapper for the queue.
67
80
  def perform(job, at: nil, max_retries: 5)
68
- log 'Passing a proc and at time is deprecated and will be removed in 0.6' if job.instance_of?(Proc) && !at.nil?
69
-
70
- job_wrapper = if job.is_a?(JobWrapper)
71
- job
72
- else
73
- JobWrapper.new(job: job, at: at, max_retries: max_retries)
74
- end
81
+ start unless started?
82
+ check_if_proper_job!(job)
83
+ job_wrapper = wrap_job(job, at: at, max_retries: max_retries)
75
84
  bank.push(job_wrapper)
76
- proc_table[job_wrapper.object_id] = job_wrapper if job_wrapper.proc_klass
77
- start_banker_thread if banker_thread.nil?
85
+ @mutex.synchronize do
86
+ proc_table[job_wrapper.object_id] = job_wrapper if job_wrapper.proc_klass
87
+ end
88
+ self.banker_thread = start_banker_thread if banker_thread.nil?
78
89
  job_wrapper
79
90
  end
80
91
  alias perform_belated perform
@@ -82,6 +93,18 @@ class Belated
82
93
 
83
94
  private
84
95
 
96
+ def check_if_proper_job!(job)
97
+ return if job.respond_to?(:call) || job.respond_to?(:perform)
98
+
99
+ raise JobError, 'job does not implement .call nor .perform!'
100
+ end
101
+
102
+ def wrap_job(job, at:, max_retries:)
103
+ return job if job.is_a?(JobWrapper)
104
+
105
+ JobWrapper.new(job: job, at: at, max_retries: max_retries)
106
+ end
107
+
85
108
  def drb_connected?
86
109
  queue.connected?
87
110
  rescue StandardError
@@ -0,0 +1,3 @@
1
+ class Belated
2
+ class JobError < StandardError; end
3
+ end
@@ -53,7 +53,7 @@ class Belated
53
53
  self.retries += 1
54
54
  return if retries > max_retries
55
55
 
56
- self.at = Time.now.utc + (retries.next**4)
56
+ self.at = Time.now + (retries.next**4)
57
57
  log "Job #{id} failed, retrying at #{at}"
58
58
  Belated.job_list.push(self)
59
59
  end
@@ -12,6 +12,14 @@ class Belated
12
12
  logger.__send__(Belated.log_level, message)
13
13
  end
14
14
 
15
+ def warn(message)
16
+ logger.warn(message)
17
+ end
18
+
19
+ def error(message)
20
+ logger.error(message)
21
+ end
22
+
15
23
  def logger=(logger)
16
24
  @logger = logger
17
25
  end
@@ -0,0 +1,37 @@
1
+ class Belated
2
+ # Testing helpers
3
+ # Enable or disable testing
4
+ class Testing
5
+ @@testing = false
6
+
7
+ def self.inline?
8
+ @@testing == true
9
+ end
10
+
11
+ def self.inline!
12
+ @@testing = true
13
+ end
14
+
15
+ def self.test_mode_off!
16
+ @@testing = false
17
+ end
18
+ end
19
+ end
20
+
21
+ class Belated
22
+ # A client that can perform jobs inline
23
+ class Client
24
+ alias old_perform perform
25
+ def perform(job, at: nil, max_retries: 5)
26
+ if Belated::Testing.inline?
27
+ if job.respond_to?(:call)
28
+ job.call
29
+ else
30
+ job.perform
31
+ end
32
+ else
33
+ old_perform(job, at: at, max_retries: max_retries)
34
+ end
35
+ end
36
+ end
37
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Belated
4
- VERSION = '0.6.1'
4
+ VERSION = '0.6.5'
5
5
  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.6.1
4
+ version: 0.6.5
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-20 00:00:00.000000000 Z
11
+ date: 2021-08-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: drb
@@ -94,11 +94,13 @@ files:
94
94
  - bin/setup
95
95
  - lib/belated.rb
96
96
  - lib/belated/client.rb
97
+ - lib/belated/exceptions.rb
97
98
  - lib/belated/job.rb
98
99
  - lib/belated/job_wrapper.rb
99
100
  - lib/belated/logging.rb
100
101
  - lib/belated/queue.rb
101
102
  - lib/belated/rails.rb
103
+ - lib/belated/testing.rb
102
104
  - lib/belated/version.rb
103
105
  - lib/belated/worker.rb
104
106
  homepage: https://github.com/sampokuokkanen/belated