postburner 1.0.0.pre.14 → 1.0.0.pre.15

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 71c6a81826b32021b0f0083c1771b27423e1b25198d7c00669cbab1150c431e7
4
- data.tar.gz: 26403dba984c15f4bad94d81f1f5833d6a6bb16c0bb216a236f6bcaf07c0d019
3
+ metadata.gz: 971531132068ea4bddfdb5b5a8aaae4c788ea576aae268010fb6cd4dbd2b82f1
4
+ data.tar.gz: 44053985f2bebd385bc2a17eb1eb655d89de17d77a8c25b4a1210177cefead66
5
5
  SHA512:
6
- metadata.gz: bc088b5d6e5b36b514562635ee3ed75955dfe809efe981e3ddbdc47c5f5387715233b7eb0eec8b4e78f97cf715202beabb7f8b90df553c605eec058eee462a9c
7
- data.tar.gz: 40dbfc6f0979226d5c67780ffe31c73700a6a7b7faf97bf8e7fd127a2c889e708db91835bc9389032191ae2fae6460f3cc1b21f28ee17a6a3f00dbc84c31fbea
6
+ metadata.gz: 19c023063d9eb06913fcb59eaaf581a537b8a944b8ab8713b95210475517223a42887f239bd8788cd313919ffe9bfb89c6c9f80c8b24869239b943bfd2c300ad
7
+ data.tar.gz: 0e0ed766a67ddd9ef02c0b97eff3a25c1e923c79068608a4d31db779ae23187eb85a5b075e8100644faf1c196f0bafaf1a7711e09db8a948b0ab09e9138f9b49
data/README.md CHANGED
@@ -2,24 +2,20 @@
2
2
 
3
3
  Fast Beanstalkd-backed job queue with **optional PostgreSQL records**.
4
4
 
5
- Depends on Beanstalkd, Postgres, ActiveRecord, and ActiveJob (1).
6
-
7
5
  Postburner provides dual-mode job execution:
8
6
  - **Fast jobs**: Fast execution via Beanstalkd
9
7
  - **Tracked jobs**: Audited jobs logs, timing, errors, and statistics
10
8
 
11
9
  Built for production environments where you want fast background processing for most jobs, but comprehensive auditing for critical operations.
12
10
 
13
- - **ActiveJob native** - Works seamlessly with Rails, ActionMailer, ActiveStorage
14
- - **Dual-mode execution** - Default or tracked (database backed)
15
- - **Rich audit trail** - Logs, timing, errors, retry tracking (tracked jobs only)
16
- - **ActiveRecord** - Query jobs with ActiveRecord (Tracked jobs only)
11
+ - **ActiveJob Adapter** - To use with Rails, ActionMailer, ActiveStorage
12
+ - **Dual-mode execution** - Beanstalkd only or tracked (database backed)
13
+ - **Rich audit trail** - Logs, timing, errors, instrumentation, retry tracking (tracked jobs only)
14
+ - **ActiveRecord** - Query jobs with ActiveRecord (on opt in)
17
15
  - **Scheduler** - Schedule jobs at fixed intervals, cron expressions, and calendar-aware anchor points.
18
- - **Beanstalkd** - Fast, reliable queue separate from your database, peristent storage
16
+ - **Test-friendly** - Testing jobs can be tricky, so we go beyond just inline.
19
17
  - **Process isolation** - Forking workers with optional threading for throughput
20
- - **Test-friendly** - Inline execution without Beanstalkd in tests
21
-
22
- (1) An ActiveJob adapter is provided for seamless integration with the default Rails stack, you can use Postburner without ActiveJob by using the `Postburner::Job` class directly.
18
+ - **Beanstalkd** - Fast, reliable queue separate from your database, persistent storage
23
19
 
24
20
  ```ruby
25
21
  # Default job (fast, no PostgreSQL overhead)
@@ -75,7 +71,7 @@ end
75
71
  # Scheduled job (fixed interval, anchor based)
76
72
  Postburner::Schedule.create!(
77
73
  name: 'daily_event_retention',
78
- job_class: EventRetentionJob, # either Postburner::Job or ActiveJob
74
+ job_class: EventRetentionJob, # either Postburner::Job or ActiveJob ancestor
79
75
  anchor: Time.zone.parse('2025-01-01 09:30:00'),
80
76
  interval: 1,
81
77
  interval_unit: 'days',
@@ -132,7 +128,7 @@ Postburner [beanstalkd](https://beanstalkd.github.io/) is used with PostgreSQL t
132
128
 
133
129
  ```ruby
134
130
  # Gemfile
135
- gem 'postburner', '~> 1.0.0.pre.11'
131
+ gem 'postburner', '~> 1.0.0.pre.14'
136
132
 
137
133
  # config/application.rb
138
134
  config.active_job.queue_adapter = :postburner
@@ -140,28 +136,28 @@ config.active_job.queue_adapter = :postburner
140
136
 
141
137
  ```yaml
142
138
  # config/postburner.yml
143
- development: # <- environment config, i.e. defaults
139
+ development: # <- environment config
144
140
  beanstalk_url: <%= ENV['BEANSTALK_URL'] || 'beanstalk://localhost:11300' %>
145
- default_forks: 2
146
- default_threads: 10
147
- default_gc_limit: 500
141
+ forks: 2
142
+ threads: 10
143
+ gc_limit: 500
148
144
 
149
- workers: # <- worker config, i.e. overrides
145
+ workers: # <- worker config (overrides env-level)
150
146
  default:
151
- threads: 16 # Overrides default_threads
147
+ threads: 16 # Overrides env-level threads
152
148
  queues:
153
149
  - default
154
150
  - mailers
155
151
  critical:
156
- forks: 4 # Overrides default_forks
157
- threads: 8 # Overrides default_threads
158
- gc_limit: 24 # Overrides default_gc_limit
152
+ forks: 4 # Overrides env-level forks
153
+ threads: 8 # Overrides env-level threads
154
+ gc_limit: 24 # Overrides env-level gc_limit
159
155
  queues:
160
156
  - payments
161
157
  slow:
162
- forks: 4 # Overrides default_forks
163
- threads: 1 # Overrides default_threads
164
- gc_limit: 1 # Overrides default_gc_limit
158
+ forks: 4 # Overrides env-level forks
159
+ threads: 1 # Overrides env-level threads
160
+ gc_limit: 1 # Overrides env-level gc_limit
165
161
  queues:
166
162
  - imports
167
163
  - video
@@ -241,8 +237,8 @@ end
241
237
  ```
