pgbus 0.3.3 → 0.3.4
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 +4 -4
- data/app/controllers/pgbus/dead_letter_controller.rb +17 -0
- data/app/controllers/pgbus/jobs_controller.rb +36 -0
- data/app/controllers/pgbus/locks_controller.rb +25 -0
- data/app/frontend/pgbus/application.js +45 -0
- data/app/models/pgbus/job_lock.rb +16 -8
- data/app/models/pgbus/uniqueness_key.rb +36 -0
- data/app/views/pgbus/dead_letter/_messages_table.html.erb +22 -2
- data/app/views/pgbus/dead_letter/index.html.erb +9 -1
- data/app/views/pgbus/jobs/_enqueued_table.html.erb +36 -6
- data/app/views/pgbus/jobs/_failed_table.html.erb +35 -4
- data/app/views/pgbus/locks/index.html.erb +53 -28
- data/config/locales/da.yml +3 -7
- data/config/locales/de.yml +3 -7
- data/config/locales/en.yml +33 -7
- data/config/locales/es.yml +3 -7
- data/config/locales/fi.yml +3 -7
- data/config/locales/fr.yml +3 -7
- data/config/locales/it.yml +3 -7
- data/config/locales/ja.yml +3 -7
- data/config/locales/nb.yml +3 -7
- data/config/locales/nl.yml +3 -7
- data/config/locales/pt.yml +3 -7
- data/config/locales/sv.yml +3 -7
- data/config/routes.rb +12 -1
- data/lib/generators/pgbus/migrate_job_locks_generator.rb +56 -0
- data/lib/generators/pgbus/templates/add_uniqueness_keys.rb.erb +13 -0
- data/lib/generators/pgbus/templates/migrate_job_locks_to_uniqueness_keys.rb.erb +33 -0
- data/lib/pgbus/active_job/executor.rb +34 -20
- data/lib/pgbus/client.rb +18 -2
- data/lib/pgbus/process/dispatcher.rb +33 -10
- data/lib/pgbus/process/worker.rb +4 -1
- data/lib/pgbus/recurring/schedule.rb +38 -35
- data/lib/pgbus/stat_buffer.rb +92 -0
- data/lib/pgbus/uniqueness.rb +24 -39
- data/lib/pgbus/version.rb +1 -1
- data/lib/pgbus/web/data_source.rb +46 -15
- metadata +6 -1
|
@@ -16,23 +16,13 @@ module Pgbus
|
|
|
16
16
|
|
|
17
17
|
def enqueue_task(task, run_at:)
|
|
18
18
|
queue = resolve_queue(task)
|
|
19
|
+
acquired_key = acquire_uniqueness_lock(task)
|
|
19
20
|
|
|
20
|
-
|
|
21
|
-
# ensures_uniqueness, we acquire the lock here so duplicate recurring
|
|
22
|
-
# enqueues are rejected while a previous instance is still queued or running.
|
|
23
|
-
if uniqueness_locked?(task)
|
|
24
|
-
Pgbus.logger.debug do
|
|
25
|
-
"[Pgbus] Recurring task #{task.key} skipped: uniqueness lock held"
|
|
26
|
-
end
|
|
27
|
-
return
|
|
28
|
-
end
|
|
21
|
+
return if acquired_key == :already_locked
|
|
29
22
|
|
|
30
23
|
RecurringExecution.record(task.key, run_at) do
|
|
31
24
|
payload = build_payload(task)
|
|
32
25
|
headers = build_headers(task, run_at)
|
|
33
|
-
|
|
34
|
-
# Inject uniqueness metadata into the payload so the worker knows
|
|
35
|
-
# to release the lock after execution.
|
|
36
26
|
payload = inject_uniqueness_metadata(task, payload)
|
|
37
27
|
|
|
38
28
|
Pgbus.client.ensure_queue(queue)
|
|
@@ -44,7 +34,11 @@ module Pgbus
|
|
|
44
34
|
end
|
|
45
35
|
end
|
|
46
36
|
rescue AlreadyRecorded
|
|
37
|
+
release_uniqueness_lock(acquired_key)
|
|
47
38
|
Pgbus.logger.debug { "[Pgbus] Recurring task #{task.key} already enqueued for #{run_at.iso8601}" }
|
|
39
|
+
rescue StandardError
|
|
40
|
+
release_uniqueness_lock(acquired_key)
|
|
41
|
+
raise
|
|
48
42
|
end
|
|
49
43
|
|
|
50
44
|
def build_payload(task)
|
|
@@ -112,36 +106,45 @@ module Pgbus
|
|
|
112
106
|
}
|
|
113
107
|
end
|
|
114
108
|
|
|
115
|
-
#
|
|
116
|
-
# Returns
|
|
117
|
-
|
|
118
|
-
|
|
109
|
+
# Acquire the uniqueness lock for a recurring task.
|
|
110
|
+
# Returns:
|
|
111
|
+
# nil — no uniqueness configured, proceed without lock
|
|
112
|
+
# :already_locked — lock held by a previous instance, caller should skip enqueue
|
|
113
|
+
# String — the lock key (lock was acquired, caller must release on failure)
|
|
114
|
+
def acquire_uniqueness_lock(task)
|
|
115
|
+
return nil unless task.class_name
|
|
119
116
|
|
|
120
117
|
job_class = task.class_name.safe_constantize
|
|
121
|
-
return
|
|
122
|
-
return
|
|
118
|
+
return nil unless job_class
|
|
119
|
+
return nil unless job_class.respond_to?(:pgbus_uniqueness)
|
|
123
120
|
|
|
124
121
|
config = job_class.pgbus_uniqueness
|
|
125
|
-
return
|
|
126
|
-
return
|
|
122
|
+
return nil unless config
|
|
123
|
+
return nil unless config[:strategy] == :until_executed
|
|
127
124
|
|
|
128
125
|
key = resolve_uniqueness_key(config, task)
|
|
129
|
-
return
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
126
|
+
return nil unless key
|
|
127
|
+
|
|
128
|
+
acquired = UniquenessKey.acquire!(key, queue_name: resolve_queue(task), msg_id: 0)
|
|
129
|
+
|
|
130
|
+
if acquired
|
|
131
|
+
key
|
|
132
|
+
else
|
|
133
|
+
Pgbus.logger.debug { "[Pgbus] Recurring task #{task.key} skipped: uniqueness lock held" }
|
|
134
|
+
:already_locked
|
|
135
|
+
end
|
|
136
|
+
rescue StandardError => e
|
|
137
|
+
Pgbus.logger.warn { "[Pgbus] Uniqueness lock failed for #{task.key}: #{e.message}" }
|
|
138
|
+
nil # Fail open — allow enqueue if lock check errors
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# Release a uniqueness lock. Safe to call with nil or :already_locked.
|
|
142
|
+
def release_uniqueness_lock(key)
|
|
143
|
+
return if key.nil? || key == :already_locked
|
|
144
|
+
|
|
145
|
+
UniquenessKey.release!(key)
|
|
142
146
|
rescue StandardError => e
|
|
143
|
-
Pgbus.logger.warn { "[Pgbus]
|
|
144
|
-
false # Fail open — allow enqueue if uniqueness check errors
|
|
147
|
+
Pgbus.logger.warn { "[Pgbus] Lock rollback failed: #{e.message}" }
|
|
145
148
|
end
|
|
146
149
|
|
|
147
150
|
# Resolve the uniqueness key for a recurring task.
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pgbus
|
|
4
|
+
# Thread-safe buffer that accumulates job stats in memory and flushes
|
|
5
|
+
# them to the database in batches. This avoids one INSERT per job
|
|
6
|
+
# execution, replacing it with periodic bulk inserts.
|
|
7
|
+
class StatBuffer
|
|
8
|
+
DEFAULT_FLUSH_SIZE = 100
|
|
9
|
+
DEFAULT_FLUSH_INTERVAL = 5 # seconds
|
|
10
|
+
|
|
11
|
+
attr_reader :flush_size, :flush_interval
|
|
12
|
+
|
|
13
|
+
def initialize(flush_size: DEFAULT_FLUSH_SIZE, flush_interval: DEFAULT_FLUSH_INTERVAL)
|
|
14
|
+
@flush_size = flush_size
|
|
15
|
+
@flush_interval = flush_interval
|
|
16
|
+
@buffer = []
|
|
17
|
+
@mutex = Mutex.new
|
|
18
|
+
@last_flush_at = monotonic_now
|
|
19
|
+
@stopped = false
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Append a stat entry to the buffer. Flushes automatically when
|
|
23
|
+
# the buffer reaches flush_size.
|
|
24
|
+
def push(attrs)
|
|
25
|
+
should_flush = false
|
|
26
|
+
|
|
27
|
+
@mutex.synchronize do
|
|
28
|
+
@buffer << attrs
|
|
29
|
+
should_flush = @buffer.size >= @flush_size
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
flush if should_flush
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Flush buffered stats to the database. Safe to call from any thread.
|
|
36
|
+
def flush
|
|
37
|
+
entries = nil
|
|
38
|
+
|
|
39
|
+
@mutex.synchronize do
|
|
40
|
+
return if @buffer.empty?
|
|
41
|
+
|
|
42
|
+
entries = @buffer.dup
|
|
43
|
+
@buffer.clear
|
|
44
|
+
@last_flush_at = monotonic_now
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
write_to_database(entries) if entries&.any?
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Flush if the interval has elapsed since the last flush.
|
|
51
|
+
# Called by the dispatcher on its maintenance tick.
|
|
52
|
+
def flush_if_due
|
|
53
|
+
due = @mutex.synchronize { monotonic_now - @last_flush_at >= @flush_interval }
|
|
54
|
+
flush if due
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def stop
|
|
58
|
+
@stopped = true
|
|
59
|
+
flush
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def size
|
|
63
|
+
@mutex.synchronize { @buffer.size }
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
private
|
|
67
|
+
|
|
68
|
+
def write_to_database(entries)
|
|
69
|
+
return unless JobStat.table_exists?
|
|
70
|
+
|
|
71
|
+
columns = %i[job_class queue_name status duration_ms]
|
|
72
|
+
columns.push(:enqueue_latency_ms, :retry_count) if JobStat.latency_columns?
|
|
73
|
+
|
|
74
|
+
rows = entries.map do |e|
|
|
75
|
+
row = [e[:job_class], e[:queue_name], e[:status], e[:duration_ms]]
|
|
76
|
+
row.push(e[:enqueue_latency_ms], e[:retry_count]) if JobStat.latency_columns?
|
|
77
|
+
row
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
JobStat.insert_all(
|
|
81
|
+
rows.map { |row| columns.zip(row).to_h },
|
|
82
|
+
record_timestamps: true
|
|
83
|
+
)
|
|
84
|
+
rescue StandardError => e
|
|
85
|
+
Pgbus.logger.debug { "[Pgbus] Stat buffer flush failed: #{e.message}" }
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def monotonic_now
|
|
89
|
+
::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
data/lib/pgbus/uniqueness.rb
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "active_support/concern"
|
|
4
|
-
require "socket"
|
|
5
4
|
|
|
6
5
|
module Pgbus
|
|
7
6
|
# Job uniqueness guarantees: prevent duplicate jobs from running concurrently.
|
|
@@ -10,14 +9,16 @@ module Pgbus
|
|
|
10
9
|
# uniqueness ensures AT MOST ONE job with a given key exists in the system
|
|
11
10
|
# at any time — from enqueue through completion.
|
|
12
11
|
#
|
|
13
|
-
# Lock lifecycle:
|
|
14
|
-
# 1. Enqueue:
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
12
|
+
# Lock lifecycle (advisory lock + thin lookup table):
|
|
13
|
+
# 1. Enqueue: pg_advisory_xact_lock serializes concurrent attempts,
|
|
14
|
+
# then INSERT INTO pgbus_uniqueness_keys ON CONFLICT DO NOTHING.
|
|
15
|
+
# The lock row lives as long as the job is in the queue or executing.
|
|
16
|
+
# 2. Execution: PGMQ's visibility timeout is the execution lock —
|
|
17
|
+
# no separate claim_for_execution step needed.
|
|
18
|
+
# 3. Completion/DLQ: DELETE FROM pgbus_uniqueness_keys WHERE lock_key = ?.
|
|
19
|
+
# 4. Crash recovery: if a worker dies, VT expires, the message becomes
|
|
20
|
+
# readable again. The uniqueness key row stays (correctly — the job
|
|
21
|
+
# hasn't finished). The next worker picks it up and executes.
|
|
21
22
|
#
|
|
22
23
|
# Strategies:
|
|
23
24
|
# :until_executed — Lock acquired at enqueue, held through execution, released on
|
|
@@ -44,7 +45,8 @@ module Pgbus
|
|
|
44
45
|
STRATEGY_KEY = "pgbus_uniqueness_strategy"
|
|
45
46
|
TTL_KEY = "pgbus_uniqueness_lock_ttl"
|
|
46
47
|
|
|
47
|
-
#
|
|
48
|
+
# TTL is kept for metadata compatibility but no longer drives lock expiry.
|
|
49
|
+
# The lock exists until the job completes or is dead-lettered.
|
|
48
50
|
DEFAULT_LOCK_TTL = 24 * 60 * 60
|
|
49
51
|
|
|
50
52
|
VALID_STRATEGIES = %i[until_executed while_executing].freeze
|
|
@@ -115,54 +117,37 @@ module Pgbus
|
|
|
115
117
|
end
|
|
116
118
|
|
|
117
119
|
# Acquire the uniqueness lock at enqueue time (:until_executed only).
|
|
118
|
-
#
|
|
119
|
-
# Returns :acquired or :
|
|
120
|
-
def acquire_enqueue_lock(key, active_job)
|
|
120
|
+
# Uses pg_advisory_xact_lock to serialize concurrent attempts.
|
|
121
|
+
# Returns :acquired, :locked, or :no_lock.
|
|
122
|
+
def acquire_enqueue_lock(key, active_job, queue_name: nil, msg_id: nil)
|
|
121
123
|
config = uniqueness_config(active_job)
|
|
122
124
|
return :no_lock unless config
|
|
123
125
|
return :no_lock unless config[:strategy] == :until_executed
|
|
124
126
|
|
|
125
|
-
acquired =
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
)
|
|
127
|
+
acquired = if msg_id && queue_name
|
|
128
|
+
UniquenessKey.acquire!(key, queue_name: queue_name, msg_id: msg_id)
|
|
129
|
+
else
|
|
130
|
+
# Pre-produce check: use advisory lock + ON CONFLICT
|
|
131
|
+
UniquenessKey.acquire!(key, queue_name: queue_name || "pending", msg_id: msg_id || 0)
|
|
132
|
+
end
|
|
132
133
|
acquired ? :acquired : :locked
|
|
133
134
|
end
|
|
134
135
|
|
|
135
|
-
# Transition a queued lock to executing state when the worker picks it up.
|
|
136
|
-
# Called for :until_executed jobs at execution start.
|
|
137
|
-
def claim_for_execution!(key, ttl:)
|
|
138
|
-
JobLock.claim_for_execution!(key, owner_pid: ::Process.pid, owner_hostname: Socket.gethostname, ttl: ttl)
|
|
139
|
-
end
|
|
140
|
-
|
|
141
136
|
# Acquire the uniqueness lock at execution time (:while_executing only).
|
|
142
|
-
# Lock state: executing with owner_pid.
|
|
143
137
|
# Returns true if acquired, false if another instance is running.
|
|
144
138
|
def acquire_execution_lock(key, payload)
|
|
145
139
|
strategy = extract_strategy(payload)
|
|
146
140
|
return true unless strategy == :while_executing
|
|
147
141
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
JobLock.acquire!(
|
|
151
|
-
key,
|
|
152
|
-
job_class: payload["job_class"],
|
|
153
|
-
job_id: payload["job_id"],
|
|
154
|
-
state: "executing",
|
|
155
|
-
owner_pid: ::Process.pid,
|
|
156
|
-
owner_hostname: Socket.gethostname,
|
|
157
|
-
ttl: ttl
|
|
158
|
-
)
|
|
142
|
+
queue_name = payload["queue_name"] || "unknown"
|
|
143
|
+
UniquenessKey.acquire!(key, queue_name: queue_name, msg_id: 0)
|
|
159
144
|
end
|
|
160
145
|
|
|
161
146
|
# Release the uniqueness lock after execution completes.
|
|
162
147
|
def release_lock(key)
|
|
163
148
|
return unless key
|
|
164
149
|
|
|
165
|
-
|
|
150
|
+
UniquenessKey.release!(key)
|
|
166
151
|
end
|
|
167
152
|
end
|
|
168
153
|
end
|
data/lib/pgbus/version.rb
CHANGED
|
@@ -57,10 +57,12 @@ module Pgbus
|
|
|
57
57
|
end
|
|
58
58
|
|
|
59
59
|
def purge_queue(name)
|
|
60
|
+
release_uniqueness_keys_for_queue(name)
|
|
60
61
|
@client.purge_queue(name, prefixed: false)
|
|
61
62
|
end
|
|
62
63
|
|
|
63
64
|
def drop_queue(name)
|
|
65
|
+
release_uniqueness_keys_for_queue(name)
|
|
64
66
|
@client.drop_queue(name, prefixed: false)
|
|
65
67
|
end
|
|
66
68
|
|
|
@@ -533,23 +535,43 @@ module Pgbus
|
|
|
533
535
|
[]
|
|
534
536
|
end
|
|
535
537
|
|
|
536
|
-
#
|
|
538
|
+
# Lock management
|
|
539
|
+
def discard_lock(lock_key)
|
|
540
|
+
UniquenessKey.where(lock_key: lock_key).delete_all
|
|
541
|
+
rescue StandardError => e
|
|
542
|
+
Pgbus.logger.debug { "[Pgbus::Web] Error discarding lock #{lock_key}: #{e.message}" }
|
|
543
|
+
0
|
|
544
|
+
end
|
|
545
|
+
|
|
546
|
+
def discard_locks(lock_keys)
|
|
547
|
+
return 0 if lock_keys.empty?
|
|
548
|
+
|
|
549
|
+
UniquenessKey.where(lock_key: lock_keys).delete_all
|
|
550
|
+
rescue StandardError => e
|
|
551
|
+
Pgbus.logger.debug { "[Pgbus::Web] Error discarding locks: #{e.message}" }
|
|
552
|
+
0
|
|
553
|
+
end
|
|
554
|
+
|
|
555
|
+
def discard_all_locks
|
|
556
|
+
UniquenessKey.delete_all
|
|
557
|
+
rescue StandardError => e
|
|
558
|
+
Pgbus.logger.debug { "[Pgbus::Web] Error discarding all locks: #{e.message}" }
|
|
559
|
+
0
|
|
560
|
+
end
|
|
561
|
+
|
|
562
|
+
# Job uniqueness keys
|
|
537
563
|
def job_locks
|
|
538
|
-
|
|
564
|
+
UniquenessKey.order(created_at: :desc).limit(100).map do |key|
|
|
539
565
|
{
|
|
540
|
-
lock_key:
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
owner_hostname: lock.owner_hostname,
|
|
546
|
-
locked_at: lock.locked_at,
|
|
547
|
-
expires_at: lock.expires_at,
|
|
548
|
-
age_seconds: lock.locked_at ? (Time.current - lock.locked_at).to_i : nil
|
|
566
|
+
lock_key: key.lock_key,
|
|
567
|
+
queue_name: key.queue_name,
|
|
568
|
+
msg_id: key.msg_id,
|
|
569
|
+
created_at: key.created_at,
|
|
570
|
+
age_seconds: key.created_at ? (Time.current - key.created_at).to_i : nil
|
|
549
571
|
}
|
|
550
572
|
end
|
|
551
573
|
rescue StandardError => e
|
|
552
|
-
Pgbus.logger.debug { "[Pgbus::Web] Error fetching
|
|
574
|
+
Pgbus.logger.debug { "[Pgbus::Web] Error fetching uniqueness keys: #{e.message}" }
|
|
553
575
|
[]
|
|
554
576
|
end
|
|
555
577
|
|
|
@@ -786,7 +808,7 @@ module Pgbus
|
|
|
786
808
|
|
|
787
809
|
payload = payload_str.is_a?(String) ? JSON.parse(payload_str) : payload_str
|
|
788
810
|
key = payload[Uniqueness::METADATA_KEY]
|
|
789
|
-
|
|
811
|
+
UniquenessKey.release!(key) if key
|
|
790
812
|
rescue JSON::ParserError => e
|
|
791
813
|
Pgbus.logger.debug { "[Pgbus::Web] Error parsing payload for lock release: #{e.message}" }
|
|
792
814
|
end
|
|
@@ -804,7 +826,7 @@ module Pgbus
|
|
|
804
826
|
nil
|
|
805
827
|
end
|
|
806
828
|
|
|
807
|
-
|
|
829
|
+
UniquenessKey.where(lock_key: keys).delete_all if keys.any?
|
|
808
830
|
rescue StandardError => e
|
|
809
831
|
Pgbus.logger.debug { "[Pgbus::Web] Error releasing locks for messages: #{e.message}" }
|
|
810
832
|
end
|
|
@@ -822,10 +844,19 @@ module Pgbus
|
|
|
822
844
|
nil
|
|
823
845
|
end
|
|
824
846
|
|
|
825
|
-
|
|
847
|
+
UniquenessKey.where(lock_key: keys).delete_all if keys.any?
|
|
826
848
|
rescue StandardError => e
|
|
827
849
|
Pgbus.logger.debug { "[Pgbus::Web] Error releasing locks for failed events: #{e.message}" }
|
|
828
850
|
end
|
|
851
|
+
|
|
852
|
+
# Release all uniqueness keys associated with a queue before purge/drop.
|
|
853
|
+
# Scans queue messages for uniqueness metadata and deletes matching rows.
|
|
854
|
+
def release_uniqueness_keys_for_queue(queue_name)
|
|
855
|
+
messages = query_queue_messages_raw(queue_name, 10_000, 0)
|
|
856
|
+
release_locks_for_messages(messages)
|
|
857
|
+
rescue StandardError => e
|
|
858
|
+
Pgbus.logger.debug { "[Pgbus::Web] Error releasing uniqueness keys for queue #{queue_name}: #{e.message}" }
|
|
859
|
+
end
|
|
829
860
|
end
|
|
830
861
|
end
|
|
831
862
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: pgbus
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.3.
|
|
4
|
+
version: 0.3.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Mikael Henriksson
|
|
@@ -155,6 +155,7 @@ files:
|
|
|
155
155
|
- app/models/pgbus/recurring_execution.rb
|
|
156
156
|
- app/models/pgbus/recurring_task.rb
|
|
157
157
|
- app/models/pgbus/semaphore.rb
|
|
158
|
+
- app/models/pgbus/uniqueness_key.rb
|
|
158
159
|
- app/views/layouts/pgbus/application.html.erb
|
|
159
160
|
- app/views/pgbus/dashboard/_processes_table.html.erb
|
|
160
161
|
- app/views/pgbus/dashboard/_queues_table.html.erb
|
|
@@ -204,12 +205,15 @@ files:
|
|
|
204
205
|
- lib/generators/pgbus/add_queue_states_generator.rb
|
|
205
206
|
- lib/generators/pgbus/add_recurring_generator.rb
|
|
206
207
|
- lib/generators/pgbus/install_generator.rb
|
|
208
|
+
- lib/generators/pgbus/migrate_job_locks_generator.rb
|
|
207
209
|
- lib/generators/pgbus/templates/add_job_locks.rb.erb
|
|
208
210
|
- lib/generators/pgbus/templates/add_job_stats.rb.erb
|
|
209
211
|
- lib/generators/pgbus/templates/add_job_stats_latency.rb.erb
|
|
210
212
|
- lib/generators/pgbus/templates/add_outbox.rb.erb
|
|
211
213
|
- lib/generators/pgbus/templates/add_queue_states.rb.erb
|
|
212
214
|
- lib/generators/pgbus/templates/add_recurring_tables.rb.erb
|
|
215
|
+
- lib/generators/pgbus/templates/add_uniqueness_keys.rb.erb
|
|
216
|
+
- lib/generators/pgbus/templates/migrate_job_locks_to_uniqueness_keys.rb.erb
|
|
213
217
|
- lib/generators/pgbus/templates/migration.rb.erb
|
|
214
218
|
- lib/generators/pgbus/templates/pgbus.yml.erb
|
|
215
219
|
- lib/generators/pgbus/templates/pgbus_binstub.erb
|
|
@@ -261,6 +265,7 @@ files:
|
|
|
261
265
|
- lib/pgbus/recurring/scheduler.rb
|
|
262
266
|
- lib/pgbus/recurring/task.rb
|
|
263
267
|
- lib/pgbus/serializer.rb
|
|
268
|
+
- lib/pgbus/stat_buffer.rb
|
|
264
269
|
- lib/pgbus/uniqueness.rb
|
|
265
270
|
- lib/pgbus/version.rb
|
|
266
271
|
- lib/pgbus/web/authentication.rb
|