solid_queue 1.2.0 → 1.2.2
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 +41 -16
 - data/app/models/solid_queue/claimed_execution.rb +4 -2
 - data/app/models/solid_queue/failed_execution.rb +5 -2
 - data/app/models/solid_queue/job.rb +2 -0
 - data/app/models/solid_queue/record.rb +13 -5
 - data/app/models/solid_queue/recurring_execution.rb +1 -1
 - data/app/models/solid_queue/recurring_task.rb +2 -3
 - data/app/models/solid_queue/semaphore.rb +1 -1
 - data/lib/puma/plugin/solid_queue.rb +19 -7
 - data/lib/solid_queue/cli.rb +1 -1
 - data/lib/solid_queue/supervisor.rb +11 -7
 - data/lib/solid_queue/version.rb +1 -1
 - metadata +10 -11
 - data/Rakefile +0 -21
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: ee016dde69aa11302fc20335e657d8f65f31049ad1fad9bd56c363c8e8a6ee4d
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: fd54a73d694f7dba45bd8a86daf426cceb4f351c87492d75130517b23929d884
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 4a30c8abce9f22bd28901387bbb7c0a1da41671926033f48c2f46339f3b447ae5677763f52f0b1b46801a9f3bf206834d1eae712462989bbe6a6d8daa2fd43e5
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 286fafe7fc7202053989993bbfc51a207b530a538f2898685482c328cfe6bf28fc3b514d4ea15c2c5fcb57cadbf010c43823fba12dd92dfc54b0b105a45f340c
         
     | 
    
        data/README.md
    CHANGED
    
    | 
         @@ -1,12 +1,12 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # Solid Queue
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
     | 
    
         
            -
            Solid Queue is a  
     | 
