cloudtasker 0.14.rc1 → 0.15.rc1

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.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/lint_rubocop.yml +1 -1
  3. data/.github/workflows/test_ruby_3.x.yml +19 -18
  4. data/.gitignore +1 -1
  5. data/.rubocop.yml +12 -12
  6. data/Appraisals +19 -16
  7. data/CHANGELOG.md +12 -3
  8. data/Gemfile +1 -1
  9. data/README.md +6 -8
  10. data/app/controllers/cloudtasker/worker_controller.rb +1 -1
  11. data/cloudtasker.gemspec +1 -1
  12. data/docs/STORABLE_JOBS.md +1 -1
  13. data/docs/UNIQUE_JOBS.md +1 -0
  14. data/gemfiles/google_cloud_tasks_1.0.gemfile +1 -1
  15. data/gemfiles/google_cloud_tasks_1.1.gemfile +1 -1
  16. data/gemfiles/google_cloud_tasks_1.2.gemfile +1 -1
  17. data/gemfiles/google_cloud_tasks_1.3.gemfile +1 -1
  18. data/gemfiles/google_cloud_tasks_1.4.gemfile +1 -1
  19. data/gemfiles/google_cloud_tasks_1.5.gemfile +1 -1
  20. data/gemfiles/google_cloud_tasks_2.0.gemfile +1 -1
  21. data/gemfiles/google_cloud_tasks_2.1.gemfile +1 -1
  22. data/gemfiles/rails_6.1.gemfile +3 -1
  23. data/gemfiles/rails_7.0.gemfile +1 -1
  24. data/gemfiles/{rails_5.2.gemfile → rails_7.1.gemfile} +2 -2
  25. data/gemfiles/{rails_6.0.gemfile → rails_8.0.gemfile} +2 -2
  26. data/gemfiles/rails_8.1.gemfile +18 -0
  27. data/gemfiles/semantic_logger_3.4.gemfile +1 -1
  28. data/gemfiles/semantic_logger_4.6.gemfile +1 -1
  29. data/gemfiles/semantic_logger_4.7.0.gemfile +1 -1
  30. data/gemfiles/semantic_logger_4.7.2.gemfile +1 -1
  31. data/lib/cloudtasker/backend/redis_task.rb +1 -1
  32. data/lib/cloudtasker/config.rb +3 -0
  33. data/lib/cloudtasker/redis_client.rb +17 -37
  34. data/lib/cloudtasker/unique_job/lock/until_completed.rb +40 -0
  35. data/lib/cloudtasker/unique_job/middleware.rb +1 -0
  36. data/lib/cloudtasker/version.rb +1 -1
  37. data/lib/cloudtasker/worker_logger.rb +2 -2
  38. metadata +8 -7
  39. data/.github/workflows/test_ruby_2.7.yml +0 -38
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ca57b71a4e3552693d0a64720f423bf7ff1168d5078018a0177c65f09ad681d8
4
- data.tar.gz: f886fe29dec2c4ce12286b3e5af843a02e4ea426fbef6db0d2cfcb300418ea34
3
+ metadata.gz: 3acbfbd2b8d2ac1c4260b01fc44c4bd43c0061db35eeb84a0bc5f4c1a9358204
4
+ data.tar.gz: 1a9e2d8048b39eac16dc794671796025a16c1f27e95967adde23ad4361be3610
5
5
  SHA512:
6
- metadata.gz: 8312536629168911d321d0630a126f0671eb9e7e6bdb51c86f675ac159d7294f8a1a87bf89d4b4948dae23f4e9411e5673718bc6edac8b73d10592f1a8527744
7
- data.tar.gz: a6aeb550354e06413bcd21e3ce47f0b136184c236b4c908104ee1ae51412f4fbcad6cdc3ab392bf991046fe9a1747f32770ca1a0f8d3bddf35dd54813f8b646e
6
+ metadata.gz: 915fb26b5107680819a63cd55d2097f56675779f160eb284e109aacbbd111af358e71e5afc99194ec1846e022a73797f57d5e7b8be7b3b2684cc3b30f0fdc787
7
+ data.tar.gz: 3ca5533216e37a51b2393720d60a6a969ca9c887c0151391f9409921bc952062fd6a82855e9c21b0673a35a604ba40fa56f9f72a590e8481397388c70306ff27
@@ -10,6 +10,6 @@ jobs:
10
10
  - uses: zhulik/redis-action@1.1.0
11
11
  - uses: ruby/setup-ruby@v1
12
12
  with:
13
- ruby-version: '3.3.0'
13
+ ruby-version: "3.3.10"
14
14
  bundler-cache: true
