pgbus 0.0.1 → 0.1.2

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.
Files changed (83) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +37 -3
  3. data/Rakefile +98 -1
  4. data/app/controllers/pgbus/application_controller.rb +8 -0
  5. data/app/controllers/pgbus/recurring_tasks_controller.rb +36 -0
  6. data/app/helpers/pgbus/application_helper.rb +41 -0
  7. data/app/models/pgbus/application_record.rb +7 -0
  8. data/app/models/pgbus/batch_entry.rb +31 -0
  9. data/app/models/pgbus/blocked_execution.rb +40 -0
  10. data/app/models/pgbus/process_entry.rb +9 -0
  11. data/app/models/pgbus/processed_event.rb +9 -0
  12. data/app/models/pgbus/recurring_execution.rb +33 -0
  13. data/app/models/pgbus/recurring_task.rb +42 -0
  14. data/app/models/pgbus/semaphore.rb +29 -0
  15. data/app/views/layouts/pgbus/application.html.erb +1 -0
  16. data/app/views/pgbus/dashboard/_stats_cards.html.erb +9 -1
  17. data/app/views/pgbus/dead_letter/_messages_table.html.erb +55 -18
  18. data/app/views/pgbus/jobs/_enqueued_table.html.erb +46 -8
  19. data/app/views/pgbus/recurring_tasks/_tasks_table.html.erb +79 -0
  20. data/app/views/pgbus/recurring_tasks/index.html.erb +6 -0
  21. data/app/views/pgbus/recurring_tasks/show.html.erb +122 -0
  22. data/config/routes.rb +7 -0
  23. data/lib/active_job/queue_adapters/pgbus_adapter.rb +29 -0
  24. data/lib/generators/pgbus/add_recurring_generator.rb +56 -0
  25. data/lib/generators/pgbus/install_generator.rb +76 -2
  26. data/lib/generators/pgbus/templates/add_recurring_tables.rb.erb +31 -0
  27. data/lib/generators/pgbus/templates/migration.rb.erb +72 -4
  28. data/lib/generators/pgbus/templates/recurring.yml.erb +40 -0
  29. data/lib/generators/pgbus/templates/upgrade_pgmq.rb.erb +30 -0
  30. data/lib/generators/pgbus/upgrade_pgmq_generator.rb +60 -0
  31. data/lib/pgbus/active_job/adapter.rb +3 -6
  32. data/lib/pgbus/active_job/executor.rb +26 -12
  33. data/lib/pgbus/batch.rb +65 -72
  34. data/lib/pgbus/cli.rb +11 -16
  35. data/lib/pgbus/client.rb +32 -15
  36. data/lib/pgbus/concurrency/blocked_execution.rb +32 -37
  37. data/lib/pgbus/concurrency/semaphore.rb +11 -39
  38. data/lib/pgbus/concurrency.rb +10 -2
  39. data/lib/pgbus/configuration.rb +48 -0
  40. data/lib/pgbus/engine.rb +19 -1
  41. data/lib/pgbus/event_bus/handler.rb +10 -23
  42. data/lib/pgbus/instrumentation.rb +29 -0
  43. data/lib/pgbus/pgmq_schema/pgmq_v1.11.0.sql +2123 -0
  44. data/lib/pgbus/pgmq_schema.rb +159 -0
  45. data/lib/pgbus/process/consumer.rb +17 -9
  46. data/lib/pgbus/process/dispatcher.rb +33 -41
  47. data/lib/pgbus/process/heartbeat.rb +15 -23
  48. data/lib/pgbus/process/signal_handler.rb +23 -1
  49. data/lib/pgbus/process/supervisor.rb +79 -2
  50. data/lib/pgbus/process/worker.rb +42 -13
  51. data/lib/pgbus/recurring/already_recorded.rb +7 -0
  52. data/lib/pgbus/recurring/command_job.rb +28 -0
  53. data/lib/pgbus/recurring/config_loader.rb +35 -0
  54. data/lib/pgbus/recurring/schedule.rb +102 -0
  55. data/lib/pgbus/recurring/scheduler.rb +102 -0
  56. data/lib/pgbus/recurring/task.rb +111 -0
  57. data/lib/pgbus/serializer.rb +16 -6
  58. data/lib/pgbus/version.rb +1 -1
  59. data/lib/pgbus/web/data_source.rb +217 -36
  60. data/lib/pgbus.rb +8 -0
  61. data/lib/tasks/pgbus_pgmq.rake +62 -0
  62. metadata +51 -24
  63. data/.bun-version +0 -1
  64. data/.claude/commands/architect.md +0 -100
  65. data/.claude/commands/github-review-comments.md +0 -237
  66. data/.claude/commands/lfg.md +0 -271
  67. data/.claude/commands/review-pr.md +0 -69
  68. data/.claude/commands/security.md +0 -122
  69. data/.claude/commands/tdd.md +0 -148
  70. data/.claude/rules/agents.md +0 -49
  71. data/.claude/rules/coding-style.md +0 -91
  72. data/.claude/rules/git-workflow.md +0 -56
  73. data/.claude/rules/performance.md +0 -73
  74. data/.claude/rules/testing.md +0 -67
  75. data/CLAUDE.md +0 -80
  76. data/CODE_OF_CONDUCT.md +0 -10
  77. data/bun.lock +0 -18
  78. data/docs/README.md +0 -28
  79. data/docs/switch_from_good_job.md +0 -279
  80. data/docs/switch_from_sidekiq.md +0 -226
  81. data/docs/switch_from_solid_queue.md +0 -247
  82. data/package.json +0 -9
  83. data/sig/pgbus.rbs +0 -4
