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.
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 +0 -3
  32. data/lib/pgbus/active_job/executor.rb +27 -12
  33. data/lib/pgbus/batch.rb +60 -69
  34. data/lib/pgbus/cli.rb +11 -16
  35. data/lib/pgbus/client.rb +25 -7
  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 +33 -0
  40. data/lib/pgbus/engine.rb +19 -1
  41. data/lib/pgbus/event_bus/handler.rb +4 -14
  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 +8 -9
  46. data/lib/pgbus/process/dispatcher.rb +26 -24
  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 +51 -2
  50. data/lib/pgbus/process/worker.rb +37 -9
  51. data/lib/pgbus/recurring/already_recorded.rb +7 -0
  52. data/lib/pgbus/recurring/command_job.rb +16 -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 +10 -6
  58. data/lib/pgbus/version.rb +1 -1
  59. data/lib/pgbus/web/data_source.rb +187 -22
  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
@@ -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
- require "pgbus/active_job/adapter"
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
- return false unless defined?(ActiveRecord::Base)
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
- return unless defined?(ActiveRecord::Base)
66
-
67
- ActiveRecord::Base.connection.exec_insert(
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