15
15
  - run: bundle exec rubocop
@@ -8,25 +8,26 @@ jobs:
8
8
  strategy:
9
9
  matrix:
10
10
  ruby:
11
- - '3.0'
12
- - '3.1'
13
- - '3.2'
14
- - '3.3'
11
+ - "3.0"
12
+ - "3.1"
13
+ - "3.2"
14
+ - "3.3"
15
+ - "3.4"
15
16
  appraisal:
16
- - 'google_cloud_tasks_1.0'
17
- - 'google_cloud_tasks_1.1'
18
- - 'google_cloud_tasks_1.2'
19
- - 'google_cloud_tasks_1.3'
20
- - 'google_cloud_tasks_1.4'
21
- - 'google_cloud_tasks_1.5'
22
- - 'google_cloud_tasks_2.0'
23
- - 'google_cloud_tasks_2.1'
24
- - 'rails_6.1'
25
- - 'rails_7.0'
26
- - 'semantic_logger_3.4'
27
- - 'semantic_logger_4.6'
28
- - 'semantic_logger_4.7.0'
29
- - 'semantic_logger_4.7.2'
17
+ - "google_cloud_tasks_1.0"
18
+ - "google_cloud_tasks_1.1"
19
+ - "google_cloud_tasks_1.2"
20
+ - "google_cloud_tasks_1.3"
21
+ - "google_cloud_tasks_1.4"
22
+ - "google_cloud_tasks_1.5"
23
+ - "google_cloud_tasks_2.0"
24
+ - "google_cloud_tasks_2.1"
25
+ - "rails_6.1"
26
+ - "rails_7.0"
27
+ - "semantic_logger_3.4"
28
+ - "semantic_logger_4.6"
29
+ - "semantic_logger_4.7.0"
30
+ - "semantic_logger_4.7.2"
30
31
  env:
31
32
  BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/${{ matrix.appraisal }}.gemfile
32
33
  steps:
data/.gitignore CHANGED
@@ -3,7 +3,7 @@
3
3
  /_yardoc/
4
4
  /coverage/
5
5
  /doc/
6
- /examples/rails/log/*.log
6
+ /examples/*/log/*.log
7
7
  /examples/rails/tmp/
8
8
  /gemfiles/*.gemfile.lock
9
9
  /log/
data/.rubocop.yml CHANGED
@@ -3,10 +3,10 @@ require: rubocop-rspec
3
3
  AllCops:
4
4
  NewCops: enable
5
5
  SuggestExtensions: false
6
- TargetRubyVersion: 2.7
6
+ TargetRubyVersion: 3.0
7
7
  Exclude:
8
- - 'gemfiles/**/*'
9
- - 'vendor/**/*'
8
+ - "gemfiles/**/*"
9
+ - "vendor/**/*"
10
10
 
11
11
  Metrics/ClassLength:
12
12
  Max: 300
@@ -17,7 +17,7 @@ Metrics/ModuleLength:
17
17
  Metrics/AbcSize:
18
18
  Max: 30
19
19
  Exclude:
20
- - 'spec/support/*'
20
+ - "spec/support/*"
21
21
 
22
22
  Metrics/PerceivedComplexity:
23
23
  Max: 20
@@ -30,7 +30,7 @@ Metrics/MethodLength:
30
30
 
31
31
  RSpec/DescribeClass:
32
32
  Exclude:
33
- - 'spec/integration/**/*_spec.rb'
33
+ - "spec/integration/**/*_spec.rb"
34
34
 
35
35
  RSpec/ExpectInHook:
36
36
  Enabled: false
@@ -44,12 +44,12 @@ RSpec/ScatteredSetup:
44
44
  Metrics/BlockLength:
45
45
  Exclude:
46
46
  - cloudtasker.gemspec
47
- - 'spec/**/*'
47
+ - "spec/**/*"
48
48
 
49
49
  Style/Documentation:
50
50
  Exclude:
51
- - 'examples/**/*'
52
- - 'spec/**/*'
51
+ - "examples/**/*"
52
+ - "spec/**/*"
53
53
 
54
54
  Metrics/ParameterLists:
55
55
  CountKeywordArgs: false
@@ -59,15 +59,15 @@ Metrics/CyclomaticComplexity:
59
59
 
60
60
  Lint/EmptyBlock:
61
61
  Exclude:
62
- - 'examples/rails/config/routes.rb'
62
+ - "examples/rails/config/routes.rb"
63
63
 
64
64
  RSpec/MessageSpies:
65
65
  Enabled: false
66
66
 
67
67
  RSpec/MultipleExpectations:
68
68
  Exclude:
