belated 0.5.7 → 0.6.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: 0c1e102693d57c373c84b2f6a4fb5624770334b0fb15bc3cffa05541cf7bbe29
4
- data.tar.gz: bb18635e1ce9a04d83618cbd41fbdb91912c9b2f5c2d5f9dc6715154ef9254dc
3
+ metadata.gz: ae5f827cc80ba5fba59161a50ae5797ef774be28feeb478ea641dda6bca79b3a
4
+ data.tar.gz: 01a61d9a44ae2b76ad27827e58dd8ba81e2040753925dc7172458c04495f010a
5
5
  SHA512:
6
- metadata.gz: 3465242fd5fe5a55ccd288cbc7d034ad6538e4bd75d8eeaa60c65b73e2f2a2c545ce63885172b85a8ec1e5c5a320dd51eaaf2038542bcc0f95da7a381973a217
7
- data.tar.gz: f889516ac99765cfb5aa744589c614e7d6c59b7488223214e1f8a8eb6da879fef0a0b66556609dd625a04dcba454300f1915b1e45ff01a68e2bc212ea4ed4857
6
+ metadata.gz: 73a7fe76eacd1334186ef70628a78f193a20a72373d4faaa6bc9b8688ff0d9786156a69b44c3dfb9405837f0f5d980d1317277ad87c33d239d5d8c13b73207a4
7
+ data.tar.gz: bcc797637c63a712e81b77189ecd93c325a67fb67f91b392a86d8231b685394341447539c12012280fee392a52a71a6fb20713fdb0ae0be32201c2b2670f5a33
data/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.6.3] - 2021-08-21
4
+
5
+ - 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.
6
+
7
+ ## [0.6.2] - 2021-08-20
8
+
9
+ - Use a mutex for the proc_table used by the client. Fixes
10
+ `RuntimeError: can't add a new key into hash during iteration (Most recent call first)`, so starting improving thread safety with this fix.
11
+
12
+ ## [0.6.1] - 2021-08-20
13
+
14
+ When the client closes and worker has a reference to a proc, a `DRb::ConnError` is raised. Rescueing it and ignoring it.
15
+ ## [0.6.0] - 2021-08-19
16
+
17
+ - Only need to keep references on the client side for procs. Not needed for classes, as they are pass-by-value. However, you can only pass procs by reference, so need to keep track of them. They're removed from the client side when they're completed though.
18
+ - The client is now a singleton. This is because it had some overhead when pushing the jobs to dRuby, so I took the approach of also doing that in a background thread. You however do not want more than one client to be running at the same time, so making it a singleton is the best option. Call the `.instance` method to get the singleton and then `.start` to get it started.
19
+
3
20
  ## [0.5.7] - 2021-08-18
4
21
 
5
22
  - Got errors under heavy load and restarting. Hopefully fixed by rescuing the DRb connection error.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- belated (0.5.7)
4
+ belated (0.6.3)
5
5
  drb
6
6
  dry-configurable
7
7
  sorted_set
data/README.md CHANGED
@@ -14,15 +14,15 @@ Note that currently the timezone is hardcoded to UTC.
14
14
 
15
15
  Can be used with or without Rails.
16
16
 
17
+ Can be used if you're on a normal instance such as EC2 or Digital Ocean drop. Not if you're on a Heroku or Docker, or anything with ephemeral storage.
18
+
17
19
  TODO LIST:
18
20
 
19
- - 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.
20
- - Rescue `DRb::DRbRemoteError` when shutting down, might not need to if using GDBM?
21
- - Don't use class instance variables.
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?
22
23
  - Make DRb port configurable.
23
- - Don't hardcode timezone.
24
+ - Don't hardcode timezone to UTC.
24
25
  - Add some checks to the client for proper jobs.
25
- - Have multiple queues?
26
26
  - Maybe support ActiveJob?
27
27
  - Have a web UI.
28
28
  - Have a job history
@@ -85,10 +85,17 @@ First, start up Belated.
85
85
  Then,
86
86
 
87
87
  ```ruby
88
- client = Belated::Client.new
88
+ # Get the client
89
+ client = Belated::Client.instance
90
+ # Start the client, only need to do this once
91
+ client.start unless client.started?
89
92
  ```
90
93
 
91
94
  and you can use the client!
95
+ Note that the client is a singleton.
96
+ This means that you can only have one client running at a time,
97
+ but it also means you only have one connection to dRuby, and that the number of threads in charge of queuing the jobs is only one.
98
+
92
99
  Call
93
100
 
94
101
  ```ruby
@@ -104,9 +111,7 @@ If you don't want the job to run right away, you can also pass it a keyword para
104
111
  client.perform_belated(job, Time.now + 1.month)
105
112
  ```
106
113
 
107
- Note that you probably want to memoize the client, as it always creates a 'banker thread' and there is the overhead of connecting to dRuby. Maybe even use it as a global!(`$client`).
108
-
109
- The client also holds references to the jobs that have been pushed so that they are not collected by GC.
114
+ The client also holds references to the jobs that are instances of `Proc` that have been pushed so that they are not collected by GC. This is because procs are passed by reference, and the client needs to keep them alive. They are removed from the list when the job is done.
110
115
 
111
116
  # Settings
112
117
 
data/lib/belated.rb CHANGED
@@ -113,7 +113,8 @@ class Belated
113
113
 
114
114
  def stop_workers
115
115
  @worker_list&.each do |worker|
116
- sleep 0.1 if worker.alive?
116
+ i = 0
117
+ sleep 0.1 while worker.alive? || (i + 0.1) < 10
117
118
  Thread.kill(worker)
