skiplock 1.0.5 → 1.0.6

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: 572dcd7b3a55c259ed181c8d893b38b0d27e682d7647347fd020e1f4cc3b7cd8
4
- data.tar.gz: 227e1f528be6ee5c86eb79173f0922fbcf5e436caab3158eafa5c40ed7e658c3
3
+ metadata.gz: 8155870e392d0172d914c5e10d981560eb05af653603d331ce46cdd3419b2efa
4
+ data.tar.gz: 940699344273b928820c22307d6cea654d4bdad6debfa80c67fbb28dc8490ea9
5
5
  SHA512:
6
- metadata.gz: 3ddaba15001823730cf79cd644398ec14db0997d16413e694b4578ac599099e3ff39f754b18b103f6ed0005803e718b050ac2b9b45bb3b095977fef1f1fe46ef
7
- data.tar.gz: 5ff4be776646109fad4ffe1476ee982f8fb47ed111e13cb836bb7c07988c9c4a8753ae43cfbd05f8694a2759264a59daedf094cf645325bada99dfa930d5d333
6
+ metadata.gz: aa423c44556fb2a31cc2ec6cf9f25d6c9712b460920a0d39d0108ade11fa7738fe24e8f1857236cd0a67042ac6ced5cb0b1c34774e8946b72fe971e3202426f7
7
+ data.tar.gz: ac5456a0613ad3d94e574de100ee26dd018bee39c2a74c16237c798b873a45798519c33b08cb05a26ab18415e15b87252699e2b4bf1978fffbc521008fccdffe
data/README.md CHANGED
@@ -54,7 +54,11 @@ The library is quite small compared to other PostgreSQL job queues (eg. *delay_j
54
54
  min_threads: 1
55
55
  max_threads: 5
56
56
  max_retries: 20
57
+ notification: auto
57
58
  purge_completion: true
59
+ queues:
60
+ default: 200
61
+ mailers: 100
58
62
  workers: 0
59
63
  ```
60
64
  Available configuration options are:
@@ -62,8 +66,10 @@ The library is quite small compared to other PostgreSQL job queues (eg. *delay_j
62
66
  - **min_threads** (*integer*): sets minimum number of threads staying idle
63
67
  - **max_threads** (*integer*): sets the maximum number of threads allowed to run jobs
64
68
  - **max_retries** (*integer*): sets the maximum attempt a job will be retrying before it is marked expired (see Retry System for more details)
69
+ - **notification** (*enumeration*): sets the library to be used for notifying errors and exceptions (`auto, airbrake, bugsnag, exception_notification, false`)
65
70
  - **purge_completion** (*boolean*): when set to **true** will delete jobs after they were completed successfully; if set to **false** then the completed jobs should be purged periodically to maximize performance (eg. clean up old jobs after 3 months)
66
- - **workers** (*integer*) sets the maximum number of processes when running in standalone mode using the `skiplock` executable; setting this to 0 will enable **async mode**
71
+ - **queues** (*hash*): defines the set of queues with priorites; lower priority takes precedence
72
+ - **workers** (*integer*) sets the maximum number of processes when running in standalone mode using the `skiplock` executable; setting this to **0** will enable **async mode**
67
73
 
68
74
  #### Async mode
69
75
  When **workers** is set to **0** then the jobs will be performed in the web server process using separate threads. If using multi-worker cluster web server like Puma, then it should be configured as below:
@@ -93,16 +99,22 @@ The library is quite small compared to other PostgreSQL job queues (eg. *delay_j
93
99
  ```
94
100
  - Skiplock supports all ActiveJob features:
95
101
  ```ruby
96
- MyJob.set(wait: 5.minutes, priority: 10).perform_later(1,2,3)
102
+ MyJob.set(queue: 'my_queue', wait: 5.minutes, priority: 10).perform_later(1,2,3)
97
103
  ```
98
104
  - Outside of Rails application, queue the jobs by inserting the job records directly to the database table eg:
99
105
  ```sql
100
106
  INSERT INTO skiplock.jobs(job_class) VALUES ('MyJob');
101
107
  ```
102
- - Or with scheduling, priority and arguments:
108
+ - Or with scheduling, priority, queue and arguments:
103
109
  ```sql
104
- INSERT INTO skiplock.jobs(job_class,priority,scheduled_at,data) VALUES ('MyJob',10,NOW()+INTERVAL '5 min','{"arguments":[1,2,3]}');
110
+ INSERT INTO skiplock.jobs(job_class,queue_name,priority,scheduled_at,data) VALUES ('MyJob','my_queue',10,NOW()+INTERVAL '5 min','{"arguments":[1,2,3]}');
105
111
  ```
112
+ ## Queues priority vs Job priority
113
+ *Why do queues have priorities when jobs already have priorities?*
114
+ - Jobs are only prioritized with other jobs from the same queue
115
+ - Queues, on the other hand, are prioritized with other queues
116
+ - Rails has built-in queues that dispatches jobs without priorities (eg. Mail Delivery will queue as **mailers** with no priority)
117
+
106
118
  ## Cron system
107
119
  `Skiplock` provides the capability to setup cron jobs for running tasks periodically. It fully supports the cron syntax to specify the frequency of the jobs. To setup a cron job, simply assign a valid cron schedule to the constant `CRON` for the Job Class.
108
120
  - setup `MyJob` to run as cron job every hour at 30 minutes past
@@ -144,7 +156,16 @@ Once the retry attempt limit configured in ActiveJob has been reached, the contr
144
156
  If the rescue blocks are not defined, then the built-in retry system of `skiplock` will kick in automatically. The retrying schedule is using an exponential formula (5 + 2**attempt). The `skiplock` configuration `max_retries` determines the the limit of attempts before the failing job is marked as expired. The maximum retry limit can be set as high as 20; this allows up to 12 days of retrying before the job is marked as expired.
145
157
 
146
158
  ## Notification system
147
- ...
159
+ `Skiplock` can use existing exception notification library to notify errors and exceptions. It supports `airbrake`, `bugsnag`, and `exception_notification`. A customized function can also be called whenever an exception occurs; this can be configured in an initializer as below:
160
+ ```ruby
161
+ # config/initializers/skiplock.rb
162
+ Skiplock.on_error = -> (ex, previous = nil) do
163
+ if ex.backtrace != previous.try(:backtrace)
164
+ # sends custom email on new exceptions only
165
+ # the same repeated exceptions will only be sent once to avoid SPAM
166
+ end
167
+ end
168
+ ```
148
169
 
149
170
  ## Contributing
150
171
 
data/lib/skiplock.rb CHANGED
@@ -4,22 +4,23 @@ require 'active_record'
4
4
  require 'skiplock/cron'
5
5
  require 'skiplock/dispatcher'
6
6
  require 'skiplock/manager'
7
- require 'skiplock/notification'
8
7
  require 'skiplock/job'
9
8
  require 'skiplock/worker'
10
9
  require 'skiplock/version'
11
10
 
12
11
  module Skiplock
13
12
  Settings = {
14
- 'logging' => :timestamp,
13
+ 'logging' => 'timestamp',
15
14
  'min_threads' => 1,
16
15
  'max_threads' => 5,
17
16
  'max_retries' => 20,
17
+ 'notification' => 'auto',
18
18
  'purge_completion' => true,
19
19
  'queues' => {
20
- 'default' => 100,
21
- 'mailers' => 999
20
+ 'default' => 200,
21
+ 'mailers' => 100
22
22
  },
23
23
  'workers' => 0
24
24
  }
25
+ mattr_accessor :on_error
25
26
  end
@@ -1,7 +1,7 @@
1
1
  module Skiplock
2
2
  class Dispatcher
3
3
  def initialize(master: true, worker_num: nil, worker_pids: [])
4
- @queues_order_query = Skiplock::Settings['queues'].map { |q,v| "WHEN queue_name = '#{q}' THEN #{v}" }.join(' ')
4
+ @queues_order_query = Settings['queues'].map { |q,v| "WHEN queue_name = '#{q}' THEN #{v}" }.join(' ') if Settings['queues'].is_a?(Hash) && Settings['queues'].count > 0
5
5
  @executor = Concurrent::ThreadPoolExecutor.new(min_threads: Settings['min_threads'], max_threads: Settings['max_threads'], max_queue: Settings['max_threads'], idletime: 60, auto_terminate: true, fallback_policy: :discard)
6
6
  @master = master
7
7
  if @master
@@ -17,19 +17,28 @@ module Skiplock
17
17
  Thread.new do
18
18
  Rails.application.reloader.wrap do
19
19
  sleep(0.1) while @running && !Rails.application.initialized?
20
- Process.setproctitle("skiplock-#{@master ? 'master' : 'worker[' + @worker_num.to_s + ']'}") if Settings['workers'] > 0
20
+ Process.setproctitle("skiplock-#{@master ? 'master[0]' : 'worker[' + @worker_num.to_s + ']'}") if Settings['workers'] > 0
21
21
  ActiveRecord::Base.connection_pool.with_connection do |connection|
22
22
  connection.exec_query('LISTEN "skiplock::jobs"')
23
- hostname = `hostname`.strip
23
+ hostname = `hostname -f`.strip
24
24
  @worker = Worker.create!(pid: Process.pid, ppid: (@master ? nil : Process.ppid), capacity: Settings['max_threads'], hostname: hostname)
25
25
  if @master
26
- # get worker ids that were not shutdown properly on the host
27
- dead_worker_ids = Worker.where(hostname: hostname).where.not(pid: @worker_pids).ids
28
- if dead_worker_ids.count > 0
29
- # reset orphaned jobs of the dead worker ids for retry
30
- Job.where(running: true, worker_id: dead_worker_ids).update_all(running: false, worker_id: nil)
31
- Worker.where(id: dead_worker_ids).delete_all
26
+ if File.exists?('tmp/cache/skiplock')
27
+ # get performed jobs that could not sync with database
28
+ job_ids = File.read('tmp/cache/skiplock').split("\n")
29
+ if Settings['purge_completion']
30
+ Job.where(id: job_ids, running: true).delete_all
31
+ else
32
+ Job.where(id: job_ids, running: true).update_all(running: false, finished_at: File.mtime('tmp/cache/skiplock'))
33
+ end
34
+ File.delete('tmp/cache/skiplock')
32
35
  end
36
+ # get current worker ids
37
+ worker_ids = Worker.where(hostname: hostname, pid: @worker_pids).ids
38
+ # reset orphaned jobs of the dead worker ids for retry
39
+ Job.where(running: true).where.not(worker_id: worker_ids).update_all(running: false, worker_id: nil)
40
+ # remove workers that were not shutdown properly on the host
41
+ Worker.where(hostname: hostname).where.not(pid: @worker_pids).delete_all
33
42
  # reset retries schedules on startup
34
43
  Job.where('scheduled_at > NOW() AND executions IS NOT NULL AND expired_at IS NULL AND finished_at IS NULL').update_all(scheduled_at: nil, updated_at: Time.now)
35
44
  Cron.setup
@@ -46,6 +55,17 @@ module Skiplock
46
55
  end
47
56
  error = false
48
57
  end
58
+ if Job::Errors.keys.count > 0
59
+ completed_ids = Job::Errors.keys.map { |k| k if Job::Errors[k] }.compact
60
+ if Settings['purge_completion'] && completed_ids.count > 0
61
+ Job.where(id: completed_ids, running: true).delete_all
62
+ elsif completed_ids.count > 0
63
+ Job.where(id: completed_ids, running: true).update_all(running: false, finished_at: Time.now)
64
+ end
65
+ orphaned_ids = Job::Errors.keys.map { |k| k unless Job::Errors[k] }.compact
66
+ Job.where(id: orphaned_ids, running: true).update_all(running: false, worker_id: nil, scheduled_at: (Time.now + 10)) if orphaned_ids.count > 0
67
+ Job::Errors.clear
68
+ end
49
69
  if Time.now.to_f >= @next_schedule_at && @executor.remaining_capacity > 0
50
70
  @executor.post { do_work }
51
71
  end
@@ -59,7 +79,7 @@ module Skiplock
59
79
  end
60
80
  notifications.each do |n|
61
81
  op, id, worker_id, queue_name, running, expired_at, finished_at, scheduled_at = n.split(',')
62
- next if op == 'DELETE' || running == 'true' || expired_at || finished_at
82
+ next if op == 'DELETE' || running == 'true' || expired_at.to_s.length > 0 || finished_at.to_s.length > 0
63
83
  if scheduled_at.to_f <= Time.now.to_f
64
84
  @next_schedule_at = Time.now.to_f
65
85
  elsif scheduled_at.to_f < @next_schedule_at
@@ -68,15 +88,18 @@ module Skiplock
68
88
  end
69
89
  end
70
90
  rescue Exception => ex
71
- # TODO: Report exception
91
+ STDERR.puts ex.message
92
+ STDERR.puts ex.backtrace
93
+ Skiplock.on_error.call(ex, @last_exception) if Skiplock.on_error.is_a?(Proc)
72
94
  error = true
73
95
  t = Time.now
74
96
  while @running
75
97
  sleep(0.5)
76
98
  break if Time.now - t > 5
77
99
  end
100
+ @last_exception = ex
78
101
  end
79
- sleep(0.1)
102
+ sleep(0.2)
80
103
  end
81
104
  connection.exec_query('UNLISTEN *')
82
105
  end
@@ -100,9 +123,11 @@ module Skiplock
100
123
  @next_schedule_at = result if result.is_a?(Float)
101
124
  break
102
125
  end
103
- rescue Exception => e
104
- puts "Exception => #{e.message} #{e.backtrace}"
105
- # TODO: Report exception
126
+ rescue Exception => ex
127
+ STDERR.puts ex.message
128
+ STDERR.puts ex.backtrace
129
+ Skiplock.on_error.call(ex, @last_exception) if Skiplock.on_error.is_a?(Proc)
130
+ @last_exception = ex
106
131
  end
107
132
  end
108
133
  end
data/lib/skiplock/job.rb CHANGED
@@ -1,11 +1,13 @@
1
1
  module Skiplock
2
2
  class Job < ActiveRecord::Base
3
3
  self.table_name = 'skiplock.jobs'
4
+ Errors = Concurrent::Map.new
4
5
 
5
6
  # Return: Skiplock::Job if it was executed; otherwise returns the next Job's schedule time in FLOAT
6
7
  def self.dispatch(queues_order_query: nil, worker_id: nil)
8
+ performed = false
7
9
  self.connection.exec_query('BEGIN')
8
- job = self.find_by_sql("SELECT id, scheduled_at FROM #{self.table_name} WHERE running = FALSE AND expired_at IS NULL AND finished_at IS NULL ORDER BY scheduled_at ASC NULLS FIRST,#{queues_order_query ? 'CASE ' + queues_order_query + ' ELSE NULL END ASC NULLS LAST,' : ''} priority ASC NULLS LAST, created_at ASC FOR UPDATE SKIP LOCKED LIMIT 1").first
10
+ job = self.find_by_sql("SELECT id, scheduled_at FROM #{self.table_name} WHERE running = FALSE AND expired_at IS NULL AND finished_at IS NULL ORDER BY scheduled_at ASC NULLS FIRST,#{queues_order_query ? ' CASE ' + queues_order_query + ' ELSE NULL END ASC NULLS LAST,' : ''} priority ASC NULLS LAST, created_at ASC FOR UPDATE SKIP LOCKED LIMIT 1").first
9
11
  if job.nil? || job.scheduled_at.to_f > Time.now.to_f
10
12
  self.connection.exec_query('END')
11
13
  return (job ? job.scheduled_at.to_f : Float::INFINITY)
@@ -21,9 +23,9 @@ module Skiplock
21
23
  ActiveJob::Base.execute(job_data)
22
24
  rescue Exception => ex
23
25
  end
26
+ performed = true
24
27
  job.running = false
25
28
  if ex
26
- # TODO: report exception
27
29
  job.exception_executions["[#{ex.class.name}]"] = (job.exception_executions["[#{ex.class.name}]"] || 0) + 1 unless job.exception_executions.key?('activejob_retry')
28
30
  if job.executions >= Settings['max_retries'] || job.exception_executions.key?('activejob_retry')
29
31
  job.expired_at = Time.now
@@ -32,11 +34,12 @@ module Skiplock
32
34
  job.scheduled_at = Time.now + (5 * 2**job.executions)
33
35
  job.save!
34
36
  end
37
+ Skiplock.on_error.call(ex) if Skiplock.on_error.is_a?(Proc) && (job.executions % 3 == 1)
35
38
  elsif job.exception_executions.key?('activejob_retry')
36
39
  job.save!
37
- elsif job['cron']
40
+ elsif job.cron
38
41
  job.data['last_cron_run'] = Time.now.utc.to_s
39
- next_cron_at = Cron.next_schedule_at(job['cron'])
42
+ next_cron_at = Cron.next_schedule_at(job.cron)
40
43
  if next_cron_at
41
44
  job.executions = 1
42
45
  job.exception_executions = nil
@@ -53,6 +56,14 @@ module Skiplock
53
56
  job.save!
54
57
  end
55
58
  job
59
+ rescue Exception => ex
60
+ if performed
61
+ Errors[job.id] = true
62
+ File.write('tmp/cache/skiplock', job.id + "\n", mode: 'a')
63
+ else
64
+ Errors[job.id] = false
65
+ end
66
+ raise ex
56
67
  ensure
57
68
  Thread.current[:skiplock_dispatch_job] = nil
58
69
  end
@@ -1,7 +1,21 @@
1
1
  module Skiplock
2
2
  class Manager
3
- def self.start(standalone: false)
4
- load_settings
3
+ def self.start(standalone: false, workers: nil, max_retries: nil, max_threads: nil, min_threads: nil, logging: nil)
4
+ unless Settings.frozen?
5
+ load_settings
6
+ Settings['logging'] = logging if logging
7
+ Settings['max_retries'] = max_retries if max_retries
8
+ Settings['max_threads'] = max_threads if max_threads
9
+ Settings['min_threads'] = min_threads if min_threads
10
+ Settings['workers'] = workers if workers
11
+ Settings['max_retries'] = 20 if Settings['max_retries'] > 20
12
+ Settings['max_retries'] = 0 if Settings['max_retries'] < 0
13
+ Settings['max_threads'] = 1 if Settings['max_threads'] < 1
14
+ Settings['max_threads'] = 20 if Settings['max_threads'] > 20
15
+ Settings['min_threads'] = 0 if Settings['min_threads'] < 0
16
+ Settings['workers'] = 0 if Settings['workers'] < 0
17
+ Settings.freeze
18
+ end
5
19
  return unless standalone || (caller.any?{|l| l =~ %r{/rack/}} && (Settings['workers'] == 0 || Rails.env.development?))
6
20
  if standalone
7
21
  self.standalone
@@ -22,37 +36,37 @@ module Skiplock
22
36
  private
23
37
 
24
38
  def self.load_settings
25
- return if Settings.frozen?
26
39
  config = YAML.load_file('config/skiplock.yml') rescue {}
27
40
  Settings.merge!(config)
28
- Settings['max_retries'] = 20 if Settings['max_retries'] > 20
29
- Settings['max_retries'] = 0 if Settings['max_retries'] < 0
30
- Settings['max_threads'] = 1 if Settings['max_threads'] < 1
31
- Settings['max_threads'] = 20 if Settings['max_threads'] > 20
32
- Settings['min_threads'] = 0 if Settings['min_threads'] < 0
33
- Settings['workers'] = 0 if Settings['workers'] < 0
34
- Settings['queues'].values.each { |v| raise 'Queue value must be an integer' unless v.is_a?(Integer) }
35
- Settings.freeze
41
+ Settings['queues'].values.each { |v| raise 'Queue value must be an integer' unless v.is_a?(Integer) } if Settings['queues'].is_a?(Hash)
42
+ case Settings['notification'].to_s.downcase
43
+ when 'auto'
44
+ if defined?(Airbrake)
45
+ Skiplock.on_error = -> (ex, previous = nil) { Airbrake.notify_sync(ex) unless ex.backtrace == previous.try(:backtrace) }
46
+ elsif defined?(Bugsnag)
47
+ Skiplock.on_error = -> (ex, previous = nil) { Bugsnag.notify(ex) unless ex.backtrace == previous.try(:backtrace) }
48
+ elsif defined?(ExceptionNotifier)
49
+ Skiplock.on_error = -> (ex, previous = nil) { ExceptionNotifier.notify_exception(ex) unless ex.backtrace == previous.try(:backtrace) }
50
+ else
51
+ puts "Unable to detect any known exception notification gem. Please define custom 'on_error' function and disable notification in 'config/skiplock.yml'"
52
+ exit
53
+ end
54
+ when 'airbrake'
55
+ raise 'airbrake gem not found' unless defined?(Airbrake)
56
+ Skiplock.on_error = -> (ex, previous = nil) { Airbrake.notify_sync(ex) unless ex.backtrace == previous.try(:backtrace) }
57
+ when 'bugsnag'
58
+ raise 'bugsnag gem not found' unless defined?(Bugsnag)
59
+ Skiplock.on_error = -> (ex, previous = nil) { Bugsnag.notify(ex) unless ex.backtrace == previous.try(:backtrace) }
60
+ when 'exception_notification'
61
+ raise 'exception_notification gem not found' unless defined?(ExceptionNotifier)
62
+ Skiplock.on_error = -> (ex, previous = nil) { ExceptionNotifier.notify_exception(ex) unless ex.backtrace == previous.try(:backtrace) }
63
+ end
36
64
  rescue Exception => e
37
65
  STDERR.puts "Invalid configuration 'config/skiplock.yml': #{e.message}"
38
66
  exit
39
67
  end
40
68
 
41
69
  def self.standalone
42
- title = "Skiplock version: #{Skiplock::VERSION} (Ruby #{RUBY_VERSION}-p#{RUBY_PATCHLEVEL})"
43
- puts "-"*(title.length)
44
- puts title
45
- puts "-"*(title.length)
46
- puts "Additional workers: #{Settings['workers']}"
47
- puts " Purge completion: #{Settings['purge_completion']}"
48
- puts " Max retries: #{Settings['max_retries']}"
49
- puts " Min threads: #{Settings['min_threads']}"
50
- puts " Max threads: #{Settings['max_threads']}"
51
- puts " Environment: #{Rails.env}"
52
- puts " Logging: #{Settings['logging']}"
53
- puts " Queues: #{Settings['queues'].map {|k,v| k + '(' + v.to_s + ')'}.join(', ')}"
54
- puts " PID: #{Process.pid}"
55
- puts "-"*(title.length)
56
70
  if Settings['logging']
57
71
  log_timestamp = (Settings['logging'].to_s == 'timestamp')
58
72
  logfile = File.open('log/skiplock.log', 'a')
@@ -66,14 +80,29 @@ module Skiplock
66
80
  Rails.logger.reopen('/dev/null')
67
81
  Rails.logger.extend(ActiveSupport::Logger.broadcast(logger))
68
82
  end
83
+ title = "Skiplock version: #{Skiplock::VERSION} (Ruby #{RUBY_VERSION}-p#{RUBY_PATCHLEVEL})"
84
+ puts "-"*(title.length)
85
+ puts title
86
+ puts "-"*(title.length)
87
+ puts "Purge completion: #{Settings['purge_completion']}"
88
+ puts " Notification: #{Settings['notification']}"
89
+ puts " Max retries: #{Settings['max_retries']}"
90
+ puts " Min threads: #{Settings['min_threads']}"
91
+ puts " Max threads: #{Settings['max_threads']}"
92
+ puts " Environment: #{Rails.env}"
93
+ puts " Logging: #{Settings['logging']}"
94
+ puts " Workers: #{Settings['workers']}"
95
+ puts " Queues: #{Settings['queues'].map {|k,v| k + '(' + v.to_s + ')'}.join(', ')}" if Settings['queues'].is_a?(Hash)
96
+ puts " PID: #{Process.pid}"
97
+ puts "-"*(title.length)
69
98
  parent_id = Process.pid
70
99
  shutdown = false
71
100
  Signal.trap("INT") { shutdown = true }
72
101
  Signal.trap("TERM") { shutdown = true }
73
102
  worker_pids = []
74
- Settings['workers'].times do |n|
103
+ (Settings['workers']-1).times do |n|
75
104
  worker_pids << fork do
76
- dispatcher = Dispatcher.new(master: false, worker_num: n)
105
+ dispatcher = Dispatcher.new(master: false, worker_num: n+1)
77
106
  thread = dispatcher.run
78
107
  loop do
79
108
  sleep 0.5
@@ -1,4 +1,4 @@
1
1
  module Skiplock
2
- VERSION = Version = '1.0.5'
2
+ VERSION = Version = '1.0.6'
3
3
  end
4
4
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: skiplock
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.5
4
+ version: 1.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tin Vo
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-03-22 00:00:00.000000000 Z
11
+ date: 2021-03-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activejob
@@ -86,7 +86,6 @@ files:
86
86
  - lib/skiplock/dispatcher.rb
87
87
  - lib/skiplock/job.rb
88
88
  - lib/skiplock/manager.rb
89
- - lib/skiplock/notification.rb
90
89
  - lib/skiplock/version.rb
91
90
  - lib/skiplock/worker.rb
92
91
  homepage: https://github.com/vtt/skiplock
@@ -1,5 +0,0 @@
1
- module Skiplock
2
- class Notification
3
- end
4
- end
5
-