69
- - 'examples/**/*'
70
- - 'spec/integration/**/*'
69
+ - "examples/**/*"
70
+ - "spec/integration/**/*"
71
71
 
72
72
  RSpec/AnyInstance:
73
73
  Enabled: false
@@ -90,4 +90,4 @@ RSpec/VerifiedDoubles:
90
90
  Exclude:
91
91
  - spec/cloudtasker/cloud_task_spec.rb
92
92
  - spec/cloudtasker/backend/google_cloud_task_v1_spec.rb
93
- - spec/cloudtasker/backend/google_cloud_task_v2_spec.rb
93
+ - spec/cloudtasker/backend/google_cloud_task_v2_spec.rb
data/Appraisals CHANGED
@@ -32,28 +32,31 @@ appraise 'google_cloud_tasks_2.1' do
32
32
  gem 'google-cloud-tasks', '~> 2.1.0'
33
33
  end
34
34
 
35
- if RUBY_VERSION < '3'
36
- appraise 'rails_5.2' do
37
- gem 'rails', '~> 5.2.0'
38
- gem 'rspec-rails'
39
- end
35
+ appraise 'rails_6.1' do
36
+ gem 'rails', '~> 6.1.0'
37
+ gem 'rspec-rails'
38
+ gem 'mutex_m'
39
+ gem 'drb'
40
+ end
40
41
 
41
- appraise 'rails_6.0' do
42
- gem 'rails', '~> 6.0.0'
43
- gem 'rspec-rails'
44
- end
42
+ appraise 'rails_7.0' do
43
+ gem 'rails', '~> 7.0.0'
44
+ gem 'rspec-rails'
45
45
  end
46
46
 
47
- appraise 'rails_6.1' do
48
- gem 'rails', '~> 6.1.0'
47
+ appraise 'rails_7.1' do
48
+ gem 'rails', '~> 7.1'
49
49
  gem 'rspec-rails'
50
50
  end
51
51
 
52
- if RUBY_VERSION >= '2.7'
53
- appraise 'rails_7.0' do
54
- gem 'rails', '~> 7.0.0'
55
- gem 'rspec-rails'
56
- end
52
+ appraise 'rails_8.0' do
53
+ gem 'rails', '~> 8.0'
54
+ gem 'rspec-rails'
55
+ end
56
+
57
+ appraise 'rails_8.1' do
58
+ gem 'rails', '~> 8.1'
59
+ gem 'rspec-rails'
57
60
  end
58
61
 
59
62
  appraise 'semantic_logger_3.4' do
data/CHANGELOG.md CHANGED
@@ -1,8 +1,18 @@
1
1
  # Changelog
2
2
 
3
- ## [v0.14.rc1](https://github.com/keypup-io/cloudtasker/tree/v0.14.rc1) (2024-09-22)
3
+ ## [v0.15.rc1](https://github.com/keypup-io/cloudtasker/tree/v0.15.rc1) (2025-10-30)
4
4
 
