pgbus 0.0.1 → 0.1.1
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/README.md +37 -3
- data/Rakefile +98 -1
- data/app/controllers/pgbus/application_controller.rb +8 -0
- data/app/controllers/pgbus/recurring_tasks_controller.rb +36 -0
- data/app/helpers/pgbus/application_helper.rb +41 -0
- data/app/models/pgbus/application_record.rb +7 -0
- data/app/models/pgbus/batch_entry.rb +31 -0
- data/app/models/pgbus/blocked_execution.rb +40 -0
- data/app/models/pgbus/process_entry.rb +9 -0
- data/app/models/pgbus/processed_event.rb +9 -0
- data/app/models/pgbus/recurring_execution.rb +33 -0
- data/app/models/pgbus/recurring_task.rb +42 -0
- data/app/models/pgbus/semaphore.rb +29 -0
- data/app/views/layouts/pgbus/application.html.erb +1 -0
- data/app/views/pgbus/dashboard/_stats_cards.html.erb +9 -1
- data/app/views/pgbus/dead_letter/_messages_table.html.erb +55 -18
- data/app/views/pgbus/jobs/_enqueued_table.html.erb +46 -8
- data/app/views/pgbus/recurring_tasks/_tasks_table.html.erb +79 -0
- data/app/views/pgbus/recurring_tasks/index.html.erb +6 -0
- data/app/views/pgbus/recurring_tasks/show.html.erb +122 -0
- data/config/routes.rb +7 -0
- data/lib/active_job/queue_adapters/pgbus_adapter.rb +29 -0
- data/lib/generators/pgbus/add_recurring_generator.rb +56 -0
- data/lib/generators/pgbus/install_generator.rb +76 -2
- data/lib/generators/pgbus/templates/add_recurring_tables.rb.erb +31 -0
- data/lib/generators/pgbus/templates/migration.rb.erb +72 -4
- data/lib/generators/pgbus/templates/recurring.yml.erb +40 -0
- data/lib/generators/pgbus/templates/upgrade_pgmq.rb.erb +30 -0
- data/lib/generators/pgbus/upgrade_pgmq_generator.rb +60 -0
- data/lib/pgbus/active_job/adapter.rb +0 -3
- data/lib/pgbus/active_job/executor.rb +27 -12
- data/lib/pgbus/batch.rb +60 -69
- data/lib/pgbus/cli.rb +11 -16
- data/lib/pgbus/client.rb +25 -7
- data/lib/pgbus/concurrency/blocked_execution.rb +32 -37
- data/lib/pgbus/concurrency/semaphore.rb +11 -39
- data/lib/pgbus/concurrency.rb +10 -2
- data/lib/pgbus/configuration.rb +33 -0
- data/lib/pgbus/engine.rb +19 -1
- data/lib/pgbus/event_bus/handler.rb +4 -14
- data/lib/pgbus/instrumentation.rb +29 -0
- data/lib/pgbus/pgmq_schema/pgmq_v1.11.0.sql +2123 -0
- data/lib/pgbus/pgmq_schema.rb +159 -0
- data/lib/pgbus/process/consumer.rb +8 -9
- data/lib/pgbus/process/dispatcher.rb +26 -24
- data/lib/pgbus/process/heartbeat.rb +15 -23
- data/lib/pgbus/process/signal_handler.rb +23 -1
- data/lib/pgbus/process/supervisor.rb +51 -2
- data/lib/pgbus/process/worker.rb +37 -9
- data/lib/pgbus/recurring/already_recorded.rb +7 -0
- data/lib/pgbus/recurring/command_job.rb +16 -0
- data/lib/pgbus/recurring/config_loader.rb +35 -0
- data/lib/pgbus/recurring/schedule.rb +102 -0
- data/lib/pgbus/recurring/scheduler.rb +102 -0
- data/lib/pgbus/recurring/task.rb +111 -0
- data/lib/pgbus/serializer.rb +10 -6
- data/lib/pgbus/version.rb +1 -1
- data/lib/pgbus/web/data_source.rb +187 -22
- data/lib/pgbus.rb +8 -0
- data/lib/tasks/pgbus_pgmq.rake +62 -0
- metadata +51 -24
- data/.bun-version +0 -1
- data/.claude/commands/architect.md +0 -100
- data/.claude/commands/github-review-comments.md +0 -237
- data/.claude/commands/lfg.md +0 -271
- data/.claude/commands/review-pr.md +0 -69
- data/.claude/commands/security.md +0 -122
- data/.claude/commands/tdd.md +0 -148
- data/.claude/rules/agents.md +0 -49
- data/.claude/rules/coding-style.md +0 -91
- data/.claude/rules/git-workflow.md +0 -56
- data/.claude/rules/performance.md +0 -73
- data/.claude/rules/testing.md +0 -67
- data/CLAUDE.md +0 -80
- data/CODE_OF_CONDUCT.md +0 -10
- data/bun.lock +0 -18
- data/docs/README.md +0 -28
- data/docs/switch_from_good_job.md +0 -279
- data/docs/switch_from_sidekiq.md +0 -226
- data/docs/switch_from_solid_queue.md +0 -247
- data/package.json +0 -9
- data/sig/pgbus.rbs +0 -4
data/lib/pgbus/configuration.rb
CHANGED
|
@@ -31,9 +31,21 @@ module Pgbus
|
|
|
31
31
|
# LISTEN/NOTIFY
|
|
32
32
|
attr_accessor :listen_notify, :notify_throttle_ms
|
|
33
33
|
|
|
34
|
+
# PGMQ schema installation mode (:auto, :extension, :embedded)
|
|
35
|
+
attr_reader :pgmq_schema_mode
|
|
36
|
+
|
|
34
37
|
# Event consumers
|
|
35
38
|
attr_accessor :event_consumers
|
|
36
39
|
|
|
40
|
+
# Recurring jobs
|
|
41
|
+
attr_accessor :recurring_tasks, :recurring_schedule_interval, :recurring_tasks_file,
|
|
42
|
+
:skip_recurring, :recurring_execution_retention
|
|
43
|
+
|
|
44
|
+
# Multi-database support (optional separate database for pgbus tables)
|
|
45
|
+
# Set to { database: { writing: :pgbus, reading: :pgbus } } to use a separate database.
|
|
46
|
+
# Requires a matching entry in config/database.yml under the "pgbus" key.
|
|
47
|
+
attr_accessor :connects_to
|
|
48
|
+
|
|
37
49
|
# Web dashboard
|
|
38
50
|
attr_accessor :web_auth, :web_refresh_interval, :web_per_page, :web_live_updates, :web_data_source
|
|
39
51
|
|
|
@@ -66,8 +78,18 @@ module Pgbus
|
|
|
66
78
|
@listen_notify = true
|
|
67
79
|
@notify_throttle_ms = 250
|
|
68
80
|
|
|
81
|
+
@pgmq_schema_mode = :auto
|
|
82
|
+
|
|
69
83
|
@event_consumers = nil
|
|
70
84
|
|
|
85
|
+
@recurring_tasks = nil
|
|
86
|
+
@recurring_schedule_interval = 1.0
|
|
87
|
+
@recurring_tasks_file = nil
|
|
88
|
+
@skip_recurring = false
|
|
89
|
+
@recurring_execution_retention = 7 * 24 * 3600 # 7 days
|
|
90
|
+
|
|
91
|
+
@connects_to = nil
|
|
92
|
+
|
|
71
93
|
@web_auth = nil
|
|
72
94
|
@web_refresh_interval = 5000
|
|
73
95
|
@web_per_page = 25
|
|
@@ -83,6 +105,17 @@ module Pgbus
|
|
|
83
105
|
"#{queue_name(name)}#{dead_letter_queue_suffix}"
|
|
84
106
|
end
|
|
85
107
|
|
|
108
|
+
VALID_PGMQ_SCHEMA_MODES = %i[auto extension embedded].freeze
|
|
109
|
+
|
|
110
|
+
def pgmq_schema_mode=(mode)
|
|
111
|
+
mode = mode.to_sym
|
|
112
|
+
unless VALID_PGMQ_SCHEMA_MODES.include?(mode)
|
|
113
|
+
raise ArgumentError, "Invalid pgmq_schema_mode: #{mode}. Must be one of: #{VALID_PGMQ_SCHEMA_MODES.join(", ")}"
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
@pgmq_schema_mode = mode
|
|
117
|
+
end
|
|
118
|
+
|
|
86
119
|
def connection_options
|
|
87
120
|
if database_url
|
|
88
121
|
database_url
|
data/lib/pgbus/engine.rb
CHANGED
|
@@ -11,9 +11,23 @@ module Pgbus
|
|
|
11
11
|
Pgbus::ConfigLoader.load(config_path) if config_path.exist?
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
+
initializer "pgbus.recurring" do |app|
|
|
15
|
+
recurring_path = app.root.join("config", "recurring.yml")
|
|
16
|
+
if recurring_path.exist? && !Pgbus.configuration.recurring_tasks
|
|
17
|
+
Pgbus.configuration.recurring_tasks = Pgbus::Recurring::ConfigLoader.load(recurring_path)
|
|
18
|
+
Pgbus.configuration.recurring_tasks_file ||= recurring_path.to_s
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
initializer "pgbus.db" do
|
|
23
|
+
ActiveSupport.on_load(:active_record) do
|
|
24
|
+
Pgbus::ApplicationRecord.connects_to(**Pgbus.configuration.connects_to) if Pgbus.configuration.connects_to
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
14
28
|
initializer "pgbus.active_job" do
|
|
15
29
|
ActiveSupport.on_load(:active_job) do
|
|
16
|
-
|
|
30
|
+
include Pgbus::Concurrency
|
|
17
31
|
end
|
|
18
32
|
end
|
|
19
33
|
|
|
@@ -23,6 +37,10 @@ module Pgbus
|
|
|
23
37
|
end
|
|
24
38
|
end
|
|
25
39
|
|
|
40
|
+
rake_tasks do
|
|
41
|
+
load File.expand_path("../tasks/pgbus_pgmq.rake", __dir__)
|
|
42
|
+
end
|
|
43
|
+
|
|
26
44
|
initializer "pgbus.web" do
|
|
27
45
|
require "pgbus/web/authentication"
|
|
28
46
|
require "pgbus/web/data_source"
|
|
@@ -52,23 +52,13 @@ module Pgbus
|
|
|
52
52
|
end
|
|
53
53
|
|
|
54
54
|
def already_processed?(event_id)
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
ActiveRecord::Base.connection.select_value(
|
|
58
|
-
"SELECT 1 FROM pgbus_processed_events WHERE event_id = $1 AND handler_class = $2",
|
|
59
|
-
"Pgbus Idempotency Check",
|
|
60
|
-
[event_id, self.class.name]
|
|
61
|
-
)
|
|
55
|
+
ProcessedEvent.exists?(event_id: event_id, handler_class: self.class.name)
|
|
62
56
|
end
|
|
63
57
|
|
|
64
58
|
def mark_processed!(event_id)
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
"INSERT INTO pgbus_processed_events (event_id, handler_class, processed_at) " \
|
|
69
|
-
"VALUES ($1, $2, $3) ON CONFLICT (event_id, handler_class) DO NOTHING",
|
|
70
|
-
"Pgbus Idempotency Mark",
|
|
71
|
-
[event_id, self.class.name, Time.now.utc]
|
|
59
|
+
ProcessedEvent.upsert(
|
|
60
|
+
{ event_id: event_id, handler_class: self.class.name, processed_at: Time.now.utc },
|
|
61
|
+
unique_by: %i[event_id handler_class]
|
|
72
62
|
)
|
|
73
63
|
end
|
|
74
64
|
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Pgbus
|
|
4
|
+
# Lightweight instrumentation via ActiveSupport::Notifications.
|
|
5
|
+
#
|
|
6
|
+
# All events are prefixed with "pgbus." and carry timing information
|
|
7
|
+
# automatically when used with the block form of AS::Notifications.instrument.
|
|
8
|
+
#
|
|
9
|
+
# Events emitted:
|
|
10
|
+
# pgbus.client.send_message — single message enqueue
|
|
11
|
+
# pgbus.client.send_batch — batch enqueue
|
|
12
|
+
# pgbus.client.read_batch — batch dequeue
|
|
13
|
+
# pgbus.client.read_message — single message dequeue
|
|
14
|
+
# pgbus.executor.execute — full job execution (deserialize + perform + archive)
|
|
15
|
+
# pgbus.serializer.serialize — job/event serialization
|
|
16
|
+
# pgbus.serializer.deserialize — job/event deserialization
|
|
17
|
+
#
|
|
18
|
+
module Instrumentation
|
|
19
|
+
module_function
|
|
20
|
+
|
|
21
|
+
def instrument(event, payload = {}, &block)
|
|
22
|
+
if defined?(ActiveSupport::Notifications)
|
|
23
|
+
ActiveSupport::Notifications.instrument(event, payload, &block)
|
|
24
|
+
elsif block
|
|
25
|
+
yield payload
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|