disqualified 0.4.0 → 0.5.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9912dd95e6d0ffe2a78c0ac5e2d12683d16898d2ac329f7c50f1add7779ad3ea
4
- data.tar.gz: 98ee2d4e7581fe04cbceed91904cb280b9a0042e6cb38ebe738103b441edecb8
3
+ metadata.gz: 6c792cca8195272d00f6f55adc0384d98d55b7b777ff20b151d9f712b4a0b3b2
4
+ data.tar.gz: cd93d8bc19cdcec9ff6824dcdbf16e8aeb445e29d21fb52352511b6463ee3eed
5
5
  SHA512:
6
- metadata.gz: 3e27a52fd2e01580afcfdac1a66511ddb2fa70faf4f67caccefde8343fabffa4f7e527ec7257e2c9b2f5d7d9e8680b5f614846332c084c2311fd7c3f8548b806
7
- data.tar.gz: 3242e97e5e18fa7ff0ea60199ba8abbeb504277e26dfab3cbf2896e63eee53b7fda96c152ea76ed49c01c317492c361f4699c94478c1aa428260eb526ce2c2ef
6
+ metadata.gz: f6e7ef1182d6889dc023dadc50b0813a345cf11a2760bdf639607f0a0ec2d3ec92f448f79b2c564fa36a53fd3cc17fcd867b2aab91778bc0823fe7849ebacc47
7
+ data.tar.gz: 9cb2967ff78a215111a013823f6da72833d185a2b8876aaa44926317ad2adf6d7fdaf50a409254f0dc37dc5566579144cee3db54aa058d00dc2c7de2e8b89479
data/README.md CHANGED
@@ -62,6 +62,10 @@ bundle exec rails generate disqualified:install
62
62
  bundle binstub disqualified
