postburner 1.0.0.rc.2 → 1.0.0.rc.3
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 +67 -5
- data/app/models/postburner/mailer.rb +14 -0
- data/lib/generators/postburner/install/install_generator.rb +10 -4
- data/lib/postburner/version.rb +1 -1
- data/lib/postburner.rb +39 -15
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9aa20d7fa2c867942efc6c70f7ad56548e651192068c9505a2854310cbb95043
|
|
4
|
+
data.tar.gz: 74606e227872a914c3a806227d74206d4b66a33b7160753cbbb48a15d48a7d9c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: cf4238e6bc83f1447de5c846032aad268d904fb5f74c1462424c69ffed8e40ffe316c51c2d248b2a6b46b7fb96df9b572f028368cc9e8e65e4baf8bffa7175b5
|
|
7
|
+
data.tar.gz: c93242ccd8501c5b337417f60633474104550360276c607a48308ed6bfcbcd5bd77aeafa9df7e6f0e6cb0f15094ffe4ada75ef13f2704e98c2fd31bc674fe791
|
data/README.md
CHANGED
|
@@ -85,7 +85,7 @@ bundle exec rake postburner:work WORKER=default
|
|
|
85
85
|
|
|
86
86
|
## Table of Contents
|
|
87
87
|
|
|
88
|
-
- [Why](#why)
|
|
88
|
+
- [Why Postburner?](#why-postburner)
|
|
89
89
|
- [Quick Start](#quick-start)
|
|
90
90
|
- [Usage](#usage)
|
|
91
91
|
- [Standard Jobs](#standard-jobs)
|
|
@@ -106,7 +106,7 @@ bundle exec rake postburner:work WORKER=default
|
|
|
106
106
|
- [Installation](#installation)
|
|
107
107
|
- [Web UI - v2 Coming Soon](#web-ui)
|
|
108
108
|
|
|
109
|
-
## Why
|
|
109
|
+
## Why Postburner?
|
|
110
110
|
|
|
111
111
|
Postburner supports Async, Queues, Delayed, Priorities, Timeouts, and Retries from the [Backend Matrix](https://api.rubyonrails.org/classes/ActiveJob/QueueAdapters.html). But uniquely, priorities are per job, in addition to the class level. Timeouts are per job and class level as well, and can be extended dynamically. Postburner also supports scheduling jobs at fixed intervals, cron expressions, and calendar-aware anchor points.
|
|
112
112
|
|
|
@@ -927,6 +927,53 @@ test "tracked job logs execution" do
|
|
|
927
927
|
end
|
|
928
928
|
```
|
|
929
929
|
|
|
930
|
+
### Testing Emails
|
|
931
|
+
|
|
932
|
+
Standard ActiveJob test assertions (`assert_enqueued_emails`, `assert_enqueued_jobs`) work when using the `:test` ActiveJob adapter in your test environment, which is the Rails default:
|
|
933
|
+
|
|
934
|
+
```ruby
|
|
935
|
+
class EmailTest < ActiveSupport::TestCase
|
|
936
|
+
# assert_enqueued_emails works — Rails' :test adapter tracks the enqueue
|
|
937
|
+
test "welcome email is enqueued" do
|
|
938
|
+
assert_enqueued_emails 1 do
|
|
939
|
+
UserMailer.welcome(user).deliver_later
|
|
940
|
+
end
|
|
941
|
+
end
|
|
942
|
+
|
|
943
|
+
# assert_emails works — the job executes inline, delivering the email
|
|
944
|
+
test "welcome email is sent" do
|
|
945
|
+
assert_emails 1 do
|
|
946
|
+
SendWelcomeEmail.perform_later(user.id)
|
|
947
|
+
end
|
|
948
|
+
end
|
|
949
|
+
end
|
|
950
|
+
```
|
|
951
|
+
|
|
952
|
+
**How this works:** `assert_enqueued_emails` and `assert_enqueued_jobs` are powered by Rails' `:test` ActiveJob adapter, which tracks enqueued jobs in an in-memory array. Postburner's queue strategies (`InlineTestQueue`, etc.) control `Postburner::Job` subclasses separately. The two systems coexist — ActiveJob assertions use the `:test` adapter, `Postburner::Job` assertions use database queries.
|
|
953
|
+
|
|
954
|
+
By default, Rails uses the `:test` adapter in test mode unless you override it. Since you set `:postburner` as your adapter in `config/application.rb`, you should set it back to `:test` for your test environment:
|
|
955
|
+
|
|
956
|
+
```ruby
|
|
957
|
+
# config/environments/test.rb
|
|
958
|
+
config.active_job.queue_adapter = :test
|
|
959
|
+
```
|
|
960
|
+
|
|
961
|
+
This is standard practice for any ActiveJob adapter (Sidekiq, SolidQueue, etc.) — the production adapter handles real execution, and the `:test` adapter enables Rails' built-in assertion helpers. Without this, `assert_enqueued_emails` will not work because Postburner's adapter executes jobs rather than tracking them in an array.
|
|
962
|
+
|
|
963
|
+
**`Postburner::Mailer`** bypasses ActiveJob entirely, so `assert_enqueued_emails` cannot see these jobs regardless of adapter. Assert on side effects instead:
|
|
964
|
+
|
|
965
|
+
```ruby
|
|
966
|
+
test "mailer job sends email" do
|
|
967
|
+
job = Postburner::Mailer.delivery(UserMailer, :welcome).with(user_id: 1)
|
|
968
|
+
|
|
969
|
+
assert_emails 1 do
|
|
970
|
+
job.queue! # Executes inline in test mode, delivering the email
|
|
971
|
+
end
|
|
972
|
+
|
|
973
|
+
assert job.reload.processed_at
|
|
974
|
+
end
|
|
975
|
+
```
|
|
976
|
+
|
|
930
977
|
### Switching Queue Strategies
|
|
931
978
|
|
|
932
979
|
For tests requiring specific queue behaviors, use `switch_queue_strategy!` and `restore_queue_strategy!`:
|
|
@@ -2031,13 +2078,15 @@ Postburner uses [Beaneater](https://github.com/beanstalkd/beaneater) as the Ruby
|
|
|
2031
2078
|
|
|
2032
2079
|
### Connection Methods
|
|
2033
2080
|
|
|
2081
|
+
Postburner uses **thread-local connections** — each thread gets its own Beanstalkd socket, matching the pattern used by worker threads. This is safe under multi-threaded servers like Puma.
|
|
2082
|
+
|
|
2034
2083
|
```ruby
|
|
2035
|
-
# Get
|
|
2084
|
+
# Get the thread-local Beaneater connection (returns Beaneater instance)
|
|
2036
2085
|
conn = Postburner.connection
|
|
2037
2086
|
conn.tubes.to_a # List all tubes
|
|
2038
2087
|
conn.beanstalk.stats # Server statistics
|
|
2039
2088
|
|
|
2040
|
-
# Block form - yields
|
|
2089
|
+
# Block form - yields thread-local connection
|
|
2041
2090
|
Postburner.connected do |conn|
|
|
2042
2091
|
conn.tubes.to_a # List all tubes
|
|
2043
2092
|
conn.tubes['postburner.production.critical'].stats
|
|
@@ -2045,6 +2094,19 @@ Postburner.connected do |conn|
|
|
|
2045
2094
|
end
|
|
2046
2095
|
```
|
|
2047
2096
|
|
|
2097
|
+
**Shutdown cleanup:**
|
|
2098
|
+
|
|
2099
|
+
On process shutdown, call `disconnect_all!` to close every thread's connection cleanly:
|
|
2100
|
+
|
|
2101
|
+
```ruby
|
|
2102
|
+
# config/puma.rb
|
|
2103
|
+
on_worker_shutdown do
|
|
2104
|
+
Postburner.disconnect_all!
|
|
2105
|
+
end
|
|
2106
|
+
```
|
|
2107
|
+
|
|
2108
|
+
This is optional — Ruby closes sockets on process exit — but gives you clean logs and explicit teardown.
|
|
2109
|
+
|
|
2048
2110
|
### Job-Level Access
|
|
2049
2111
|
|
|
2050
2112
|
```ruby
|
|
@@ -2248,7 +2310,7 @@ beanstalkd -l 127.0.0.1 -p 11300 -b /var/lib/beanstalkd
|
|
|
2248
2310
|
|
|
2249
2311
|
```ruby
|
|
2250
2312
|
# Gemfile
|
|
2251
|
-
gem 'postburner', '~> 1.0.0.
|
|
2313
|
+
gem 'postburner', '~> 1.0.0.rc.2
|
|
2252
2314
|
```
|
|
2253
2315
|
|
|
2254
2316
|
```bash
|
|
@@ -50,6 +50,20 @@ module Postburner
|
|
|
50
50
|
# @example Queue to specific queue
|
|
51
51
|
# Postburner::Mailer.delivery(UserMailer, :welcome).with(user_id: 1).queue!(queue: 'bulk_mailers')
|
|
52
52
|
#
|
|
53
|
+
# == Testing
|
|
54
|
+
#
|
|
55
|
+
# Because Postburner::Mailer bypasses ActiveJob, Rails' +assert_enqueued_emails+
|
|
56
|
+
# will not detect these jobs. Use +assert_emails+ (which checks actual deliveries)
|
|
57
|
+
# or assert on the job record:
|
|
58
|
+
#
|
|
59
|
+
# # Assert email was delivered (inline in test mode)
|
|
60
|
+
# assert_emails 1 do
|
|
61
|
+
# Postburner::Mailer.delivery(UserMailer, :welcome).with(user_id: 1).queue!
|
|
62
|
+
# end
|
|
63
|
+
#
|
|
64
|
+
# # Assert job was processed
|
|
65
|
+
# assert job.reload.processed_at
|
|
66
|
+
#
|
|
53
67
|
# @see Postburner::Job Base class for tracked jobs
|
|
54
68
|
# @see Postburner::Configuration#default_mailer_queue Configuration option
|
|
55
69
|
class Mailer < Job
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
require 'time'
|
|
2
|
+
|
|
1
3
|
class Postburner::InstallGenerator < Rails::Generators::Base
|
|
2
4
|
source_root File.expand_path('templates', __dir__)
|
|
3
5
|
include Rails::Generators::Migration
|
|
@@ -28,11 +30,15 @@ class Postburner::InstallGenerator < Rails::Generators::Base
|
|
|
28
30
|
_max = timestamps.max || timestamp[-2..-1].to_i
|
|
29
31
|
_next = _max + 1
|
|
30
32
|
raise "MISSING NEXT" if _next.blank?
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
|
|
34
|
+
# When seconds overflow past 99, roll forward by 1 minute and reset.
|
|
35
|
+
if _next > 99
|
|
36
|
+
rolled = Time.parse("#{stem}00 UTC") + 60
|
|
37
|
+
stem = rolled.strftime('%Y%m%d%H%M')
|
|
38
|
+
_next = 0
|
|
34
39
|
end
|
|
35
|
-
|
|
40
|
+
|
|
41
|
+
"#{stem}#{_next.to_s.rjust(2, '0')}"
|
|
36
42
|
end
|
|
37
43
|
|
|
38
44
|
private
|
data/lib/postburner/version.rb
CHANGED
data/lib/postburner.rb
CHANGED
|
@@ -319,31 +319,31 @@ module Postburner
|
|
|
319
319
|
# conn = Postburner.connection
|
|
320
320
|
# conn.tubes['my.tube'].stats
|
|
321
321
|
#
|
|
322
|
-
# @note Connection is cached in
|
|
322
|
+
# @note Connection is cached in Thread.current[:postburner_connection] (thread-local)
|
|
323
323
|
# @note Automatically reconnects if connection is not active
|
|
324
324
|
#
|
|
325
325
|
# @see #connected
|
|
326
326
|
#
|
|
327
327
|
def self.connection
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
328
|
+
Thread.current[:postburner_connection] ||= Postburner::Connection.new
|
|
329
|
+
conn = Thread.current[:postburner_connection]
|
|
330
|
+
conn.reconnect! unless conn.connected?
|
|
331
|
+
conn
|
|
331
332
|
end
|
|
332
333
|
|
|
333
334
|
# Yields a Beanstalkd connection or returns cached connection.
|
|
334
335
|
#
|
|
335
|
-
# When called with a block, yields the connection
|
|
336
|
-
#
|
|
337
|
-
# cached connection.
|
|
336
|
+
# When called with a block, yields the thread-local connection.
|
|
337
|
+
# When called without a block, returns the thread-local connection.
|
|
338
338
|
#
|
|
339
339
|
# @overload connected
|
|
340
340
|
# Returns the cached Beanstalkd connection.
|
|
341
341
|
# @return [Postburner::Connection] Beanstalkd connection
|
|
342
342
|
#
|
|
343
343
|
# @overload connected {|conn| ... }
|
|
344
|
-
# Yields connection
|
|
344
|
+
# Yields connection for the duration of the block.
|
|
345
345
|
# @yieldparam conn [Postburner::Connection] Beanstalkd connection
|
|
346
|
-
# @return [
|
|
346
|
+
# @return [Object] return value of the block
|
|
347
347
|
#
|
|
348
348
|
# @example With block (recommended)
|
|
349
349
|
# Postburner.connected do |conn|
|
|
@@ -351,7 +351,6 @@ module Postburner
|
|
|
351
351
|
# puts tube.name
|
|
352
352
|
# end
|
|
353
353
|
# end
|
|
354
|
-
# # Connection automatically closed
|
|
355
354
|
#
|
|
356
355
|
# @example Without block
|
|
357
356
|
# conn = Postburner.connected
|
|
@@ -364,19 +363,44 @@ module Postburner
|
|
|
364
363
|
# end
|
|
365
364
|
#
|
|
366
365
|
# @see #connection
|
|
366
|
+
# @see #disconnect_all!
|
|
367
367
|
#
|
|
368
368
|
def self.connected
|
|
369
369
|
if block_given?
|
|
370
|
-
|
|
371
|
-
yield connection
|
|
372
|
-
ensure
|
|
373
|
-
connection.close if connection
|
|
374
|
-
end
|
|
370
|
+
yield connection
|
|
375
371
|
else
|
|
376
372
|
connection
|
|
377
373
|
end
|
|
378
374
|
end
|
|
379
375
|
|
|
376
|
+
# Closes and clears Beanstalkd connections on all known threads.
|
|
377
|
+
#
|
|
378
|
+
# Call this on worker shutdown to release sockets cleanly. Because connections
|
|
379
|
+
# are thread-local, a single `connection.close` call from the main thread would
|
|
380
|
+
# only close that thread's socket. This method iterates every live thread and
|
|
381
|
+
# closes each thread's connection, if any.
|
|
382
|
+
#
|
|
383
|
+
# @return [void]
|
|
384
|
+
#
|
|
385
|
+
# @example Puma worker shutdown (config/puma.rb)
|
|
386
|
+
# on_worker_shutdown do
|
|
387
|
+
# Postburner.disconnect_all!
|
|
388
|
+
# end
|
|
389
|
+
#
|
|
390
|
+
def self.disconnect_all!
|
|
391
|
+
Thread.list.each do |thread|
|
|
392
|
+
if (conn = thread[:postburner_connection])
|
|
393
|
+
begin
|
|
394
|
+
conn.close if conn.respond_to?(:close) && conn.connected?
|
|
395
|
+
rescue => e
|
|
396
|
+
warn "Postburner: failed to close connection on shutdown: #{e.message}" if $VERBOSE
|
|
397
|
+
ensure
|
|
398
|
+
thread[:postburner_connection] = nil
|
|
399
|
+
end
|
|
400
|
+
end
|
|
401
|
+
end
|
|
402
|
+
end
|
|
403
|
+
|
|
380
404
|
# Clears jobs from specified tubes or shows stats for all tubes.
|
|
381
405
|
#
|
|
382
406
|
# High-level method with formatted output. Delegates to Connection#clear_tubes!
|