skiplock 1.0.25 → 1.1.0

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: 2e920cb349a73036c794a785a955013644642fbe543d1d36605ee20e2db99e99
4
- data.tar.gz: 9b4d8971fe38e5e13d5378f2781249b77aefd68dc03ab978ef7daa8ccf1b1c5a
3
+ metadata.gz: 71cf3158d71dfe424c218da13a55d785365bee9027f827bb1498ba5e0110fda9
4
+ data.tar.gz: 1512863ab863b392f84d67a137959227a823ac33dd9ca8e1ab8e87c63fe04adf
5
5
  SHA512:
6
- metadata.gz: 363a9cad645d4e01ae82fd692717bf237b36399049858196cd36df95d6d0acdc86f0c0b0e2fd7dda17a083a9bf5044134466e90baffc87620e125bb1a6c0b856
7
- data.tar.gz: 6b1af9d83854a982430959bef45dab748f9b8697f35a3a121465f5d0ba484634c12b3744606c89c5009e3cc634f7b8f8481fccb00eccc0aa07a2c5d93b09f5e2
6
+ metadata.gz: 79ef005a284b8aabfff1e1435bba2019a3b3698f714803f6521562f143b3d5f4f6536991a040bbd44574a52fa3be2df7ac565c37cccf4da40da95fe4b150b1c6
7
+ data.tar.gz: 1f7fd443df703c98133b66838c6d6d49a9cfa3b54d33cafaff15d59fd3a538591400fdb917ccfb5ec4b8a2831d88b6a1f85ef622ecb7566842f2f761e97bba9f
data/README.md CHANGED
@@ -56,8 +56,8 @@ The library is quite small compared to other PostgreSQL job queues (eg. *delay_j
56
56
  max_retries: 20
57
57
  logfile: skiplock.log
58
58
  loglevel: info
59
+ namespace:
59
60
  notification: custom
60
- actioncable: false
61
61
  extensions: false
62
62
  purge_completion: true
63
63
  queues:
@@ -72,8 +72,8 @@ The library is quite small compared to other PostgreSQL job queues (eg. *delay_j
72
72
  - **max_retries** (*integer*): sets the maximum attempt a job will be retrying before it is marked expired. See `Retry system` for more details
73
73
  - **logfile** (*string*): filename for skiplock logs; empty logfile will disable logging
74
74
  - **loglevel** (*string*): sets logging level (`debug, info, warn, error, fatal, unknown`)
75
+ - **namespace** (*string*): sets namespace for jobs (workers will only process jobs of specified namespace)
75
76
  - **notification** (*string*): sets the library to be used for notifying errors and exceptions (`auto, airbrake, bugsnag, exception_notification, custom`); using `auto` will detect library if available. See `Notification system` for more details
76
- - **actioncable** (*boolean*): enable or disable usage of ActionCable notification
77
77
  - **extensions** (*multi*): enable or disable the class method extension. See `ClassMethod extension` for more details
78
78
  - **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); queued jobs can manually override using `purge` option
79
79
  - **queues** (*hash*): defines the set of queues with priorities; lower priority takes precedence
@@ -87,10 +87,10 @@ The library is quite small compared to other PostgreSQL job queues (eg. *delay_j
87
87
  ```
88
88
  $ bundle exec skiplock -h
89
89
  Usage: skiplock [options]
90
- -a, --actioncable YESNO Actioncable notification
91
90
  -e, --environment STRING Rails environment
92
91
  -l, --logfile STRING Log filename
93
92
  -L, --loglevel STRING Log level (debug, info, warn, error, fatal, unknown)
93
+ -n, --namespace STRING Job namespace
94
94
  -s, --graceful-shutdown NUM Number of seconds to wait for graceful shutdown
95
95
  -r, --max-retries NUM Number of maxixum retries
96
96
  -t, --max-threads NUM Number of maximum threads
data/bin/skiplock CHANGED
@@ -5,10 +5,10 @@ options = {}
5
5
  begin
6
6
  op = OptionParser.new do |opts|
7
7
  opts.banner = "Usage: #{File.basename($0)} [options]"
8
- opts.on('-a', '--actioncable YESNO', TrueClass, 'Actioncable notification')
9
8
  opts.on('-e', '--environment STRING', String, 'Rails environment')
10
9
  opts.on('-l', '--logfile STRING', String, 'Log filename')
11
10
  opts.on('-L', '--loglevel STRING', String, 'Log level (debug, info, warn, error, fatal, unknown)')
11
+ opts.on('-n', '--namespace STRING', String, 'Job namespace')
12
12
  opts.on('-s', '--graceful-shutdown NUM', Integer, 'Number of seconds to wait for graceful shutdown')
13
13
  opts.on('-r', '--max-retries NUM', Integer, 'Number of maxixum retries')
14
14
  opts.on('-t', '--max-threads NUM', Integer, 'Number of maximum threads')
@@ -7,11 +7,14 @@ class CreateSkiplockSchema < ActiveRecord::Migration<%= "[#{ActiveRecord::VERSIO
7
7
  t.integer :expiries, null: false, default: 0
8
8
  t.integer :failures, null: false, default: 0
9
9
  t.integer :retries, null: false, default: 0
10
- t.date :day, null: false, index: { unique: true }
10
+ t.string :namespace, null: false, default: '', index: true
11
+ t.date :day, null: false
12
+ t.index [ :namespace, :day ], unique: true
11
13
  end
12
14
  create_table 'skiplock.jobs', id: :uuid do |t|
13
15
  t.uuid :worker_id, index: true
14
16
  t.string :job_class, null: false
17
+ t.string :namespace, null: false, default: '', index: true
15
18
  t.string :queue_name, index: true
16
19
  t.string :locale
17
20
  t.string :timezone
@@ -31,6 +34,7 @@ class CreateSkiplockSchema < ActiveRecord::Migration<%= "[#{ActiveRecord::VERSIO
31
34
  t.integer :sid, null: false
32
35
  t.integer :capacity, null: false
33
36
  t.string :hostname, null: false, index: true
37
+ t.string :namespace, null: false, default: '', index: true
34
38
  t.boolean :master, null: false, default: false, index: true
35
39
  t.jsonb :data
36
40
  t.timestamps null: false, index: true, default: -> { 'now()' }
@@ -41,27 +45,29 @@ class CreateSkiplockSchema < ActiveRecord::Migration<%= "[#{ActiveRecord::VERSIO
41
45
  record RECORD;
42
46
  BEGIN
43
47
  record = NEW;
44
- IF (TG_OP = 'DELETE') THEN
48
+ IF TG_OP = 'DELETE' THEN
45
49
  record = OLD;
46
- IF (record.running = TRUE) THEN
47
- INSERT INTO skiplock.counters (day,completions) VALUES (NOW(),1) ON CONFLICT (day) DO UPDATE SET completions = skiplock.counters.completions + 1;
50
+ IF record.running IS TRUE THEN
51
+ INSERT INTO skiplock.counters (namespace,day,completions) VALUES (record.namespace,NOW(),1) ON CONFLICT (namespace,day) DO UPDATE SET completions = skiplock.counters.completions + 1;
48
52
  END IF;
49
- ELSIF (TG_OP = 'UPDATE') THEN
50
- IF (OLD.running = FALSE AND record.running = TRUE) THEN
51
- IF (record.executions > 0) THEN
52
- INSERT INTO skiplock.counters (day,retries) VALUES (NOW(),1) ON CONFLICT (day) DO UPDATE SET retries = skiplock.counters.retries + 1;
53
+ ELSIF TG_OP = 'UPDATE' THEN
54
+ IF OLD.running IS FALSE AND record.running IS TRUE THEN
55
+ IF record.executions > 0 THEN
56
+ INSERT INTO skiplock.counters (namespace,day,retries) VALUES (record.namespace,NOW(),1) ON CONFLICT (namespace,day) DO UPDATE SET retries = skiplock.counters.retries + 1;
53
57
  ELSE
54
- INSERT INTO skiplock.counters (day,dispatches) VALUES (NOW(),1) ON CONFLICT (day) DO UPDATE SET dispatches = skiplock.counters.dispatches + 1;
58
+ INSERT INTO skiplock.counters (namespace,day,dispatches) VALUES (record.namespace,NOW(),1) ON CONFLICT (namespace,day) DO UPDATE SET dispatches = skiplock.counters.dispatches + 1;
59
+ END IF;
60
+ ELSIF OLD.finished_at IS NULL AND record.finished_at IS NOT NULL THEN
61
+ INSERT INTO skiplock.counters (namespace,day,completions) VALUES (record.namespace,NOW(),1) ON CONFLICT (namespace,day) DO UPDATE SET completions = skiplock.counters.completions + 1;
62
+ ELSIF OLD.running IS TRUE AND record.running IS FALSE THEN
63
+ IF record.expired_at IS NOT NULL THEN
64
+ INSERT INTO skiplock.counters (namespace,day,expiries) VALUES (record.namespace,NOW(),1) ON CONFLICT (namespace,day) DO UPDATE SET expiries = skiplock.counters.expiries + 1;
65
+ ELSE
66
+ INSERT INTO skiplock.counters (namespace,day,failures) VALUES (record.namespace,NOW(),1) ON CONFLICT (namespace,day) DO UPDATE SET failures = skiplock.counters.failures + 1;
55
67
  END IF;
56
- ELSIF (OLD.finished_at IS NULL AND record.finished_at IS NOT NULL) THEN
57
- INSERT INTO skiplock.counters (day,completions) VALUES (NOW(),1) ON CONFLICT (day) DO UPDATE SET completions = skiplock.counters.completions + 1;
58
- ELSIF (OLD.running = TRUE AND record.running = FALSE AND record.expired_at IS NOT NULL) THEN
59
- INSERT INTO skiplock.counters (day,expiries) VALUES (NOW(),1) ON CONFLICT (day) DO UPDATE SET expiries = skiplock.counters.expiries + 1;
60
- ELSIF (OLD.running = TRUE AND record.running = FALSE AND record.expired_at IS NULL AND record.finished_at IS NULL) THEN
61
- INSERT INTO skiplock.counters (day,failures) VALUES (NOW(),1) ON CONFLICT (day) DO UPDATE SET failures = skiplock.counters.failures + 1;
62
68
  END IF;
63
69
  END IF;
64
- PERFORM pg_notify('skiplock::jobs', CONCAT(TG_OP,',',record.id::TEXT,',',record.worker_id::TEXT,',',record.job_class,',',record.queue_name,',',record.running::TEXT,',',CAST(EXTRACT(EPOCH FROM record.expired_at) AS FLOAT)::TEXT,',',CAST(EXTRACT(EPOCH FROM record.finished_at) AS FLOAT)::TEXT,',',CAST(EXTRACT(EPOCH FROM CASE WHEN record.scheduled_at IS NULL THEN record.updated_at ELSE record.scheduled_at END) AS FLOAT)::TEXT));
70
+ PERFORM pg_notify('skiplock::jobs', CONCAT(TG_OP,',',record.id::TEXT,',',record.worker_id::TEXT,',',record.namespace,',',record.job_class,',',record.queue_name,',',record.running::TEXT,',',CAST(EXTRACT(EPOCH FROM record.expired_at) AS FLOAT)::TEXT,',',CAST(EXTRACT(EPOCH FROM record.finished_at) AS FLOAT)::TEXT,',',CAST(EXTRACT(EPOCH FROM CASE WHEN record.scheduled_at IS NULL THEN record.updated_at ELSE record.scheduled_at END) AS FLOAT)::TEXT));
65
71
  RETURN NULL;
66
72
  END;
67
73
  $$ LANGUAGE plpgsql
@@ -71,12 +77,12 @@ class CreateSkiplockSchema < ActiveRecord::Migration<%= "[#{ActiveRecord::VERSIO
71
77
  DECLARE
72
78
  record RECORD;
73
79
  BEGIN
74
- IF (TG_OP = 'DELETE') THEN
80
+ IF TG_OP = 'DELETE' THEN
75
81
  record = OLD;
76
82
  ELSE
77
83
  record = NEW;
78
84
  END IF;
79
- PERFORM pg_notify('skiplock::workers', CONCAT(TG_OP,',',record.id::TEXT,',',record.hostname,',',record.master::TEXT,',',record.capacity,',',record.pid,',',record.sid,',',CAST(EXTRACT(EPOCH FROM record.created_at) AS FLOAT)::TEXT,',',CAST(EXTRACT(EPOCH FROM record.updated_at) AS FLOAT)::TEXT));
85
+ PERFORM pg_notify('skiplock::workers', CONCAT(TG_OP,',',record.id::TEXT,',',record.namespace,',',record.hostname,',',record.master::TEXT,',',record.capacity,',',record.pid,',',record.sid,',',CAST(EXTRACT(EPOCH FROM record.created_at) AS FLOAT)::TEXT,',',CAST(EXTRACT(EPOCH FROM record.updated_at) AS FLOAT)::TEXT));
80
86
  RETURN NULL;
81
87
  END;
82
88
  $$ LANGUAGE plpgsql;
@@ -87,7 +93,7 @@ class CreateSkiplockSchema < ActiveRecord::Migration<%= "[#{ActiveRecord::VERSIO
87
93
  execute "CREATE INDEX jobs_retry_index ON skiplock.jobs(scheduled_at) WHERE running = FALSE AND executions IS NOT NULL AND expired_at IS NULL AND finished_at IS NULL"
88
94
  execute "CREATE INDEX jobs_cron_index ON skiplock.jobs(scheduled_at ASC NULLS FIRST, priority ASC NULLS LAST, created_at ASC) WHERE cron IS NOT NULL AND finished_at IS NULL"
89
95
  execute "CREATE UNIQUE INDEX jobs_unique_cron_index ON skiplock.jobs (job_class) WHERE cron IS NOT NULL"
90
- execute "CREATE UNIQUE INDEX workers_unique_master_index ON skiplock.workers(hostname) WHERE master = TRUE"
96
+ execute "CREATE UNIQUE INDEX workers_unique_master_index ON skiplock.workers(hostname,namespace) WHERE master = TRUE"
91
97
  end
92
98
 
93
99
  def down
data/lib/skiplock/job.rb CHANGED
@@ -3,14 +3,15 @@ module Skiplock
3
3
  self.implicit_order_column = 'updated_at'
4
4
  attribute :activejob_error
5
5
  attribute :exception
6
- attribute :max_retries, :integer
7
- attribute :purge, :boolean
6
+ attribute :max_retries
7
+ attribute :purge
8
8
  belongs_to :worker, inverse_of: :jobs, required: false
9
9
 
10
10
  def self.dispatch(purge_completion: true, max_retries: 20)
11
+ namespace_query = Skiplock.namespace.nil? ? "namespace IS NULL" : "namespace = '#{Skiplock.namespace}'"
11
12
  job = nil
12
13
  self.connection.transaction do
13
- job = self.find_by_sql("SELECT id, scheduled_at FROM skiplock.jobs WHERE running = FALSE AND expired_at IS NULL AND finished_at IS NULL ORDER BY scheduled_at ASC NULLS FIRST, priority ASC NULLS LAST, created_at ASC FOR UPDATE SKIP LOCKED LIMIT 1").first
14
+ job = self.find_by_sql("SELECT id, scheduled_at FROM skiplock.jobs WHERE running = FALSE AND expired_at IS NULL AND finished_at IS NULL AND #{namespace_query} ORDER BY scheduled_at ASC NULLS FIRST, priority ASC NULLS LAST, created_at ASC FOR UPDATE SKIP LOCKED LIMIT 1").first
14
15
  return if job.nil? || job.scheduled_at.to_f > Time.now.to_f
15
16
  job = self.find_by_sql("UPDATE skiplock.jobs SET running = TRUE, updated_at = NOW() WHERE id = '#{job.id}' RETURNING *").first
16
17
  end
@@ -33,7 +34,7 @@ module Skiplock
33
34
  Thread.current[:skiplock_job]
34
35
  else
35
36
  serialize = activejob.serialize
36
- self.create!(serialize.slice(*self.column_names).merge('id' => serialize['job_id'], 'data' => { 'arguments' => serialize['arguments'], 'options' => options }, 'scheduled_at' => timestamp))
37
+ self.create!(serialize.slice(*self.column_names).merge('id' => serialize['job_id'], 'data' => { 'arguments' => serialize['arguments'], 'options' => options }, 'namespace' => Skiplock.namespace, 'scheduled_at' => timestamp))
37
38
  end
38
39
  end
39
40
 
@@ -18,7 +18,7 @@ module Skiplock
18
18
  setup_logger
19
19
  configure
20
20
  Worker.cleanup(@hostname)
21
- @worker = Worker.generate(capacity: @config[:max_threads], hostname: @hostname, actioncable: @config[:actioncable])
21
+ @worker = Worker.generate(capacity: @config[:max_threads], hostname: @hostname)
22
22
  Cron.setup if @worker.master
23
23
  @worker.start(**@config)
24
24
  at_exit { @worker.shutdown }
@@ -40,13 +40,13 @@ module Skiplock
40
40
  Signal.trap('TERM') { @shutdown = true }
41
41
  Signal.trap('HUP') { setup_logger }
42
42
  Worker.cleanup(@hostname)
43
- @worker = Worker.generate(capacity: @config[:max_threads], hostname: @hostname, actioncable: @config[:actioncable])
43
+ @worker = Worker.generate(capacity: @config[:max_threads], hostname: @hostname)
44
44
  ActiveRecord::Base.connection.disconnect! if @config[:workers] > 1
45
45
  (@config[:workers] - 1).times do |n|
46
46
  fork do
47
47
  sleep(0.25*n + 1)
48
48
  ActiveRecord::Base.establish_connection
49
- worker = Worker.generate(capacity: @config[:max_threads], hostname: @hostname, master: false, actioncable: @config[:actioncable])
49
+ worker = Worker.generate(capacity: @config[:max_threads], hostname: @hostname, master: false)
50
50
  worker.start(worker_num: n + 1, **@config)
51
51
  loop do
52
52
  sleep 0.5
@@ -76,7 +76,6 @@ module Skiplock
76
76
  @logger.info "-"*(title.length)
77
77
  @logger.info title
78
78
  @logger.info "-"*(title.length)
79
- @logger.info "ActionCable notification: #{@config[:actioncable]}#{' (not available)' unless defined?(ActionCable)}"
80
79
  @logger.info " ClassMethod extensions: #{@config[:extensions]}"
81
80
  @logger.info " Purge completion: #{@config[:purge_completion]}"
82
81
  @logger.info " Notification: #{@config[:notification]}"
@@ -84,6 +83,7 @@ module Skiplock
84
83
  @logger.info " Min threads: #{@config[:min_threads]}"
85
84
  @logger.info " Max threads: #{@config[:max_threads]}"
86
85
  @logger.info " Environment: #{Rails.env}"
86
+ @logger.info " Namespace: #{@config[:namespace] || '(nil)'}"
87
87
  @logger.info " Loglevel: #{@config[:loglevel]}"
88
88
  @logger.info " Logfile: #{@config[:logfile] || '(disabled)'}"
89
89
  @logger.info " Workers: #{@config[:workers]}"
@@ -135,7 +135,7 @@ module Skiplock
135
135
  else
136
136
  @config[:notification] = 'custom'
137
137
  end
138
- @logger.error 'ActionCable is not found!' if @config[:actioncable] && !defined?(ActionCable)
138
+ Skiplock.namespace = @config[:namespace]
139
139
  Skiplock.on_errors.freeze
140
140
  end
141
141
 
@@ -152,8 +152,6 @@ module Skiplock
152
152
  Rails.logger.reopen('/dev/null') rescue Rails.logger.reopen('NUL') # supports Windows NUL device
153
153
  Rails.logger.level = @logger.level
154
154
  Rails.logger.extend(ActiveSupport::Logger.broadcast(@logger))
155
- # disable ActionCable logging
156
- ActionCable.server.config.logger = Logger.new(nil) if defined?(ActionCable)
157
155
  end
158
156
  rescue Exception => ex
159
157
  @logger.error "Exception with logger: #{ex.to_s}"
@@ -1,4 +1,4 @@
1
1
  module Skiplock
2
- VERSION = Version = '1.0.25'
2
+ VERSION = Version = '1.1.0'
3
3
  end
4
4
 
@@ -5,26 +5,23 @@ module Skiplock
5
5
 
6
6
  def self.cleanup(hostname = nil)
7
7
  delete_ids = []
8
- self.where(hostname: hostname || Socket.gethostname).each do |worker|
8
+ self.where(namespace: Skiplock.namespace, hostname: hostname || Socket.gethostname).each do |worker|
9
9
  sid = Process.getsid(worker.pid) rescue nil
10
10
  delete_ids << worker.id if worker.sid != sid || worker.updated_at < 10.minutes.ago
11
11
  end
12
12
  self.where(id: delete_ids).delete_all if delete_ids.count > 0
13
13
  end
14
14
 
15
- def self.generate(capacity:, hostname:, master: true, actioncable: false)
16
- worker = self.create!(pid: Process.pid, sid: Process.getsid(), master: master, hostname: hostname, capacity: capacity)
15
+ def self.generate(capacity:, hostname:, master: true)
16
+ worker = self.create!(pid: Process.pid, sid: Process.getsid(), master: master, hostname: hostname, capacity: capacity, namespace: Skiplock.namespace)
17
17
  rescue
18
- worker = self.create!(pid: Process.pid, sid: Process.getsid(), master: false, hostname: hostname, capacity: capacity)
19
- ensure
20
- ActionCable.server.broadcast('skiplock', { worker: { op: 'CREATE', id: worker.id, hostname: worker.hostname, master: worker.master, capacity: worker.capacity, pid: worker.pid, sid: worker.sid, created_at: worker.created_at.to_f, updated_at: worker.updated_at.to_f } }) if actioncable && defined?(ActionCable)
18
+ worker = self.create!(pid: Process.pid, sid: Process.getsid(), master: false, hostname: hostname, capacity: capacity, namespace: Skiplock.namespace)
21
19
  end
22
20
 
23
21
  def shutdown
24
22
  @running = false
25
23
  @executor.shutdown
26
24
  @executor.kill unless @executor.wait_for_termination(@config[:graceful_shutdown])
27
- ActionCable.server.broadcast('skiplock', { worker: { op: 'DELETE', id: self.id, hostname: self.hostname, master: self.master, capacity: self.capacity, pid: self.pid, sid: self.sid, created_at: self.created_at.to_f, updated_at: self.updated_at.to_f } }) if self.delete && @config[:actioncable] && defined?(ActionCable)
28
25
  Skiplock.logger.info "[Skiplock] Shutdown of #{self.master ? 'master' : 'cluster'} worker#{(' ' + @num.to_s) if @num > 0 && @config[:workers] > 2} (PID: #{self.pid}) was completed."
29
26
  end
30
27
 
@@ -32,6 +29,7 @@ module Skiplock
32
29
  @num = worker_num
33
30
  @config = config
34
31
  @pg_config = ActiveRecord::Base.connection.raw_connection.conninfo_hash.compact
32
+ @namespace_query = Skiplock.namespace.nil? ? "namespace IS NULL" : "namespace = '#{Skiplock.namespace}'"
35
33
  @queues_order_query = @config[:queues].map { |q,v| "WHEN queue_name = '#{q}' THEN #{v}" }.join(' ') if @config[:queues].is_a?(Hash) && @config[:queues].count > 0
36
34
  @running = true
37
35
  @map = ::PG::TypeMapByOid.new
@@ -77,7 +75,7 @@ module Skiplock
77
75
  if Time.now.to_f >= next_schedule_at && @executor.remaining_capacity > 1 # reserves 1 slot in queue for Job.flush in case of pg_connection error
78
76
  result = nil
79
77
  @connection.transaction do |conn|
80
- conn.exec("SELECT id, running, scheduled_at FROM skiplock.jobs 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") do |r|
78
+ conn.exec("SELECT id, running, scheduled_at FROM skiplock.jobs WHERE running = FALSE AND expired_at IS NULL AND finished_at IS NULL AND #{@namespace_query} 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") do |r|
81
79
  result = r.first
82
80
  conn.exec("UPDATE skiplock.jobs SET running = TRUE, worker_id = '#{self.id}', updated_at = NOW() WHERE id = '#{result['id']}' RETURNING *") { |r| result = r.first } if result && result['scheduled_at'].to_f <= Time.now.to_f
83
81
  end
@@ -97,17 +95,10 @@ module Skiplock
97
95
  notifications[payload[:relname]] << payload[:extra]
98
96
  end
99
97
  notifications['skiplock::jobs'].each do |n|
100
- op, id, worker_id, job_class, queue_name, running, expired_at, finished_at, scheduled_at = n.split(',')
101
- ActionCable.server.broadcast('skiplock', { job: { op: op, id: id, worker_id: worker_id, job_class: job_class, queue_name: queue_name, running: (running == 'true'), expired_at: expired_at.to_f, finished_at: finished_at.to_f, scheduled_at: scheduled_at.to_f } }) if self.master && @config[:actioncable] && defined?(ActionCable)
102
- next if op == 'DELETE' || running == 'true' || expired_at.to_f > 0 || finished_at.to_f > 0
98
+ op, id, worker_id, namespace, job_class, queue_name, running, expired_at, finished_at, scheduled_at = n.split(',')
99
+ next if op == 'DELETE' || running == 'true' || namespace != Skiplock.namespace || expired_at.to_f > 0 || finished_at.to_f > 0
103
100
  next_schedule_at = scheduled_at.to_f if scheduled_at.to_f < next_schedule_at
104
101
  end
105
- if self.master && @config[:actioncable] && defined?(ActionCable)
106
- notifications['skiplock::workers'].each do |w|
107
- op, id, hostname, master, capacity, pid, sid, created_at, updated_at = w.split(',')
108
- ActionCable.server.broadcast('skiplock', { worker: { op: op, id: id, hostname: hostname, master: (master == 'true'), capacity: capacity.to_i, pid: pid.to_i, sid: sid.to_i, created_at: created_at.to_f, updated_at: updated_at.to_f } })
109
- end
110
- end
111
102
  end
112
103
  if Process.clock_gettime(Process::CLOCK_MONOTONIC) - timestamp > 60
113
104
  @connection.exec("UPDATE skiplock.workers SET updated_at = NOW() WHERE id = '#{self.id}'").clear
data/lib/skiplock.rb CHANGED
@@ -11,7 +11,7 @@ require 'skiplock/worker'
11
11
  require 'skiplock/version'
12
12
 
13
13
  module Skiplock
14
- DEFAULT_CONFIG = { 'graceful_shutdown' => 15, 'min_threads' => 1, 'max_threads' => 10, 'max_retries' => 20, 'logfile' => 'skiplock.log', 'loglevel' => 'info', 'notification' => 'custom', 'actioncable' => false, 'extensions' => false, 'purge_completion' => true, 'queues' => { 'default' => 100, 'mailers' => 999 }, 'workers' => 0 }.freeze
14
+ DEFAULT_CONFIG = { 'graceful_shutdown' => 15, 'min_threads' => 1, 'max_threads' => 10, 'max_retries' => 20, 'logfile' => 'skiplock.log', 'loglevel' => 'info', 'namespace' => nil, 'notification' => 'custom', 'extensions' => false, 'purge_completion' => true, 'queues' => { 'default' => 100, 'mailers' => 999 }, 'workers' => 0 }.freeze
15
15
 
16
16
  def self.logger=(l)
17
17
  @logger = l
@@ -21,6 +21,14 @@ module Skiplock
21
21
  @logger
22
22
  end
23
23
 
24
+ def self.namespace=(n)
25
+ @namespace = n
26
+ end
27
+
28
+ def self.namespace
29
+ @namespace || ''
30
+ end
31
+
24
32
  def self.on_error(&block)
25
33
  @on_errors ||= []
26
34
  @on_errors << block
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.25
4
+ version: 1.1.0
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-11-28 00:00:00.000000000 Z
11
+ date: 2022-01-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activejob