postburner 1.0.0.pre.11 → 1.0.0.pre.12

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 (34) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +961 -555
  3. data/app/concerns/postburner/commands.rb +1 -1
  4. data/app/concerns/postburner/execution.rb +11 -11
  5. data/app/concerns/postburner/insertion.rb +1 -1
  6. data/app/concerns/postburner/logging.rb +2 -2
  7. data/app/concerns/postburner/statistics.rb +1 -1
  8. data/app/models/postburner/job.rb +27 -4
  9. data/app/models/postburner/mailer.rb +1 -1
  10. data/app/models/postburner/schedule.rb +703 -0
  11. data/app/models/postburner/schedule_execution.rb +353 -0
  12. data/app/views/postburner/jobs/show.html.haml +3 -3
  13. data/lib/generators/postburner/install/install_generator.rb +1 -0
  14. data/lib/generators/postburner/install/templates/config/postburner.yml +15 -6
  15. data/lib/generators/postburner/install/templates/migrations/create_postburner_schedules.rb.erb +71 -0
  16. data/lib/postburner/active_job/adapter.rb +3 -3
  17. data/lib/postburner/active_job/payload.rb +5 -0
  18. data/lib/postburner/advisory_lock.rb +123 -0
  19. data/lib/postburner/configuration.rb +43 -7
  20. data/lib/postburner/connection.rb +7 -6
  21. data/lib/postburner/runner.rb +26 -3
  22. data/lib/postburner/scheduler.rb +427 -0
  23. data/lib/postburner/strategies/immediate_test_queue.rb +24 -7
  24. data/lib/postburner/strategies/nice_queue.rb +1 -1
  25. data/lib/postburner/strategies/null_queue.rb +2 -2
  26. data/lib/postburner/strategies/test_queue.rb +2 -2
  27. data/lib/postburner/time_helpers.rb +4 -2
  28. data/lib/postburner/tube.rb +9 -1
  29. data/lib/postburner/version.rb +1 -1
  30. data/lib/postburner/worker.rb +684 -0
  31. data/lib/postburner.rb +32 -13
  32. metadata +7 -3
  33. data/lib/postburner/workers/base.rb +0 -205
  34. data/lib/postburner/workers/worker.rb +0 -396
@@ -105,6 +105,10 @@ module Postburner
105
105
  # is invoked. If the job has a future run_at, travels to that time before
106
106
  # execution. Otherwise executes immediately.
107
107
  #
108
+ # Uses recursion detection to prevent infinite loops when jobs create and
109
+ # enqueue other jobs (e.g., schedule callbacks). Jobs enqueued while already
110
+ # inside a perform! call are queued but not executed immediately.
111
+ #
108
112
  # @param job [Postburner::Job] The job to execute
109
113
  # @param options [Hash] Unused in test mode
110
114
  #
@@ -115,17 +119,30 @@ module Postburner
115
119
  # @api private
116
120
  #
117
121
  def insert(job, options = {})
118
- # If job has a future run_at, travel to that time for execution
119
- if job.run_at && job.run_at > Time.zone.now
120
- travel_to(job.run_at) do
122
+ # Detect recursion: if we're already performing a job, don't execute
123
+ # the newly enqueued job immediately. This prevents infinite loops
124
+ # when job callbacks (like schedule_next_execution) enqueue new jobs.
125
+ if Thread.current[:postburner_performing]
126
+ return { status: 'INLINE_DEFERRED', id: nil }
127
+ end
128
+
129
+ begin
130
+ Thread.current[:postburner_performing] = true
131
+
132
+ # If job has a future run_at, travel to that time for execution
133
+ if job.run_at && job.run_at > Time.current
134
+ travel_to(job.run_at) do
135
+ job.perform!(job.args)
136
+ end
137
+ else
138
+ # No future run_at, execute normally
121
139
  job.perform!(job.args)
122
140
  end
123
- else
124
- # No future run_at, execute normally
125
- job.perform!(job.args)
141
+ ensure
142
+ Thread.current[:postburner_performing] = false
126
143
  end
127
144
 
128
- # Return format matching Backburner response (symbol keys)
145
+ # Return format matching Beanstalkd response (symbol keys)
129
146
  { status: 'INLINE', id: nil }
130
147
  end
131
148
  end
@@ -77,7 +77,7 @@ module Postburner
77
77
  # @note Logs "PREMATURE; RE-INSERTED" message to job's audit trail
78
78
  #
79
79
  def handle_premature_perform(job)