63
63
  ```
64
64
 
65
+ Please remember to run `rails generate disqualified:install` when upgrading
66
+ Disqualified! It is mostly an idempotent command and will prepare any necessary
67
+ database migrations.
68
+
65
69
 
66
70
  ### ActiveJob
67
71
 
@@ -1,7 +1,18 @@
1
1
  class Disqualified::Record < Disqualified::BaseRecord
2
2
  self.table_name = "disqualified_jobs"
3
3
 
4
- scope :runnable, -> { where(finished_at: nil, run_at: (..Time.now), locked_by: nil) }
4
+ belongs_to :disqualified_sequence,
5
+ class_name: "Disqualified::SequenceRecord",
6
+ foreign_key: "sequence_uuid",
7
+ primary_key: "uuid",
8
+ optional: true
9
+
10
+ scope :with_sequence, -> {
11
+ joins("LEFT OUTER JOIN disqualified_sequences ds ON ds.uuid = sequence_uuid AND ds.current_step = sequence_step")
12
+ .where("ds.uuid = sequence_uuid OR (ds.uuid IS NULL AND sequence_uuid IS NULL)")
13
+ }
14
+ scope :pending, -> { where(finished_at: nil, run_at: (..Time.now), locked_by: nil) }
15
+ scope :runnable, -> { with_sequence.pending }
5
16
 
6
17
  def self.claim_one!(id: nil)
7
18
  run_id = SecureRandom.uuid
@@ -22,31 +33,52 @@ class Disqualified::Record < Disqualified::BaseRecord
22
33
  attempts: Arel.sql("attempts + 1")
23
34
  )
24
35
 
25
- raise ActiveRecord::RecordNotFound if claimed_count == 0
36
+ raise Disqualified::Error::NoClaimableJob if claimed_count == 0
26
37
 
27
38
  Disqualified::Record.find_by!(locked_by: run_id)
39
+ rescue ActiveRecord::RecordNotFound
40
+ raise Disqualified::Error::NoClaimableJob
28
41
  end
29
42
 
30
43
  def run!
31
44
  record = self.class.claim_one!(id:)
32
- record.send(:instantiate_handler_and_perform_with_args)
33
- record.finish
45
+ begin
46
+ record.send(:instantiate_handler_and_perform_with_args)
47
+ rescue => e
48
+ record.unclaim
49
+ raise e
50
+ else
51
+ record.finish
52
+ end
34
53
  record
35
54
  end
36
55
 
37
56
  def finish
38
- update!(locked_by: nil, locked_at: nil, finished_at: Time.now)
57
+ transaction do
58
+ update!(locked_by: nil, locked_at: nil, finished_at: Time.now)
59
+ if sequence_uuid && sequence_step
60
+ Disqualified::SequenceRecord
61
+ .where(uuid: sequence_uuid, current_step: sequence_step)
62
+ .update_all(
63
+ current_step: sequence_step + 1,
64
+ updated_at: Time.now
65
+ )
66
+ end
67
+ end
39
68
  end
40
69
 
41
70
  def requeue
42
71
  retry_count = attempts - 1
43
72
  sleep = (retry_count**4) + 15 + (rand(10) * (retry_count + 1))
44
- unqueue(run_at: Time.now + sleep)
73
+ unclaim(next_run_at: Time.now + sleep)
45
74
  end
46
75
 
47
- def unqueue(run_at: nil)
48
- run_at ||= Time.now
49
- update!(locked_by: nil, locked_at: nil, run_at:)
76
+ def unclaim(next_run_at: nil)
77
+ if next_run_at
78
+ update!(locked_by: nil, locked_at: nil, run_at: next_run_at)
79
+ else
80
+ update!(locked_by: nil, locked_at: nil)
81
+ end
50
82
  end
51
83
 
52
84
  private def instantiate_handler_and_perform_with_args
@@ -0,0 +1,3 @@
1
+ class Disqualified::SequenceRecord < Disqualified::BaseRecord
2
+ self.table_name = "disqualified_sequences"
3
+ end
data/disqualified.gemspec CHANGED
@@ -22,6 +22,8 @@ Gem::Specification.new do |spec|
22
22
 
23
23
  spec.required_ruby_version = ">= 3.1.0"
24
24
 
25
+ spec.post_install_message = "Remember to update! Run `rails g disqualified:install`"
26
+
25
27
  spec.add_dependency "rails", ">= 7.0.0"
26
28
  spec.add_dependency "concurrent-ruby", "~> 1.0"
27
29
  end
@@ -8,5 +8,8 @@ module Disqualified
8
8
 
9
9
  class JobNotClaimed < DisqualifiedError
10
10
  end
11
+
12
+ class NoClaimableJob < DisqualifiedError
13
+ end
11
14
  end
12
15
  end
@@ -1,11 +1,19 @@
1
1
  module Disqualified::Job
2
2
  module ClassMethods
3
3
  def perform_at(the_time, *args)
4
+ if Thread.current[Disqualified::Sequence::UUID]
5
+ Thread.current[Disqualified::Sequence::COUNT] += 1
6
+ sequence_uuid = Thread.current[Disqualified::Sequence::UUID]
7
+ sequence_step = Thread.current[Disqualified::Sequence::COUNT]
8
+ end
9
+
4
10
  Disqualified::Record.create!(
5
11
  handler: name,
6
12
  arguments: JSON.dump(args),
7
13
  queue: "default",
8
- run_at: the_time
14
+ run_at: the_time,
15
+ sequence_uuid:,
16
+ sequence_step:
9
17
  )
10
18
  end
11
19
 
@@ -18,7 +26,7 @@ module Disqualified::Job
18
26
  end
19
27
  end
20
28
 
21
- def self.included(klass)
22
- klass.extend(ClassMethods)
29
+ def self.included(other)
30
+ other.extend(ClassMethods)
23
31
  end
24
32
  end
@@ -8,28 +8,21 @@ class Disqualified::Main
8
8
 
9
9
  def call
10
10
  Rails.application.reloader.wrap do
11
- begin
12
- record = Disqualified::Record.claim_one!
13
- run_id = record.locked_by
14
- rescue ActiveRecord::RecordNotFound
15
- @logger.warn do
16
- format_log("Disqualified::Main#call", "Job not found")
17
- end
18
- next
11
+ record = Disqualified::Record.claim_one!
12
+ run_id = record.locked_by
13
+ record.send(:instantiate_handler_and_perform_with_args)
14
+ record.finish
15
+ @logger.info do
16
+ format_log("Disqualified::Main#call", "Runner #{run_id}", "Done")
19
17
  end
20
-
21
- begin
22
- record.send(:instantiate_handler_and_perform_with_args)
23
- record.finish
24
-
25
- @logger.info do
26
- format_log("Disqualified::Main#call", "Runner #{run_id}", "Done")
27
- end
28
- rescue => e
29
- handle_error(@error_hooks, e, {record: record.attributes})
30
- @logger.error { format_log("Disqualified::Main#run", "Runner #{run_id}", "Rescued Record ##{record.id}") }
31
- record.requeue
18
+ rescue Disqualified::Error::NoClaimableJob
19
+ @logger.warn do
20
+ format_log("Disqualified::Main#call", "No claimable jobs")
32
21
  end
22
+ rescue => e
23
+ handle_error(@error_hooks, e, {record: record.attributes})
24
+ @logger.error { format_log("Disqualified::Main#run", "Runner #{run_id}", "Rescued Record ##{record.id}") }
25
+ record.requeue
33
26
  end
34
27
  end
35
28
  end
@@ -0,0 +1,21 @@
1
+ class Disqualified::Sequence
2
+ UUID = :disqualified_sequence_uuid
3
+ COUNT = :disqualified_sequence_count
4
+
5
+ def self.queue(description: nil, &block)
6
+ Disqualified::SequenceRecord.transaction do
7
+ Thread.current[UUID] = SecureRandom.uuid
8
+ Thread.current[COUNT] = 0
9
+ yield
10
+ Disqualified::SequenceRecord.create!(
11
+ uuid: Thread.current[UUID],
12
+ current_step: 1,
13
+ final_step: Thread.current[COUNT],
14
+ description:
15
+ )
16
+ end
17
+ ensure
18
+ Thread.current[UUID] = nil
19
+ Thread.current[COUNT] = nil
20
+ end
21
+ end
@@ -1,3 +1,3 @@
1
1
  module Disqualified
2
- VERSION = "0.4.0"
2
+ VERSION = "0.5.1"
3
3
  end
data/lib/disqualified.rb CHANGED
@@ -17,4 +17,5 @@ require_relative "disqualified/engine"
17
17
  require_relative "disqualified/job"
18
18
  require_relative "disqualified/main"
19
19
  require_relative "disqualified/pool"
20
+ require_relative "disqualified/sequence"
20
21
  require_relative "disqualified/version"
@@ -3,6 +3,8 @@ Description:
3
3
 
4
4
  Example:
5
5
  bin/rails generate disqualified:install
6
+ bin/rails generate disqualified:install --database primary
7
+ bin/rails g disqualified:install --db=animals
6
8
 
7
9
  This will create:
8
10
  db/migrate/20220703062536_create_disqualified_jobs.rb
@@ -1,8 +1,20 @@
1
+ require "rails/generators/active_record"
2
+
1
3
  class Disqualified::InstallGenerator < Rails::Generators::Base
4
+ include ActiveRecord::Generators::Migration
5
+
2
6
  source_root File.expand_path("templates", __dir__)
3
7
 
8
+ class_option :database,
9
+ type: :string,
10
+ aliases: %i[--db],
11
+ desc: "The database for your migration. By default, the current environment's primary database is used."
12
+
4
13
  def copy_migration_file
5
14
  basename = "20220703062536_create_disqualified_jobs.rb"
6
- copy_file basename, "db/migrate/#{basename}"
15
+ copy_file(basename, File.join(db_migrate_path, basename))
16
+
17
+ basename = "20241119023328_create_disqualified_sequences.rb"
18
+ copy_file(basename, File.join(db_migrate_path, basename))
7
19
  end
8
20
  end
@@ -0,0 +1,17 @@
1
+ class CreateDisqualifiedSequences < ActiveRecord::Migration[7.2]
2
+ def change
3
+ create_table :disqualified_sequences do |t|
4
+ t.text :uuid, null: false
5
+ t.integer :current_step, null: false
6
+ t.integer :final_step, null: false
7
+ t.text :description
8
+ t.timestamps
9
+
10
+ t.index :uuid, unique: true
11
+ end
12
+
13
+ add_column :disqualified_jobs, :sequence_uuid, :text
14
+ add_column :disqualified_jobs, :sequence_step, :integer
15
+ add_index :disqualified_jobs, [:sequence_uuid, :sequence_step], unique: true
16
+ end
17
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: disqualified
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Zach Ahn
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-08-11 00:00:00.000000000 Z
11
+ date: 2024-12-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -50,6 +50,7 @@ files:
50
50
  - README.md
51
51
  - app/models/disqualified/base_record.rb
52
52
  - app/models/disqualified/record.rb
53
+ - app/models/disqualified/sequence_record.rb
53
54
  - disqualified.gemspec
54
55
  - exe/disqualified
55
56
  - lib/disqualified.rb
@@ -63,10 +64,12 @@ files:
63
64
  - lib/disqualified/logging.rb
64
65
  - lib/disqualified/main.rb
65
66
  - lib/disqualified/pool.rb
67
+ - lib/disqualified/sequence.rb
66
68
  - lib/disqualified/version.rb
67
69
  - lib/generators/disqualified/install/USAGE
68
70
  - lib/generators/disqualified/install/install_generator.rb
69
71
  - lib/generators/disqualified/install/templates/20220703062536_create_disqualified_jobs.rb
72
+ - lib/generators/disqualified/install/templates/20241119023328_create_disqualified_sequences.rb
70
73
  homepage: https://github.com/zachahn/disqualified
71
74
  licenses:
72
75
  - LGPL-3.0-only
@@ -74,7 +77,7 @@ metadata:
74
77
  homepage_uri: https://github.com/zachahn/disqualified
75
78
  source_code_uri: https://github.com/zachahn/disqualified
76
79
  changelog_uri: https://github.com/zachahn/disqualified/blob/main/CHANGELOG.md
77
- post_install_message:
80
+ post_install_message: Remember to update! Run `rails g disqualified:install`
78
81
  rdoc_options: []
79
82
  require_paths:
80
83
  - lib