skiplock 1.0.25 → 1.1.0

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: 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