solid_queue 0.2.0 → 0.2.1
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/MIT-LICENSE +1 -1
 - data/README.md +1 -1
 - data/app/models/solid_queue/claimed_execution.rb +13 -1
 - data/app/models/solid_queue/execution/dispatching.rb +20 -0
 - data/app/models/solid_queue/execution/job_attributes.rb +4 -4
 - data/app/models/solid_queue/execution.rb +44 -2
 - data/app/models/solid_queue/failed_execution.rb +26 -16
 - data/app/models/solid_queue/job/clearable.rb +4 -1
 - data/app/models/solid_queue/job/concurrency_controls.rb +17 -1
 - data/app/models/solid_queue/job/executable.rb +20 -9
 - data/app/models/solid_queue/job/schedulable.rb +9 -1
 - data/app/models/solid_queue/job.rb +3 -0
 - data/app/models/solid_queue/queue.rb +1 -1
 - data/app/models/solid_queue/ready_execution.rb +10 -0
 - data/app/models/solid_queue/scheduled_execution.rb +3 -11
 - data/app/models/solid_queue/semaphore.rb +75 -46
 - data/db/migrate/20240110143450_add_missing_index_to_blocked_executions.rb +5 -0
 - data/lib/active_job/uniqueness.rb +41 -0
 - data/lib/generators/solid_queue/install/USAGE +2 -2
 - data/lib/generators/solid_queue/install/templates/config.yml +1 -1
 - data/lib/solid_queue/dispatcher/scheduled_executions_dispatcher.rb +6 -0
 - data/lib/solid_queue/processes/runnable.rb +2 -2
 - data/lib/solid_queue/version.rb +1 -1
 - data/lib/solid_queue/worker.rb +1 -1
 - metadata +6 -2
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 0eb31439e2768af5f5d3fb9289ac51056570c762ad140bc54aba81ee770d5a12
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 4d3e4c2608b0a3ed2de82c7712c8a524e8745bc4e2ddf6907de8b3723560ab04
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 0f4271aaf7b55b86d81f97bf19bbfa2b76de1ff994a6b71bb097b89ab9585212f28c0fdbd4fbb86b74c18b49cf229e7913eaa724784b3a62f000d60c2ade3263
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: a84d91b13db4a3ec96d1afa5fb5f39abc9ea0bf4bc216c14e20f4bf7e1bf2ff414e08f78166377950351b8170cbd734aa69322feb9076a67a3f9939ebacf6573
         
     | 
    
        data/MIT-LICENSE
    CHANGED
    
    
    
        data/README.md
    CHANGED
    
    | 
         @@ -31,7 +31,7 @@ $ bin/rails generate solid_queue:install 
     | 
|
| 
       31 
31 
     | 
    
         | 
| 
       32 
32 
     | 
    
         
             
            This will set `solid_queue` as the Active Job's adapter in production, and will copy the required migration over to your app.
         
     | 
| 
       33 
33 
     | 
    
         | 
| 
       34 
     | 
    
         
            -
            Alternatively, you can add  
     | 
| 
      
 34 
     | 
    
         
            +
            Alternatively, you can add only the migration to your app:
         
     | 
| 
       35 
35 
     | 
    
         
             
            ```bash
         
     | 
| 
       36 
36 
     | 
    
         
             
            $ bin/rails solid_queue:install:migrations
         
     | 
| 
       37 
37 
     | 
    
         
             
            ```
         
     | 
| 
         @@ -23,6 +23,14 @@ class SolidQueue::ClaimedExecution < SolidQueue::Execution 
     | 
|
| 
       23 
23 
     | 
    
         
             
                def release_all
         
     | 
| 
       24 
24 
     | 
    
         
             
                  includes(:job).each(&:release)
         
     | 
| 
       25 
25 
     | 
    
         
             
                end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                def discard_all_in_batches(*)
         
     | 
| 
      
 28 
     | 
    
         
            +
                  raise UndiscardableError, "Can't discard jobs in progress"
         
     | 
| 
      
 29 
     | 
    
         
            +
                end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                def discard_all_from_jobs(*)
         
     | 
| 
      
 32 
     | 
    
         
            +
                  raise UndiscardableError, "Can't discard jobs in progress"
         
     | 
| 
      
 33 
     | 
    
         
            +
                end
         
     | 
| 
       26 
34 
     | 
    
         
             
              end
         
     | 
| 
       27 
35 
     | 
    
         | 
| 
       28 
36 
     | 
    
         
             
              def perform
         
     | 
| 
         @@ -39,11 +47,15 @@ class SolidQueue::ClaimedExecution < SolidQueue::Execution 
     | 
|
| 
       39 
47 
     | 
    
         | 
| 
       40 
48 
     | 
    
         
             
              def release
         
     | 
| 
       41 
49 
     | 
    
         
             
                transaction do
         
     | 
| 
       42 
     | 
    
         
            -
                  job. 
     | 
| 
      
 50 
     | 
    
         
            +
                  job.dispatch_bypassing_concurrency_limits
         
     | 
| 
       43 
51 
     | 
    
         
             
                  destroy!
         
     | 
| 
       44 
52 
     | 
    
         
             
                end
         
     | 
| 
       45 
53 
     | 
    
         
             
              end
         
     | 
| 
       46 
54 
     | 
    
         | 
| 
      
 55 
     | 
    
         
            +
              def discard
         
     | 
| 
      
 56 
     | 
    
         
            +
                raise UndiscardableError, "Can't discard a job in progress"
         
     | 
| 
      
 57 
     | 
    
         
            +
              end
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
       47 
59 
     | 
    
         
             
              private
         
     | 
| 
       48 
60 
     | 
    
         
             
                def execute
         
     | 
| 
       49 
61 
     | 
    
         
             
                  ActiveJob::Base.execute(job.arguments)
         
     | 
| 
         @@ -0,0 +1,20 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module SolidQueue
         
     | 
| 
      
 4 
     | 
    
         
            +
              class Execution
         
     | 
| 
      
 5 
     | 
    
         
            +
                module Dispatching
         
     | 
| 
      
 6 
     | 
    
         
            +
                  extend ActiveSupport::Concern
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                  class_methods do
         
     | 
| 
      
 9 
     | 
    
         
            +
                    def dispatch_jobs(job_ids)
         
     | 
| 
      
 10 
     | 
    
         
            +
                      jobs = Job.where(id: job_ids)
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                      Job.dispatch_all(jobs).map(&:id).tap do |dispatched_job_ids|
         
     | 
| 
      
 13 
     | 
    
         
            +
                        where(job_id: dispatched_job_ids).delete_all
         
     | 
| 
      
 14 
     | 
    
         
            +
                        SolidQueue.logger.info("[SolidQueue] Dispatched #{dispatched_job_ids.size} jobs")
         
     | 