242
238
 
243
239
  **Configuration options:**
244
- - `queue_priority` - Beanstalkd priority (0-4294967295, lower = higher priority)
245
- - `queue_ttr` - Time-to-run in seconds before job times out
240
+ - `priority` - Beanstalkd priority (0-4294967295, lower = higher priority)
241
+ - `ttr` - Time-to-run in seconds before job times out
246
242
 
247
243
  Jobs without `Postburner::Beanstalkd` use defaults from `config/postburner.yml`:
248
244
  - `default_priority: 65536`
@@ -276,7 +272,7 @@ end
276
272
 
277
273
  Tracked jobs store full execution details in PostgreSQL, providing comprehensive audit trails (i.e. logging, timing, errors, retry tracking) for critical operations.
278
274
 
279
- **Note:** `Postburner::Tracked` automatically includes `Postburner::Beanstalkd`, giving you access to `queue_priority`, `queue_ttr`, `bk`, and `extend!`.
275
+ **Note:** `Postburner::Tracked` automatically includes `Postburner::Beanstalkd`, giving you access to `priority`, `ttr`, `bk`, and `extend!`.
280
276
 
281
277
  ```ruby
282
278
  class ProcessPayment < ApplicationJob
@@ -375,8 +371,8 @@ Override queue priority and TTR per job instance for dynamic behavior:
375
371
  # Set priority during creation
376
372
  job = ProcessPayment.create!(
377
373
  args: { 'payment_id' => 123 },
378
- queue_priority: 1500, # Override class-level priority
379
- queue_ttr: 300 # Override class-level TTR
374
+ priority: 1500, # Override class-level priority
375
+ ttr: 300 # Override class-level TTR
380
376
  )
381
377
  job.queue!
382
378
 
@@ -427,7 +423,7 @@ Postburner includes a lightweight, fixed-rate scheduler for recurring jobs. Perf
427
423
  The scheduler uses **immediate enqueue** combined with a **watchdog safety net**:
428
424
 
429
425
  1. When an execution is created, it's immediately enqueued to Beanstalkd's delayed queue with the appropriate delay until `run_at`
430
- 2. For `Postburner::Job` and `ActiveJob` with `Postburner::Tracked` schedules the next execution when the current job runs - providing immediate pickup without waiting for the watchdog. Normal `ActiveJob` schedules need to rely on the watchdog to create the next execution, so set the `default_scheduler_interval` to pick up exections appropriately.
426
+ 2. For `Postburner::Job` and `ActiveJob` with `Postburner::Tracked` schedules the next execution when the current job runs - providing immediate pickup without waiting for the watchdog. Normal `ActiveJob` schedules need to rely on the watchdog to create the next execution, so set the `scheduler_interval` to pick up executions appropriately.
431
427
  3. A lightweight watchdog job in the `scheduler` tube acts as a safety net:
432
428
  ```json
433
429
  { "scheduler": true, "interval": 300 }
@@ -464,8 +460,8 @@ Add scheduler settings to `config/postburner.yml`:
464
460
  ```yaml
465
461
  production:
466
462
  beanstalk_url: <%= ENV['BEANSTALK_URL'] %>
467
- default_scheduler_interval: 300 # Pickup new schedules every 5 minutes
468
- default_scheduler_priority: 100 # Scheduler jobs run at priority 100
463
+ scheduler_interval: 300 # Pickup new schedules every 5 minutes
464
+ scheduler_priority: 100 # Scheduler jobs run at priority 100
469
465
 
470
466
  workers:
471
467
  default:
@@ -475,8 +471,8 @@ production:
475
471
  ```
476
472
 
477
473
  **Configuration options:**
478
- - `default_scheduler_interval` - How often (in seconds) to check for due schedules (default: 300)
479
- - `default_scheduler_priority` - Beanstalkd priority for watchdog jobs (default: 100)
474
+ - `scheduler_interval` - How often (in seconds) to check for due schedules (default: 300)
475
+ - `scheduler_priority` - Beanstalkd priority for watchdog jobs (default: 100)
480
476
 
481
477
  **Choosing an interval:** Since executions are enqueued immediately to Beanstalkd's delayed queue, the watchdog interval primarily affects:
482
478
  - How quickly new schedules are auto-bootstrapped (if you don't call `start!`)
@@ -636,8 +632,8 @@ schedule.next_run_at_times(5) # Next 5 run times
636
632
 
637
633
  # View executions
638
634
  schedule.executions.pending
639
- schedule.executions.completed
640
- schedule.executions.failed
635
+ schedule.executions.scheduled
636
+ schedule.executions.skipped
641
637
  ```
642
638
 
643
639
  #### Starting Schedules
@@ -664,7 +660,7 @@ The job is immediately in Beanstalkd's delayed queue and will run at the schedul
664
660
 
665
661
  **Option 2: Auto-bootstrap (eventual pickup)**
666
662
 
667
- If you don't call `start!`, the scheduler watchdog will automatically bootstrap the schedule on its next run. This adds up to one `default_scheduler_interval` of delay before the first execution is enqueued:
663
+ If you don't call `start!`, the scheduler watchdog will automatically bootstrap the schedule on its next run. This adds up to one `scheduler_interval` of delay before the first execution is enqueued:
668
664
 
669
665
  ```ruby
670
666
  schedule = Postburner::Schedule.create!(...)
@@ -683,10 +679,9 @@ Each scheduled run creates an execution record for tracking:
683
679
  ```ruby
684
680
  execution = Postburner::ScheduleExecution.find(123)
685
681
 
686
- execution.status # pending, running, completed, failed, skipped
682
+ execution.status # pending, scheduled, skipped
687
683
  execution.run_at # Scheduled time
688
684
  execution.enqueued_at # When job was queued
689
- execution.completed_at # When job finished
690
685
  execution.beanstalk_job_id # Beanstalkd job ID
691
686
  execution.job_id # Postburner::Job ID (if using Postburner::Job)
692
687
  ```
@@ -905,25 +900,25 @@ Postburner uses named worker configurations to support different deployment patt
905
900
  Configure multiple named workers with different concurrency profiles:
906
901
 
907
902
  ```yaml
908
- production: # <- environment config, i.e. defaults
903
+ production: # <- environment config
909
904
  beanstalk_url: <%= ENV['BEANSTALK_URL'] %>
910
- default_forks: 2
911
- default_threads: 10
912
- default_gc_limit: 5000
905
+ forks: 2
906
+ threads: 10
907
+ gc_limit: 5000
913
908
 
914
- workers: # <- worker config, i.e. overrides
909
+ workers: # <- worker config (overrides env-level)
915
910
  # Heavy, memory-intensive jobs - more processes, fewer threads
916
911
  imports:
917
- forks: 4 # Overrides default_forks
918
- threads: 1 # Overrides default_threads
919
- gc_limit: 500 # Overrides default_gc_limit
912
+ forks: 4 # Overrides env-level forks
913
+ threads: 1 # Overrides env-level threads
914
+ gc_limit: 500 # Overrides env-level gc_limit
920
915
  queues:
921
916
  - imports
922
917
  - data_processing
923
918
 
924
919
  # General jobs - fewer processes, many threads
925
920
  general:
926
- threads: 100 # Overrides default_threads (forks uses default_forks=2)
921
+ threads: 100 # Overrides env-level threads (forks uses env-level forks=2)
927
922
  queues:
928
923
  - default
929
924
  - mailers
@@ -957,12 +952,12 @@ development: # <- environment config
957
952
  ```yaml
958
953
  staging: # <- environment config
959
954
  beanstalk_url: beanstalk://localhost:11300
960
- default_threads: 10
961
- default_gc_limit: 5000
955
+ threads: 10
956
+ gc_limit: 5000
962
957
 
963
958
  workers: # <- worker config
964
959
  default:
965
- # Uses env defaults: default_threads=10, default_gc_limit=5000
960
+ # Uses env-level: threads=10, gc_limit=5000
966
961
  queues:
967
962
  - critical
968
963
  - default
@@ -971,25 +966,25 @@ staging: # <- environment config
971
966
 
972
967
  **Production (multiple workers with different profiles):**
973
968
  ```yaml
974
- production: # <- environment config, i.e. defaults
969
+ production: # <- environment config
975
970
  beanstalk_url: <%= ENV['BEANSTALK_URL'] %>
976
- default_forks: 2
977
- default_threads: 10
978
- default_gc_limit: 5000
971
+ forks: 2
972
+ threads: 10
973
+ gc_limit: 5000
979
974
 
980
- workers: # <- worker config, i.e. overrides
975
+ workers: # <- worker config (overrides env-level)
981
976
  imports:
982
- forks: 4 # Overrides default_forks (4 processes)
983
- threads: 1 # Overrides default_threads (1 thread per process = 4 concurrent jobs)
984
- gc_limit: 500 # Overrides default_gc_limit
977
+ forks: 4 # Overrides env-level forks (4 processes)
978
+ threads: 1 # Overrides env-level threads (1 thread per process = 4 concurrent jobs)
979
+ gc_limit: 500 # Overrides env-level gc_limit
985
980
  queues:
986
981
  - imports
987
982
  - data_processing
988
983
 
989
984
  general:
990
- # forks uses default_forks=2 (2 processes)
991
- threads: 100 # Overrides default_threads (100 threads per process = 200 concurrent jobs)
992
- # gc_limit uses default_gc_limit=5000
985
+ # forks uses env-level forks=2 (2 processes)
986
+ threads: 100 # Overrides env-level threads (100 threads per process = 200 concurrent jobs)
987
+ # gc_limit uses env-level gc_limit=5000
993
988
  queues:
994
989
  - default
995
990
  - mailers
@@ -1148,8 +1143,8 @@ The `shutdown_timeout` controls how long workers wait for in-flight jobs to comp
1148
1143
 
1149
1144
  ```yaml
1150
1145
  production:
1151
- default_ttr: 300 # Default TTR for jobs
1152
- default_shutdown_timeout: 300 # Defaults to default_ttr if not specified
1146
+ default_ttr: 300 # Default TTR for jobs
1147
+ shutdown_timeout: 300 # Defaults to default_ttr if not specified
1153
1148
 
1154
1149
  workers:
1155
1150
  imports:
@@ -1164,7 +1159,7 @@ production:
1164
1159
 
1165
1160
  ### GC Limits
1166
1161
 
1167
- Set `default_gc_limit` at environment level or `gc_limit` per worker to automatically restart after processing N jobs.
1162
+ Set `gc_limit` at environment level or per worker to automatically restart after processing N jobs.
1168
1163
 
1169
1164
  - Worker processes N jobs
1170
1165
  - Worker exits with code 99
@@ -1173,22 +1168,22 @@ Set `default_gc_limit` at environment level or `gc_limit` per worker to automati
1173
1168
 
1174
1169
  ```yaml
1175
1170
  production: # <- environment config
1176
- default_forks: 2
1177
- default_threads: 10
1178
- default_gc_limit: 5000
1171
+ forks: 2
1172
+ threads: 10
1173
+ gc_limit: 5000
1179
1174
 
1180
1175
  workers: # <- worker config
1181
1176
  imports:
1182
- forks: 4 # Overrides default_forks
1183
- threads: 1 # Overrides default_threads
1184
- gc_limit: 500 # Overrides default_gc_limit (restart after 500 jobs, memory-intensive)
1177
+ forks: 4 # Overrides env-level forks
1178
+ threads: 1 # Overrides env-level threads
1179
+ gc_limit: 500 # Overrides env-level gc_limit (restart after 500 jobs, memory-intensive)
1185
1180
  queues:
1186
1181
  - imports
1187
1182
  - data_processing
1188
1183
 
1189
1184
  general:
1190
- # Uses default_forks=2, default_threads=100, default_gc_limit=5000 (restart after 5000 jobs)
1191
- threads: 100 # Overrides default_threads
1185
+ # Uses env-level forks=2, threads=100, gc_limit=5000 (restart after 5000 jobs)
1186
+ threads: 100 # Overrides env-level threads
1192
1187
  queues:
1193
1188
  - default
1194
1189
  - mailers
@@ -1229,41 +1224,41 @@ test: # <- environment config
1229
1224
 
1230
1225
  staging: # <- environment config
1231
1226
  <<: *default
1232
- default_threads: 10 # Multi-threaded, single process
1233
- default_gc_limit: 5000
1227
+ threads: 10 # Multi-threaded, single process
1228
+ gc_limit: 5000
1234
1229
 
1235
1230
  workers: # <- worker config
1236
1231
  default:
1237
- # Uses env defaults: default_threads=10, default_gc_limit=5000
1232
+ # Uses env-level: threads=10, gc_limit=5000
1238
1233
  queues:
1239
1234
  - default
1240
1235
  - mailers
1241
1236
 
1242
- production: # <- environment config, i.e. defaults
1237
+ production: # <- environment config
1243
1238
  <<: *default
1244
- default_forks: 2 # Default for workers
1245
- default_threads: 10 # Default for workers
1246
- default_gc_limit: 5000 # Default for workers
1239
+ forks: 2 # Inherited by workers
1240
+ threads: 10 # Inherited by workers
1241
+ gc_limit: 5000 # Inherited by workers
1247
1242
 
1248
- workers: # <- worker config, i.e. overrides
1243
+ workers: # <- worker config (overrides env-level)
1249
1244
  critical:
1250
- forks: 1 # Overrides default_forks
1251
- threads: 1 # Overrides default_threads (1 concurrent job)
1252
- gc_limit: 100 # Overrides default_gc_limit
1245
+ forks: 1 # Overrides env-level forks
1246
+ threads: 1 # Overrides env-level threads (1 concurrent job)
1247
+ gc_limit: 100 # Overrides env-level gc_limit
1253
1248
  queues:
1254
1249
  - critical
1255
1250
 
1256
1251
  default:
1257
- forks: 4 # Overrides default_forks
1258
- threads: 10 # Overrides default_threads (40 total concurrent jobs: 4 × 10)
1259
- gc_limit: 1000 # Overrides default_gc_limit
1252
+ forks: 4 # Overrides env-level forks
1253
+ threads: 10 # Overrides env-level threads (40 total concurrent jobs: 4 × 10)
1254
+ gc_limit: 1000 # Overrides env-level gc_limit
1260
1255
  queues:
1261
1256
  - default
1262
1257
 
1263
1258
  mailers:
1264
- # forks uses default_forks=2
1265
- threads: 5 # Overrides default_threads (10 total email senders: 2 × 5)
1266
- gc_limit: 500 # Overrides default_gc_limit
1259
+ # forks uses env-level forks=2
1260
+ threads: 5 # Overrides env-level threads (10 total email senders: 2 × 5)
1261
+ gc_limit: 500 # Overrides env-level gc_limit
1267
1262
  queues:
1268
1263
  - mailers
1269
1264
  ```
@@ -1311,7 +1306,7 @@ Both refer to the Beanstalkd tube `postburner.production.mailers`.
1311
1306
 
1312
1307
  **Naming convention:** Use underscores for multi-word queue names (e.g., `background_jobs`, `high_priority`). Avoid hyphens as they can cause issues with some Beanstalkd client libraries.
1313
1308
 
1314
- **Important:** No need to set `config.active_job.queue_name_prefix` - Postburner handles prefixing automatically, when jobs are enqueued with postburner.
1309
+ **Important:** No need to set `config.active_job.queue_name_prefix` - Postburner handles prefixing automatically when jobs are enqueued with postburner.
1315
1310
 
1316
1311
  ### Queue Configuration Methods
1317
1312
 
@@ -1534,16 +1529,16 @@ end
1534
1529
 
1535
1530
  Beanstalkd is a simple, fast, and reliable queue system. It is a good choice for production environments where you want fast background processing for most jobs, but comprehensive auditing for critical operations.
1536
1531
 
1537
- The [protocol](https://github.com/beanstalkd/beanstalkd/blob/master/doc/protocol.txt) reads more like a README than a protocol. Check it out and you will instantly understand how it works. Here is a help
1532
+ The [protocol](https://github.com/beanstalkd/beanstalkd/blob/master/doc/protocol.txt) reads more like a README than a protocol. Check it out and you will instantly understand how it works.
1538
1533
 
1539
- Here is a picture of the typical job lifecycle:
1534
+ Diagram of the typical job lifecycle:
1540
1535
 
1541
1536
  ```
1542
1537
  put reserve delete
1543
1538
  -----> [READY] ---------> [RESERVED] --------> *poof*`
1544
1539
  ```
1545
1540
 
1546
- Here is a picture with more possibilities:
1541
+ Diagram with all lifecycle:
1547
1542
 
1548
1543
  ```
1549
1544
  put with delay release with delay
@@ -1748,7 +1743,7 @@ end
1748
1743
  ```ruby
1749
1744
  class DataImport < Postburner::Job
1750
1745
  queue 'imports'
1751
- ttr 1800 # 30 minutes (equivalent to queue_ttr)
1746
+ ttr 1800 # 30 minutes
1752
1747
 
1753
1748
  def perform(args)
1754
1749
  # Long-running import logic
@@ -1761,8 +1756,8 @@ end
1761
1756
  For jobs that may take longer than expected, you can extend the TTR dynamically using `extend!`. This calls Beanstalkd's `touch` command, which resets the TTR countdown.
1762
1757
 
1763
1758
  **Available for:**
1764
- - `Postburner::Job` subclasses (always available via `bk.extend!`)
1765
- - `Postburner::Tracked` ActiveJob classes (includes `extend!` method)
1759
+ - `Postburner::Job` subclasses (via `extend!` method)
1760
+ - `Postburner::Tracked` ActiveJob classes (via `extend!` method)
1766
1761
 
1767
1762
  ```ruby
1768
1763
  class ProcessImport < ApplicationJob
@@ -1799,7 +1794,7 @@ class LargeDataProcessor < Postburner::Job
1799
1794
  dataset.each_chunk do |chunk|
1800
1795
  process_chunk(chunk)
1801
1796
 
1802
- bk.extend! # Reset TTR via beanstalkd job accessor
1797
+ extend! # Reset TTR (calls bk.touch internally)
1803
1798
  log "Chunk processed, TTR extended"
1804
1799
  end
1805
1800
  end
@@ -2000,7 +1995,7 @@ Postburner.connected do |conn|
2000
1995
  end
2001
1996
  ```
2002
1997
 
2003
- ## Web UI -- Dated
1998
+ ## Web UI - v2 Coming Soon
2004
1999
 
2005
2000
  Mount the inspection interface:
2006
2001
 
@@ -2043,7 +2038,7 @@ beanstalkd -l 127.0.0.1 -p 11300 -b /var/lib/beanstalkd
2043
2038
 
2044
2039
  ```ruby
2045
2040
  # Gemfile
2046
- gem 'postburner', '~> 1.0.0.pre.1'
2041
+ gem 'postburner', '~> 1.0.0.pre.14'
2047
2042
  ```
2048
2043
 
2049
2044
  ```bash
@@ -2094,7 +2089,7 @@ Key changes in v1.0:
2094
2089
 
2095
2090
  1. **Update Gemfile:**
2096
2091
  ```ruby
2097
- gem 'postburner', '~> 1.0.0.pre.1'
2092
+ gem 'postburner', '~> 1.0.0.pre.14'
2098
2093
  ```
2099
2094
 
2100
2095
  2. **Remove Backburner config:**
@@ -2138,7 +2133,7 @@ We encourage AI tools, but do not vibe, as the code must look like it was writte
2138
2133
  ```bash
2139
2134
  bundle install
2140
2135
  bundle exec rails test # must have beanstalkd on 11300 by default
2141
- bundle exec rails app:postburner:work # if you want to run the worker
2136
+ bundle exec rails app:postburner:work # run worker from engine root (uses test/dummy app)
2142
2137
  ```
2143
2138
 
2144
2139
  ## License
@@ -1,4 +1,5 @@
1
1
  module Postburner
2
2
  class ApplicationController < ActionController::Base
3
+ protect_from_forgery with: :exception
3
4
  end
4
5
  end
data/bin/postburner CHANGED
@@ -18,7 +18,7 @@
18
18
  # bin/postburner
19
19
  # bin/postburner --worker default
20
20
  # bin/postburner --env production --worker imports
21
- # bin/postburner --worker default --queues default,mailers
21
+ # bin/postburner --worker default --queues default,mailers # just using --worker is preferred
22
22
  #
23
23
 
24
24
  require 'optparse'
@@ -33,6 +33,6 @@ test:
33
33
  # Multi-process, multi-thread (forks: 2, threads: 4)
34
34
  production:
35
35
  <<: *shared
36
- default_forks: 2
37
- default_threads: 4
38
- default_gc_limit: 64
36
+ forks: 2
37
+ threads: 4
38
+ gc_limit: 64
@@ -21,7 +21,7 @@ module Postburner
21
21
  #
22
22
  class Configuration
23
23
  # Global settings
24
- attr_accessor :beanstalk_url, :logger, :default_queue, :default_priority, :default_ttr
24
+ attr_accessor :beanstalk_url, :logger, :default_priority, :default_ttr
25
25
  attr_accessor :default_scheduler_interval, :default_scheduler_priority
26
26
  attr_accessor :enqueue_options
27
27
 
@@ -31,7 +31,6 @@ module Postburner
31
31
  # @param options [Hash] Configuration options
32
32
  # @option options [String] :beanstalk_url Beanstalkd URL (default: ENV['BEANSTALK_URL'] or localhost)
33
33
  # @option options [Logger] :logger Logger instance (default: Rails.logger)
34
- # @option options [String] :default_queue Default queue name (default: 'default')
35
34
  # @option options [Integer] :default_priority Default job priority (default: 65536, lower = higher priority)
36
35
  # @option options [Integer] :default_ttr Default time-to-run in seconds (default: 300)
37
36
  # @option options [Integer] :default_scheduler_interval Scheduler check interval in seconds (default: 300)
@@ -51,7 +50,6 @@ module Postburner
51
50
  def initialize(options = {})
52
51
  @beanstalk_url = options[:beanstalk_url] || ENV['BEANSTALK_URL'] || 'beanstalk://localhost:11300'
53
52
  @logger = options[:logger] || (defined?(Rails) ? Rails.logger : Logger.new(STDOUT))
54
- @default_queue = options[:default_queue] || 'default'
55
53
  @default_priority = options[:default_priority] || 65536
56
54
  @default_ttr = options[:default_ttr] || 300
57
55
  @default_scheduler_interval = options[:default_scheduler_interval] || 300
@@ -153,20 +151,19 @@ module Postburner
153
151
  worker_config = {
154
152
  name: worker_name,
155
153
  queues: worker_yaml['queues'] || ['default'],
156
- forks: worker_yaml['forks'] || env_config['default_forks'] || 0,
157
- threads: worker_yaml['threads'] || env_config['default_threads'] || 1,
158
- gc_limit: worker_yaml['gc_limit'] || env_config['default_gc_limit'],
154
+ forks: worker_yaml['forks'] || env_config['forks'] || 0,
155
+ threads: worker_yaml['threads'] || env_config['threads'] || 1,
156
+ gc_limit: worker_yaml['gc_limit'] || env_config['gc_limit'],
159
157
  timeout: worker_yaml['timeout'] || 3,
160
- shutdown_timeout: worker_yaml['shutdown_timeout'] || env_config['default_shutdown_timeout'] || default_ttr
158
+ shutdown_timeout: worker_yaml['shutdown_timeout'] || env_config['shutdown_timeout'] || default_ttr
161
159
  }
162
160
 
163
161
  options = {
164
162
  beanstalk_url: env_config['beanstalk_url'],
165
- default_queue: env_config['default_queue'],
166
163
  default_priority: env_config['default_priority'],
167
164
  default_ttr: env_config['default_ttr'],
168
- default_scheduler_interval: env_config['default_scheduler_interval'],
169
- default_scheduler_priority: env_config['default_scheduler_priority'],
165
+ default_scheduler_interval: env_config['scheduler_interval'],
166
+ default_scheduler_priority: env_config['scheduler_priority'],
170
167
  worker_config: worker_config
171
168
  }
172
169
 
@@ -190,8 +187,8 @@ module Postburner
190
187
  # Beanstalkd tube name with environment namespace (e.g.,
191
188
  # 'postburner.production.critical').
192
189
  #
193
- # @param queue_name [String, Symbol, nil] Base queue name (defaults to configured default_queue)
194
- # @param env [String, Symbol, nil] Environment name (defaults to Rails.env or 'development')
190
+ # @param queue_name [String, Symbol] Base queue name
191
+ # @param env [String, Symbol, nil] Environment name (defaults to Rails.env)
195
192
  #
196
193
  # @return [String] Full tube name with environment prefix
197
194
  #
@@ -199,14 +196,11 @@ module Postburner
199
196
  # config.expand_tube_name('critical', 'production')
200
197
  # # => "postburner.production.critical"
201
198
  #
202
- # @example With configured default
203
- # config.default_queue = 'background'
204
- # config.expand_tube_name
205
- # # => "postburner.development.background"
199
+ # config.expand_tube_name('default')
200
+ # # => "postburner.test.default" (in test env)
206
201
  #
207
- def expand_tube_name(queue_name = nil, env = nil)
202
+ def expand_tube_name(queue_name, env = nil)
208
203
  env ||= defined?(Rails) ? Rails.env : nil
209
- queue_name ||= @default_queue
210
204
  [
211
205
  tube_prefix(env),
212
206
  queue_name,
@@ -45,8 +45,8 @@ module Postburner
45
45
  # Add to config/postburner.yml:
46
46
  #
47
47
  # production:
48
- # default_scheduler_interval: 300 # Check every 5 minutes (default)
49
- # default_scheduler_priority: 100 # Watchdog priority (default)
48
+ # scheduler_interval: 300 # Check every 5 minutes (default)
49
+ # scheduler_priority: 100 # Watchdog priority (default)
50
50
  #
51
51
  # The interval primarily affects:
52
52
  # - How quickly new schedules are auto-bootstrapped (if you don't call start!)
@@ -247,8 +247,8 @@ module Postburner
247
247
  # The watchdog will execute after the delay, process all schedules, and re-queue itself.
248
248
  #
249
249
  # Reads interval and priority from configuration if not provided:
250
- # - default_scheduler_interval (default: 300 seconds)
251
- # - default_scheduler_priority (default: 100)
250
+ # - scheduler_interval (default: 300 seconds)
251
+ # - scheduler_priority (default: 100)
252
252
  #
253
253
  # @param interval [Integer, nil] Seconds until next run (default: from config)
254
254
  # @param priority [Integer, nil] Beanstalkd priority (default: from config)
@@ -1,3 +1,3 @@
1
1
  module Postburner
2
- VERSION = '1.0.0.pre.14'
2
+ VERSION = '1.0.0.pre.15'
3
3
  end
@@ -110,6 +110,7 @@ module Postburner
110
110
  all_tubes = config.expanded_tube_names + [config.scheduler_tube_name]
111
111
  logger.info "[Postburner] #{config.beanstalk_url} known tubes: #{all_tubes.join(', ')}"
112
112
  log_next_scheduler_watchdog
113
+ ensure_watchdog_on_startup!
113
114
 
114
115
  if worker_config[:forks] > 0
115
116
  start_forked_mode
@@ -678,6 +679,29 @@ module Postburner
678
679
  Postburner::Scheduler.ensure_watchdog!(connection: connection)
679
680
  end
680
681
 
682
+ # Ensure watchdog exists on worker startup.
683
+ #
684
+ # Called before spawning forks or threads to ensure the scheduler watchdog
685
+ # exists. In forked mode, this prevents a race condition where multiple forks
686
+ # all see no watchdog and each create one. In single-process mode, this
687
+ # ensures the watchdog exists immediately rather than waiting for first timeout.
688
+ #
689
+ # @return [void]
690
+ # @api private
691
+ def ensure_watchdog_on_startup!
692
+ Postburner.connected do |conn|
693
+ if Postburner::Scheduler.watchdog_exists?(connection: conn)
694
+ logger.debug "[Postburner::Worker] Watchdog already exists"
695
+ else
696
+ logger.info "[Postburner::Worker] Creating scheduler watchdog"
697
+ Postburner::Scheduler.enqueue_watchdog
698
+ end
699
+ end
700
+ rescue => e
701
+ logger.warn "[Postburner::Worker] Failed to ensure watchdog on startup: #{e.message}"
702
+ # Non-fatal - threads will create it on timeout, though may create duplicates in forked mode
703
+ end
704
+
681
705
  # Deletes a job from Beanstalkd with one retry on failure.
682
706
  #
683
707
  # If delete fails (e.g., network blip), waits 1 second and retries once.
data/lib/postburner.rb CHANGED
@@ -428,6 +428,30 @@ module Postburner
428
428
  result
429
429
  end
430
430
 
431
+ # Clears all configured tubes including scheduler.
432
+ #
433
+ # Convenience method that clears all watched tubes plus the scheduler tube
434
+ # in a single call. This is the equivalent of:
435
+ #
436
+ # Postburner.clear_jobs!(Postburner.watched_tube_names + [Postburner.scheduler_tube_name])
437
+ #
438
+ # @param silent [Boolean] If true, suppress output to stdout (default: false)
439
+ #
440
+ # @return [Hash] Statistics and results (see Connection#clear_tubes!)
441
+ #
442
+ # @example Clear everything
443
+ # Postburner.clear_all!
444
+ #
445
+ # @example Silent mode
446
+ # result = Postburner.clear_all!(silent: true)
447
+ #
448
+ # @see #clear_jobs!
449
+ #
450
+ def self.clear_all!(silent: false)
451
+ all_tubes = watched_tube_names + [scheduler_tube_name]
452
+ clear_jobs!(all_tubes, silent: silent)
453
+ end
454
+
431
455
  # Returns array of watched tube names with environment prefix.
432
456
  #
433
457
  # Expands configured queue names to full tube names and memoizes the result.
@@ -15,4 +15,108 @@ namespace :postburner do
15
15
  runner = Postburner::Runner.new(options)
16
16
  runner.run
17
17
  end
18
+
19
+ desc "Show Beanstalkd tube statistics"
20
+ task stats: :environment do
21
+ puts "Postburner Tube Statistics (#{Rails.env})"
22
+ puts "=" * 60
23
+ Postburner.clear_jobs!
24
+ end
25
+
26
+ # Clear tasks remove jobs from Beanstalkd only. PostgreSQL records (TrackedJob,
27
+ # Postburner::Job) are not affected. Tracked jobs can be requeued after clearing
28
+ # if needed:
29
+ #
30
+ # Postburner::TrackedJob.where(processed_at: nil).find_each(&:requeue!)
31
+ # Postburner::Job.where(processed_at: nil).find_each(&:requeue!)
32
+ #
33
+ namespace :clear do
34
+ desc "Clear Postburner tubes (jobs + scheduler). Prompts for selective clearing. Beanstalkd only - DB records preserved."
35
+ task all: :environment do
36
+ tubes = Postburner.watched_tube_names + [Postburner.scheduler_tube_name]
37
+ clear_tubes_with_confirmation(tubes, "all Postburner tubes")
38
+ end
39
+
40
+ desc "Clear job tubes only (excludes scheduler). Prompts for selective clearing. Beanstalkd only - DB records preserved."
41
+ task jobs: :environment do
42
+ tubes = Postburner.watched_tube_names
43
+ clear_tubes_with_confirmation(tubes, "job tubes")
44
+ end
45
+
46
+ desc "Clear scheduler watchdog tube only. Beanstalkd only - will be recreated on next worker timeout."
47
+ task scheduler: :environment do
48
+ tubes = [Postburner.scheduler_tube_name]
49
+ clear_tubes_with_confirmation(tubes, "scheduler tube")
50
+ end
51
+ end
52
+
53
+ # Convenience alias
54
+ desc "Clear Postburner tubes (alias for clear:all). Beanstalkd only - DB records preserved."
55
+ task clear: "clear:all"
56
+
57
+ def clear_tubes_with_confirmation(tubes, description)
58
+ puts "Postburner Clear Tubes (#{Rails.env})"
59
+ puts "=" * 60
60
+ puts
61
+ puts "NOTE: This clears Beanstalkd only. Database records are preserved"
62
+ puts " and tracked jobs can be requeued if needed."
63
+ puts
64
+
65
+ # Show current stats
66
+ stats = Postburner.stats(tubes)
67
+ total_jobs = stats[:totals][:total]
68
+
69
+ if total_jobs == 0
70
+ puts "No jobs to clear in #{description}."
71
+ return
72
+ end
73
+
74
+ # Collect tubes that actually have jobs
75
+ tubes_with_jobs = stats[:tubes].select { |t| t[:total] > 0 }
76
+
77
+ puts "Tubes with jobs:"
78
+ tubes_with_jobs.each do |tube|
79
+ puts " #{tube[:name]}: #{tube[:total]} jobs (ready: #{tube[:ready]}, delayed: #{tube[:delayed]}, buried: #{tube[:buried]})"
80
+ end
81
+ puts
82
+ puts "Total: #{total_jobs} jobs will be deleted"
83
+ puts
84
+
85
+ # Confirmation prompt - require typing tube short names (last segment)
86
+ if ENV['FORCE'] == 'true'
87
+ confirmed_tubes = tubes_with_jobs.map { |t| t[:name] }
88
+ else
89
+ # Build mapping of short name -> full name
90
+ tube_short_names = tubes_with_jobs.map { |t| t[:name].split('.').last }
91
+ puts "Type the queue name(s) you want to clear (space-separated):"
92
+ puts " #{tube_short_names.join(' ')}"
93
+ puts
94
+ puts "Only the queues you type will be cleared. Press Enter to abort."
95
+ puts
96
+ print "> "
97
+ response = $stdin.gets&.strip || ""
98
+ entered_names = response.split(/\s+/).map(&:strip).reject(&:empty?)
99
+
100
+ # Match entered short names to full tube names
101
+ confirmed_tubes = tubes_with_jobs
102
+ .select { |t| entered_names.include?(t[:name].split('.').last) }
103
+ .map { |t| t[:name] }
104
+
105
+ unrecognized = entered_names - tube_short_names
106
+ if unrecognized.any?
107
+ puts
108
+ puts "Unrecognized queues ignored: #{unrecognized.join(', ')}"
109
+ end
110
+ end
111
+
112
+ if confirmed_tubes.any?
113
+ jobs_to_clear = tubes_with_jobs.select { |t| confirmed_tubes.include?(t[:name]) }.sum { |t| t[:total] }
114
+ puts
115
+ puts "Clearing #{confirmed_tubes.size} tube(s)..."
116
+ Postburner.clear_jobs!(confirmed_tubes, silent: true)
117
+ puts "Done. Cleared #{jobs_to_clear} jobs."
118
+ else
119
+ puts "Aborted. No tubes cleared."
120
+ end
121
+ end
18
122
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: postburner
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.pre.14
4
+ version: 1.0.0.pre.15
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matt Smith
@@ -133,8 +133,6 @@ files:
133
133
  - bin/postburner
134
134
  - bin/rails
135
135
  - config/environment.rb
136
- - config/postburner.yml
137
- - config/postburner.yml.example
138
136
  - config/routes.rb
139
137
  - lib/generators/postburner/install/USAGE
140
138
  - lib/generators/postburner/install/install_generator.rb
@@ -1,22 +0,0 @@
1
- development:
2
- beanstalk_url: beanstalk://localhost:11300
3
- worker_type: simple
4
- queues:
5
- default: {}
6
-
7
- test:
8
- beanstalk_url: beanstalk://localhost:11300
9
- worker_type: simple
10
- queues:
11
- default: {}
12
-
13
- production:
14
- beanstalk_url: beanstalk://localhost:11300
15
- worker_type: threads_on_fork
16
- queues:
17
- critical:
18
- threads: 1
19
- gc_limit: 100
20
- default:
21
- threads: 5
22
- gc_limit: 500
@@ -1,144 +0,0 @@
1
- # Postburner Configuration Example
2
- #
3
- # Copy this file to config/postburner.yml and customize for your environment.
4
- #
5
- # ## Named Workers Configuration
6
- #
7
- # Postburner uses named worker configurations to support different deployment patterns:
8
- # - Single worker: bin/postburner (auto-selects the single worker)
9
- # - Multiple workers: bin/postburner --worker <name> (must specify which worker)
10
- #
11
- # Each worker can have different fork/thread settings and process different queues.
12
- # This enables running different queue groups in separate OS processes with distinct
13
- # concurrency profiles.
14
- #
15
- # ## Puma-Style Architecture
16
- #
17
- # - **forks: 0** = Single process with thread pool (development/staging)
18
- # - **forks: 1+** = Multiple processes with thread pools (production)
19
- #
20
- # Scale by adjusting forks and threads per worker:
21
- # - Development: forks=0, threads=1 (single-threaded, easiest debugging)
22
- # - Staging: forks=0, threads=10 (multi-threaded, moderate load)
23
- # - Production: forks=4, threads=10 (40 concurrent jobs per worker)
24
- #
25
-
26
- default: &default
27
- # Beanstalkd connection URL
28
- # Override with ENV['BEANSTALK_URL'] if set
29
- beanstalk_url: <%= ENV['BEANSTALK_URL'] || 'beanstalk://localhost:11300' %>
30
-
31
- development:
32
- <<: *default
33
-
34
- workers:
35
- default:
36
- # Single-threaded, single process (simplest for debugging)
37
- # If not specified, uses env-level defaults
38
- queues:
39
- - default
40
- - mailers
41
-
42
- test:
43
- <<: *default
44
-
45
- workers:
46
- default:
47
- # Test mode uses inline strategies automatically
48
- queues:
49
- - default
50
-
51
- staging: # <- environment config
52
- <<: *default
53
-
54
- # Env-level defaults (use default_ prefix)
55
- default_threads: 10
56
- default_gc_limit: 5000
57
-
58
- workers: # <- worker config overrides
59
- default:
60
- # Multi-threaded, single process (moderate concurrency)
61
- # Uses env-level defaults: default_threads=10, default_gc_limit=5000
62
- queues:
63
- - critical
64
- - default
65
- - mailers
66
-
67
- production: # <- environment config, i.e. defaults, NOT worker config
68
- <<: *default
69
-
70
- # Env-level defaults (use default_ prefix)
71
- default_forks: 2
72
- default_threads: 10
73
- default_gc_limit: 5000
74
-
75
- # Example 1: Single worker using env defaults
76
- # Run: bin/postburner
77
- #
78
- # workers:
79
- # default:
80
- # # Uses default_forks=2, default_threads=10
81
- # queues:
82
- # - critical
83
- # - default
84
- # - mailers
85
- # - imports
86
-
87
- # Example 2: Multiple workers with different concurrency profiles
88
- # Run separate processes:
89
- # bin/postburner --worker imports (4 forks, 1 thread each)
90
- # bin/postburner --worker general (2 forks, 100 threads each)
91
- #
92
- workers: # <- worker config, i.e. overrides, NOT environment config
93
- # Heavy, memory-intensive jobs - more processes, fewer threads
94
- imports:
95
- forks: 4 # Overrides default_forks
96
- threads: 1 # Overrides default_threads
97
- gc_limit: 500 # Overrides default_gc_limit
98
- queues:
99
- - imports
100
- - data_processing
101
-
102
- # General jobs - uses env defaults (forks=2, threads=10)
103
- # Override threads for higher concurrency
104
- general:
105
- threads: 100 # Overrides default_threads (forks uses default_forks=2)
106
- queues:
107
- - default
108
- - mailers
109
- - notifications
110
-
111
- # Example 3: Fine-grained control with multiple specialized workers
112
- # Run separate processes:
113
- # bin/postburner --worker critical
114
- # bin/postburner --worker default
115
- # bin/postburner --worker mailers
116
- #
117
- # workers:
118
- # critical:
119
- # forks: 1
120
- # threads: 1
121
- # gc_limit: 100
122
- # queues:
123
- # - critical
124
- #
125
- # default:
126
- # forks: 4
127
- # threads: 10
128
- # queues:
129
- # - default
130
- #
131
- # mailers:
132
- # forks: 2
133
- # threads: 5
134
- # queues:
135
- # - mailers
136
-
137
- # Env-Level Defaults (can be overridden per worker):
138
- #
139
- # default_queue: default # Default queue name (optional)
140
- # default_priority: 65536 # Lower = higher priority (optional, 0 is highest)
141
- # default_ttr: 300 # Time-to-run in seconds (optional)
142
- # default_threads: 1 # Thread count per fork (optional, defaults to 1)
143
- # default_forks: 0 # Fork count (optional, defaults to 0 = single process)
144
- # default_gc_limit: nil # Exit after N jobs for restart (optional, nil = no limit)