80
- response = job.insert! delay: job.run_at - Time.zone.now
80
+ response = job.insert! delay: job.run_at - Time.current
81
81
  job.log! "PREMATURE; RE-INSERTED: #{response}"
82
82
  end
83
83
  end
@@ -88,7 +88,7 @@ module Postburner
88
88
  # @api private
89
89
  #
90
90
  def insert(job, options = {})
91
- # Return format matching Backburner response (symbol keys)
91
+ # Return format matching Beanstalkd response (symbol keys)
92
92
  { status: 'NULL', id: nil }
93
93
  end
94
94
 
@@ -118,7 +118,7 @@ module Postburner
118
118
  # @api private
119
119
  #
120
120
  def handle_perform!(job)
121
- if job.run_at && job.run_at > Time.zone.now
121
+ if job.run_at && job.run_at > Time.current
122
122
  travel_to(job.run_at) do
123
123
  job.perform!(job.args)
124
124
  end
@@ -105,7 +105,7 @@ module Postburner
105
105
  # Will raise PrematurePerform if run_at is in the future
106
106
  job.perform!(job.args)
107
107
 
108
- # Return format matching Backburner response (symbol keys)
108
+ # Return format matching Beanstalkd response (symbol keys)
109
109
  { status: 'INLINE', id: nil }
110
110
  end
111
111
 
@@ -121,7 +121,7 @@ module Postburner
121
121
  # @raise [Postburner::Job::PrematurePerform] Always raises with helpful message
122
122
  #
123
123
  def handle_premature_perform(job)
124
- raise Postburner::Job::PrematurePerform, "Job scheduled for #{job.run_at} (#{((job.run_at - Time.zone.now) / 60).round(1)} minutes from now). Use `travel_to(job.run_at)` in your test, or set `Postburner.inline_immediate_test_strategy!` to execute scheduled jobs immediately."
124
+ raise Postburner::Job::PrematurePerform, "Job scheduled for #{job.run_at} (#{((job.run_at - Time.current) / 60).round(1)} minutes from now). Use `travel_to(job.run_at)` in your test, or set `Postburner.inline_immediate_test_strategy!` to execute scheduled jobs immediately."
125
125
  end
126
126
  end
127
127
  end
@@ -18,7 +18,8 @@ module Postburner
18
18
  #
19
19
  # travel_to(2.days.from_now) do
20
20
  # # Code here executes as if it's 2 days in the future
21
- # Time.zone.now # => 2 days from now
21
+ # Time.current # => 2 days from now (preferred in Rails)
22
+ # Time.zone.now # => 2 days from now (equivalent)
22
23
  # end
23
24
  #
24
25
  # @example In a class method
@@ -54,7 +55,8 @@ module Postburner
54
55
  #
55
56
  # @example Travel to specific time
56
57
  # travel_to(Time.zone.parse('2025-12-25 00:00:00')) do
57
- # puts Time.zone.now # => 2025-12-25 00:00:00
58
+ # puts Time.current # => 2025-12-25 00:00:00 (preferred)
59
+ # puts Time.zone.now # => 2025-12-25 00:00:00 (equivalent)
58
60
  # end
59
61
  #
60
62
  # @example Travel relative to now
@@ -31,7 +31,11 @@ module Postburner
31
31
  # Just pass the last known id to after for the next batch.
32
32
  #
33
33
  def jobs(count=20, limit: 1000, after: nil)
34
+ # Access raw hash to avoid beaneater FastStruct method definition issues in Ruby 3.4
34
35
  stats = @tube.stats
36
+ stats_hash = stats.instance_variable_get(:@hash) || {}
37
+ tube_name = stats_hash['name']
38
+
35
39
  jobs = Array.new
36
40
 
37
41
  min_known = (
@@ -42,7 +46,11 @@ module Postburner
42
46
 
43
47
  for i in min..max
44
48
  job = @tube.client.jobs.find(i)
45
- jobs << job if job && stats[:name] == job.stats[:tube]
49
+ if job
50
+ job_stats = job.stats
51
+ job_stats_hash = job_stats.instance_variable_get(:@hash) || {}
52
+ jobs << job if job_stats_hash['tube'] == tube_name
53
+ end
46
54
  break if jobs.length >= count
47
55
  end
48
56
 
@@ -1,3 +1,3 @@
1
1
  module Postburner
2
- VERSION = '1.0.0.pre.11'
2
+ VERSION = '1.0.0.pre.12'
3
3
  end