| 
      
 15 
     | 
    
         
            +
                      end
         
     | 
| 
      
 16 
     | 
    
         
            +
                    end
         
     | 
| 
      
 17 
     | 
    
         
            +
                  end
         
     | 
| 
      
 18 
     | 
    
         
            +
                end
         
     | 
| 
      
 19 
     | 
    
         
            +
              end
         
     | 
| 
      
 20 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -6,23 +6,23 @@ module SolidQueue 
     | 
|
| 
       6 
6 
     | 
    
         
             
                  extend ActiveSupport::Concern
         
     | 
| 
       7 
7 
     | 
    
         | 
| 
       8 
8 
     | 
    
         
             
                  included do
         
     | 
| 
       9 
     | 
    
         
            -
                    class_attribute : 
     | 
| 
      
 9 
     | 
    
         
            +
                    class_attribute :assumable_attributes_from_job, instance_accessor: false, default: %i[ queue_name priority ]
         
     | 
| 
       10 
10 
     | 
    
         
             
                  end
         
     | 
| 
       11 
11 
     | 
    
         | 
| 
       12 
12 
     | 
    
         
             
                  class_methods do
         
     | 
| 
       13 
13 
     | 
    
         
             
                    def assumes_attributes_from_job(*attribute_names)
         
     | 
| 
       14 
     | 
    
         
            -
                      self. 
     | 
| 
      
 14 
     | 
    
         
            +
                      self.assumable_attributes_from_job |= attribute_names
         
     | 
| 
       15 
15 
     | 
    
         
             
                      before_create -> { assume_attributes_from_job }
         
     | 
| 
       16 
16 
     | 
    
         
             
                    end
         
     | 
| 
       17 
17 
     | 
    
         | 
| 
       18 
18 
     | 
    
         
             
                    def attributes_from_job(job)
         
     | 