118
119
  end
119
120
  @@queue.save_jobs
@@ -1,4 +1,5 @@
1
1
  require 'belated/job_wrapper'
2
+ require 'singleton'
2
3
  class Belated
3
4
  # The client class is responsible for managing the connection to the
4
5
  # DRb server. If it has no connection, it adds the jobs to a bank queue.
@@ -7,18 +8,26 @@ class Belated
7
8
  # client = Belated::Client.new
8
9
  # client.enqueue(JubJub.new, at: Time.now + 5.seconds)
9
10
  class Client
10
- attr_accessor :queue, :bank, :banker_thread, :table
11
+ include Singleton unless $TESTING
12
+
13
+ attr_accessor :queue, :bank, :banker_thread, :proc_table
11
14
 
12
15
  # Starts up the client.
13
16
  # Connects to the queue through DRb.
14
17
  # @return [void]
15
- def initialize
18
+ def start
16
19
  server_uri = Belated::URI
17
20
  DRb.start_service
18
- self.table = {}
21
+ self.proc_table = {}
19
22
  self.bank = Thread::Queue.new
20
23
  self.queue = DRbObject.new_with_uri(server_uri)
21
- start_banker_thread
24
+ @started = true
25
+ @mutex = Mutex.new
26
+ end
27
+ alias initialize start
28
+
29
+ def started?
30
+ @started
22
31
  end
23
32
 
24
33
  # Thread in charge of handling the bank queue.
@@ -28,24 +37,30 @@ class Belated
28
37
  def start_banker_thread
29
38
  self.banker_thread = Thread.new do
30
39
  loop do
31
- sleep 0.01
32
-
33
- delete_from_table
34
-
40
+ delete_from_table if proc_table.length > 20
35
41
  if bank.empty?
36
42
  sleep 10
37
- else
38
- perform(bank.pop)
43
+ next
44
+ end
45
+ until bank.empty?
46
+ begin
47
+ queue.push(wrapper = bank.pop)
48
+ rescue DRb::DRbConnError
49
+ bank.push(wrapper)
50
+ sleep 5
51
+ end
39
52
  end
40
53
  end
41
54
  end
42
55
  end
43
56
 
44
57
  def delete_from_table
45
- return if table.empty?
58
+ return if proc_table.empty?
46
59
 
47
- table.select { |_k, v| v.completed }.each do |key, _value|
48
- table.delete(key)
60
+ @mutex.synchronize do
61
+ proc_table.select { |_k, v| v.completed }.each do |key, _value|
62
+ proc_table.delete(key)
63
+ end
49
64
  end
50
65
  end
51
66
 
@@ -56,23 +71,25 @@ class Belated
56
71
  # @param max_retries [Integer] - Times the job should be retried if it fails.
57
72
  # @return [JobWrapper] - The job wrapper for the queue.
58
73
  def perform(job, at: nil, max_retries: 5)
59
- log 'Passing a proc and at time is deprecated and will be removed in 0.6' if job.instance_of?(Proc) && !at.nil?
60
-
61
- job_wrapper = if job.is_a?(JobWrapper)
62
- job
63
- else
64
- JobWrapper.new(job: job, at: at, max_retries: max_retries)
65
- end
66
- queue.push(job_wrapper)
67
- table[job_wrapper.object_id] = job_wrapper
68
- rescue DRb::DRbConnError
74
+ job_wrapper = wrap_job(job, at: at, max_retries: max_retries)
69
75
  bank.push(job_wrapper)
76
+ @mutex.synchronize do
77
+ proc_table[job_wrapper.object_id] = job_wrapper if job_wrapper.proc_klass
78
+ end
79
+ start_banker_thread if banker_thread.nil?
80
+ job_wrapper
70
81
  end
71
82
  alias perform_belated perform
72
83
  alias perform_later perform
73
84
 
74
85
  private
75
86
 
87
+ def wrap_job(job, at:, max_retries:)
88
+ return job if job.is_a?(JobWrapper)
89
+
90
+ JobWrapper.new(job: job, at: at, max_retries: max_retries)
91
+ end
92
+
76
93
  def drb_connected?
77
94
  queue.connected?
78
95
  rescue StandardError
@@ -13,7 +13,7 @@ class Belated
13
13
  class JobWrapper
14
14
  include Comparable
15
15
  include Logging
16
- attr_accessor :retries, :max_retries, :id, :job, :at, :completed
16
+ attr_accessor :retries, :max_retries, :id, :job, :at, :completed, :proc_klass
17
17
 
18
18
  def initialize(job:, max_retries: 5, at: nil)
19
19
  self.retries = 0
@@ -22,6 +22,7 @@ class Belated
22
22
  self.job = job
23
23
  self.at = at
24
24
  self.completed = false
25
+ self.proc_klass = job.instance_of?(Proc)
25
26
  end
26
27
 
27
28
  def <=>(other)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Belated
4
- VERSION = '0.5.7'
4
+ VERSION = '0.6.3'
5
5
  end
@@ -20,7 +20,7 @@ class Belated
20
20
 
21
21
  log "Worker #{@number} got job: #{job.inspect}"
22
22
  log job.perform
23
- rescue Errno::ECONNREFUSED, RangeError => e
23
+ rescue DRb::DRbConnError, Errno::ECONNREFUSED, RangeError => e
24
24
  log e
25
25
  end
26
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.5.7
4
+ version: 0.6.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-18 00:00:00.000000000 Z
11
+ date: 2021-08-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: drb