5
- [Full Changelog](https://github.com/keypup-io/cloudtasker/compare/v0.13.2...v0.14.rc1)
5
+ [Full Changelog](https://github.com/keypup-io/cloudtasker/compare/v0.14.0...v0.15.rc1)
6
+
7
+ **Improvements:**
8
+ - Unique Jobs: add `until_completed` strategy to lock jobs until they are completed or have exhausted all retries (thanks @jam-packed).
9
+
10
+ **Maintenance:**
11
+ - Supported rubies: drop support for ruby `2.7`. Cloudtasker now requires ruby `3.0` and above.
12
+
13
+ ## [v0.14.0](https://github.com/keypup-io/cloudtasker/tree/v0.14.0) (2025-02-11)
14
+
15
+ [Full Changelog](https://github.com/keypup-io/cloudtasker/compare/v0.13.2...v0.14.0)
6
16
 
7
17
  **Improvements:**
8
18
  - Authentication: To support OIDC and regular Cloudtasker authentication, we moved the Cloudtasker Authentication header from `Authorization` to `X-Cloudtasker-Authorization`. Backward compatibility is maintained for existing jobs.
@@ -23,7 +33,6 @@
23
33
  - Rails: Use `skip_forgery_protection` instead of `skip_before_action`. The later was causing occasional issues on some setups.
24
34
 
25
35
 
26
-
27
36
  ## [v0.13.2](https://github.com/keypup-io/cloudtasker/tree/v0.13.2) (2023-07-02)
28
37
 
29
38
  [Full Changelog](https://github.com/keypup-io/cloudtasker/compare/v0.13.1...v0.13.2)
data/Gemfile CHANGED
@@ -6,7 +6,7 @@ source 'https://rubygems.org'
6
6
  gemspec
7
7
 
8
8
  # Dev dependencies
9
- gem 'appraisal', github: 'thoughtbot/appraisal'
9
+ gem 'appraisal'
10
10
  gem 'bundler', '~> 2.0'
11
11
  gem 'rake', '>= 12.3.3'
12
12
  gem 'rspec', '~> 3.0'
data/README.md CHANGED
@@ -1,6 +1,4 @@
1
- ![Build Status](https://github.com/keypup-io/cloudtasker/workflows/Test/badge.svg) [![Gem Version](https://badge.fury.io/rb/cloudtasker.svg)](https://badge.fury.io/rb/cloudtasker)
2
-
3
- 🚀🚀🚀 Cloudtasker 0.14 release candidate (`v0.14.rc1`) is out and it's quite big ([Changelog](https://github.com/keypup-io/cloudtasker/blob/master/CHANGELOG.md)). Any help testing this release is welcome, and feel free to open issues if you spot any regression.
1
+ ![Build Status 3.x](https://github.com/keypup-io/cloudtasker/actions/workflows/test_ruby_3.x.yml/badge.svg) [![Gem Version](https://badge.fury.io/rb/cloudtasker.svg)](https://badge.fury.io/rb/cloudtasker)
4
2
 
5
3
  # Cloudtasker
6
4
 
@@ -428,7 +426,7 @@ Cloudtasker.configure do |config|
428
426
  # See https://cloud.google.com/tasks/docs/creating-http-target-tasks#sa for more information on
429
427
  # setting up service accounts for use with Cloud Tasks.
430
428
  #
431
- # Supported since: v0.14.rc1
429
+ # Supported since: v0.14.0
432
430
  #
433
431
  # Default: nil
434
432
  #
@@ -698,7 +696,7 @@ end
698
696
  See the [Cloudtasker::Worker class](lib/cloudtasker/worker.rb) for more information on attributes available to be logged in your `log_context_processor` proc.
699
697
 
700
698
  ### Truncating log arguments
701
- **Supported since**: `v0.14.rc1`
699
+ **Supported since**: `v0.14.0`
702
700
 
703
701
  By default Cloudtasker does not log job arguments as arguments can contain sensitive data and generate voluminous logs, which may lead to noticeable costs with your log provider (e.g. GCP Logging). Also some providers (e.g. GCP Logging) will automatically truncate log entries that are too big and reduce their searchability.
704
702
 
@@ -834,7 +832,7 @@ By default jobs are retried 25 times - using an exponential backoff - before bei
834
832
 
835
833
  Note that the number of retries set on your Cloud Task queue should be many times higher than the number of retries configured in Cloudtasker because Cloud Task also includes failures to connect to your application. Ideally set the number of retries to `unlimited` in Cloud Tasks.
836
834
 
837
- **Note**: Versions prior to `v0.14.rc1` use the `X-CloudTasks-TaskRetryCount` header for retries instead of the `X-CloudTasks-TaskExecutionCount` header to detect the number of retries, because there a previous bug on the GCP side which made the `X-CloudTasks-TaskExecutionCount` stay at zero instead of increasing on successive executions. Versions prior to `v0.14.rc1` count any failure as failure, including failures due to the backend being unavailable (`HTTP 503`). Versions `v0.14.rc1` and later only count application failure (`HTTP 4xx`) as failure for retry purpose.
835
+ **Note**: Versions prior to `v0.14.0` use the `X-CloudTasks-TaskRetryCount` header for retries instead of the `X-CloudTasks-TaskExecutionCount` header to detect the number of retries, because there a previous bug on the GCP side which made the `X-CloudTasks-TaskExecutionCount` stay at zero instead of increasing on successive executions. Versions prior to `v0.14.0` count any failure as failure, including failures due to the backend being unavailable (`HTTP 503`). Versions `v0.14.0` and later only count application failure (`HTTP 4xx`) as failure for retry purpose.
838
836
 
839
837
  E.g. Set max number of retries globally via the cloudtasker initializer.
840
838
  ```ruby
@@ -891,7 +889,7 @@ end
891
889
  ```
892
890
 
893
891
  ### Conditional reenqueues using retry errors
894
- **Supported since**: `v0.14.rc1`
892
+ **Supported since**: `v0.14.0`
895
893
 
896
894
  If your worker is waiting for some precondition to occur and you want to re-enqueue it until the condition has been met, you can raise a `Cloudtasker::RetryWorkerError`. This special error will fail your job **without logging an error** while still increasing the number of retries.
897
895
 
@@ -1005,7 +1003,7 @@ Each testing mode accepts a block argument to temporarily switch to it:
1005
1003
  Cloudtasker::Testing.fake!
1006
1004
 
1007
1005
  # Enable inline! mode temporarily for a given test
1008
- Cloudtasker.inline! do
1006
+ Cloudtasker::Testing.inline! do
1009
1007
  MyWorker.perform_async(1,2)
1010
1008
  end
1011
1009
  ```
@@ -94,7 +94,7 @@ module Cloudtasker
94
94
  # Verify content signature
95
95
  Authenticator.verify_signature!(signature, json_payload)
96
96
  else
97
- # Get authorization token from custom header (since v0.14.rc1) or fallback to
97
+ # Get authorization token from custom header (since v0.14.0) or fallback to
98
98
  # former authorization header (jobs enqueued by v0.13 and below)
99
99
  bearer_token = request.headers[Cloudtasker::Config::CT_AUTHORIZATION_HEADER].to_s.split.last ||
100
100
  request.headers[Cloudtasker::Config::OIDC_AUTHORIZATION_HEADER].to_s.split.last
data/cloudtasker.gemspec CHANGED
@@ -28,7 +28,7 @@ Gem::Specification.new do |spec|
28
28
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
29
29
  spec.require_paths = ['lib']
30
30
 
31
- spec.required_ruby_version = '>= 2.7.0'
31
+ spec.required_ruby_version = '>= 3.0'
32
32
 
33
33
  spec.add_dependency 'activesupport'
34
34
  spec.add_dependency 'connection_pool'
@@ -1,6 +1,6 @@
1
1
  # Cloudtasker Storable Jobs
2
2
 
3
- **Supported since**: `v0.14.rc1`
3
+ **Supported since**: `v0.14.0`
4
4
  **Note**: this extension requires redis
5
5
 
6
6
  The Cloudtasker storage extension allows you to park jobs in a specific garage lane and enqueue (pull) them when specific conditions have been met.
data/docs/UNIQUE_JOBS.md CHANGED
@@ -70,6 +70,7 @@ For each lock strategy the table specifies the lock period (start/end) and which
70
70
  | `until_executing` | The job is scheduled | The job starts processing | `reject` (default) or `raise` |
71
71
  | `while_executing` | The job starts processing | The job ends processing | `reject` (default), `reschedule` or `raise` |
72
72
  | `until_executed` | The job is scheduled | The job ends processing | `reject` (default) or `raise` |
73
+ | `until_completed` | The job is scheduled | The job completes successfully or a `DeadWorkerError` is raised. Supported since `v0.15.rc1`. | `reject` (default) or `raise` |
73
74
 
74
75
  ## Available conflict strategies
75
76
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "appraisal", github: "thoughtbot/appraisal"
5
+ gem "appraisal"
6
6
  gem "bundler", "~> 2.0"
7
7
  gem "rake", ">= 12.3.3"
8
8
  gem "rspec", "~> 3.0"
@@ -2,7 +2,7 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "appraisal", github: "thoughtbot/appraisal"
5
+ gem "appraisal"
6
6
  gem "bundler", "~> 2.0"
7
7
  gem "rake", ">= 12.3.3"
8
8
  gem "rspec", "~> 3.0"
@@ -2,7 +2,7 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "appraisal", github: "thoughtbot/appraisal"
5
+ gem "appraisal"
6
6
  gem "bundler", "~> 2.0"
7
7
  gem "rake", ">= 12.3.3"
8
8
  gem "rspec", "~> 3.0"
@@ -2,7 +2,7 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "appraisal", github: "thoughtbot/appraisal"
5
+ gem "appraisal"
6
6
  gem "bundler", "~> 2.0"
7
7
  gem "rake", ">= 12.3.3"
8
8
  gem "rspec", "~> 3.0"
@@ -2,7 +2,7 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "appraisal", github: "thoughtbot/appraisal"
5
+ gem "appraisal"
6
6
  gem "bundler", "~> 2.0"
7
7
  gem "rake", ">= 12.3.3"
8
8
  gem "rspec", "~> 3.0"
@@ -2,7 +2,7 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "appraisal", github: "thoughtbot/appraisal"
5
+ gem "appraisal"
6
6
  gem "bundler", "~> 2.0"
7
7
  gem "rake", ">= 12.3.3"
8
8
  gem "rspec", "~> 3.0"
@@ -2,7 +2,7 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "appraisal", github: "thoughtbot/appraisal"
5
+ gem "appraisal"
6
6
  gem "bundler", "~> 2.0"
7
7
  gem "rake", ">= 12.3.3"
8
8
  gem "rspec", "~> 3.0"
@@ -2,7 +2,7 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "appraisal", github: "thoughtbot/appraisal"
5
+ gem "appraisal"
6
6
  gem "bundler", "~> 2.0"
7
7
  gem "rake", ">= 12.3.3"
8
8
  gem "rspec", "~> 3.0"
@@ -2,7 +2,7 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "appraisal", github: "thoughtbot/appraisal"
5
+ gem "appraisal"
6
6
  gem "bundler", "~> 2.0"
7
7
  gem "rake", ">= 12.3.3"
8
8
  gem "rspec", "~> 3.0"
@@ -14,5 +14,7 @@ gem "timecop"
14
14
  gem "webmock"
15
15
  gem "rails", "~> 6.1.0"
16
16
  gem "rspec-rails"
17
+ gem "mutex_m"
18
+ gem "drb"
17
19
 
18
20
  gemspec path: "../"
@@ -2,7 +2,7 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "appraisal", github: "thoughtbot/appraisal"
5
+ gem "appraisal"
6
6
  gem "bundler", "~> 2.0"
7
7
  gem "rake", ">= 12.3.3"
8
8
  gem "rspec", "~> 3.0"
@@ -2,7 +2,7 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "appraisal", github: "thoughtbot/appraisal"
5
+ gem "appraisal"
6
6
  gem "bundler", "~> 2.0"
7
7
  gem "rake", ">= 12.3.3"
8
8
  gem "rspec", "~> 3.0"
@@ -12,7 +12,7 @@ gem "rubocop-rspec", "~> 3.0.1"
12
12
  gem "semantic_logger"
13
13
  gem "timecop"
14
14
  gem "webmock"
15
- gem "rails", "~> 5.2.0"
15
+ gem "rails", "~> 7.1"
16
16
  gem "rspec-rails"
17
17
 
18
18
  gemspec path: "../"
@@ -2,7 +2,7 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "appraisal", github: "thoughtbot/appraisal"
5
+ gem "appraisal"
6
6
  gem "bundler", "~> 2.0"
7
7
  gem "rake", ">= 12.3.3"
8
8
  gem "rspec", "~> 3.0"
@@ -12,7 +12,7 @@ gem "rubocop-rspec", "~> 3.0.1"
12
12
  gem "semantic_logger"
13
13
  gem "timecop"
14
14
  gem "webmock"
15
- gem "rails", "~> 6.0.0"
15
+ gem "rails", "~> 8.0"
16
16
  gem "rspec-rails"
17
17
 
18
18
  gemspec path: "../"
@@ -0,0 +1,18 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "appraisal"
6
+ gem "bundler", "~> 2.0"
7
+ gem "rake", ">= 12.3.3"
8
+ gem "rspec", "~> 3.0"
9
+ gem "rspec-json_expectations", "~> 2.2"
10
+ gem "rubocop", "~> 1.64.1"
11
+ gem "rubocop-rspec", "~> 3.0.1"
12
+ gem "semantic_logger"
13
+ gem "timecop"
14
+ gem "webmock"
15
+ gem "rails", "~> 8.1"
16
+ gem "rspec-rails"
17
+
18
+ gemspec path: "../"
@@ -2,7 +2,7 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "appraisal", github: "thoughtbot/appraisal"
5
+ gem "appraisal"
6
6
  gem "bundler", "~> 2.0"
7
7
  gem "rake", ">= 12.3.3"
8
8
  gem "rspec", "~> 3.0"
@@ -2,7 +2,7 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "appraisal", github: "thoughtbot/appraisal"
5
+ gem "appraisal"
6
6
  gem "bundler", "~> 2.0"
7
7
  gem "rake", ">= 12.3.3"
8
8
  gem "rspec", "~> 3.0"
@@ -2,7 +2,7 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "appraisal", github: "thoughtbot/appraisal"
5
+ gem "appraisal"
6
6
  gem "bundler", "~> 2.0"
7
7
  gem "rake", ">= 12.3.3"
8
8
  gem "rspec", "~> 3.0"
@@ -2,7 +2,7 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "appraisal", github: "thoughtbot/appraisal"
5
+ gem "appraisal"
6
6
  gem "bundler", "~> 2.0"
7
7
  gem "rake", ">= 12.3.3"
8
8
  gem "rspec", "~> 3.0"
@@ -9,7 +9,7 @@ module Cloudtasker
9
9
  class RedisTask
10
10
  attr_reader :id, :http_request, :schedule_time, :retries, :queue, :dispatch_deadline
11
11
 
12
- RETRY_INTERVAL = 20 # seconds
12
+ RETRY_INTERVAL = Config::LOCAL_SERVER_RETRY_DELAY
13
13
 
14
14
  #
15
15
  # Return the Cloudtasker redis client
@@ -71,6 +71,9 @@ module Cloudtasker
71
71
  # failures due to the instance being unreachable.
72
72
  DEFAULT_MAX_RETRY_ATTEMPTS = 25
73
73
 
74
+ # How long to wait between retries in local server mode
75
+ LOCAL_SERVER_RETRY_DELAY = (ENV['CLOUDTASKER_LOCAL_RETRY_DELAY'] || 20).to_i # seconds
76
+
74
77
  PROCESSOR_HOST_MISSING = <<~DOC
75
78
  Missing host for processing.
76
79
  Please specify a processor hostname in form of `https://some-public-dns.example.com`'
@@ -132,45 +132,25 @@ module Cloudtasker
132
132
  list
133
133
  end
134
134
 
135
- if RUBY_VERSION < '3'
136
- #
137
- # Delegate all methods to the redis client.
138
- # Old delegation method.
139
- #
140
- # @param [String, Symbol] name The method to delegate.
141
- # @param [Array<any>] *args The list of method positional arguments.
142
- # @param [Hash<any>] *kwargs The list of method keyword arguments.
143
- # @param [Proc] &block Block passed to the method.
144
- #
145
- # @return [Any] The method return value
146
- #
147
- def method_missing(name, *args, &block)
148
- if Redis.method_defined?(name)
149
- client.with { |c| c.send(name, *args, &block) }
150
- else
151
- super
152
- end
153
- end
154
- else
155
- #
156
- # Delegate all methods to the redis client.
157
- # Ruby 3 delegation method style.
158
- #
159
- # @param [String, Symbol] name The method to delegate.
160
- # @param [Array<any>] *args The list of method positional arguments.
161
- # @param [Hash<any>] *kwargs The list of method keyword arguments.
162
- # @param [Proc] &block Block passed to the method.
163
- #
164
- # @return [Any] The method return value
165
- #
166
- def method_missing(name, *args, **kwargs, &block)
167
- if Redis.method_defined?(name)
168
- client.with { |c| c.send(name, *args, **kwargs, &block) }
169
- else
170
- super
171
- end
135
+ #
136
+ # Delegate all methods to the redis client.
137
+ # Ruby 3 delegation method style.
138
+ #
139
+ # @param [String, Symbol] name The method to delegate.
140
+ # @param [Array<any>] *args The list of method positional arguments.
141
+ # @param [Hash<any>] *kwargs The list of method keyword arguments.
142
+ # @param [Proc] &block Block passed to the method.
143
+ #
144
+ # @return [Any] The method return value
145
+ #
146
+ def method_missing(name, ...)
147
+ if Redis.method_defined?(name)
148
+ client.with { |c| c.send(name, ...) }
149
+ else
150
+ super
172
151
  end
173
152
  end
153
+
174
154
  #
175
155
  # Check if the class respond to a certain method.
176
156
  #
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cloudtasker
4
+ module UniqueJob
5
+ module Lock
6
+ # Conflict if any other job with the same args is scheduled or moved to execution
7
+ # while the first job is pending or executing. Unlocks only on successful completion
8
+ # or when a DeadWorkerError is raised.
9
+ class UntilCompleted < BaseLock
10
+ #
11
+ # Acquire a lock for the job and trigger a conflict
12
+ # if the lock could not be acquired.
13
+ #
14
+ def schedule(&block)
15
+ job.lock!
16
+ yield
17
+ rescue LockError
18
+ conflict_instance.on_schedule(&block)
19
+ end
20
+
21
+ #
22
+ # Acquire a lock for the job and trigger a conflict
23
+ # if the lock could not be acquired.
24
+ #
25
+ def execute(&block)
26
+ job.lock!
27
+ yield
28
+ # Unlock on successful completion
29
+ job.unlock!
30
+ rescue LockError
31
+ conflict_instance.on_execute(&block)
32
+ rescue Cloudtasker::DeadWorkerError
33
+ # Unlock when DeadWorkerError is raised
34
+ job.unlock!
35
+ raise
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -14,6 +14,7 @@ require_relative 'lock/no_op'
14
14
  require_relative 'lock/until_executed'
15
15
  require_relative 'lock/until_executing'
16
16
  require_relative 'lock/while_executing'
17
+ require_relative 'lock/until_completed'
17
18
 
18
19
  require_relative 'job'
19
20
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Cloudtasker
4
- VERSION = '0.14.rc1'
4
+ VERSION = '0.15.rc1'
5
5
  end
@@ -184,9 +184,9 @@ module Cloudtasker
184
184
  #
185
185
  # @return [Any] The method return value
186
186
  #
187
- def method_missing(name, *args, &block)
187
+ def method_missing(name, ...)
188
188
  if logger.respond_to?(name)
189
- logger.send(name, *args, &block)
189
+ logger.send(name, ...)
190
190
  else
191
191
  super
192
192
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cloudtasker
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.14.rc1
4
+ version: 0.15.rc1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Arnaud Lachaume
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-09-22 00:00:00.000000000 Z
11
+ date: 2025-10-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -117,7 +117,6 @@ extensions: []
117
117
  extra_rdoc_files: []
118
118
  files:
119
119
  - ".github/workflows/lint_rubocop.yml"
120
- - ".github/workflows/test_ruby_2.7.yml"
121
120
  - ".github/workflows/test_ruby_3.x.yml"
122
121
  - ".gitignore"
123
122
  - ".rspec"
@@ -149,10 +148,11 @@ files:
149
148
  - gemfiles/google_cloud_tasks_1.5.gemfile
150
149
  - gemfiles/google_cloud_tasks_2.0.gemfile
151
150
  - gemfiles/google_cloud_tasks_2.1.gemfile
152
- - gemfiles/rails_5.2.gemfile
153
- - gemfiles/rails_6.0.gemfile
154
151
  - gemfiles/rails_6.1.gemfile
155
152
  - gemfiles/rails_7.0.gemfile
153
+ - gemfiles/rails_7.1.gemfile
154
+ - gemfiles/rails_8.0.gemfile
155
+ - gemfiles/rails_8.1.gemfile
156
156
  - gemfiles/semantic_logger_3.4.gemfile
157
157
  - gemfiles/semantic_logger_4.6.gemfile
158
158
  - gemfiles/semantic_logger_4.7.0.gemfile
@@ -200,6 +200,7 @@ files:
200
200
  - lib/cloudtasker/unique_job/job.rb
201
201
  - lib/cloudtasker/unique_job/lock/base_lock.rb
202
202
  - lib/cloudtasker/unique_job/lock/no_op.rb
203
+ - lib/cloudtasker/unique_job/lock/until_completed.rb
203
204
  - lib/cloudtasker/unique_job/lock/until_executed.rb
204
205
  - lib/cloudtasker/unique_job/lock/until_executing.rb
205
206
  - lib/cloudtasker/unique_job/lock/while_executing.rb
@@ -229,14 +230,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
229
230
  requirements:
230
231
  - - ">="
231
232
  - !ruby/object:Gem::Version
232
- version: 2.7.0
233
+ version: '3.0'
233
234
  required_rubygems_version: !ruby/object:Gem::Requirement
234
235
  requirements:
235
236
  - - ">="
236
237
  - !ruby/object:Gem::Version
237
238
  version: '0'
238
239
  requirements: []
239
- rubygems_version: 3.5.4
240
+ rubygems_version: 3.5.22
240
241
  signing_key:
241
242
  specification_version: 4
242
243
  summary: Background jobs for Ruby using Google Cloud Tasks (beta)
@@ -1,38 +0,0 @@
1
- name: Ruby 2.7
2
-
3
- on: [push, pull_request]
4
-
5
- jobs:
6
- build:
7
- runs-on: ubuntu-latest
8
- strategy:
9
- matrix:
10
- ruby:
11
- - '2.7'
12
- appraisal:
13
- - 'google_cloud_tasks_1.0'
14
- - 'google_cloud_tasks_1.1'
15
- - 'google_cloud_tasks_1.2'
16
- - 'google_cloud_tasks_1.3'
17
- - 'google_cloud_tasks_1.4'
18
- - 'google_cloud_tasks_1.5'
19
- - 'google_cloud_tasks_2.0'
20
- - 'google_cloud_tasks_2.1'
21
- - 'rails_5.2'
22
- - 'rails_6.0'
23
- - 'rails_6.1'
24
- - 'rails_7.0'
25
- - 'semantic_logger_3.4'
26
- - 'semantic_logger_4.6'
27
- - 'semantic_logger_4.7.0'
28
- - 'semantic_logger_4.7.2'
29
- env:
30
- BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/${{ matrix.appraisal }}.gemfile
31
- steps:
32
- - uses: actions/checkout@v2
33
- - uses: zhulik/redis-action@1.1.0
34
- - uses: ruby/setup-ruby@v1
35
- with:
36
- ruby-version: ${{ matrix.ruby }}
37
- bundler-cache: true
38
- - run: bundle exec rspec