skiplock 1.0.5 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
-