| 
       19 
     | 
    
         
            -
                      job.attributes.symbolize_keys.slice(* 
     | 
| 
      
 19 
     | 
    
         
            +
                      job.attributes.symbolize_keys.slice(*assumable_attributes_from_job)
         
     | 
| 
       20 
20 
     | 
    
         
             
                    end
         
     | 
| 
       21 
21 
     | 
    
         
             
                  end
         
     | 
| 
       22 
22 
     | 
    
         | 
| 
       23 
23 
     | 
    
         
             
                  private
         
     | 
| 
       24 
24 
     | 
    
         
             
                    def assume_attributes_from_job
         
     | 
| 
       25 
     | 
    
         
            -
                      self.class. 
     | 
| 
      
 25 
     | 
    
         
            +
                      self.class.assumable_attributes_from_job.each do |attribute|
         
     | 
| 
       26 
26 
     | 
    
         
             
                        send("#{attribute}=", job.send(attribute))
         
     | 
| 
       27 
27 
     | 
    
         
             
                      end
         
     | 
| 
       28 
28 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -2,6 +2,8 @@ 
     | 
|
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            module SolidQueue
         
     | 
| 
       4 
4 
     | 
    
         
             
              class Execution < Record
         
     | 
| 
      
 5 
     | 
    
         
            +
                class UndiscardableError < StandardError; end
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
       5 
7 
     | 
    
         
             
                include JobAttributes
         
     | 
| 
       6 
8 
     | 
    
         | 
| 
       7 
9 
     | 
    
         
             
                self.abstract_class = true
         
     | 
| 
         @@ -10,8 +12,6 @@ module SolidQueue 
     | 
|
| 
       10 
12 
     | 
    
         | 
| 
       11 
13 
     | 
    
         
             
                belongs_to :job
         
     | 
| 
       12 
14 
     | 
    
         | 
| 
       13 
     | 
    
         
            -
                alias_method :discard, :destroy
         
     | 
| 
       14 
     | 
    
         
            -
             
     | 
| 
       15 
15 
     | 
    
         
             
                class << self
         
     | 
| 
       16 
16 
     | 
    
         
             
                  def create_all_from_jobs(jobs)
         
     | 
| 
       17 
17 
     | 
    
         
             
                    insert_all execution_data_from_jobs(jobs)
         
     | 
| 
         @@ -22,6 +22,48 @@ module SolidQueue 
     | 
|
| 
       22 
22 
     | 
    
         
             
                      attributes_from_job(job).merge(job_id: job.id)
         
     | 
| 
       23 
23 
     | 
    
         
             
                    end
         
     | 
| 
       24 
24 
     | 
    
         
             
                  end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                  def discard_all_in_batches(batch_size: 500)
         
     | 
| 
      
 27 
     | 
    
         
            +
                    pending = count
         
     | 
| 
      
 28 
     | 
    
         
            +
                    discarded = 0
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                    loop do
         
     | 
| 
      
 31 
     | 
    
         
            +
                      transaction do
         
     | 
| 
      
 32 
     | 
    
         
            +
                        job_ids = limit(batch_size).order(:job_id).lock.pluck(:job_id)
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                        discard_jobs job_ids
         
     | 
| 
      
 35 
     | 
    
         
            +
                        discarded = where(job_id: job_ids).delete_all
         
     | 
| 
      
 36 
     | 
    
         
            +
                        pending -= discarded
         
     | 
| 
      
 37 
     | 
    
         
            +
                      end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                      break if pending <= 0 || discarded == 0
         
     | 
| 
      
 40 
     | 
    
         
            +
                    end
         
     | 
| 
      
 41 
     | 
    
         
            +
                  end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                  def discard_all_from_jobs(jobs)
         
     | 
| 
      
 44 
     | 
    
         
            +
                    transaction do
         
     | 
| 
      
 45 
     | 
    
         
            +
                      job_ids = lock_all_from_jobs(jobs)
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                      discard_jobs job_ids
         
     | 
| 
      
 48 
     | 
    
         
            +
                      where(job_id: job_ids).delete_all
         
     | 
| 
      
 49 
     | 
    
         
            +
                    end
         
     | 
| 
      
 50 
     | 
    
         
            +
                  end
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                  private
         
     | 
| 
      
 53 
     | 
    
         
            +
                    def lock_all_from_jobs(jobs)
         
     | 
| 
      
 54 
     | 
    
         
            +
                      where(job_id: jobs.map(&:id)).order(:job_id).lock.pluck(:job_id)
         
     | 
| 
      
 55 
     | 
    
         
            +
                    end
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                    def discard_jobs(job_ids)
         
     | 
| 
      
 58 
     | 
    
         
            +
                      Job.where(id: job_ids).delete_all
         
     | 
| 
      
 59 
     | 
    
         
            +
                    end
         
     | 
| 
      
 60 
     | 
    
         
            +
                end
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                def discard
         
     | 
| 
      
 63 
     | 
    
         
            +
                  with_lock do
         
     | 
| 
      
 64 
     | 
    
         
            +
                    job.destroy
         
     | 
| 
      
 65 
     | 
    
         
            +
                    destroy
         
     | 
| 
      
 66 
     | 
    
         
            +
                  end
         
     | 
| 
       25 
67 
     | 
    
         
             
                end
         
     | 
| 
       26 
68 
     | 
    
         
             
              end
         
     | 
| 
       27 
69 
     | 
    
         
             
            end
         
     | 
| 
         @@ -1,27 +1,37 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # frozen_string_literal: true
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
     | 
    
         
            -
             
     | 
| 
       4 
     | 
    
         
            -
               
     | 
| 
      
 3 
     | 
    
         
            +
            module SolidQueue
         
     | 
| 
      
 4 
     | 
    
         
            +
              class FailedExecution < Execution
         
     | 
| 
      
 5 
     | 
    
         
            +
                include Dispatching
         
     | 
| 
       5 
6 
     | 
    
         | 
| 
       6 
     | 
    
         
            -
             
     | 
| 
      
 7 
     | 
    
         
            +
                serialize :error, coder: JSON
         
     | 
| 
       7 
8 
     | 
    
         | 
| 
       8 
     | 
    
         
            -
             
     | 
| 
      
 9 
     | 
    
         
            +
                before_create :expand_error_details_from_exception
         
     | 
| 
       9 
10 
     | 
    
         | 
| 
       10 
     | 
    
         
            -
             
     | 
| 
       11 
     | 
    
         
            -
             
     | 
| 
       12 
     | 
    
         
            -
             
     | 
| 
       13 
     | 
    
         
            -
                   
     | 
| 
      
 11 
     | 
    
         
            +
                attr_accessor :exception
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                def self.retry_all(jobs)
         
     | 
| 
      
 14 
     | 
    
         
            +
                  transaction do
         
     | 
| 
      
 15 
     | 
    
         
            +
                    dispatch_jobs lock_all_from_jobs(jobs)
         
     | 
| 
      
 16 
     | 
    
         
            +
                  end
         
     | 
| 
      
 17 
     | 
    
         
            +
                end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                def retry
         
     | 
| 
      
 20 
     | 
    
         
            +
                  with_lock do
         
     | 
| 
      
 21 
     | 
    
         
            +
                    job.prepare_for_execution
         
     | 
| 
      
 22 
     | 
    
         
            +
                    destroy!
         
     | 
| 
      
 23 
     | 
    
         
            +
                  end
         
     | 
| 
       14 
24 
     | 
    
         
             
                end
         
     | 
| 
       15 
     | 
    
         
            -
              end
         
     | 
| 
       16 
25 
     | 
    
         | 
| 
       17 
     | 
    
         
            -
             
     | 
| 
       18 
     | 
    
         
            -
             
     | 
| 
       19 
     | 
    
         
            -
             
     | 
| 
      
 26 
     | 
    
         
            +
                %i[ exception_class message backtrace ].each do |attribute|
         
     | 
| 
      
 27 
     | 
    
         
            +
                  define_method(attribute) { error.with_indifferent_access[attribute] }
         
     | 
| 
      
 28 
     | 
    
         
            +
                end
         
     | 
| 
       20 
29 
     | 
    
         | 
| 
       21 
     | 
    
         
            -
             
     | 
| 
       22 
     | 
    
         
            -
             
     | 
| 
       23 
     | 
    
         
            -
             
     | 
| 
       24 
     | 
    
         
            -
             
     | 
| 
      
 30 
     | 
    
         
            +
                private
         
     | 
| 
      
 31 
     | 
    
         
            +
                  def expand_error_details_from_exception
         
     | 
| 
      
 32 
     | 
    
         
            +
                    if exception
         
     | 
| 
      
 33 
     | 
    
         
            +
                      self.error = { exception_class: exception.class.name, message: exception.message, backtrace: exception.backtrace }
         
     | 
| 
      
 34 
     | 
    
         
            +
                    end
         
     | 
| 
       25 
35 
     | 
    
         
             
                  end
         
     | 
| 
       26 
36 
     | 
    
         
             
                end
         
     | 
| 
       27 
37 
     | 
    
         
             
            end
         
     | 
| 
         @@ -11,7 +11,10 @@ module SolidQueue 
     | 
|
| 
       11 
11 
     | 
    
         | 
| 
       12 
12 
     | 
    
         
             
                  class_methods do
         
     | 
| 
       13 
13 
     | 
    
         
             
                    def clear_finished_in_batches(batch_size: 500, finished_before: SolidQueue.clear_finished_jobs_after.ago)
         
     | 
| 
       14 
     | 
    
         
            -
                       
     | 
| 
      
 14 
     | 
    
         
            +
                      loop do
         
     | 
| 
      
 15 
     | 
    
         
            +
                        records_deleted = clearable(finished_before: finished_before).limit(batch_size).delete_all
         
     | 
| 
      
 16 
     | 
    
         
            +
                        break if records_deleted == 0
         
     | 
| 
      
 17 
     | 
    
         
            +
                      end
         
     | 
| 
       15 
18 
     | 
    
         
             
                    end
         
     | 
| 
       16 
19 
     | 
    
         
             
                  end
         
     | 
| 
       17 
20 
     | 
    
         
             
                end
         
     | 
| 
         @@ -6,9 +6,17 @@ module SolidQueue 
     | 
|
| 
       6 
6 
     | 
    
         
             
                  extend ActiveSupport::Concern
         
     | 
| 
       7 
7 
     | 
    
         | 
| 
       8 
8 
     | 
    
         
             
                  included do
         
     | 
| 
       9 
     | 
    
         
            -
                    has_one :blocked_execution 
     | 
| 
      
 9 
     | 
    
         
            +
                    has_one :blocked_execution
         
     | 
| 
       10 
10 
     | 
    
         | 
| 
       11 
11 
     | 
    
         
             
                    delegate :concurrency_limit, :concurrency_duration, to: :job_class
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                    before_destroy :unblock_next_blocked_job, if: -> { concurrency_limited? && ready? }
         
     | 
| 
      
 14 
     | 
    
         
            +
                  end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                  class_methods do
         
     | 
| 
      
 17 
     | 
    
         
            +
                    def release_all_concurrency_locks(jobs)
         
     | 
| 
      
 18 
     | 
    
         
            +
                      Semaphore.signal_all(jobs.select(&:concurrency_limited?))
         
     | 
| 
      
 19 
     | 
    
         
            +
                    end
         
     | 
| 
       12 
20 
     | 
    
         
             
                  end
         
     | 
| 
       13 
21 
     | 
    
         | 
| 
       14 
22 
     | 
    
         
             
                  def unblock_next_blocked_job
         
     | 
| 
         @@ -21,6 +29,10 @@ module SolidQueue 
     | 
|
| 
       21 
29 
     | 
    
         
             
                    concurrency_key.present?
         
     | 
| 
       22 
30 
     | 
    
         
             
                  end
         
     | 
| 
       23 
31 
     | 
    
         | 
| 
      
 32 
     | 
    
         
            +
                  def blocked?
         
     | 
| 
      
 33 
     | 
    
         
            +
                    blocked_execution.present?
         
     | 
| 
      
 34 
     | 
    
         
            +
                  end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
       24 
36 
     | 
    
         
             
                  private
         
     | 
| 
       25 
37 
     | 
    
         
             
                    def acquire_concurrency_lock
         
     | 
| 
       26 
38 
     | 
    
         
             
                      return true unless concurrency_limited?
         
     | 
| 
         @@ -45,6 +57,10 @@ module SolidQueue 
     | 
|
| 
       45 
57 
     | 
    
         
             
                    def job_class
         
     | 
| 
       46 
58 
     | 
    
         
             
                      @job_class ||= class_name.safe_constantize
         
     | 
| 
       47 
59 
     | 
    
         
             
                    end
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
                    def execution
         
     | 
| 
      
 62 
     | 
    
         
            +
                      super || blocked_execution
         
     | 
| 
      
 63 
     | 
    
         
            +
                    end
         
     | 
| 
       48 
64 
     | 
    
         
             
                end
         
     | 
| 
       49 
65 
     | 
    
         
             
              end
         
     | 
| 
       50 
66 
     | 
    
         
             
            end
         
     | 
| 
         @@ -8,9 +8,9 @@ module SolidQueue 
     | 
|
| 
       8 
8 
     | 
    
         
             
                  included do
         
     | 
| 
       9 
9 
     | 
    
         
             
                    include Clearable, ConcurrencyControls, Schedulable
         
     | 
| 
       10 
10 
     | 
    
         | 
| 
       11 
     | 
    
         
            -
                    has_one :ready_execution 
     | 
| 
       12 
     | 
    
         
            -
                    has_one :claimed_execution 
     | 
| 
       13 
     | 
    
         
            -
                    has_one :failed_execution 
     | 
| 
      
 11 
     | 
    
         
            +
                    has_one :ready_execution
         
     | 
| 
      
 12 
     | 
    
         
            +
                    has_one :claimed_execution
         
     | 
| 
      
 13 
     | 
    
         
            +
                    has_one :failed_execution
         
     | 
| 
       14 
14 
     | 
    
         | 
| 
       15 
15 
     | 
    
         
             
                    after_create :prepare_for_execution
         
     | 
| 
       16 
16 
     | 
    
         | 
| 
         @@ -73,6 +73,10 @@ module SolidQueue 
     | 
|
| 
       73 
73 
     | 
    
         
             
                    end
         
     | 
| 
       74 
74 
     | 
    
         
             
                  end
         
     | 
| 
       75 
75 
     | 
    
         | 
| 
      
 76 
     | 
    
         
            +
                  def dispatch_bypassing_concurrency_limits
         
     | 
| 
      
 77 
     | 
    
         
            +
                    ready
         
     | 
| 
      
 78 
     | 
    
         
            +
                  end
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
       76 
80 
     | 
    
         
             
                  def finished!
         
     | 
| 
       77 
81 
     | 
    
         
             
                    if preserve_finished_jobs?
         
     | 
| 
       78 
82 
     | 
    
         
             
                      touch(:finished_at)
         
     | 
| 
         @@ -85,18 +89,22 @@ module SolidQueue 
     | 
|
| 
       85 
89 
     | 
    
         
             
                    finished_at.present?
         
     | 
| 
       86 
90 
     | 
    
         
             
                  end
         
     | 
| 
       87 
91 
     | 
    
         | 
| 
       88 
     | 
    
         
            -
                  def  
     | 
| 
       89 
     | 
    
         
            -
                     
     | 
| 
       90 
     | 
    
         
            -
             
     | 
| 
       91 
     | 
    
         
            -
             
     | 
| 
       92 
     | 
    
         
            -
             
     | 
| 
       93 
     | 
    
         
            -
                     
     | 
| 
      
 92 
     | 
    
         
            +
                  def status
         
     | 
| 
      
 93 
     | 
    
         
            +
                    if finished?
         
     | 
| 
      
 94 
     | 
    
         
            +
                      :finished
         
     | 
| 
      
 95 
     | 
    
         
            +
                    elsif execution.present?
         
     | 
| 
      
 96 
     | 
    
         
            +
                      execution.model_name.element.sub("_execution", "").to_sym
         
     | 
| 
      
 97 
     | 
    
         
            +
                    end
         
     | 
| 
       94 
98 
     | 
    
         
             
                  end
         
     | 
| 
       95 
99 
     | 
    
         | 
| 
       96 
100 
     | 
    
         
             
                  def retry
         
     | 
| 
       97 
101 
     | 
    
         
             
                    failed_execution&.retry
         
     | 
| 
       98 
102 
     | 
    
         
             
                  end
         
     | 
| 
       99 
103 
     | 
    
         | 
| 
      
 104 
     | 
    
         
            +
                  def discard
         
     | 
| 
      
 105 
     | 
    
         
            +
                    execution&.discard
         
     | 
| 
      
 106 
     | 
    
         
            +
                  end
         
     | 
| 
      
 107 
     | 
    
         
            +
             
     | 
| 
       100 
108 
     | 
    
         
             
                  def failed_with(exception)
         
     | 
| 
       101 
109 
     | 
    
         
             
                    FailedExecution.create_or_find_by!(job_id: id, exception: exception)
         
     | 
| 
       102 
110 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -106,6 +114,9 @@ module SolidQueue 
     | 
|
| 
       106 
114 
     | 
    
         
             
                      ReadyExecution.create_or_find_by!(job_id: id)
         
     | 
| 
       107 
115 
     | 
    
         
             
                    end
         
     | 
| 
       108 
116 
     | 
    
         | 
| 
      
 117 
     | 
    
         
            +
                    def execution
         
     | 
| 
      
 118 
     | 
    
         
            +
                      %w[ ready claimed failed ].reduce(nil) { |acc, status| acc || public_send("#{status}_execution") }
         
     | 
| 
      
 119 
     | 
    
         
            +
                    end
         
     | 
| 
       109 
120 
     | 
    
         | 
| 
       110 
121 
     | 
    
         
             
                    def preserve_finished_jobs?
         
     | 
| 
       111 
122 
     | 
    
         
             
                      SolidQueue.preserve_finished_jobs
         
     | 
| 
         @@ -6,7 +6,7 @@ module SolidQueue 
     | 
|
| 
       6 
6 
     | 
    
         
             
                  extend ActiveSupport::Concern
         
     | 
| 
       7 
7 
     | 
    
         | 
| 
       8 
8 
     | 
    
         
             
                  included do
         
     | 
| 
       9 
     | 
    
         
            -
                    has_one :scheduled_execution 
     | 
| 
      
 9 
     | 
    
         
            +
                    has_one :scheduled_execution
         
     | 
| 
       10 
10 
     | 
    
         | 
| 
       11 
11 
     | 
    
         
             
                    scope :scheduled, -> { where.not(finished_at: nil) }
         
     | 
| 
       12 
12 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -31,10 +31,18 @@ module SolidQueue 
     | 
|
| 
       31 
31 
     | 
    
         
             
                    scheduled_at.nil? || scheduled_at <= Time.current
         
     | 
| 
       32 
32 
     | 
    
         
             
                  end
         
     | 
| 
       33 
33 
     | 
    
         | 
| 
      
 34 
     | 
    
         
            +
                  def scheduled?
         
     | 
| 
      
 35 
     | 
    
         
            +
                    scheduled_execution.present?
         
     | 
| 
      
 36 
     | 
    
         
            +
                  end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
       34 
38 
     | 
    
         
             
                  private
         
     | 
| 
       35 
39 
     | 
    
         
             
                    def schedule
         
     | 
| 
       36 
40 
     | 
    
         
             
                      ScheduledExecution.create_or_find_by!(job_id: id)
         
     | 
| 
       37 
41 
     | 
    
         
             
                    end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                    def execution
         
     | 
| 
      
 44 
     | 
    
         
            +
                      super || scheduled_execution
         
     | 
| 
      
 45 
     | 
    
         
            +
                    end
         
     | 
| 
       38 
46 
     | 
    
         
             
                end
         
     | 
| 
       39 
47 
     | 
    
         
             
              end
         
     | 
| 
       40 
48 
     | 
    
         
             
            end
         
     | 
| 
         @@ -15,9 +15,12 @@ module SolidQueue 
     | 
|
| 
       15 
15 
     | 
    
         
             
                      prepare_all_for_execution(jobs).tap do |enqueued_jobs|
         
     | 
| 
       16 
16 
     | 
    
         
             
                        enqueued_jobs.each do |enqueued_job|
         
     | 
| 
       17 
17 
     | 
    
         
             
                          active_jobs_by_job_id[enqueued_job.active_job_id].provider_job_id = enqueued_job.id
         
     | 
| 
      
 18 
     | 
    
         
            +
                          active_jobs_by_job_id[enqueued_job.active_job_id].successfully_enqueued = true
         
     | 
| 
       18 
19 
     | 
    
         
             
                        end
         
     | 
| 
       19 
20 
     | 
    
         
             
                      end
         
     | 
| 
       20 
21 
     | 
    
         
             
                    end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                    active_jobs.count(&:successfully_enqueued?)
         
     | 
| 
       21 
24 
     | 
    
         
             
                  end
         
     | 
| 
       22 
25 
     | 
    
         | 
| 
       23 
26 
     | 
    
         
             
                  def enqueue(active_job, scheduled_at: Time.current)
         
     | 
| 
         @@ -15,6 +15,10 @@ module SolidQueue 
     | 
|
| 
       15 
15 
     | 
    
         
             
                    end
         
     | 
| 
       16 
16 
     | 
    
         
             
                  end
         
     | 
| 
       17 
17 
     | 
    
         | 
| 
      
 18 
     | 
    
         
            +
                  def aggregated_count_across(queue_list)
         
     | 
| 
      
 19 
     | 
    
         
            +
                    QueueSelector.new(queue_list, self).scoped_relations.map(&:count).sum
         
     | 
| 
      
 20 
     | 
    
         
            +
                  end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
       18 
22 
     | 
    
         
             
                  private
         
     | 
| 
       19 
23 
     | 
    
         
             
                    def select_and_lock(queue_relation, process_id, limit)
         
     | 
| 
       20 
24 
     | 
    
         
             
                      return [] if limit <= 0
         
     | 
| 
         @@ -36,6 +40,12 @@ module SolidQueue 
     | 
|
| 
       36 
40 
     | 
    
         
             
                        where(job_id: claimed.pluck(:job_id)).delete_all
         
     | 
| 
       37 
41 
     | 
    
         
             
                      end
         
     | 
| 
       38 
42 
     | 
    
         
             
                    end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                    def discard_jobs(job_ids)
         
     | 
| 
      
 46 
     | 
    
         
            +
                      Job.release_all_concurrency_locks Job.where(id: job_ids)
         
     | 
| 
      
 47 
     | 
    
         
            +
                      super
         
     | 
| 
      
 48 
     | 
    
         
            +
                    end
         
     | 
| 
       39 
49 
     | 
    
         
             
                end
         
     | 
| 
       40 
50 
     | 
    
         
             
              end
         
     | 
| 
       41 
51 
     | 
    
         
             
            end
         
     | 
| 
         @@ -2,6 +2,8 @@ 
     | 
|
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            module SolidQueue
         
     | 
| 
       4 
4 
     | 
    
         
             
              class ScheduledExecution < Execution
         
     | 
| 
      
 5 
     | 
    
         
            +
                include Dispatching
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
       5 
7 
     | 
    
         
             
                scope :due, -> { where(scheduled_at: ..Time.current) }
         
     | 
| 
       6 
8 
     | 
    
         
             
                scope :ordered, -> { order(scheduled_at: :asc, priority: :asc) }
         
     | 
| 
       7 
9 
     | 
    
         
             
                scope :next_batch, ->(batch_size) { due.ordered.limit(batch_size) }
         
     | 
| 
         @@ -14,20 +16,10 @@ module SolidQueue 
     | 
|
| 
       14 
16 
     | 
    
         
             
                      job_ids = next_batch(batch_size).non_blocking_lock.pluck(:job_id)
         
     | 
| 
       15 
17 
     | 
    
         
             
                      if job_ids.empty? then []
         
     | 
| 
       16 
18 
     | 
    
         
             
                      else
         
     | 
| 
       17 
     | 
    
         
            -
                         
     | 
| 
      
 19 
     | 
    
         
            +
                        dispatch_jobs(job_ids)
         
     | 
| 
       18 
20 
     | 
    
         
             
                      end
         
     | 
| 
       19 
21 
     | 
    
         
             
                    end
         
     | 
| 
       20 
22 
     | 
    
         
             
                  end
         
     | 
| 
       21 
     | 
    
         
            -
             
     | 
| 
       22 
     | 
    
         
            -
                  private
         
     | 
| 
       23 
     | 
    
         
            -
                    def dispatch_batch(job_ids)
         
     | 
| 
       24 
     | 
    
         
            -
                      jobs = Job.where(id: job_ids)
         
     | 
| 
       25 
     | 
    
         
            -
             
     | 
| 
       26 
     | 
    
         
            -
                      Job.dispatch_all(jobs).map(&:id).tap do |dispatched_job_ids|
         
     | 
| 
       27 
     | 
    
         
            -
                        where(job_id: dispatched_job_ids).delete_all
         
     | 
| 
       28 
     | 
    
         
            -
                        SolidQueue.logger.info("[SolidQueue] Dispatched scheduled batch with #{dispatched_job_ids.size} jobs")
         
     | 
| 
       29 
     | 
    
         
            -
                      end
         
     | 
| 
       30 
     | 
    
         
            -
                    end
         
     | 
| 
       31 
23 
     | 
    
         
             
                end
         
     | 
| 
       32 
24 
     | 
    
         
             
              end
         
     | 
| 
       33 
25 
     | 
    
         
             
            end
         
     | 
| 
         @@ -1,65 +1,94 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # frozen_string_literal: true
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
     | 
    
         
            -
             
     | 
| 
       4 
     | 
    
         
            -
               
     | 
| 
       5 
     | 
    
         
            -
             
     | 
| 
      
 3 
     | 
    
         
            +
            module SolidQueue
         
     | 
| 
      
 4 
     | 
    
         
            +
              class Semaphore < Record
         
     | 
| 
      
 5 
     | 
    
         
            +
                scope :available, -> { where("value > 0") }
         
     | 
| 
      
 6 
     | 
    
         
            +
                scope :expired, -> { where(expires_at: ...Time.current) }
         
     | 
| 
       6 
7 
     | 
    
         | 
| 
       7 
     | 
    
         
            -
             
     | 
| 
       8 
     | 
    
         
            -
             
     | 
| 
       9 
     | 
    
         
            -
             
     | 
| 
       10 
     | 
    
         
            -
             
     | 
| 
       11 
     | 
    
         
            -
             
     | 
| 
       12 
     | 
    
         
            -
                def signal(job)
         
     | 
| 
       13 
     | 
    
         
            -
                  Proxy.new(job, self).signal
         
     | 
| 
       14 
     | 
    
         
            -
                end
         
     | 
| 
       15 
     | 
    
         
            -
              end
         
     | 
| 
       16 
     | 
    
         
            -
             
     | 
| 
       17 
     | 
    
         
            -
              class Proxy
         
     | 
| 
       18 
     | 
    
         
            -
                def initialize(job, proxied_class)
         
     | 
| 
       19 
     | 
    
         
            -
                  @job = job
         
     | 
| 
       20 
     | 
    
         
            -
                  @proxied_class = proxied_class
         
     | 
| 
       21 
     | 
    
         
            -
                end
         
     | 
| 
      
 8 
     | 
    
         
            +
                class << self
         
     | 
| 
      
 9 
     | 
    
         
            +
                  def wait(job)
         
     | 
| 
      
 10 
     | 
    
         
            +
                    Proxy.new(job).wait
         
     | 
| 
      
 11 
     | 
    
         
            +
                  end
         
     | 
| 
       22 
12 
     | 
    
         | 
| 
       23 
     | 
    
         
            -
             
     | 
| 
       24 
     | 
    
         
            -
             
     | 
| 
       25 
     | 
    
         
            -
                    semaphore.value > 0 && attempt_decrement
         
     | 
| 
       26 
     | 
    
         
            -
                  else
         
     | 
| 
       27 
     | 
    
         
            -
                    attempt_creation
         
     | 
| 
      
 13 
     | 
    
         
            +
                  def signal(job)
         
     | 
| 
      
 14 
     | 
    
         
            +
                    Proxy.new(job).signal
         
     | 
| 
       28 
15 
     | 
    
         
             
                  end
         
     | 
| 
       29 
     | 
    
         
            -
                end
         
     | 
| 
       30 
16 
     | 
    
         | 
| 
       31 
     | 
    
         
            -
             
     | 
| 
       32 
     | 
    
         
            -
             
     | 
| 
      
 17 
     | 
    
         
            +
                  def signal_all(jobs)
         
     | 
| 
      
 18 
     | 
    
         
            +
                    Proxy.signal_all(jobs)
         
     | 
| 
      
 19 
     | 
    
         
            +
                  end
         
     | 
| 
       33 
20 
     | 
    
         
             
                end
         
     | 
| 
       34 
21 
     | 
    
         | 
| 
       35 
     | 
    
         
            -
                 
     | 
| 
       36 
     | 
    
         
            -
                   
     | 
| 
       37 
     | 
    
         
            -
             
     | 
| 
       38 
     | 
    
         
            -
                  def attempt_creation
         
     | 
| 
       39 
     | 
    
         
            -
                    proxied_class.create!(key: key, value: limit - 1, expires_at: expires_at)
         
     | 
| 
       40 
     | 
    
         
            -
                    true
         
     | 
| 
       41 
     | 
    
         
            -
                  rescue ActiveRecord::RecordNotUnique
         
     | 
| 
       42 
     | 
    
         
            -
                    attempt_decrement
         
     | 
| 
      
 22 
     | 
    
         
            +
                class Proxy
         
     | 
| 
      
 23 
     | 
    
         
            +
                  def self.signal_all(jobs)
         
     | 
| 
      
 24 
     | 
    
         
            +
                    Semaphore.where(key: jobs.map(&:concurrency_key)).update_all("value = value + 1")
         
     | 
| 
       43 
25 
     | 
    
         
             
                  end
         
     | 
| 
       44 
26 
     | 
    
         | 
| 
       45 
     | 
    
         
            -
                  def  
     | 
| 
       46 
     | 
    
         
            -
                     
     | 
| 
      
 27 
     | 
    
         
            +
                  def initialize(job)
         
     | 
| 
      
 28 
     | 
    
         
            +
                    @job = job
         
     | 
| 
      
 29 
     | 
    
         
            +
                    @retries = 0
         
     | 
| 
       47 
30 
     | 
    
         
             
                  end
         
     | 
| 
       48 
31 
     | 
    
         | 
| 
       49 
     | 
    
         
            -
                  def  
     | 
| 
       50 
     | 
    
         
            -
                     
     | 
| 
      
 32 
     | 
    
         
            +
                  def wait
         
     | 
| 
      
 33 
     | 
    
         
            +
                    if semaphore = Semaphore.find_by(key: key)
         
     | 
| 
      
 34 
     | 
    
         
            +
                      semaphore.value > 0 && attempt_decrement
         
     | 
| 
      
 35 
     | 
    
         
            +
                    else
         
     | 
| 
      
 36 
     | 
    
         
            +
                      attempt_creation
         
     | 
| 
      
 37 
     | 
    
         
            +
                    end
         
     | 
| 
       51 
38 
     | 
    
         
             
                  end
         
     | 
| 
       52 
39 
     | 
    
         | 
| 
       53 
     | 
    
         
            -
                  def  
     | 
| 
       54 
     | 
    
         
            -
                     
     | 
| 
      
 40 
     | 
    
         
            +
                  def signal
         
     | 
| 
      
 41 
     | 
    
         
            +
                    attempt_increment
         
     | 
| 
       55 
42 
     | 
    
         
             
                  end
         
     | 
| 
       56 
43 
     | 
    
         | 
| 
       57 
     | 
    
         
            -
                   
     | 
| 
       58 
     | 
    
         
            -
                    job 
     | 
| 
       59 
     | 
    
         
            -
                  end
         
     | 
| 
      
 44 
     | 
    
         
            +
                  private
         
     | 
| 
      
 45 
     | 
    
         
            +
                    attr_accessor :job, :retries
         
     | 
| 
       60 
46 
     | 
    
         | 
| 
       61 
     | 
    
         
            -
             
     | 
| 
       62 
     | 
    
         
            -
             
     | 
| 
       63 
     | 
    
         
            -
             
     | 
| 
      
 47 
     | 
    
         
            +
                    def attempt_creation
         
     | 
| 
      
 48 
     | 
    
         
            +
                      Semaphore.create!(key: key, value: limit - 1, expires_at: expires_at)
         
     | 
| 
      
 49 
     | 
    
         
            +
                      true
         
     | 
| 
      
 50 
     | 
    
         
            +
                    rescue ActiveRecord::RecordNotUnique
         
     | 
| 
      
 51 
     | 
    
         
            +
                      attempt_decrement
         
     | 
| 
      
 52 
     | 
    
         
            +
                    end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                    def attempt_decrement
         
     | 
| 
      
 55 
     | 
    
         
            +
                      Semaphore.available.where(key: key).update_all([ "value = value - 1, expires_at = ?", expires_at ]) > 0
         
     | 
| 
      
 56 
     | 
    
         
            +
                    rescue ActiveRecord::Deadlocked
         
     | 
| 
      
 57 
     | 
    
         
            +
                      if retriable? then attempt_retry
         
     | 
| 
      
 58 
     | 
    
         
            +
                      else
         
     | 
| 
      
 59 
     | 
    
         
            +
                        raise
         
     | 
| 
      
 60 
     | 
    
         
            +
                      end
         
     | 
| 
      
 61 
     | 
    
         
            +
                    end
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                    def attempt_increment
         
     | 
| 
      
 64 
     | 
    
         
            +
                      Semaphore.where(key: key, value: ...limit).update_all([ "value = value + 1, expires_at = ?", expires_at ]) > 0
         
     | 
| 
      
 65 
     | 
    
         
            +
                    end
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                    def attempt_retry
         
     | 
| 
      
 68 
     | 
    
         
            +
                      self.retries += 1
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
                      if semaphore = Semaphore.find_by(key: key)
         
     | 
| 
      
 71 
     | 
    
         
            +
                        semaphore.value > 0 && attempt_decrement
         
     | 
| 
      
 72 
     | 
    
         
            +
                      end
         
     | 
| 
      
 73 
     | 
    
         
            +
                    end
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                    MAX_RETRIES = 1
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
                    def retriable?
         
     | 
| 
      
 78 
     | 
    
         
            +
                      retries < MAX_RETRIES
         
     | 
| 
      
 79 
     | 
    
         
            +
                    end
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
                    def key
         
     | 
| 
      
 82 
     | 
    
         
            +
                      job.concurrency_key
         
     | 
| 
      
 83 
     | 
    
         
            +
                    end
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
                    def expires_at
         
     | 
| 
      
 86 
     | 
    
         
            +
                      job.concurrency_duration.from_now
         
     | 
| 
      
 87 
     | 
    
         
            +
                    end
         
     | 
| 
      
 88 
     | 
    
         
            +
             
     | 
| 
      
 89 
     | 
    
         
            +
                    def limit
         
     | 
| 
      
 90 
     | 
    
         
            +
                      job.concurrency_limit
         
     | 
| 
      
 91 
     | 
    
         
            +
                    end
         
     | 
| 
      
 92 
     | 
    
         
            +
                end
         
     | 
| 
       64 
93 
     | 
    
         
             
              end
         
     | 
| 
       65 
94 
     | 
    
         
             
            end
         
     | 
| 
         @@ -0,0 +1,41 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module ActiveJob
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Uniqueness
         
     | 
| 
      
 5 
     | 
    
         
            +
                extend ActiveSupport::Concern
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                DEFAULT_UNIQUENESS_GROUP = ->(*) { self.class.name }
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                included do
         
     | 
| 
      
 10 
     | 
    
         
            +
                  class_attribute :uniqueness_key, instance_accessor: false
         
     | 
| 
      
 11 
     | 
    
         
            +
                  class_attribute :uniqueness_group, default: DEFAULT_UNIQUENESS_GROUP, instance_accessor: false
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                  class_attribute :uniqueness_duration
         
     | 
| 
      
 14 
     | 
    
         
            +
                end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                class_methods do
         
     | 
| 
      
 17 
     | 
    
         
            +
                  def enqueued_uniquely_by(key:, group: DEFAULT_UNIQUENESS_GROUP, duration: SolidQueue.default_uniqueness_period)
         
     | 
| 
      
 18 
     | 
    
         
            +
                    self.uniqueness_key = key
         
     | 
| 
      
 19 
     | 
    
         
            +
                    self.uniqueness_group = group
         
     | 
| 
      
 20 
     | 
    
         
            +
                    self.uniqueness_duration = duration
         
     | 
| 
      
 21 
     | 
    
         
            +
                  end
         
     | 
| 
      
 22 
     | 
    
         
            +
                end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                def uniqueness_key
         
     | 
| 
      
 25 
     | 
    
         
            +
                  if self.class.uniqueness_key
         
     | 
| 
      
 26 
     | 
    
         
            +
                    param = compute_concurrency_parameter(self.class.concurrency_key)
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                    case param
         
     | 
| 
      
 29 
     | 
    
         
            +
                    when ActiveRecord::Base
         
     | 
| 
      
 30 
     | 
    
         
            +
                      [ concurrency_group, param.class.name, param.id ]
         
     | 
| 
      
 31 
     | 
    
         
            +
                    else
         
     | 
| 
      
 32 
     | 
    
         
            +
                      [ concurrency_group, param ]
         
     | 
| 
      
 33 
     | 
    
         
            +
                    end.compact.join("/")
         
     | 
| 
      
 34 
     | 
    
         
            +
                  end
         
     | 
| 
      
 35 
     | 
    
         
            +
                end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                def enqueued_uniquely?
         
     | 
| 
      
 38 
     | 
    
         
            +
                  uniqueness_key.present?
         
     | 
| 
      
 39 
     | 
    
         
            +
                end
         
     | 
| 
      
 40 
     | 
    
         
            +
              end
         
     | 
| 
      
 41 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -4,6 +4,6 @@ Description: 
     | 
|
| 
       4 
4 
     | 
    
         
             
            Example:
         
     | 
| 
       5 
5 
     | 
    
         
             
                bin/rails generate solid_queue:install
         
     | 
| 
       6 
6 
     | 
    
         | 
| 
       7 
     | 
    
         
            -
                This will  
     | 
| 
      
 7 
     | 
    
         
            +
                This will perform the following:
         
     | 
| 
       8 
8 
     | 
    
         
             
                    Installs solid_queue migrations
         
     | 
| 
       9 
     | 
    
         
            -
                    Replaces Active Job's adapter in  
     | 
| 
      
 9 
     | 
    
         
            +
                    Replaces Active Job's adapter in environment configuration
         
     | 
| 
         @@ -4,6 +4,8 @@ module SolidQueue::Processes 
     | 
|
| 
       4 
4 
     | 
    
         
             
              module Runnable
         
     | 
| 
       5 
5 
     | 
    
         
             
                include Supervised
         
     | 
| 
       6 
6 
     | 
    
         | 
| 
      
 7 
     | 
    
         
            +
                attr_writer :mode
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
       7 
9 
     | 
    
         
             
                def start
         
     | 
| 
       8 
10 
     | 
    
         
             
                  @stopping = false
         
     | 
| 
       9 
11 
     | 
    
         | 
| 
         @@ -19,8 +21,6 @@ module SolidQueue::Processes 
     | 
|
| 
       19 
21 
     | 
    
         
             
                end
         
     | 
| 
       20 
22 
     | 
    
         | 
| 
       21 
23 
     | 
    
         
             
              private
         
     | 
| 
       22 
     | 
    
         
            -
                attr_writer :mode
         
     | 
| 
       23 
     | 
    
         
            -
             
     | 
| 
       24 
24 
     | 
    
         
             
                DEFAULT_MODE = :async
         
     | 
| 
       25 
25 
     | 
    
         | 
| 
       26 
26 
     | 
    
         
             
                def mode
         
     | 
    
        data/lib/solid_queue/version.rb
    CHANGED
    
    
    
        data/lib/solid_queue/worker.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: 0.2. 
     | 
| 
      
 4 
     | 
    
         
            +
              version: 0.2.1
         
     | 
| 
       5 
5 
     | 
    
         
             
            platform: ruby
         
     | 
| 
       6 
6 
     | 
    
         
             
            authors:
         
     | 
| 
       7 
7 
     | 
    
         
             
            - Rosa Gutierrez
         
     | 
| 
       8 
8 
     | 
    
         
             
            autorequire:
         
     | 
| 
       9 
9 
     | 
    
         
             
            bindir: bin
         
     | 
| 
       10 
10 
     | 
    
         
             
            cert_chain: []
         
     | 
| 
       11 
     | 
    
         
            -
            date:  
     | 
| 
      
 11 
     | 
    
         
            +
            date: 2024-01-30 00:00:00.000000000 Z
         
     | 
| 
       12 
12 
     | 
    
         
             
            dependencies:
         
     | 
| 
       13 
13 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       14 
14 
     | 
    
         
             
              name: rails
         
     | 
| 
         @@ -79,6 +79,7 @@ files: 
     | 
|
| 
       79 
79 
     | 
    
         
             
            - app/models/solid_queue/blocked_execution.rb
         
     | 
| 
       80 
80 
     | 
    
         
             
            - app/models/solid_queue/claimed_execution.rb
         
     | 
| 
       81 
81 
     | 
    
         
             
            - app/models/solid_queue/execution.rb
         
     | 
| 
      
 82 
     | 
    
         
            +
            - app/models/solid_queue/execution/dispatching.rb
         
     | 
| 
       82 
83 
     | 
    
         
             
            - app/models/solid_queue/execution/job_attributes.rb
         
     | 
| 
       83 
84 
     | 
    
         
             
            - app/models/solid_queue/failed_execution.rb
         
     | 
| 
       84 
85 
     | 
    
         
             
            - app/models/solid_queue/job.rb
         
     | 
| 
         @@ -97,8 +98,10 @@ files: 
     | 
|
| 
       97 
98 
     | 
    
         
             
            - app/models/solid_queue/semaphore.rb
         
     | 
| 
       98 
99 
     | 
    
         
             
            - config/routes.rb
         
     | 
| 
       99 
100 
     | 
    
         
             
            - db/migrate/20231211200639_create_solid_queue_tables.rb
         
     | 
| 
      
 101 
     | 
    
         
            +
            - db/migrate/20240110143450_add_missing_index_to_blocked_executions.rb
         
     | 
| 
       100 
102 
     | 
    
         
             
            - lib/active_job/concurrency_controls.rb
         
     | 
| 
       101 
103 
     | 
    
         
             
            - lib/active_job/queue_adapters/solid_queue_adapter.rb
         
     | 
| 
      
 104 
     | 
    
         
            +
            - lib/active_job/uniqueness.rb
         
     | 
| 
       102 
105 
     | 
    
         
             
            - lib/generators/solid_queue/install/USAGE
         
     | 
| 
       103 
106 
     | 
    
         
             
            - lib/generators/solid_queue/install/install_generator.rb
         
     | 
| 
       104 
107 
     | 
    
         
             
            - lib/generators/solid_queue/install/templates/config.yml
         
     | 
| 
         @@ -107,6 +110,7 @@ files: 
     | 
|
| 
       107 
110 
     | 
    
         
             
            - lib/solid_queue/app_executor.rb
         
     | 
| 
       108 
111 
     | 
    
         
             
            - lib/solid_queue/configuration.rb
         
     | 
| 
       109 
112 
     | 
    
         
             
            - lib/solid_queue/dispatcher.rb
         
     | 
| 
      
 113 
     | 
    
         
            +
            - lib/solid_queue/dispatcher/scheduled_executions_dispatcher.rb
         
     | 
| 
       110 
114 
     | 
    
         
             
            - lib/solid_queue/engine.rb
         
     | 
| 
       111 
115 
     | 
    
         
             
            - lib/solid_queue/pool.rb
         
     | 
| 
       112 
116 
     | 
    
         
             
            - lib/solid_queue/processes/base.rb
         
     |