@@ -1,247 +0,0 @@
1
- # Switch from SolidQueue to Pgbus
2
-
3
- ## Overview
4
-
5
- SolidQueue and Pgbus are both PostgreSQL-backed job processors with similar architectures: supervisor/worker process model, `FOR UPDATE SKIP LOCKED` for contention-free polling, and forked processes. The migration is straightforward since both are ActiveJob adapters.
6
-
7
- **Key differences:** Pgbus adds LISTEN/NOTIFY for instant wake-up (SolidQueue only polls), dead letter queues, worker recycling, and an event bus. Pgbus uses PGMQ under the hood instead of custom tables.
8
-
9
- **Effort estimate:** Low. Both are pure ActiveJob adapters, so your jobs work unchanged.
10
-
11
- ## Step 1: Update dependencies
12
-
13
- ```ruby
14
- # Gemfile
15
-
16
- # Remove
17
- gem "solid_queue"
18
- gem "mission_control-jobs" # if used
19
-
20
- # Add
21
- gem "pgbus"
22
- ```
23
-
24
- ```bash
25
- bundle install
26
- rails generate pgbus:install
27
- rails db:migrate
28
- ```
29
-
30
- ## Step 2: Switch the adapter
31
-
32
- ```ruby
33
- # config/application.rb
34
-
35
- # Before
36
- config.active_job.queue_adapter = :solid_queue
37
-
38
- # After
39
- config.active_job.queue_adapter = :pgbus
40
- ```
41
-
42
- If you set the adapter per-environment or use `config.solid_queue.connects_to` for a separate queue database, update those too.
43
-
44
- ## Step 3: Convert worker configuration
45
-
46
- ```yaml
47
- # Before: config/queue.yml (SolidQueue)
48
- production:
49
- dispatchers:
50
- - polling_interval: 1
51
- batch_size: 500
52
- workers:
53
- - queues: "critical"
54
- threads: 5
55
- processes: 2
56
- polling_interval: 0.1
57
- - queues: "default,low"
58
- threads: 3
59
- processes: 3
60
- polling_interval: 1
61
- ```
62
-
63
- ```yaml
64
- # After: config/pgbus.yml
65
- production:
66
- workers:
67
- - queues: [critical]
68
- threads: 5
69
- - queues: [critical]
70
- threads: 5
71
- - queues: [default, low]
72
- threads: 3
73
- - queues: [default, low]
74
- threads: 3
75
- - queues: [default, low]
76
- threads: 3
77
- max_retries: 5
78
- max_jobs_per_worker: 10000
79
- max_memory_mb: 512
80
- ```
81
-
82
- > **Note:** SolidQueue's `processes: N` forks N identical workers. In Pgbus, list the same worker config N times, or let the supervisor handle it via configuration (one entry per process).
83
-
84
- ### Configuration mapping
85
-
86
- | SolidQueue | Pgbus | Notes |
87
- |------------|-------|-------|
88
- | `polling_interval` | `polling_interval` | Pgbus defaults to 0.1s; LISTEN/NOTIFY makes this a fallback only |
89
- | `threads` | `threads` | Same concept |
90
- | `processes` | Repeat worker entry | One entry per forked process |
91
- | `dispatchers[].batch_size` | N/A | Pgbus dispatcher does maintenance, not dispatch |
92
- | `queues: "a,b"` (string) | `queues: [a, b]` (array) | Different format |
93
- | `queues: "*"` (wildcard) | List queues explicitly | PGMQ queues are explicit |
94
-
95
- ## Step 4: Remove concurrency controls
96
-
97
- SolidQueue's `limits_concurrency` is a SolidQueue-specific mixin. Remove it from your jobs:
98
-
99
- ```ruby
100
- # Before: SolidQueue concurrency control
101
- class ProcessOrderJob < ApplicationJob
102
- limits_concurrency to: 1,
103
- key: ->(order) { order.account_id },
104
- duration: 15.minutes,
105
- on_conflict: :block
106
-
107
- def perform(order)
108
- # ...
109
- end
110
- end
111
- ```
112
-
113
- ```ruby
114
- # After: Remove the SolidQueue mixin
115
- class ProcessOrderJob < ApplicationJob
116
- def perform(order)
117
- # ...
118
- end
119
- end
120
- ```
121
-
122
- > Pgbus supports concurrency controls via `Pgbus::Concurrency`:
123
- > ```ruby
124
- > class ProcessOrderJob < ApplicationJob
125
- > include Pgbus::Concurrency
126
- > limits_concurrency to: 1,
127
- > key: ->(order) { order.account_id },
128
- > duration: 15.minutes,
129
- > on_conflict: :block
130
- > def perform(order)
131
- > # ...
132
- > end
133
- > end
134
- > ```
135
-
136
- ## Step 5: Migrate recurring tasks
137
-
138
- If you use SolidQueue's `config/recurring.yml`:
139
-
140
- ```yaml
141
- # Before: config/recurring.yml (SolidQueue)
142
- production:
143
- daily_cleanup:
144
- class: CleanupJob
145
- schedule: "every day at 2am"
146
- queue: maintenance
147
- hourly_sync:
148
- class: SyncJob
149
- schedule: "0 * * * *"
150
- args: [42, "sync"]
151
- ```
152
-
153
- Pgbus does not yet have built-in recurring task support. Options:
154
-
155
- 1. **Use the `whenever` gem** for cron-based scheduling:
156
- ```ruby
157
- # config/schedule.rb (whenever gem)
158
- every 1.day, at: "2:00 am" do
159
- runner "CleanupJob.perform_later"
160
- end
161
- every :hour do
162
- runner "SyncJob.perform_later(42, 'sync')"
163
- end
164
- ```
165
-
166
- 2. **Use system cron** directly:
167
- ```cron
168
- 0 2 * * * cd /app && bin/rails runner "CleanupJob.perform_later"
169
- 0 * * * * cd /app && bin/rails runner "SyncJob.perform_later(42, 'sync')"
170
- ```
171
-
172
- 3. Wait for Pgbus recurring task support (planned).
173
-
174
- ## Step 6: Replace the dashboard
175
-
176
- ```ruby
177
- # Before: config/routes.rb
178
- mount MissionControl::Jobs::Engine, at: "/jobs"
179
-
180
- # After:
181
- mount Pgbus::Engine => "/pgbus"
182
- ```
183
-
184
- ## Step 7: Update process management
185
-
186
- ```bash
187
- # Before
188
- bundle exec rake solid_queue:start
189
-
190
- # After
191
- bundle exec pgbus start
192
- ```
193
-
194
- ## Step 8: Clean up SolidQueue tables
195
-
196
- After verifying Pgbus processes jobs correctly and SolidQueue's tables are drained:
197
-
198
- ```ruby
199
- class RemoveSolidQueue < ActiveRecord::Migration[7.1]
200
- def up
201
- drop_table :solid_queue_blocked_executions, if_exists: true
202
- drop_table :solid_queue_claimed_executions, if_exists: true
203
- drop_table :solid_queue_failed_executions, if_exists: true
204
- drop_table :solid_queue_pauses, if_exists: true
205
- drop_table :solid_queue_processes, if_exists: true
206
- drop_table :solid_queue_ready_executions, if_exists: true
207
- drop_table :solid_queue_recurring_executions, if_exists: true
208
- drop_table :solid_queue_recurring_tasks, if_exists: true
209
- drop_table :solid_queue_scheduled_executions, if_exists: true
210
- drop_table :solid_queue_semaphores, if_exists: true
211
- drop_table :solid_queue_jobs, if_exists: true
212
- end
213
- end
214
- ```
215
-
216
- ## What you gain
217
-
218
- - **LISTEN/NOTIFY** -- SolidQueue only polls (100-150ms latency baseline). Pgbus wakes workers instantly via PostgreSQL LISTEN/NOTIFY, with polling as fallback only.
219
- - **Dead letter queues** -- SolidQueue marks jobs as failed but keeps them in the same table. Pgbus routes failures to dedicated `_dlq` queues after `max_retries` for clear separation.
220
- - **Worker recycling** -- SolidQueue workers run indefinitely. Pgbus recycles workers by job count, memory, or lifetime to prevent memory bloat.
221
- - **Event bus** -- AMQP-style pub/sub with topic routing, not available in SolidQueue.
222
- - **PGMQ under the hood** -- battle-tested message queue extension with visibility timeouts and atomic operations.
223
-
224
- ## What you lose (for now)
225
-
226
- | SolidQueue feature | Status in Pgbus |
227
- |--------------------|-----------------|
228
- | `limits_concurrency` | `Pgbus::Concurrency` with `limits_concurrency` DSL |
229
- | `config/recurring.yml` | Planned |
230
- | Queue pausing (`SolidQueue::Queue.pause`) | Planned |
231
- | Separate queue database | Not planned (PGMQ lives in your primary DB) |
232
- | Numeric job priorities | PGMQ reads in FIFO order per queue; use separate queues for priority |
233
-
234
- ## Gotchas
235
-
236
- 1. **PgBouncer**: If you run PgBouncer in transaction mode, LISTEN/NOTIFY won't work. Use session mode or direct connections for Pgbus worker processes. This also applies to PGMQ's `FOR UPDATE SKIP LOCKED`.
237
-
238
- 2. **Separate queue database**: SolidQueue supports `connects_to` for a dedicated queue DB. Pgbus requires PGMQ in the same database it connects to. If you need isolation, use a separate PostgreSQL database with PGMQ installed and configure `Pgbus.configuration.database_url`.
239
-
240
- 3. **Queue naming**: SolidQueue uses bare queue names (`default`). Pgbus prefixes all queues (`pgbus_default`). Your `queue_as :default` declarations work unchanged -- the prefix is applied automatically.
241
-
242
- 4. **`ActionMailer::MailDeliveryJob`**: This can bypass the application-level adapter setting in some Rails versions. If mailer jobs don't appear in Pgbus, add to `ApplicationMailer`:
243
- ```ruby
244
- class ApplicationMailer < ActionMailer::Base
245
- self.deliver_later_queue_name = :mailers
246
- end
247
- ```
data/package.json DELETED
@@ -1,9 +0,0 @@
1
- {
2
- "private": true,
3
- "engines": {
4
- "bun": ">=1.1.0"
5
- },
6
- "devDependencies": {
7
- "playwright": "^1.50.0"
8
- }
9
- }
data/sig/pgbus.rbs DELETED
@@ -1,4 +0,0 @@
1
- module Pgbus
2
- VERSION: String
3
- # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
- end