| 
      
 3 
     | 
    
         
            +
            Solid Queue is a database-based queuing backend for [Active Job](https://edgeguides.rubyonrails.org/active_job_basics.html), designed with simplicity and performance in mind.
         
     | 
| 
       4 
4 
     | 
    
         | 
| 
       5 
     | 
    
         
            -
             
     | 
| 
      
 5 
     | 
    
         
            +
            In addition to regular job enqueuing and processing, Solid Queue supports delayed jobs, concurrency controls, recurring jobs, pausing queues, numeric priorities per job, priorities by queue order, and bulk enqueuing (`enqueue_all` for Active Job's `perform_all_later`).
         
     | 
| 
       6 
6 
     | 
    
         | 
| 
       7 
     | 
    
         
            -
            Solid Queue can be used with SQL databases such as MySQL, PostgreSQL or SQLite, and it leverages the `FOR UPDATE SKIP LOCKED` clause, if available, to avoid blocking and waiting on locks when polling jobs. It relies on Active Job for retries, discarding, error handling, serialization,  
     | 
| 
      
 7 
     | 
    
         
            +
            Solid Queue can be used with SQL databases such as MySQL, PostgreSQL, or SQLite, and it leverages the `FOR UPDATE SKIP LOCKED` clause, if available, to avoid blocking and waiting on locks when polling jobs. It relies on Active Job for retries, discarding, error handling, serialization, and delays, and it's compatible with Ruby on Rails's multi-threading.
         
     | 
| 
       8 
8 
     | 
    
         | 
| 
       9 
     | 
    
         
            -
            ## Table of  
     | 
| 
      
 9 
     | 
    
         
            +
            ## Table of Contents
         
     | 
| 
       10 
10 
     | 
    
         | 
| 
       11 
11 
     | 
    
         
             
            - [Installation](#installation)
         
     | 
| 
       12 
12 
     | 
    
         
             
              - [Usage in development and other non-production environments](#usage-in-development-and-other-non-production-environments)
         
     | 
| 
         @@ -15,10 +15,10 @@ Solid Queue can be used with SQL databases such as MySQL, PostgreSQL or SQLite, 
     | 
|
| 
       15 
15 
     | 
    
         
             
              - [Incremental adoption](#incremental-adoption)
         
     | 
| 
       16 
16 
     | 
    
         
             
              - [High performance requirements](#high-performance-requirements)
         
     | 
| 
       17 
17 
     | 
    
         
             
            - [Configuration](#configuration)
         
     | 
| 
       18 
     | 
    
         
            -
              - [Workers, dispatchers and scheduler](#workers-dispatchers-and-scheduler)
         
     | 
| 
      
 18 
     | 
    
         
            +
              - [Workers, dispatchers, and scheduler](#workers-dispatchers-and-scheduler)
         
     | 
| 
       19 
19 
     | 
    
         
             
              - [Queue order and priorities](#queue-order-and-priorities)
         
     | 
| 
       20 
20 
     | 
    
         
             
              - [Queues specification and performance](#queues-specification-and-performance)
         
     | 
| 
       21 
     | 
    
         
            -
              - [Threads, processes and signals](#threads-processes-and-signals)
         
     | 
| 
      
 21 
     | 
    
         
            +
              - [Threads, processes, and signals](#threads-processes-and-signals)
         
     | 
| 
       22 
22 
     | 
    
         
             
              - [Database configuration](#database-configuration)
         
     | 
| 
       23 
23 
     | 
    
         
             
              - [Other configuration settings](#other-configuration-settings)
         
     | 
| 
       24 
24 
     | 
    
         
             
            - [Lifecycle hooks](#lifecycle-hooks)
         
     | 
| 
         @@ -36,7 +36,7 @@ Solid Queue can be used with SQL databases such as MySQL, PostgreSQL or SQLite, 
     | 
|
| 
       36 
36 
     | 
    
         | 
| 
       37 
37 
     | 
    
         
             
            ## Installation
         
     | 
| 
       38 
38 
     | 
    
         | 
| 
       39 
     | 
    
         
            -
            Solid Queue is configured by default in new Rails 8 applications.  
     | 
| 
      
 39 
     | 
    
         
            +
            Solid Queue is configured by default in new Rails 8 applications. If you're running an earlier version, you can add it manually following these steps:
         
     | 
| 
       40 
40 
     | 
    
         | 
| 
       41 
41 
     | 
    
         
             
            1. `bundle add solid_queue`
         
     | 
| 
       42 
42 
     | 
    
         
             
            2. `bin/rails solid_queue:install`
         
     | 
| 
         @@ -45,7 +45,7 @@ Solid Queue is configured by default in new Rails 8 applications. But if you're 
     | 
|
| 
       45 
45 
     | 
    
         | 
| 
       46 
46 
     | 
    
         
             
            This will configure Solid Queue as the production Active Job backend, create the configuration files `config/queue.yml` and `config/recurring.yml`, and create the `db/queue_schema.rb`. It'll also create a `bin/jobs` executable wrapper that you can use to start Solid Queue.
         
     | 
| 
       47 
47 
     | 
    
         | 
| 
       48 
     | 
    
         
            -
            Once you've done that, you will  
     | 
| 
      
 48 
     | 
    
         
            +
            Once you've done that, you will have to add the configuration for the queue database in `config/database.yml`. If you're using SQLite, it'll look like this:
         
     | 
| 
       49 
49 
     | 
    
         | 
| 
       50 
50 
     | 
    
         
             
            ```yaml
         
     | 
| 
       51 
51 
     | 
    
         
             
            production:
         
     | 
| 
         @@ -79,7 +79,7 @@ Now you're ready to start processing jobs by running `bin/jobs` on the server th 
     | 
|
| 
       79 
79 
     | 
    
         | 
| 
       80 
80 
     | 
    
         
             
            For small projects, you can run Solid Queue on the same machine as your webserver. When you're ready to scale, Solid Queue supports horizontal scaling out-of-the-box. You can run Solid Queue on a separate server from your webserver, or even run `bin/jobs` on multiple machines at the same time. Depending on the configuration, you can designate some machines to run only dispatchers or only workers. See the [configuration](#configuration) section for more details on this.
         
     | 
| 
       81 
81 
     | 
    
         | 
| 
       82 
     | 
    
         
            -
            **Note**:  
     | 
| 
      
 82 
     | 
    
         
            +
            **Note**: Future changes to the schema will come in the form of regular migrations.
         
     | 
| 
       83 
83 
     | 
    
         | 
| 
       84 
84 
     | 
    
         
             
            ### Usage in development and other non-production environments
         
     | 
| 
       85 
85 
     | 
    
         | 
| 
         @@ -150,7 +150,7 @@ development: 
     | 
|
| 
       150 
150 
     | 
    
         | 
| 
       151 
151 
     | 
    
         
             
            ### Single database configuration
         
     | 
| 
       152 
152 
     | 
    
         | 
| 
       153 
     | 
    
         
            -
            Running Solid Queue in a separate database is recommended, but it's also possible to use one single database for both the app and the queue.  
     | 
| 
      
 153 
     | 
    
         
            +
            Running Solid Queue in a separate database is recommended, but it's also possible to use one single database for both the app and the queue. Follow these steps:
         
     | 
| 
       154 
154 
     | 
    
         | 
| 
       155 
155 
     | 
    
         
             
            1. Copy the contents of `db/queue_schema.rb` into a normal migration and delete `db/queue_schema.rb`
         
     | 
| 
       156 
156 
     | 
    
         
             
            2. Remove `config.solid_queue.connects_to` from `production.rb`
         
     | 
| 
         @@ -158,7 +158,7 @@ Running Solid Queue in a separate database is recommended, but it's also possibl 
     | 
|
| 
       158 
158 
     | 
    
         | 
| 
       159 
159 
     | 
    
         
             
            You won't have multiple databases, so `database.yml` doesn't need to have primary and queue database.
         
     | 
| 
       160 
160 
     | 
    
         | 
| 
       161 
     | 
    
         
            -
            ### Dashboard  
     | 
| 
      
 161 
     | 
    
         
            +
            ### Dashboard UI Setup
         
     | 
| 
       162 
162 
     | 
    
         | 
| 
       163 
163 
     | 
    
         
             
            For viewing information about your jobs via a UI, we recommend taking a look at [mission_control-jobs](https://github.com/rails/mission_control-jobs), a dashboard where, among other things, you can examine and retry/discard failed jobs.
         
     | 
| 
       164 
164 
     | 
    
         | 
| 
         @@ -181,7 +181,7 @@ Solid Queue was designed for the highest throughput when used with MySQL 8+ or P 
     | 
|
| 
       181 
181 
     | 
    
         | 
| 
       182 
182 
     | 
    
         
             
            ## Configuration
         
     | 
| 
       183 
183 
     | 
    
         | 
| 
       184 
     | 
    
         
            -
            ### Workers, dispatchers and scheduler
         
     | 
| 
      
 184 
     | 
    
         
            +
            ### Workers, dispatchers, and scheduler
         
     | 
| 
       185 
185 
     | 
    
         | 
| 
       186 
186 
     | 
    
         
             
            We have several types of actors in Solid Queue:
         
     | 
| 
       187 
187 
     | 
    
         | 
| 
         @@ -330,7 +330,7 @@ queues: back* 
     | 
|
| 
       330 
330 
     | 
    
         
             
            ```
         
     | 
| 
       331 
331 
     | 
    
         | 
| 
       332 
332 
     | 
    
         | 
| 
       333 
     | 
    
         
            -
            ### Threads, processes and signals
         
     | 
| 
      
 333 
     | 
    
         
            +
            ### Threads, processes, and signals
         
     | 
| 
       334 
334 
     | 
    
         | 
| 
       335 
335 
     | 
    
         
             
            Workers in Solid Queue use a thread pool to run work in multiple threads, configurable via the `threads` parameter above. Besides this, parallelism can be achieved via multiple processes on one machine (configurable via different workers or the `processes` parameter above) or by horizontal scaling.
         
     | 
| 
       336 
336 
     | 
    
         | 
| 
         @@ -366,7 +366,7 @@ There are several settings that control how Solid Queue works that you can set a 
     | 
|
| 
       366 
366 
     | 
    
         | 
| 
       367 
367 
     | 
    
         
             
              **This is not used for errors raised within a job execution**. Errors happening in jobs are handled by Active Job's `retry_on` or `discard_on`, and ultimately will result in [failed jobs](#failed-jobs-and-retries). This is for errors happening within Solid Queue itself.
         
     | 
| 
       368 
368 
     | 
    
         | 
| 
       369 
     | 
    
         
            -
            - `use_skip_locked`: whether to use `FOR UPDATE SKIP LOCKED` when performing locking reads. This will be automatically detected in the future, and for now, you 
     | 
| 
      
 369 
     | 
    
         
            +
            - `use_skip_locked`: whether to use `FOR UPDATE SKIP LOCKED` when performing locking reads. This will be automatically detected in the future, and for now, you only need to set this to `false` if your database doesn't support it. For MySQL, that'd be versions < 8, and for PostgreSQL, versions < 9.5. If you use SQLite, this has no effect, as writes are sequential.
         
     | 
| 
       370 
370 
     | 
    
         
             
            - `process_heartbeat_interval`: the heartbeat interval that all processes will follow—defaults to 60 seconds.
         
     | 
| 
       371 
371 
     | 
    
         
             
            - `process_alive_threshold`: how long to wait until a process is considered dead after its last heartbeat—defaults to 5 minutes.
         
     | 
| 
       372 
372 
     | 
    
         
             
            - `shutdown_timeout`: time the supervisor will wait since it sent the `TERM` signal to its supervised processes before sending a `QUIT` version to them requesting immediate termination—defaults to 5 seconds.
         
     | 
| 
         @@ -433,7 +433,9 @@ In the case of recurring tasks, if such error is raised when enqueuing the job c 
     | 
|
| 
       433 
433 
     | 
    
         | 
| 
       434 
434 
     | 
    
         
             
            ## Concurrency controls
         
     | 
| 
       435 
435 
     | 
    
         | 
| 
       436 
     | 
    
         
            -
            Solid Queue extends Active Job with concurrency controls, that allows you to limit how many jobs of a certain type or with certain arguments can run at the same time. When limited in this way, by default, jobs will be blocked from running 
     | 
| 
      
 436 
     | 
    
         
            +
            Solid Queue extends Active Job with concurrency controls, that allows you to limit how many jobs of a certain type or with certain arguments can run at the same time. When limited in this way, **by default, jobs will be blocked from running**, and they'll stay blocked until another job finishes and unblocks them, or after the set expiry time (concurrency limit's _duration_) elapses.
         
     | 
| 
      
 437 
     | 
    
         
            +
             
     | 
| 
      
 438 
     | 
    
         
            +
            **Alternatively, jobs can be configured to be discarded instead of blocked**. This means that if a job with certain arguments has already been enqueued, other jobs with the same characteristics (in the same concurrency _class_) won't be enqueued.
         
     | 
| 
       437 
439 
     | 
    
         | 
| 
       438 
440 
     | 
    
         
             
            ```ruby
         
     | 
| 
       439 
441 
     | 
    
         
             
            class MyJob < ApplicationJob
         
     | 
| 
         @@ -496,6 +498,29 @@ Jobs are unblocked in order of priority but **queue order is not taken into acco 
     | 
|
| 
       496 
498 
     | 
    
         | 
| 
       497 
499 
     | 
    
         
             
            Finally, failed jobs that are automatically or manually retried work in the same way as new jobs that get enqueued: they get in the queue for getting an open semaphore, and whenever they get it, they'll be run. It doesn't matter if they had already gotten an open semaphore in the past.
         
     | 
| 
       498 
500 
     | 
    
         | 
| 
      
 501 
     | 
    
         
            +
            ### Scheduled jobs
         
     | 
| 
      
 502 
     | 
    
         
            +
             
     | 
| 
      
 503 
     | 
    
         
            +
            Jobs set to run in the future (via Active Job's `wait` or `wait_until` options) have concurrency limits enforced when they're due, not when they're scheduled. For example, consider this job:
         
     | 
| 
      
 504 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 505 
     | 
    
         
            +
            class DeliverAnnouncementToContactJob < ApplicationJob
         
     | 
| 
      
 506 
     | 
    
         
            +
              limits_concurrency to: 1, key: ->(contact) { contact.account }, duration: 5.minutes
         
     | 
| 
      
 507 
     | 
    
         
            +
             
     | 
| 
      
 508 
     | 
    
         
            +
              def perform(contact)
         
     | 
| 
      
 509 
     | 
    
         
            +
                # ...
         
     | 
| 
      
 510 
     | 
    
         
            +
            ```
         
     | 
| 
      
 511 
     | 
    
         
            +
             
     | 
| 
      
 512 
     | 
    
         
            +
            If several jobs are enqueued like this:
         
     | 
| 
      
 513 
     | 
    
         
            +
             
     | 
| 
      
 514 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 515 
     | 
    
         
            +
            DeliverAnnouncementToContactJob.set(wait: 10.minutes).perform_later(contact)
         
     | 
| 
      
 516 
     | 
    
         
            +
            DeliverAnnouncementToContactJob.set(wait: 10.minutes).perform_later(contact)
         
     | 
| 
      
 517 
     | 
    
         
            +
            DeliverAnnouncementToContactJob.set(wait: 30.minutes).perform_later(contact)
         
     | 
| 
      
 518 
     | 
    
         
            +
            ```
         
     | 
| 
      
 519 
     | 
    
         
            +
             
     | 
| 
      
 520 
     | 
    
         
            +
            The 3 jobs will go into the scheduled queue and will wait there until they're due. Then, 10 minutes after, the first two jobs will be enqueued and the second one most likely will be blocked because the first one will be running first. Then, assuming the jobs are fast and finish in a few seconds, when the third job is due, it'll be enqueued normally.
         
     | 
| 
      
 521 
     | 
    
         
            +
             
     | 
| 
      
 522 
     | 
    
         
            +
            Normally scheduled jobs are enqueued in batches, but with concurrency controls, jobs need to be enqueued one by one. This has an impact on performance, similarly to the impact of concurrency controls in bulk enqueuing. Read below for more details. I'd generally advise against mixing concurrency controls with waiting/scheduling in the future.
         
     | 
| 
      
 523 
     | 
    
         
            +
             
     | 
| 
       499 
524 
     | 
    
         
             
            ### Performance considerations
         
     | 
| 
       500 
525 
     | 
    
         | 
| 
       501 
526 
     | 
    
         
             
            Concurrency controls introduce significant overhead (blocked executions need to be created and promoted to ready, semaphores need to be created and updated) so you should consider carefully whether you need them. For throttling purposes, where you plan to have `limit` significantly larger than 1, I'd encourage relying on a limited number of workers per queue instead. For example:
         
     | 
| 
         @@ -591,7 +616,7 @@ end 
     | 
|
| 
       591 
616 
     | 
    
         | 
| 
       592 
617 
     | 
    
         
             
            Using this option, you can also use Solid Queue in the same database as your app but not rely on transactional integrity.
         
     | 
| 
       593 
618 
     | 
    
         | 
| 
       594 
     | 
    
         
            -
            If you don't set this option but still want to make sure you're not inadvertently on transactional integrity, you can make sure that:
         
     | 
| 
      
 619 
     | 
    
         
            +
            If you don't set this option but still want to make sure you're not inadvertently relying on transactional integrity, you can make sure that:
         
     | 
| 
       595 
620 
     | 
    
         
             
            - Your jobs relying on specific data are always enqueued on [`after_commit` callbacks](https://guides.rubyonrails.org/active_record_callbacks.html#after-commit-and-after-rollback) or otherwise from a place where you're certain that whatever data the job will use has been committed to the database before the job is enqueued.
         
     | 
| 
       596 
621 
     | 
    
         
             
            - Or, you configure a different database for Solid Queue, even if it's the same as your app, ensuring that a different connection on the thread handling requests or running jobs for your app will be used to enqueue jobs. For example:
         
     | 
| 
       597 
622 
     | 
    
         | 
| 
         @@ -37,8 +37,10 @@ class SolidQueue::ClaimedExecution < SolidQueue::Execution 
     | 
|
| 
       37 
37 
     | 
    
         
             
                end
         
     | 
| 
       38 
38 
     | 
    
         | 
| 
       39 
39 
     | 
    
         
             
                def fail_all_with(error)
         
     | 
| 
       40 
     | 
    
         
            -
                   
     | 
| 
       41 
     | 
    
         
            -
                     
     | 
| 
      
 40 
     | 
    
         
            +
                  includes(:job).tap do |executions|
         
     | 
| 
      
 41 
     | 
    
         
            +
                    return if executions.empty?
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                    SolidQueue.instrument(:fail_many_claimed) do |payload|
         
     | 
| 
       42 
44 
     | 
    
         
             
                      executions.each do |execution|
         
     | 
| 
       43 
45 
     | 
    
         
             
                        execution.failed_with(error)
         
     | 
| 
       44 
46 
     | 
    
         
             
                        execution.unblock_next_job
         
     | 
| 
         @@ -58,8 +58,11 @@ module SolidQueue 
     | 
|
| 
       58 
58 
     | 
    
         
             
                  end
         
     | 
| 
       59 
59 
     | 
    
         | 
| 
       60 
60 
     | 
    
         
             
                  def determine_backtrace_size_limit
         
     | 
| 
       61 
     | 
    
         
            -
                    column = self.class.connection 
     | 
| 
       62 
     | 
    
         
            -
             
     | 
| 
      
 61 
     | 
    
         
            +
                    column = self.class.connection_pool.with_connection do |connection|
         
     | 
| 
      
 62 
     | 
    
         
            +
                      connection.schema_cache.columns_hash(self.class.table_name)["error"]
         
     | 
| 
      
 63 
     | 
    
         
            +
                    end
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                    if column && column.limit.present?
         
     | 
| 
       63 
66 
     | 
    
         
             
                      column.limit - exception_class_name.bytesize - exception_message.bytesize - JSON_OVERHEAD
         
     | 
| 
       64 
67 
     | 
    
         
             
                    end
         
     | 
| 
       65 
68 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -10,6 +10,7 @@ module SolidQueue 
     | 
|
| 
       10 
10 
     | 
    
         | 
| 
       11 
11 
     | 
    
         
             
                class << self
         
     | 
| 
       12 
12 
     | 
    
         
             
                  def enqueue_all(active_jobs)
         
     | 
| 
      
 13 
     | 
    
         
            +
                    active_jobs.each { |job| job.scheduled_at ||= Time.current }
         
     | 
| 
       13 
14 
     | 
    
         
             
                    active_jobs_by_job_id = active_jobs.index_by(&:job_id)
         
     | 
| 
       14 
15 
     | 
    
         | 
| 
       15 
16 
     | 
    
         
             
                    transaction do
         
     | 
| 
         @@ -30,6 +31,7 @@ module SolidQueue 
     | 
|
| 
       30 
31 
     | 
    
         | 
| 
       31 
32 
     | 
    
         
             
                    create_from_active_job(active_job).tap do |enqueued_job|
         
     | 
| 
       32 
33 
     | 
    
         
             
                      active_job.provider_job_id = enqueued_job.id if enqueued_job.persisted?
         
     | 
| 
      
 34 
     | 
    
         
            +
                      active_job.successfully_enqueued = enqueued_job.persisted?
         
     | 
| 
       33 
35 
     | 
    
         
             
                    end
         
     | 
| 
       34 
36 
     | 
    
         
             
                  end
         
     | 
| 
       35 
37 
     | 
    
         | 
| 
         @@ -6,11 +6,19 @@ module SolidQueue 
     | 
|
| 
       6 
6 
     | 
    
         | 
| 
       7 
7 
     | 
    
         
             
                connects_to(**SolidQueue.connects_to) if SolidQueue.connects_to
         
     | 
| 
       8 
8 
     | 
    
         | 
| 
       9 
     | 
    
         
            -
                 
     | 
| 
       10 
     | 
    
         
            -
                   
     | 
| 
       11 
     | 
    
         
            -
                     
     | 
| 
       12 
     | 
    
         
            -
             
     | 
| 
       13 
     | 
    
         
            -
                     
     | 
| 
      
 9 
     | 
    
         
            +
                class << self
         
     | 
| 
      
 10 
     | 
    
         
            +
                  def non_blocking_lock
         
     | 
| 
      
 11 
     | 
    
         
            +
                    if SolidQueue.use_skip_locked
         
     | 
| 
      
 12 
     | 
    
         
            +
                      lock(Arel.sql("FOR UPDATE SKIP LOCKED"))
         
     | 
| 
      
 13 
     | 
    
         
            +
                    else
         
     | 
| 
      
 14 
     | 
    
         
            +
                      lock
         
     | 
| 
      
 15 
     | 
    
         
            +
                    end
         
     | 
| 
      
 16 
     | 
    
         
            +
                  end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                  def supports_insert_conflict_target?
         
     | 
| 
      
 19 
     | 
    
         
            +
                    connection_pool.with_connection do |connection|
         
     | 
| 
      
 20 
     | 
    
         
            +
                      connection.supports_insert_conflict_target?
         
     | 
| 
      
 21 
     | 
    
         
            +
                    end
         
     | 
| 
       14 
22 
     | 
    
         
             
                  end
         
     | 
| 
       15 
23 
     | 
    
         
             
                end
         
     | 
| 
       16 
24 
     | 
    
         
             
              end
         
     | 
| 
         @@ -8,7 +8,7 @@ module SolidQueue 
     | 
|
| 
       8 
8 
     | 
    
         | 
| 
       9 
9 
     | 
    
         
             
                class << self
         
     | 
| 
       10 
10 
     | 
    
         
             
                  def create_or_insert!(**attributes)
         
     | 
| 
       11 
     | 
    
         
            -
                    if  
     | 
| 
      
 11 
     | 
    
         
            +
                    if supports_insert_conflict_target?
         
     | 
| 
       12 
12 
     | 
    
         
             
                      # PostgreSQL fails and aborts the current transaction when it hits a duplicate key conflict
         
     | 
| 
       13 
13 
     | 
    
         
             
                      # during two concurrent INSERTs for the same value of an unique index. We need to explicitly
         
     | 
| 
       14 
14 
     | 
    
         
             
                      # indicate unique_by to ignore duplicate rows by this value when inserting
         
     | 
| 
         @@ -36,7 +36,7 @@ module SolidQueue 
     | 
|
| 
       36 
36 
     | 
    
         
             
                  end
         
     | 
| 
       37 
37 
     | 
    
         | 
| 
       38 
38 
     | 
    
         
             
                  def create_or_update_all(tasks)
         
     | 
| 
       39 
     | 
    
         
            -
                    if  
     | 
| 
      
 39 
     | 
    
         
            +
                    if supports_insert_conflict_target?
         
     | 
| 
       40 
40 
     | 
    
         
             
                      # PostgreSQL fails and aborts the current transaction when it hits a duplicate key conflict
         
     | 
| 
       41 
41 
     | 
    
         
             
                      # during two concurrent INSERTs for the same value of an unique index. We need to explicitly
         
     | 
| 
       42 
42 
     | 
    
         
             
                      # indicate unique_by to ignore duplicate rows by this value when inserting
         
     | 
| 
         @@ -48,7 +48,7 @@ module SolidQueue 
     | 
|
| 
       48 
48 
     | 
    
         
             
                end
         
     | 
| 
       49 
49 
     | 
    
         | 
| 
       50 
50 
     | 
    
         
             
                def delay_from_now
         
     | 
| 
       51 
     | 
    
         
            -
                  [ (next_time - Time.current).to_f, 0 ].max
         
     | 
| 
      
 51 
     | 
    
         
            +
                  [ (next_time - Time.current).to_f, 0.1 ].max
         
     | 
| 
       52 
52 
     | 
    
         
             
                end
         
     | 
| 
       53 
53 
     | 
    
         | 
| 
       54 
54 
     | 
    
         
             
                def next_time
         
     | 
| 
         @@ -130,7 +130,6 @@ module SolidQueue 
     | 
|
| 
       130 
130 
     | 
    
         
             
                        active_job.run_callbacks(:enqueue) do
         
     | 
| 
       131 
131 
     | 
    
         
             
                          Job.enqueue(active_job)
         
     | 
| 
       132 
132 
     | 
    
         
             
                        end
         
     | 
| 
       133 
     | 
    
         
            -
                        active_job.successfully_enqueued = true
         
     | 
| 
       134 
133 
     | 
    
         
             
                      end
         
     | 
| 
       135 
134 
     | 
    
         
             
                    end
         
     | 
| 
       136 
135 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -20,7 +20,7 @@ module SolidQueue 
     | 
|
| 
       20 
20 
     | 
    
         | 
| 
       21 
21 
     | 
    
         
             
                  # Requires a unique index on key
         
     | 
| 
       22 
22 
     | 
    
         
             
                  def create_unique_by(attributes)
         
     | 
| 
       23 
     | 
    
         
            -
                    if  
     | 
| 
      
 23 
     | 
    
         
            +
                    if supports_insert_conflict_target?
         
     | 
| 
       24 
24 
     | 
    
         
             
                      insert({ **attributes }, unique_by: :key).any?
         
     | 
| 
       25 
25 
     | 
    
         
             
                    else
         
     | 
| 
       26 
26 
     | 
    
         
             
                      create!(**attributes)
         
     | 
| 
         @@ -11,15 +11,27 @@ Puma::Plugin.create do 
     | 
|
| 
       11 
11 
     | 
    
         
             
                  monitor_solid_queue
         
     | 
| 
       12 
12 
     | 
    
         
             
                end
         
     | 
| 
       13 
13 
     | 
    
         | 
| 
       14 
     | 
    
         
            -
                 
     | 
| 
       15 
     | 
    
         
            -
                   
     | 
| 
       16 
     | 
    
         
            -
                     
     | 
| 
       17 
     | 
    
         
            -
             
     | 
| 
      
 14 
     | 
    
         
            +
                if Gem::Version.new(Puma::Const::VERSION) < Gem::Version.new("7")
         
     | 
| 
      
 15 
     | 
    
         
            +
                  launcher.events.on_booted do
         
     | 
| 
      
 16 
     | 
    
         
            +
                    @solid_queue_pid = fork do
         
     | 
| 
      
 17 
     | 
    
         
            +
                      Thread.new { monitor_puma }
         
     | 
| 
      
 18 
     | 
    
         
            +
                      SolidQueue::Supervisor.start
         
     | 
| 
      
 19 
     | 
    
         
            +
                    end
         
     | 
| 
      
 20 
     | 
    
         
            +
                  end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                  launcher.events.on_stopped { stop_solid_queue }
         
     | 
| 
      
 23 
     | 
    
         
            +
                  launcher.events.on_restart { stop_solid_queue }
         
     | 
| 
      
 24 
     | 
    
         
            +
                else
         
     | 
| 
      
 25 
     | 
    
         
            +
                  launcher.events.after_booted do
         
     | 
| 
      
 26 
     | 
    
         
            +
                    @solid_queue_pid = fork do
         
     | 
| 
      
 27 
     | 
    
         
            +
                      Thread.new { monitor_puma }
         
     | 
| 
      
 28 
     | 
    
         
            +
                      SolidQueue::Supervisor.start
         
     | 
| 
      
 29 
     | 
    
         
            +
                    end
         
     | 
| 
       18 
30 
     | 
    
         
             
                  end
         
     | 
| 
       19 
     | 
    
         
            -
                end
         
     | 
| 
       20 
31 
     | 
    
         | 
| 
       21 
     | 
    
         
            -
             
     | 
| 
       22 
     | 
    
         
            -
             
     | 
| 
      
 32 
     | 
    
         
            +
                  launcher.events.after_stopped { stop_solid_queue }
         
     | 
| 
      
 33 
     | 
    
         
            +
                  launcher.events.before_restart { stop_solid_queue }
         
     | 
| 
      
 34 
     | 
    
         
            +
                end
         
     | 
| 
       23 
35 
     | 
    
         
             
              end
         
     | 
| 
       24 
36 
     | 
    
         | 
| 
       25 
37 
     | 
    
         
             
              private
         
     | 
    
        data/lib/solid_queue/cli.rb
    CHANGED
    
    | 
         @@ -12,7 +12,7 @@ module SolidQueue 
     | 
|
| 
       12 
12 
     | 
    
         
             
                  desc: "Path to recurring schedule definition (default: #{Configuration::DEFAULT_RECURRING_SCHEDULE_FILE_PATH}).",
         
     | 
| 
       13 
13 
     | 
    
         
             
                  banner: "SOLID_QUEUE_RECURRING_SCHEDULE"
         
     | 
| 
       14 
14 
     | 
    
         | 
| 
       15 
     | 
    
         
            -
                class_option :skip_recurring, type: :boolean, 
     | 
| 
      
 15 
     | 
    
         
            +
                class_option :skip_recurring, type: :boolean,
         
     | 
| 
       16 
16 
     | 
    
         
             
                  desc: "Whether to skip recurring tasks scheduling",
         
     | 
| 
       17 
17 
     | 
    
         
             
                  banner: "SOLID_QUEUE_SKIP_RECURRING"
         
     | 
| 
       18 
18 
     | 
    
         | 
| 
         @@ -29,18 +29,22 @@ module SolidQueue 
     | 
|
| 
       29 
29 
     | 
    
         
             
                end
         
     | 
| 
       30 
30 
     | 
    
         | 
| 
       31 
31 
     | 
    
         
             
                def start
         
     | 
| 
       32 
     | 
    
         
            -
                   
     | 
| 
       33 
     | 
    
         
            -
             
     | 
| 
      
 32 
     | 
    
         
            +
                  wrap_in_app_executor do
         
     | 
| 
      
 33 
     | 
    
         
            +
                    boot
         
     | 
| 
      
 34 
     | 
    
         
            +
                    run_start_hooks
         
     | 
| 
       34 
35 
     | 
    
         | 
| 
       35 
     | 
    
         
            -
             
     | 
| 
       36 
     | 
    
         
            -
             
     | 
| 
      
 36 
     | 
    
         
            +
                    start_processes
         
     | 
| 
      
 37 
     | 
    
         
            +
                    launch_maintenance_task
         
     | 
| 
       37 
38 
     | 
    
         | 
| 
       38 
     | 
    
         
            -
             
     | 
| 
      
 39 
     | 
    
         
            +
                    supervise
         
     | 
| 
      
 40 
     | 
    
         
            +
                  end
         
     | 
| 
       39 
41 
     | 
    
         
             
                end
         
     | 
| 
       40 
42 
     | 
    
         | 
| 
       41 
43 
     | 
    
         
             
                def stop
         
     | 
| 
       42 
     | 
    
         
            -
                   
     | 
| 
       43 
     | 
    
         
            -
             
     | 
| 
      
 44 
     | 
    
         
            +
                  wrap_in_app_executor do
         
     | 
| 
      
 45 
     | 
    
         
            +
                    super
         
     | 
| 
      
 46 
     | 
    
         
            +
                    run_stop_hooks
         
     | 
| 
      
 47 
     | 
    
         
            +
                  end
         
     | 
| 
       44 
48 
     | 
    
         
             
                end
         
     | 
| 
       45 
49 
     | 
    
         | 
| 
       46 
50 
     | 
    
         
             
                private
         
     | 
    
        data/lib/solid_queue/version.rb
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | 
         @@ -1,14 +1,14 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            --- !ruby/object:Gem::Specification
         
     | 
| 
       2 
2 
     | 
    
         
             
            name: solid_queue
         
     | 
| 
       3 
3 
     | 
    
         
             
            version: !ruby/object:Gem::Version
         
     | 
| 
       4 
     | 
    
         
            -
              version: 1.2. 
     | 
| 
      
 4 
     | 
    
         
            +
              version: 1.2.2
         
     | 
| 
       5 
5 
     | 
    
         
             
            platform: ruby
         
     | 
| 
       6 
6 
     | 
    
         
             
            authors:
         
     | 
| 
       7 
7 
     | 
    
         
             
            - Rosa Gutierrez
         
     | 
| 
       8 
8 
     | 
    
         
             
            autorequire:
         
     | 
| 
       9 
9 
     | 
    
         
             
            bindir: bin
         
     | 
| 
       10 
10 
     | 
    
         
             
            cert_chain: []
         
     | 
| 
       11 
     | 
    
         
            -
            date: 2025- 
     | 
| 
      
 11 
     | 
    
         
            +
            date: 2025-10-21 00:00:00.000000000 Z
         
     | 
| 
       12 
12 
     | 
    
         
             
            dependencies:
         
     | 
| 
       13 
13 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       14 
14 
     | 
    
         
             
              name: activerecord
         
     | 
| 
         @@ -72,26 +72,26 @@ dependencies: 
     | 
|
| 
       72 
72 
     | 
    
         
             
                requirements:
         
     | 
| 
       73 
73 
     | 
    
         
             
                - - "~>"
         
     | 
| 
       74 
74 
     | 
    
         
             
                  - !ruby/object:Gem::Version
         
     | 
| 
       75 
     | 
    
         
            -
                    version: 1.11 
     | 
| 
      
 75 
     | 
    
         
            +
                    version: '1.11'
         
     | 
| 
       76 
76 
     | 
    
         
             
              type: :runtime
         
     | 
| 
       77 
77 
     | 
    
         
             
              prerelease: false
         
     | 
| 
       78 
78 
     | 
    
         
             
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
       79 
79 
     | 
    
         
             
                requirements:
         
     | 
| 
       80 
80 
     | 
    
         
             
                - - "~>"
         
     | 
| 
       81 
81 
     | 
    
         
             
                  - !ruby/object:Gem::Version
         
     | 
| 
       82 
     | 
    
         
            -
                    version: 1.11 
     | 
| 
      
 82 
     | 
    
         
            +
                    version: '1.11'
         
     | 
| 
       83 
83 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       84 
84 
     | 
    
         
             
              name: thor
         
     | 
| 
       85 
85 
     | 
    
         
             
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
       86 
86 
     | 
    
         
             
                requirements:
         
     | 
| 
       87 
     | 
    
         
            -
                - - " 
     | 
| 
      
 87 
     | 
    
         
            +
                - - ">="
         
     | 
| 
       88 
88 
     | 
    
         
             
                  - !ruby/object:Gem::Version
         
     | 
| 
       89 
89 
     | 
    
         
             
                    version: 1.3.1
         
     | 
| 
       90 
90 
     | 
    
         
             
              type: :runtime
         
     | 
| 
       91 
91 
     | 
    
         
             
              prerelease: false
         
     | 
| 
       92 
92 
     | 
    
         
             
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
       93 
93 
     | 
    
         
             
                requirements:
         
     | 
| 
       94 
     | 
    
         
            -
                - - " 
     | 
| 
      
 94 
     | 
    
         
            +
                - - ">="
         
     | 
| 
       95 
95 
     | 
    
         
             
                  - !ruby/object:Gem::Version
         
     | 
| 
       96 
96 
     | 
    
         
             
                    version: 1.3.1
         
     | 
| 
       97 
97 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
         @@ -140,16 +140,16 @@ dependencies: 
     | 
|
| 
       140 
140 
     | 
    
         
             
              name: puma
         
     | 
| 
       141 
141 
     | 
    
         
             
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
       142 
142 
     | 
    
         
             
                requirements:
         
     | 
| 
       143 
     | 
    
         
            -
                - - " 
     | 
| 
      
 143 
     | 
    
         
            +
                - - "~>"
         
     | 
| 
       144 
144 
     | 
    
         
             
                  - !ruby/object:Gem::Version
         
     | 
| 
       145 
     | 
    
         
            -
                    version: '0'
         
     | 
| 
      
 145 
     | 
    
         
            +
                    version: '7.0'
         
     | 
| 
       146 
146 
     | 
    
         
             
              type: :development
         
     | 
| 
       147 
147 
     | 
    
         
             
              prerelease: false
         
     | 
| 
       148 
148 
     | 
    
         
             
              version_requirements: !ruby/object:Gem::Requirement
         
     | 
| 
       149 
149 
     | 
    
         
             
                requirements:
         
     | 
| 
       150 
     | 
    
         
            -
                - - " 
     | 
| 
      
 150 
     | 
    
         
            +
                - - "~>"
         
     | 
| 
       151 
151 
     | 
    
         
             
                  - !ruby/object:Gem::Version
         
     | 
| 
       152 
     | 
    
         
            -
                    version: '0'
         
     | 
| 
      
 152 
     | 
    
         
            +
                    version: '7.0'
         
     | 
| 
       153 
153 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       154 
154 
     | 
    
         
             
              name: mysql2
         
     | 
| 
       155 
155 
     | 
    
         
             
              requirement: !ruby/object:Gem::Requirement
         
     | 
| 
         @@ -257,7 +257,6 @@ extra_rdoc_files: [] 
     | 
|
| 
       257 
257 
     | 
    
         
             
            files:
         
     | 
| 
       258 
258 
     | 
    
         
             
            - MIT-LICENSE
         
     | 
| 
       259 
259 
     | 
    
         
             
            - README.md
         
     | 
| 
       260 
     | 
    
         
            -
            - Rakefile
         
     | 
| 
       261 
260 
     | 
    
         
             
            - UPGRADING.md
         
     | 
| 
       262 
261 
     | 
    
         
             
            - app/jobs/solid_queue/recurring_job.rb
         
     | 
| 
       263 
262 
     | 
    
         
             
            - app/models/solid_queue/blocked_execution.rb
         
     | 
    
        data/Rakefile
    DELETED
    
    | 
         @@ -1,21 +0,0 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            # frozen_string_literal: true
         
     | 
| 
       2 
     | 
    
         
            -
             
     | 
| 
       3 
     | 
    
         
            -
            require "bundler/setup"
         
     | 
| 
       4 
     | 
    
         
            -
             
     | 
| 
       5 
     | 
    
         
            -
            APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
         
     | 
| 
       6 
     | 
    
         
            -
            load "rails/tasks/engine.rake"
         
     | 
| 
       7 
     | 
    
         
            -
             
     | 
| 
       8 
     | 
    
         
            -
            load "rails/tasks/statistics.rake"
         
     | 
| 
       9 
     | 
    
         
            -
             
     | 
| 
       10 
     | 
    
         
            -
            require "bundler/gem_tasks"
         
     | 
| 
       11 
     | 
    
         
            -
             
     | 
| 
       12 
     | 
    
         
            -
            def databases
         
     | 
| 
       13 
     | 
    
         
            -
              %w[ mysql postgres sqlite ]
         
     | 
| 
       14 
     | 
    
         
            -
            end
         
     | 
| 
       15 
     | 
    
         
            -
             
     | 
| 
       16 
     | 
    
         
            -
            task :test do
         
     | 
| 
       17 
     | 
    
         
            -
              databases.each do |database|
         
     | 
| 
       18 
     | 
    
         
            -
                sh("TARGET_DB=#{database} bin/setup")
         
     | 
| 
       19 
     | 
    
         
            -
                sh("TARGET_DB=#{database} bin/rails test")
         
     | 
| 
       20 
     | 
    
         
            -
              end
         
     | 
| 
       21 
     | 
    
         
            -
            end
         
     |