solid_queue_autoscaler 1.0.7 → 1.0.8
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/CHANGELOG.md +22 -0
- data/README.md +756 -0
- data/lib/generators/solid_queue_autoscaler/templates/create_solid_queue_autoscaler_events.rb.erb +9 -0
- data/lib/generators/solid_queue_autoscaler/templates/create_solid_queue_autoscaler_state.rb.erb +9 -0
- data/lib/generators/solid_queue_autoscaler/templates/initializer.rb +6 -0
- data/lib/solid_queue_autoscaler/autoscale_job.rb +34 -1
- data/lib/solid_queue_autoscaler/configuration.rb +7 -0
- data/lib/solid_queue_autoscaler/version.rb +1 -1
- metadata +15 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2caadbfc7fd3df7b06be9dbea2e3e667b5e8b49e8c302720fb07d56cfb1a7d1c
|
|
4
|
+
data.tar.gz: 0b4c14b56f29e1ed6537ecac97d279661d08d943320df901bf7c73f3a6bd8694
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f2b316153846191155d72961c4be0e95b6d6c2dd71aedc32a268dff92bd570736e703cc2a8e1e7be564b9899d407c80959350014a3ea167f0f3472d0224d2109
|
|
7
|
+
data.tar.gz: 4048d221c232b9b079d08f24e571dcc9b56c7c4b3c69d7ce14262e5986568c87c93d07152664667021e981018da79ac581335db82420070c5fcc243bfe626efd
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.0.8] - 2025-01-17
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- **`job_queue` configuration option** - Configure which queue the AutoscaleJob runs on (default: `:autoscaler`)
|
|
14
|
+
- **`job_priority` configuration option** - Set job priority for AutoscaleJob (lower = higher priority)
|
|
15
|
+
- **Multi-database migration support** - Migration templates now automatically create tables in the same database as Solid Queue
|
|
16
|
+
- **Common Configuration Examples** - New README section with 8 copy-paste ready configurations for different use cases:
|
|
17
|
+
- Simple/Starter setup
|
|
18
|
+
- Cost-Optimized (scale to zero)
|
|
19
|
+
- E-Commerce/SaaS (multiple worker types)
|
|
20
|
+
- High-Volume API (webhook processing with proportional scaling)
|
|
21
|
+
- Data Processing/ETL
|
|
22
|
+
- High-Availability
|
|
23
|
+
- Kubernetes
|
|
24
|
+
- Development/Testing
|
|
25
|
+
- **Expanded Troubleshooting** - 15+ new troubleshooting topics with code examples
|
|
26
|
+
- **Configuration Comparison Table** - Quick reference for common configurations
|
|
27
|
+
|
|
28
|
+
### Changed
|
|
29
|
+
- AutoscaleJob now uses `queue_as` and `queue_with_priority` blocks for dynamic queue/priority selection
|
|
30
|
+
- Each worker configuration can have its own `job_queue` and `job_priority` settings
|
|
31
|
+
|
|
10
32
|
## [1.0.7] - 2025-01-16
|
|
11
33
|
|
|
12
34
|
### Fixed
|
data/README.md
CHANGED
|
@@ -240,6 +240,13 @@ Scaling down triggers when **ALL** thresholds are met:
|
|
|
240
240
|
| `scale_down_cooldown_seconds` | Integer | `nil` | Override for scale-down cooldown |
|
|
241
241
|
| `persist_cooldowns` | Boolean | `true` | Save cooldowns to database |
|
|
242
242
|
|
|
243
|
+
### AutoscaleJob Settings
|
|
244
|
+
|
|
245
|
+
| Option | Type | Default | Description |
|
|
246
|
+
|--------|------|---------|-------------|
|
|
247
|
+
| `job_queue` | Symbol/String | `:autoscaler` | Queue for the AutoscaleJob |
|
|
248
|
+
| `job_priority` | Integer | `nil` | Priority for the AutoscaleJob (lower = higher priority) |
|
|
249
|
+
|
|
243
250
|
### Heroku-Specific
|
|
244
251
|
|
|
245
252
|
| Option | Type | Default | Description |
|
|
@@ -256,6 +263,482 @@ Scaling down triggers when **ALL** thresholds are met:
|
|
|
256
263
|
| `kubernetes_deployment` | String | `nil` | Deployment name to scale |
|
|
257
264
|
| `kubernetes_config_path` | String | `nil` | Path to kubeconfig (optional) |
|
|
258
265
|
|
|
266
|
+
## Common Configuration Examples
|
|
267
|
+
|
|
268
|
+
These examples show typical setups for different use cases. Copy and adapt them to your needs.
|
|
269
|
+
|
|
270
|
+
### Simple Setup (Single Worker, Heroku)
|
|
271
|
+
|
|
272
|
+
Ideal for small apps, side projects, or getting started:
|
|
273
|
+
|
|
274
|
+
```ruby
|
|
275
|
+
# config/initializers/solid_queue_autoscaler.rb
|
|
276
|
+
SolidQueueAutoscaler.configure do |config|
|
|
277
|
+
config.adapter = :heroku
|
|
278
|
+
config.heroku_api_key = ENV['HEROKU_API_KEY']
|
|
279
|
+
config.heroku_app_name = ENV['HEROKU_APP_NAME']
|
|
280
|
+
config.process_type = 'worker'
|
|
281
|
+
|
|
282
|
+
config.min_workers = 1
|
|
283
|
+
config.max_workers = 5
|
|
284
|
+
|
|
285
|
+
# Scale up when queue backs up
|
|
286
|
+
config.scale_up_queue_depth = 50
|
|
287
|
+
config.scale_up_latency_seconds = 180 # 3 minutes
|
|
288
|
+
|
|
289
|
+
# Scale down when queue is nearly empty
|
|
290
|
+
config.scale_down_queue_depth = 5
|
|
291
|
+
config.scale_down_latency_seconds = 30
|
|
292
|
+
|
|
293
|
+
# Safety: only run in production
|
|
294
|
+
config.dry_run = !Rails.env.production?
|
|
295
|
+
config.enabled = Rails.env.production?
|
|
296
|
+
end
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
```yaml
|
|
300
|
+
# config/recurring.yml
|
|
301
|
+
autoscaler:
|
|
302
|
+
class: SolidQueueAutoscaler::AutoscaleJob
|
|
303
|
+
queue: autoscaler
|
|
304
|
+
schedule: every 30 seconds
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
---
|
|
308
|
+
|
|
309
|
+
### Cost-Optimized Setup (Scale to Zero)
|
|
310
|
+
|
|
311
|
+
For apps with sporadic workloads where you want to minimize costs during idle periods:
|
|
312
|
+
|
|
313
|
+
```ruby
|
|
314
|
+
SolidQueueAutoscaler.configure do |config|
|
|
315
|
+
config.adapter = :heroku
|
|
316
|
+
config.heroku_api_key = ENV['HEROKU_API_KEY']
|
|
317
|
+
config.heroku_app_name = ENV['HEROKU_APP_NAME']
|
|
318
|
+
config.process_type = 'worker'
|
|
319
|
+
|
|
320
|
+
# Allow scaling to zero - no workers when idle
|
|
321
|
+
config.min_workers = 0
|
|
322
|
+
config.max_workers = 5
|
|
323
|
+
|
|
324
|
+
# Scale up immediately when any job is queued
|
|
325
|
+
config.scale_up_queue_depth = 1
|
|
326
|
+
config.scale_up_latency_seconds = 60 # 1 minute
|
|
327
|
+
|
|
328
|
+
# Scale down aggressively when empty
|
|
329
|
+
config.scale_down_queue_depth = 0
|
|
330
|
+
config.scale_down_latency_seconds = 10
|
|
331
|
+
|
|
332
|
+
# Shorter cooldowns for faster response
|
|
333
|
+
config.scale_up_cooldown_seconds = 30
|
|
334
|
+
config.scale_down_cooldown_seconds = 300 # 5 min before scaling to zero
|
|
335
|
+
|
|
336
|
+
config.enabled = Rails.env.production?
|
|
337
|
+
end
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
**⚠️ Note:** With `min_workers = 0`, there's cold-start latency when the first job arrives. The autoscaler must run on a web dyno or separate process, not on the workers themselves.
|
|
341
|
+
|
|
342
|
+
---
|
|
343
|
+
|
|
344
|
+
### E-Commerce / SaaS (Multiple Worker Types)
|
|
345
|
+
|
|
346
|
+
For apps with different job priorities (payments, notifications, reports):
|
|
347
|
+
|
|
348
|
+
```ruby
|
|
349
|
+
# Critical jobs - payments, webhooks, user-facing notifications
|
|
350
|
+
SolidQueueAutoscaler.configure(:critical_worker) do |config|
|
|
351
|
+
config.adapter = :heroku
|
|
352
|
+
config.heroku_api_key = ENV['HEROKU_API_KEY']
|
|
353
|
+
config.heroku_app_name = ENV['HEROKU_APP_NAME']
|
|
354
|
+
config.process_type = 'critical_worker'
|
|
355
|
+
|
|
356
|
+
config.queues = ['critical', 'payments', 'webhooks']
|
|
357
|
+
|
|
358
|
+
# Always have capacity, scale aggressively
|
|
359
|
+
config.min_workers = 2
|
|
360
|
+
config.max_workers = 10
|
|
361
|
+
config.scale_up_queue_depth = 5
|
|
362
|
+
config.scale_up_latency_seconds = 30
|
|
363
|
+
|
|
364
|
+
# Short cooldowns for responsiveness
|
|
365
|
+
config.cooldown_seconds = 60
|
|
366
|
+
|
|
367
|
+
# High-priority autoscaler job
|
|
368
|
+
config.job_queue = :autoscaler
|
|
369
|
+
config.job_priority = 0
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
# Default jobs - emails, notifications, analytics
|
|
373
|
+
SolidQueueAutoscaler.configure(:default_worker) do |config|
|
|
374
|
+
config.adapter = :heroku
|
|
375
|
+
config.heroku_api_key = ENV['HEROKU_API_KEY']
|
|
376
|
+
config.heroku_app_name = ENV['HEROKU_APP_NAME']
|
|
377
|
+
config.process_type = 'worker'
|
|
378
|
+
|
|
379
|
+
config.queues = ['default', 'mailers', 'analytics']
|
|
380
|
+
|
|
381
|
+
# Standard capacity, moderate scaling
|
|
382
|
+
config.min_workers = 1
|
|
383
|
+
config.max_workers = 8
|
|
384
|
+
config.scale_up_queue_depth = 100
|
|
385
|
+
config.scale_up_latency_seconds = 300
|
|
386
|
+
|
|
387
|
+
config.cooldown_seconds = 120
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
# Batch jobs - reports, exports, data processing
|
|
391
|
+
SolidQueueAutoscaler.configure(:batch_worker) do |config|
|
|
392
|
+
config.adapter = :heroku
|
|
393
|
+
config.heroku_api_key = ENV['HEROKU_API_KEY']
|
|
394
|
+
config.heroku_app_name = ENV['HEROKU_APP_NAME']
|
|
395
|
+
config.process_type = 'batch_worker'
|
|
396
|
+
|
|
397
|
+
config.queues = ['batch', 'reports', 'exports']
|
|
398
|
+
|
|
399
|
+
# Scale to zero when no batch jobs, scale up for any batch work
|
|
400
|
+
config.min_workers = 0
|
|
401
|
+
config.max_workers = 3
|
|
402
|
+
config.scale_up_queue_depth = 1
|
|
403
|
+
config.scale_down_queue_depth = 0
|
|
404
|
+
|
|
405
|
+
# Long cooldowns - batch jobs take time
|
|
406
|
+
config.cooldown_seconds = 300
|
|
407
|
+
end
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
```yaml
|
|
411
|
+
# config/recurring.yml
|
|
412
|
+
# Scale critical workers frequently
|
|
413
|
+
autoscaler_critical:
|
|
414
|
+
class: SolidQueueAutoscaler::AutoscaleJob
|
|
415
|
+
queue: autoscaler
|
|
416
|
+
schedule: every 15 seconds
|
|
417
|
+
args: [:critical_worker]
|
|
418
|
+
|
|
419
|
+
# Scale default workers normally
|
|
420
|
+
autoscaler_default:
|
|
421
|
+
class: SolidQueueAutoscaler::AutoscaleJob
|
|
422
|
+
queue: autoscaler
|
|
423
|
+
schedule: every 30 seconds
|
|
424
|
+
args: [:default_worker]
|
|
425
|
+
|
|
426
|
+
# Scale batch workers less frequently
|
|
427
|
+
autoscaler_batch:
|
|
428
|
+
class: SolidQueueAutoscaler::AutoscaleJob
|
|
429
|
+
queue: autoscaler
|
|
430
|
+
schedule: every 60 seconds
|
|
431
|
+
args: [:batch_worker]
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
---
|
|
435
|
+
|
|
436
|
+
### High-Volume API (Webhook Processing)
|
|
437
|
+
|
|
438
|
+
For apps processing many incoming webhooks or API callbacks:
|
|
439
|
+
|
|
440
|
+
```ruby
|
|
441
|
+
SolidQueueAutoscaler.configure do |config|
|
|
442
|
+
config.adapter = :heroku
|
|
443
|
+
config.heroku_api_key = ENV['HEROKU_API_KEY']
|
|
444
|
+
config.heroku_app_name = ENV['HEROKU_APP_NAME']
|
|
445
|
+
config.process_type = 'worker'
|
|
446
|
+
|
|
447
|
+
config.queues = ['webhooks', 'callbacks', 'api_jobs']
|
|
448
|
+
|
|
449
|
+
# Maintain baseline capacity
|
|
450
|
+
config.min_workers = 2
|
|
451
|
+
config.max_workers = 20
|
|
452
|
+
|
|
453
|
+
# Proportional scaling - scale based on actual load
|
|
454
|
+
config.scaling_strategy = :proportional
|
|
455
|
+
config.scale_up_queue_depth = 50
|
|
456
|
+
config.scale_up_latency_seconds = 60
|
|
457
|
+
|
|
458
|
+
# Add 1 worker per 25 jobs over threshold
|
|
459
|
+
config.scale_up_jobs_per_worker = 25
|
|
460
|
+
# Add 1 worker per 30 seconds over latency threshold
|
|
461
|
+
config.scale_up_latency_per_worker = 30
|
|
462
|
+
|
|
463
|
+
# Scale down when under capacity
|
|
464
|
+
config.scale_down_queue_depth = 10
|
|
465
|
+
config.scale_down_jobs_per_worker = 50
|
|
466
|
+
|
|
467
|
+
# Fast cooldowns for responsive scaling
|
|
468
|
+
config.scale_up_cooldown_seconds = 30
|
|
469
|
+
config.scale_down_cooldown_seconds = 120
|
|
470
|
+
|
|
471
|
+
config.job_priority = 0 # Process autoscaler jobs first
|
|
472
|
+
end
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
---
|
|
476
|
+
|
|
477
|
+
### Data Processing / ETL Pipeline
|
|
478
|
+
|
|
479
|
+
For apps with heavy data processing, imports, or batch ETL jobs:
|
|
480
|
+
|
|
481
|
+
```ruby
|
|
482
|
+
SolidQueueAutoscaler.configure(:etl_worker) do |config|
|
|
483
|
+
config.adapter = :heroku
|
|
484
|
+
config.heroku_api_key = ENV['HEROKU_API_KEY']
|
|
485
|
+
config.heroku_app_name = ENV['HEROKU_APP_NAME']
|
|
486
|
+
config.process_type = 'etl_worker'
|
|
487
|
+
|
|
488
|
+
config.queues = ['imports', 'exports', 'etl', 'data_sync']
|
|
489
|
+
|
|
490
|
+
# Scale to zero when no work, burst when needed
|
|
491
|
+
config.min_workers = 0
|
|
492
|
+
config.max_workers = 10
|
|
493
|
+
|
|
494
|
+
# Scale up as soon as work is queued
|
|
495
|
+
config.scale_up_queue_depth = 1
|
|
496
|
+
config.scale_up_latency_seconds = 120
|
|
497
|
+
|
|
498
|
+
# Use fixed scaling for predictable behavior
|
|
499
|
+
config.scaling_strategy = :fixed
|
|
500
|
+
config.scale_up_increment = 2 # Add 2 workers at a time
|
|
501
|
+
config.scale_down_decrement = 1
|
|
502
|
+
|
|
503
|
+
# Long cooldowns - ETL jobs are long-running
|
|
504
|
+
config.scale_up_cooldown_seconds = 120
|
|
505
|
+
config.scale_down_cooldown_seconds = 600 # 10 minutes
|
|
506
|
+
|
|
507
|
+
# Scale down only when truly idle
|
|
508
|
+
config.scale_down_queue_depth = 0
|
|
509
|
+
config.scale_down_latency_seconds = 0
|
|
510
|
+
end
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
---
|
|
514
|
+
|
|
515
|
+
### High-Availability Setup
|
|
516
|
+
|
|
517
|
+
For mission-critical apps requiring guaranteed capacity:
|
|
518
|
+
|
|
519
|
+
```ruby
|
|
520
|
+
SolidQueueAutoscaler.configure do |config|
|
|
521
|
+
config.adapter = :heroku
|
|
522
|
+
config.heroku_api_key = ENV['HEROKU_API_KEY']
|
|
523
|
+
config.heroku_app_name = ENV['HEROKU_APP_NAME']
|
|
524
|
+
config.process_type = 'worker'
|
|
525
|
+
|
|
526
|
+
# Always maintain minimum capacity
|
|
527
|
+
config.min_workers = 3
|
|
528
|
+
config.max_workers = 15
|
|
529
|
+
|
|
530
|
+
# Scale up proactively before queue backs up
|
|
531
|
+
config.scale_up_queue_depth = 25
|
|
532
|
+
config.scale_up_latency_seconds = 60
|
|
533
|
+
|
|
534
|
+
# Conservative scale-down
|
|
535
|
+
config.scale_down_queue_depth = 5
|
|
536
|
+
config.scale_down_latency_seconds = 15
|
|
537
|
+
|
|
538
|
+
# Longer cooldowns to prevent flapping
|
|
539
|
+
config.cooldown_seconds = 180
|
|
540
|
+
config.scale_down_cooldown_seconds = 300 # Extra cautious on scale-down
|
|
541
|
+
|
|
542
|
+
# Record all events for monitoring
|
|
543
|
+
config.record_events = true
|
|
544
|
+
config.record_all_events = true # Even no-change events
|
|
545
|
+
end
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
---
|
|
549
|
+
|
|
550
|
+
### Kubernetes Setup
|
|
551
|
+
|
|
552
|
+
For apps deployed on Kubernetes:
|
|
553
|
+
|
|
554
|
+
```ruby
|
|
555
|
+
SolidQueueAutoscaler.configure do |config|
|
|
556
|
+
config.adapter = :kubernetes
|
|
557
|
+
config.kubernetes_namespace = ENV.fetch('K8S_NAMESPACE', 'production')
|
|
558
|
+
config.kubernetes_deployment = 'solid-queue-worker'
|
|
559
|
+
|
|
560
|
+
# Optional: specify kubeconfig for local development
|
|
561
|
+
# config.kubernetes_kubeconfig = '~/.kube/config'
|
|
562
|
+
# config.kubernetes_context = 'my-cluster'
|
|
563
|
+
|
|
564
|
+
config.min_workers = 2 # Minimum replicas
|
|
565
|
+
config.max_workers = 20
|
|
566
|
+
|
|
567
|
+
config.scale_up_queue_depth = 100
|
|
568
|
+
config.scale_up_latency_seconds = 180
|
|
569
|
+
|
|
570
|
+
config.scale_down_queue_depth = 10
|
|
571
|
+
config.scale_down_latency_seconds = 30
|
|
572
|
+
|
|
573
|
+
# K8s scaling can be faster than Heroku
|
|
574
|
+
config.cooldown_seconds = 60
|
|
575
|
+
|
|
576
|
+
config.enabled = Rails.env.production?
|
|
577
|
+
end
|
|
578
|
+
```
|
|
579
|
+
|
|
580
|
+
**Required RBAC configuration:**
|
|
581
|
+
|
|
582
|
+
```yaml
|
|
583
|
+
apiVersion: rbac.authorization.k8s.io/v1
|
|
584
|
+
kind: Role
|
|
585
|
+
metadata:
|
|
586
|
+
name: solid-queue-autoscaler
|
|
587
|
+
namespace: production
|
|
588
|
+
rules:
|
|
589
|
+
- apiGroups: ["apps"]
|
|
590
|
+
resources: ["deployments", "deployments/scale"]
|
|
591
|
+
verbs: ["get", "patch", "update"]
|
|
592
|
+
---
|
|
593
|
+
apiVersion: rbac.authorization.k8s.io/v1
|
|
594
|
+
kind: RoleBinding
|
|
595
|
+
metadata:
|
|
596
|
+
name: solid-queue-autoscaler
|
|
597
|
+
namespace: production
|
|
598
|
+
subjects:
|
|
599
|
+
- kind: ServiceAccount
|
|
600
|
+
name: solid-queue-autoscaler
|
|
601
|
+
namespace: production
|
|
602
|
+
roleRef:
|
|
603
|
+
kind: Role
|
|
604
|
+
name: solid-queue-autoscaler
|
|
605
|
+
apiGroup: rbac.authorization.k8s.io
|
|
606
|
+
```
|
|
607
|
+
|
|
608
|
+
---
|
|
609
|
+
|
|
610
|
+
### Development / Testing Setup
|
|
611
|
+
|
|
612
|
+
For local development and CI environments:
|
|
613
|
+
|
|
614
|
+
```ruby
|
|
615
|
+
SolidQueueAutoscaler.configure do |config|
|
|
616
|
+
config.adapter = :heroku
|
|
617
|
+
config.heroku_api_key = ENV['HEROKU_API_KEY']
|
|
618
|
+
config.heroku_app_name = ENV['HEROKU_APP_NAME']
|
|
619
|
+
config.process_type = 'worker'
|
|
620
|
+
|
|
621
|
+
config.min_workers = 1
|
|
622
|
+
config.max_workers = 3
|
|
623
|
+
|
|
624
|
+
config.scale_up_queue_depth = 10
|
|
625
|
+
config.scale_up_latency_seconds = 60
|
|
626
|
+
|
|
627
|
+
# IMPORTANT: Disable in development, use dry_run in staging
|
|
628
|
+
case Rails.env
|
|
629
|
+
when 'production'
|
|
630
|
+
config.enabled = true
|
|
631
|
+
config.dry_run = false
|
|
632
|
+
when 'staging'
|
|
633
|
+
config.enabled = true
|
|
634
|
+
config.dry_run = true # Log decisions but don't scale
|
|
635
|
+
else
|
|
636
|
+
config.enabled = false
|
|
637
|
+
end
|
|
638
|
+
|
|
639
|
+
# Verbose logging for debugging
|
|
640
|
+
config.logger = Logger.new(STDOUT)
|
|
641
|
+
config.logger.level = Rails.env.production? ? Logger::INFO : Logger::DEBUG
|
|
642
|
+
end
|
|
643
|
+
```
|
|
644
|
+
|
|
645
|
+
---
|
|
646
|
+
|
|
647
|
+
### Configuration Comparison
|
|
648
|
+
|
|
649
|
+
| Use Case | min | max | scale_up_depth | scale_up_latency | cooldown | strategy |
|
|
650
|
+
|----------|-----|-----|----------------|------------------|----------|----------|
|
|
651
|
+
| Simple/Starter | 1 | 5 | 50 | 180s | 120s | fixed |
|
|
652
|
+
| Cost-Optimized | 0 | 5 | 1 | 60s | 30s/300s | fixed |
|
|
653
|
+
| E-Commerce Critical | 2 | 10 | 5 | 30s | 60s | fixed |
|
|
654
|
+
| E-Commerce Default | 1 | 8 | 100 | 300s | 120s | fixed |
|
|
655
|
+
| Webhook Processing | 2 | 20 | 50 | 60s | 30s/120s | proportional |
|
|
656
|
+
| ETL/Batch | 0 | 10 | 1 | 120s | 120s/600s | fixed |
|
|
657
|
+
| High-Availability | 3 | 15 | 25 | 60s | 180s/300s | fixed |
|
|
658
|
+
|
|
659
|
+
## Configuring a High-Priority Queue for the Autoscaler
|
|
660
|
+
|
|
661
|
+
The autoscaler job should run reliably and quickly, even when your queues are backed up. By default, the autoscaler job runs on the `:autoscaler` queue. You can configure this and set up Solid Queue to prioritize it.
|
|
662
|
+
|
|
663
|
+
### Configure the Job Queue and Priority
|
|
664
|
+
|
|
665
|
+
In your initializer, set the queue and priority for the autoscaler job:
|
|
666
|
+
|
|
667
|
+
```ruby
|
|
668
|
+
SolidQueueAutoscaler.configure do |config|
|
|
669
|
+
# Use a dedicated high-priority queue for the autoscaler
|
|
670
|
+
config.job_queue = :autoscaler # Default value
|
|
671
|
+
|
|
672
|
+
# Or use an existing high-priority queue
|
|
673
|
+
config.job_queue = :critical
|
|
674
|
+
|
|
675
|
+
# Set job priority (lower = higher priority, processed first)
|
|
676
|
+
# This works with queue backends that support job-level priority like Solid Queue
|
|
677
|
+
config.job_priority = 0 # Highest priority
|
|
678
|
+
|
|
679
|
+
# ... other config
|
|
680
|
+
end
|
|
681
|
+
```
|
|
682
|
+
|
|
683
|
+
For multi-worker configurations, each worker type can have its own queue and priority:
|
|
684
|
+
|
|
685
|
+
```ruby
|
|
686
|
+
SolidQueueAutoscaler.configure(:critical_worker) do |config|
|
|
687
|
+
config.job_queue = :autoscaler_critical
|
|
688
|
+
config.job_priority = 0 # Highest priority for critical worker scaling
|
|
689
|
+
# ... other config
|
|
690
|
+
end
|
|
691
|
+
|
|
692
|
+
SolidQueueAutoscaler.configure(:default_worker) do |config|
|
|
693
|
+
config.job_queue = :autoscaler_default
|
|
694
|
+
config.job_priority = 10 # Lower priority for default worker scaling
|
|
695
|
+
# ... other config
|
|
696
|
+
end
|
|
697
|
+
```
|
|
698
|
+
|
|
699
|
+
### Configure Solid Queue to Prioritize the Autoscaler
|
|
700
|
+
|
|
701
|
+
In your `config/solid_queue.yml`, ensure the autoscaler queue is processed by a dedicated worker or listed first in the queue order:
|
|
702
|
+
|
|
703
|
+
```yaml
|
|
704
|
+
# Option 1: Dedicated dispatcher/worker for autoscaler (recommended)
|
|
705
|
+
production:
|
|
706
|
+
dispatchers:
|
|
707
|
+
- polling_interval: 1
|
|
708
|
+
batch_size: 500
|
|
709
|
+
concurrency_maintenance_interval: 30
|
|
710
|
+
|
|
711
|
+
workers:
|
|
712
|
+
# Dedicated worker for autoscaler - always responsive
|
|
713
|
+
- queues: [autoscaler]
|
|
714
|
+
threads: 1
|
|
715
|
+
processes: 1
|
|
716
|
+
polling_interval: 0.5 # Check frequently
|
|
717
|
+
|
|
718
|
+
# Main workers for business logic
|
|
719
|
+
- queues: [critical, default, mailers]
|
|
720
|
+
threads: 5
|
|
721
|
+
processes: 2
|
|
722
|
+
polling_interval: 1
|
|
723
|
+
```
|
|
724
|
+
|
|
725
|
+
```yaml
|
|
726
|
+
# Option 2: Include autoscaler first in queue list (simpler)
|
|
727
|
+
production:
|
|
728
|
+
workers:
|
|
729
|
+
- queues: [autoscaler, critical, default, mailers]
|
|
730
|
+
threads: 5
|
|
731
|
+
processes: 2
|
|
732
|
+
```
|
|
733
|
+
|
|
734
|
+
Solid Queue processes queues in order, so listing `autoscaler` first ensures those jobs are picked up before others.
|
|
735
|
+
|
|
736
|
+
### Why This Matters
|
|
737
|
+
|
|
738
|
+
- **Responsiveness**: When your queues are backed up, you want the autoscaler to scale up workers quickly
|
|
739
|
+
- **Reliability**: A dedicated queue prevents autoscaler jobs from waiting behind thousands of business jobs
|
|
740
|
+
- **Isolation**: Separating autoscaler jobs makes monitoring and debugging easier
|
|
741
|
+
|
|
259
742
|
## Usage
|
|
260
743
|
|
|
261
744
|
### Running as a Solid Queue Recurring Job (Recommended)
|
|
@@ -506,10 +989,32 @@ KEEP_DAYS=7 bundle exec rake solid_queue_autoscaler:cleanup_events
|
|
|
506
989
|
|
|
507
990
|
Another autoscaler instance is currently running. This is expected behavior — only one instance should run at a time per worker type.
|
|
508
991
|
|
|
992
|
+
**If you believe no other instance is running:**
|
|
993
|
+
|
|
994
|
+
```ruby
|
|
995
|
+
# Check for stale advisory locks
|
|
996
|
+
ActiveRecord::Base.connection.execute(<<~SQL)
|
|
997
|
+
SELECT * FROM pg_locks WHERE locktype = 'advisory'
|
|
998
|
+
SQL
|
|
999
|
+
|
|
1000
|
+
# Force release a stuck lock (use with caution!)
|
|
1001
|
+
lock_key = SolidQueueAutoscaler.config.lock_key
|
|
1002
|
+
lock_id = Zlib.crc32(lock_key) & 0x7FFFFFFF
|
|
1003
|
+
ActiveRecord::Base.connection.execute("SELECT pg_advisory_unlock(#{lock_id})")
|
|
1004
|
+
```
|
|
1005
|
+
|
|
509
1006
|
### "Cooldown active"
|
|
510
1007
|
|
|
511
1008
|
A recent scaling event triggered the cooldown. Wait for the cooldown to expire or adjust `cooldown_seconds`.
|
|
512
1009
|
|
|
1010
|
+
```ruby
|
|
1011
|
+
# Check cooldown status
|
|
1012
|
+
bundle exec rake solid_queue_autoscaler:cooldown
|
|
1013
|
+
|
|
1014
|
+
# Reset cooldowns (for testing only)
|
|
1015
|
+
SolidQueueAutoscaler::CooldownTracker.reset!
|
|
1016
|
+
```
|
|
1017
|
+
|
|
513
1018
|
### Workers not scaling
|
|
514
1019
|
|
|
515
1020
|
1. Check that `enabled` is `true`
|
|
@@ -518,12 +1023,263 @@ A recent scaling event triggered the cooldown. Wait for the cooldown to expire o
|
|
|
518
1023
|
4. Enable dry-run to see what decisions would be made
|
|
519
1024
|
5. Check the logs for error messages
|
|
520
1025
|
|
|
1026
|
+
**Debug with a manual scale attempt:**
|
|
1027
|
+
|
|
1028
|
+
```ruby
|
|
1029
|
+
# Check configuration
|
|
1030
|
+
config = SolidQueueAutoscaler.config
|
|
1031
|
+
puts "Enabled: #{config.enabled?}"
|
|
1032
|
+
puts "Dry Run: #{config.dry_run?}"
|
|
1033
|
+
puts "API Key Set: #{config.heroku_api_key.present?}"
|
|
1034
|
+
|
|
1035
|
+
# Check current metrics
|
|
1036
|
+
metrics = SolidQueueAutoscaler.metrics
|
|
1037
|
+
puts "Queue depth: #{metrics.queue_depth}"
|
|
1038
|
+
puts "Latency: #{metrics.oldest_job_age_seconds}s"
|
|
1039
|
+
|
|
1040
|
+
# Try a manual scale
|
|
1041
|
+
result = SolidQueueAutoscaler.scale!
|
|
1042
|
+
puts result.decision.inspect if result.decision
|
|
1043
|
+
puts result.skipped_reason if result.skipped?
|
|
1044
|
+
puts result.error if result.error
|
|
1045
|
+
```
|
|
1046
|
+
|
|
1047
|
+
### Workers not scaling down
|
|
1048
|
+
|
|
1049
|
+
Scale-down requires **ALL** conditions to be met:
|
|
1050
|
+
|
|
1051
|
+
```ruby
|
|
1052
|
+
metrics = SolidQueueAutoscaler.metrics
|
|
1053
|
+
config = SolidQueueAutoscaler.config
|
|
1054
|
+
|
|
1055
|
+
puts "Queue depth: #{metrics.queue_depth} (threshold: <= #{config.scale_down_queue_depth})"
|
|
1056
|
+
puts "Latency: #{metrics.oldest_job_age_seconds}s (threshold: <= #{config.scale_down_latency_seconds}s)"
|
|
1057
|
+
puts "Claimed jobs: #{metrics.claimed_jobs}" # Must be 0 for idle scale-down
|
|
1058
|
+
puts "Current workers: #{SolidQueueAutoscaler.current_workers}"
|
|
1059
|
+
puts "Min workers: #{config.min_workers}" # Can't scale below this
|
|
1060
|
+
```
|
|
1061
|
+
|
|
1062
|
+
### Heroku API errors
|
|
1063
|
+
|
|
1064
|
+
**401 Unauthorized:**
|
|
1065
|
+
|
|
1066
|
+
```bash
|
|
1067
|
+
# Check if API key is valid
|
|
1068
|
+
heroku authorizations
|
|
1069
|
+
|
|
1070
|
+
# Create a new authorization
|
|
1071
|
+
heroku authorizations:create -d "Solid Queue Autoscaler"
|
|
1072
|
+
```
|
|
1073
|
+
|
|
1074
|
+
**404 Not Found:**
|
|
1075
|
+
|
|
1076
|
+
```bash
|
|
1077
|
+
# Verify app name
|
|
1078
|
+
heroku apps
|
|
1079
|
+
heroku apps:info -a $HEROKU_APP_NAME
|
|
1080
|
+
```
|
|
1081
|
+
|
|
1082
|
+
**429 Rate Limited:**
|
|
1083
|
+
|
|
1084
|
+
Increase cooldown to reduce API calls:
|
|
1085
|
+
|
|
1086
|
+
```ruby
|
|
1087
|
+
config.cooldown_seconds = 180 # 3 minutes instead of default 2
|
|
1088
|
+
```
|
|
1089
|
+
|
|
521
1090
|
### Kubernetes authentication issues
|
|
522
1091
|
|
|
523
1092
|
1. Ensure the service account has permissions to patch deployments
|
|
524
1093
|
2. Check namespace is correct
|
|
525
1094
|
3. Verify deployment name matches exactly
|
|
526
1095
|
|
|
1096
|
+
**Check RBAC permissions:**
|
|
1097
|
+
|
|
1098
|
+
```yaml
|
|
1099
|
+
# Required RBAC rules for the autoscaler service account
|
|
1100
|
+
apiVersion: rbac.authorization.k8s.io/v1
|
|
1101
|
+
kind: Role
|
|
1102
|
+
metadata:
|
|
1103
|
+
name: solid-queue-autoscaler
|
|
1104
|
+
rules:
|
|
1105
|
+
- apiGroups: ["apps"]
|
|
1106
|
+
resources: ["deployments", "deployments/scale"]
|
|
1107
|
+
verbs: ["get", "patch", "update"]
|
|
1108
|
+
```
|
|
1109
|
+
|
|
1110
|
+
### AutoscaleJob not running
|
|
1111
|
+
|
|
1112
|
+
**Check recurring.yml configuration:**
|
|
1113
|
+
|
|
1114
|
+
```yaml
|
|
1115
|
+
# config/recurring.yml
|
|
1116
|
+
autoscaler:
|
|
1117
|
+
class: SolidQueueAutoscaler::AutoscaleJob
|
|
1118
|
+
queue: autoscaler
|
|
1119
|
+
schedule: every 30 seconds
|
|
1120
|
+
```
|
|
1121
|
+
|
|
1122
|
+
**Ensure a worker processes the autoscaler queue:**
|
|
1123
|
+
|
|
1124
|
+
```yaml
|
|
1125
|
+
# config/solid_queue.yml
|
|
1126
|
+
workers:
|
|
1127
|
+
- queues: [autoscaler] # Must include autoscaler queue
|
|
1128
|
+
threads: 1
|
|
1129
|
+
```
|
|
1130
|
+
|
|
1131
|
+
**Test manual enqueue:**
|
|
1132
|
+
|
|
1133
|
+
```ruby
|
|
1134
|
+
SolidQueueAutoscaler::AutoscaleJob.perform_later
|
|
1135
|
+
```
|
|
1136
|
+
|
|
1137
|
+
### Multi-worker configuration issues
|
|
1138
|
+
|
|
1139
|
+
**"Unknown worker: :my_worker":**
|
|
1140
|
+
|
|
1141
|
+
Ensure you've configured the worker before referencing it:
|
|
1142
|
+
|
|
1143
|
+
```ruby
|
|
1144
|
+
# Configure the worker first
|
|
1145
|
+
SolidQueueAutoscaler.configure(:my_worker) do |config|
|
|
1146
|
+
config.heroku_api_key = ENV['HEROKU_API_KEY']
|
|
1147
|
+
config.heroku_app_name = ENV['HEROKU_APP_NAME']
|
|
1148
|
+
config.process_type = 'my_worker'
|
|
1149
|
+
end
|
|
1150
|
+
|
|
1151
|
+
# Then reference it
|
|
1152
|
+
SolidQueueAutoscaler.scale!(:my_worker)
|
|
1153
|
+
```
|
|
1154
|
+
|
|
1155
|
+
**List all registered workers:**
|
|
1156
|
+
|
|
1157
|
+
```ruby
|
|
1158
|
+
SolidQueueAutoscaler.registered_workers
|
|
1159
|
+
# => [:default, :critical_worker, :batch_worker]
|
|
1160
|
+
```
|
|
1161
|
+
|
|
1162
|
+
### Database/Migration issues
|
|
1163
|
+
|
|
1164
|
+
**"relation 'solid_queue_autoscaler_state' does not exist":**
|
|
1165
|
+
|
|
1166
|
+
```bash
|
|
1167
|
+
rails generate solid_queue_autoscaler:migration
|
|
1168
|
+
rails db:migrate
|
|
1169
|
+
```
|
|
1170
|
+
|
|
1171
|
+
**"relation 'solid_queue_ready_executions' does not exist":**
|
|
1172
|
+
|
|
1173
|
+
Solid Queue tables are missing. Run Solid Queue migrations:
|
|
1174
|
+
|
|
1175
|
+
```bash
|
|
1176
|
+
rails solid_queue:install:migrations
|
|
1177
|
+
rails db:migrate
|
|
1178
|
+
```
|
|
1179
|
+
|
|
1180
|
+
**Multi-database setup (Solid Queue on separate database):**
|
|
1181
|
+
|
|
1182
|
+
The autoscaler automatically detects `SolidQueue::Record.connection`. If auto-detection fails:
|
|
1183
|
+
|
|
1184
|
+
```ruby
|
|
1185
|
+
SolidQueueAutoscaler.configure do |config|
|
|
1186
|
+
config.database_connection = SolidQueue::Record.connection
|
|
1187
|
+
end
|
|
1188
|
+
```
|
|
1189
|
+
|
|
1190
|
+
### Dashboard not loading
|
|
1191
|
+
|
|
1192
|
+
**404 when visiting /autoscaler:**
|
|
1193
|
+
|
|
1194
|
+
Ensure the engine is mounted in `config/routes.rb`:
|
|
1195
|
+
|
|
1196
|
+
```ruby
|
|
1197
|
+
mount SolidQueueAutoscaler::Dashboard::Engine => "/autoscaler"
|
|
1198
|
+
```
|
|
1199
|
+
|
|
1200
|
+
**"ActionView::MissingTemplate" errors:**
|
|
1201
|
+
|
|
1202
|
+
Run the dashboard generator:
|
|
1203
|
+
|
|
1204
|
+
```bash
|
|
1205
|
+
rails generate solid_queue_autoscaler:dashboard
|
|
1206
|
+
rails db:migrate
|
|
1207
|
+
```
|
|
1208
|
+
|
|
1209
|
+
### Wrong process type being scaled
|
|
1210
|
+
|
|
1211
|
+
```ruby
|
|
1212
|
+
# Check what process type is configured
|
|
1213
|
+
puts SolidQueueAutoscaler.config.process_type
|
|
1214
|
+
|
|
1215
|
+
# Verify it matches your Procfile
|
|
1216
|
+
# Procfile:
|
|
1217
|
+
# web: bundle exec puma -C config/puma.rb
|
|
1218
|
+
# worker: bundle exec rake solid_queue:start # <- This is "worker"
|
|
1219
|
+
```
|
|
1220
|
+
|
|
1221
|
+
### Scaling too aggressively or too slowly
|
|
1222
|
+
|
|
1223
|
+
**Scaling up too often (flapping):**
|
|
1224
|
+
|
|
1225
|
+
```ruby
|
|
1226
|
+
config.cooldown_seconds = 180 # Increase cooldown
|
|
1227
|
+
config.scale_up_cooldown_seconds = 120 # Or set scale-up specific cooldown
|
|
1228
|
+
config.scale_up_queue_depth = 200 # Increase threshold
|
|
1229
|
+
```
|
|
1230
|
+
|
|
1231
|
+
**Not scaling up fast enough:**
|
|
1232
|
+
|
|
1233
|
+
```ruby
|
|
1234
|
+
config.scale_up_queue_depth = 50 # Lower threshold
|
|
1235
|
+
config.scale_up_latency_seconds = 120 # Trigger on 2 min latency
|
|
1236
|
+
config.cooldown_seconds = 60 # Reduce cooldown
|
|
1237
|
+
config.scaling_strategy = :proportional # Scale based on load, not fixed increment
|
|
1238
|
+
config.scale_up_jobs_per_worker = 25 # More workers per jobs over threshold
|
|
1239
|
+
```
|
|
1240
|
+
|
|
1241
|
+
**Not scaling down:**
|
|
1242
|
+
|
|
1243
|
+
```ruby
|
|
1244
|
+
config.scale_down_queue_depth = 5 # More aggressive scale-down threshold
|
|
1245
|
+
config.scale_down_latency_seconds = 10 # Tighter latency requirement
|
|
1246
|
+
config.min_workers = 0 # Allow scaling to zero (if appropriate)
|
|
1247
|
+
```
|
|
1248
|
+
|
|
1249
|
+
### Debugging tips
|
|
1250
|
+
|
|
1251
|
+
**Enable debug logging:**
|
|
1252
|
+
|
|
1253
|
+
```ruby
|
|
1254
|
+
SolidQueueAutoscaler.configure do |config|
|
|
1255
|
+
config.logger = Logger.new(STDOUT)
|
|
1256
|
+
config.logger.level = Logger::DEBUG
|
|
1257
|
+
end
|
|
1258
|
+
```
|
|
1259
|
+
|
|
1260
|
+
**Simulate a scaling decision without making changes:**
|
|
1261
|
+
|
|
1262
|
+
```ruby
|
|
1263
|
+
metrics = SolidQueueAutoscaler.metrics
|
|
1264
|
+
workers = SolidQueueAutoscaler.current_workers
|
|
1265
|
+
engine = SolidQueueAutoscaler::DecisionEngine.new(config: SolidQueueAutoscaler.config)
|
|
1266
|
+
decision = engine.decide(metrics: metrics, current_workers: workers)
|
|
1267
|
+
|
|
1268
|
+
puts "Action: #{decision.action}" # :scale_up, :scale_down, or :no_change
|
|
1269
|
+
puts "From: #{decision.from} -> To: #{decision.to}"
|
|
1270
|
+
puts "Reason: #{decision.reason}"
|
|
1271
|
+
```
|
|
1272
|
+
|
|
1273
|
+
**Run diagnostics:**
|
|
1274
|
+
|
|
1275
|
+
```bash
|
|
1276
|
+
bundle exec rake solid_queue_autoscaler:metrics
|
|
1277
|
+
bundle exec rake solid_queue_autoscaler:formation
|
|
1278
|
+
bundle exec rake solid_queue_autoscaler:cooldown
|
|
1279
|
+
```
|
|
1280
|
+
|
|
1281
|
+
For more detailed troubleshooting, see [docs/troubleshooting.md](docs/troubleshooting.md).
|
|
1282
|
+
|
|
527
1283
|
## Architecture Notes
|
|
528
1284
|
|
|
529
1285
|
This gem acts as a **control plane** for Solid Queue:
|
data/lib/generators/solid_queue_autoscaler/templates/create_solid_queue_autoscaler_events.rb.erb
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
class CreateSolidQueueAutoscalerEvents < ActiveRecord::Migration<%= migration_version %>
|
|
4
|
+
# Use the same database connection as SolidQueue for multi-database setups
|
|
5
|
+
def self.connection
|
|
6
|
+
if defined?(SolidQueue::Record) && SolidQueue::Record.respond_to?(:connection)
|
|
7
|
+
SolidQueue::Record.connection
|
|
8
|
+
else
|
|
9
|
+
super
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
4
13
|
def change
|
|
5
14
|
create_table :solid_queue_autoscaler_events do |t|
|
|
6
15
|
t.string :worker_name, null: false
|
data/lib/generators/solid_queue_autoscaler/templates/create_solid_queue_autoscaler_state.rb.erb
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
class CreateSolidQueueAutoscalerState < ActiveRecord::Migration<%= migration_version %>
|
|
4
|
+
# Use the same database connection as SolidQueue for multi-database setups
|
|
5
|
+
def self.connection
|
|
6
|
+
if defined?(SolidQueue::Record) && SolidQueue::Record.respond_to?(:connection)
|
|
7
|
+
SolidQueue::Record.connection
|
|
8
|
+
else
|
|
9
|
+
super
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
4
13
|
def change
|
|
5
14
|
create_table :solid_queue_autoscaler_state do |t|
|
|
6
15
|
t.string :key, null: false
|
|
@@ -55,4 +55,10 @@ SolidQueueAutoscaler.configure do |config|
|
|
|
55
55
|
config.record_events = true
|
|
56
56
|
# Also record no_change events (verbose, generates many records)
|
|
57
57
|
# config.record_all_events = false
|
|
58
|
+
|
|
59
|
+
# AutoscaleJob Settings
|
|
60
|
+
# Queue for the autoscaler job (use a fast/high-priority Solid Queue queue)
|
|
61
|
+
config.job_queue = :autoscaler
|
|
62
|
+
# Priority for the autoscaler job (lower = higher priority, nil = default)
|
|
63
|
+
# config.job_priority = 0 # Uncomment to set highest priority
|
|
58
64
|
end
|
|
@@ -2,7 +2,40 @@
|
|
|
2
2
|
|
|
3
3
|
module SolidQueueAutoscaler
|
|
4
4
|
class AutoscaleJob < ActiveJob::Base
|
|
5
|
-
|
|
5
|
+
# Use configured queue for the target worker (defaults to :autoscaler)
|
|
6
|
+
queue_as do
|
|
7
|
+
# perform(worker_name = :default)
|
|
8
|
+
worker_name = arguments.first
|
|
9
|
+
|
|
10
|
+
# When scaling all workers, or when worker_name is nil, use the default configuration
|
|
11
|
+
config_name =
|
|
12
|
+
if worker_name.nil? || worker_name == :all || worker_name == "all"
|
|
13
|
+
:default
|
|
14
|
+
else
|
|
15
|
+
# Handle both Symbol and String values safely
|
|
16
|
+
worker_name.to_sym rescue :default
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
SolidQueueAutoscaler.config(config_name).job_queue || :autoscaler
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Use configured priority for the target worker (defaults to nil/no priority)
|
|
23
|
+
queue_with_priority do
|
|
24
|
+
# perform(worker_name = :default)
|
|
25
|
+
worker_name = arguments.first
|
|
26
|
+
|
|
27
|
+
# When scaling all workers, or when worker_name is nil, use the default configuration
|
|
28
|
+
config_name =
|
|
29
|
+
if worker_name.nil? || worker_name == :all || worker_name == "all"
|
|
30
|
+
:default
|
|
31
|
+
else
|
|
32
|
+
# Handle both Symbol and String values safely
|
|
33
|
+
worker_name.to_sym rescue :default
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
SolidQueueAutoscaler.config(config_name).job_priority
|
|
37
|
+
end
|
|
38
|
+
|
|
6
39
|
discard_on ConfigurationError
|
|
7
40
|
|
|
8
41
|
# Scale a specific worker type, or all workers if :all is passed
|
|
@@ -63,6 +63,9 @@ module SolidQueueAutoscaler
|
|
|
63
63
|
# Dashboard/event recording settings
|
|
64
64
|
attr_accessor :record_events, :record_all_events
|
|
65
65
|
|
|
66
|
+
# AutoscaleJob settings
|
|
67
|
+
attr_accessor :job_queue, :job_priority
|
|
68
|
+
|
|
66
69
|
def initialize
|
|
67
70
|
# Configuration name (auto-set when using named configurations)
|
|
68
71
|
@name = :default
|
|
@@ -128,6 +131,10 @@ module SolidQueueAutoscaler
|
|
|
128
131
|
# Dashboard/event recording settings
|
|
129
132
|
@record_events = true # Record scale events to database
|
|
130
133
|
@record_all_events = false # Also record no_change events (verbose)
|
|
134
|
+
|
|
135
|
+
# AutoscaleJob settings
|
|
136
|
+
@job_queue = :autoscaler # Queue name for the autoscaler job
|
|
137
|
+
@job_priority = nil # Job priority (lower = higher priority, nil = default)
|
|
131
138
|
end
|
|
132
139
|
|
|
133
140
|
# Returns the lock key, auto-generating based on name if not explicitly set
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: solid_queue_autoscaler
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0.
|
|
4
|
+
version: 1.0.8
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- reillyse
|
|
@@ -52,6 +52,20 @@ dependencies:
|
|
|
52
52
|
- - "~>"
|
|
53
53
|
- !ruby/object:Gem::Version
|
|
54
54
|
version: '3.0'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: activejob
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - ">="
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '7.0'
|
|
62
|
+
type: :development
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - ">="
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '7.0'
|
|
55
69
|
- !ruby/object:Gem::Dependency
|
|
56
70
|
name: rake
|
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|