job-iteration 1.1.14 → 1.3.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0da9051861fb27696febf2d0191b4d2f459ece64b34cf0231398984c0f36ef3d
4
- data.tar.gz: 5fc84c784cdc1a0558a96891a236ac3cea315068e32a6730d633427ef584204b
3
+ metadata.gz: 63917aaf8f527e78cd47f62111dbda8473c90907eeaa8827ed23aaba77f494a0
4
+ data.tar.gz: 4fa0d5ce8eb5c1e7cdc2712daae6ddf689cfd1ae7cc3f840abe7b45628ee7735
5
5
  SHA512:
6
- metadata.gz: f30dbb37ada6149fdd877c4b92fff5cf91e078202408230cbdd19593a341c25f4311b4d12dcdceb539a1553e30e0386930a0b49e4c082a4261a0e788c82125b6
7
- data.tar.gz: 88e9846617f7c45959b45328e46b1465b5acb43caf1a5e0b9fa4a5d0848d00d842392ef4383e372f68c7ccb7aa3f0954549a64846cd3e15c712d9a0d7a60d781
6
+ metadata.gz: ae0a1330b24adfa55c045cd079806c350e151ee7c12555c9397143919c8f77fdc3023cb23330fc529af3771da31379991830513ce0459e1ff5ff21614e0518d0
7
+ data.tar.gz: 0733a5377c27ff39acd118f3e719e52bd82a7a63d7f03508b61c6ced900baef9bc0e05bdc5b43d87d6e36f05339bf9a84c1ffd5c1ce70c06cb172ee3c3905240
@@ -0,0 +1,16 @@
1
+ version: 2
2
+
3
+ updates:
4
+
5
+ - package-ecosystem: bundler
6
+ directory: '/'
7
+ versioning-strategy: increase
8
+ open-pull-requests-limit: 100
9
+ insecure-external-code-execution: allow
10
+ schedule:
11
+ interval: weekly
12
+
13
+ - package-ecosystem: github-actions
14
+ directory: '/'
15
+ schedule:
16
+ interval: daily
@@ -13,11 +13,9 @@ jobs:
13
13
  - 6379:6379
14
14
  strategy:
15
15
  matrix:
16
- ruby: [2.5, 2.6, 2.7, 3.0]
16
+ ruby: [2.6, 2.7, 3.0]
17
17
  gemfile: [rails_5_2, rails_6_0, rails_edge]
18
18
  exclude:
19
- - ruby: 2.5
20
- gemfile: rails_edge
21
19
  - ruby: 2.6
22
20
  gemfile: rails_edge
23
21
  - ruby: 3.0
data/.gitignore CHANGED
@@ -9,4 +9,3 @@
9
9
  .ruby-version
10
10
  .rubocop-http---shopify-github-io-ruby-style-guide-rubocop-yml
11
11
  gemfiles/*.lock
12
- Gemfile.lock
data/.rubocop.yml CHANGED
@@ -2,7 +2,7 @@ inherit_gem:
2
2
  rubocop-shopify: rubocop.yml
3
3
 
4
4
  AllCops:
5
- TargetRubyVersion: 2.4.4
5
+ TargetRubyVersion: 2.6.5
6
6
  Exclude:
7
7
  - 'vendor/bundle/**/*'
8
8
  Lint/SuppressedException:
data/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  ### Master (unreleased)
2
2
 
3
+ ## v1.3.2 (Nov 12, 2021)
4
+ - [148](https://github.com/Shopify/job-iteration/pull/148) - Revert "Do not evaluate enumerator when throttled", due to backwards incompatibility.
5
+
6
+ ## v1.3.1 (Nov 11, 2021)
7
+ - [87](https://github.com/Shopify/job-iteration/pull/87) - Do not evaluate enumerator when throttled (REVERTED)
8
+
9
+
10
+ ## v1.3.0 (Oct 7, 2021)
11
+ - [133](https://github.com/Shopify/job-iteration/pull/133) - Moves attributes out of JobIteration::Iteration included block
12
+
13
+
14
+ ## v1.2.0 (Sept 21, 2021)
15
+ - [107](https://github.com/Shopify/job-iteration/pull/107) - Remove broken links from README
16
+ - [108](https://github.com/Shopify/job-iteration/pull/108) - Drop support for ruby 2.5
17
+ - [110](https://github.com/Shopify/job-iteration/pull/110) - Update rubocop TargetRubyVersion
18
+
3
19
  ## v1.1.14 (May 28, 2021)
4
20
 
5
21
  #### Bug fix
data/Gemfile CHANGED
@@ -21,7 +21,6 @@ gem "pry"
21
21
  gem "mocha"
22
22
 
23
23
  gem "rubocop-shopify", require: false
24
- gem "rubocop", "<= 1.12.1", require: false # 1.13.0 drops Ruby 2.4 support
25
24
  gem "yard"
26
25
  gem "rake"
27
26
 
data/Gemfile.lock ADDED
@@ -0,0 +1,123 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ job-iteration (1.3.2)
5
+ activejob (>= 5.2)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ activejob (6.1.4.1)
11
+ activesupport (= 6.1.4.1)
12
+ globalid (>= 0.3.6)
13
+ activemodel (6.1.4.1)
14
+ activesupport (= 6.1.4.1)
15
+ activerecord (6.1.4.1)
16
+ activemodel (= 6.1.4.1)
17
+ activesupport (= 6.1.4.1)
18
+ activesupport (6.1.4.1)
19
+ concurrent-ruby (~> 1.0, >= 1.0.2)
20
+ i18n (>= 1.6, < 2)
21
+ minitest (>= 5.1)
22
+ tzinfo (~> 2.0)
23
+ zeitwerk (~> 2.3)
24
+ ast (2.4.2)
25
+ coderay (1.1.3)
26
+ concurrent-ruby (1.1.9)
27
+ connection_pool (2.2.5)
28
+ database_cleaner (2.0.1)
29
+ database_cleaner-active_record (~> 2.0.0)
30
+ database_cleaner-active_record (2.0.1)
31
+ activerecord (>= 5.a)
32
+ database_cleaner-core (~> 2.0.0)
33
+ database_cleaner-core (2.0.1)
34
+ globalid (0.5.2)
35
+ activesupport (>= 5.0)
36
+ i18n (1.8.10)
37
+ concurrent-ruby (~> 1.0)
38
+ method_source (1.0.0)
39
+ minitest (5.14.4)
40
+ mocha (1.13.0)
41
+ mono_logger (1.1.1)
42
+ multi_json (1.15.0)
43
+ mustermann (1.1.1)
44
+ ruby2_keywords (~> 0.0.1)
45
+ mysql2 (0.5.3)
46
+ parallel (1.21.0)
47
+ parser (3.0.2.0)
48
+ ast (~> 2.4.1)
49
+ pry (0.14.1)
50
+ coderay (~> 1.1)
51
+ method_source (~> 1.0)
52
+ rack (2.2.3)
53
+ rack-protection (2.1.0)
54
+ rack
55
+ rainbow (3.0.0)
56
+ rake (13.0.6)
57
+ redis (4.5.1)
58
+ redis-namespace (1.8.1)
59
+ redis (>= 3.0.4)
60
+ regexp_parser (2.1.1)
61
+ resque (2.1.0)
62
+ mono_logger (~> 1.0)
63
+ multi_json (~> 1.0)
64
+ redis-namespace (~> 1.6)
65
+ sinatra (>= 0.9.2)
66
+ vegas (~> 0.1.2)
67
+ rexml (3.2.5)
68
+ rubocop (1.22.1)
69
+ parallel (~> 1.10)
70
+ parser (>= 3.0.0.0)
71
+ rainbow (>= 2.2.2, < 4.0)
72
+ regexp_parser (>= 1.8, < 3.0)
73
+ rexml
74
+ rubocop-ast (>= 1.12.0, < 2.0)
75
+ ruby-progressbar (~> 1.7)
76
+ unicode-display_width (>= 1.4.0, < 3.0)
77
+ rubocop-ast (1.12.0)
78
+ parser (>= 3.0.1.1)
79
+ rubocop-shopify (2.3.0)
80
+ rubocop (~> 1.22)
81
+ ruby-progressbar (1.11.0)
82
+ ruby2_keywords (0.0.5)
83
+ sidekiq (6.2.2)
84
+ connection_pool (>= 2.2.2)
85
+ rack (~> 2.0)
86
+ redis (>= 4.2.0)
87
+ sinatra (2.1.0)
88
+ mustermann (~> 1.0)
89
+ rack (~> 2.2)
90
+ rack-protection (= 2.1.0)
91
+ tilt (~> 2.0)
92
+ sorbet-runtime (0.5.9219)
93
+ tilt (2.0.10)
94
+ tzinfo (2.0.4)
95
+ concurrent-ruby (~> 1.0)
96
+ unicode-display_width (2.1.0)
97
+ vegas (0.1.11)
98
+ rack (>= 1.0.0)
99
+ yard (0.9.26)
100
+ zeitwerk (2.4.2)
101
+
102
+ PLATFORMS
103
+ ruby
104
+
105
+ DEPENDENCIES
106
+ activerecord
107
+ database_cleaner
108
+ globalid
109
+ i18n
110
+ job-iteration!
111
+ mocha
112
+ mysql2 (~> 0.5)
113
+ pry
114
+ rake
115
+ redis
116
+ resque
117
+ rubocop-shopify
118
+ sidekiq
119
+ sorbet-runtime
120
+ yard
121
+
122
+ BUNDLED WITH
123
+ 2.2.22
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Job Iteration API
2
2
 
3
- [![Build Status](https://travis-ci.com/Shopify/job-iteration.svg?branch=master)](https://travis-ci.com/Shopify/job-iteration)
3
+ [![CI](https://github.com/Shopify/job-iteration/actions/workflows/ci.yml/badge.svg)](https://github.com/Shopify/job-iteration/actions/workflows/ci.yml)
4
4
 
5
5
  Meet Iteration, an extension for [ActiveJob](https://github.com/rails/rails/tree/master/activejob) that makes your jobs interruptible and resumable, saving all progress that the job has made (aka checkpoint for jobs).
6
6
 
@@ -171,7 +171,7 @@ There a few configuration assumptions that are required for Iteration to work wi
171
171
 
172
172
  **Why is it important that `each_iteration` takes less than 30 seconds?** When the job worker is scheduled for restart or shutdown, it gets a notice to finish remaining unit of work. To guarantee that no progress is lost we need to make sure that `each_iteration` completes within a reasonable amount of time.
173
173
 
174
- **What do I do if each iteration takes a long time, because it's doing nested operations?** If your `each_iteration` is complex, we recommend enqueuing another job, which will run your nested business logic. We may expose primitives in the future to do this more effectively, but this is not terribly common today. We recommend to read https://goo.gl/UobaaU to learn more about nested operations.
174
+ **What do I do if each iteration takes a long time, because it's doing nested operations?** If your `each_iteration` is complex, we recommend enqueuing another job, which will run your nested business logic. We may expose primitives in the future to do this more effectively, but this is not terribly common today.
175
175
 
176
176
  **Why do I use have to use this ugly helper in `build_enumerator`? Why can't you automatically infer it?** This is how the first version of the API worked. We checked the type of object returned by `build_enumerable`, and whether it was ActiveRecord Relation or an Array, we used the matching adapter. This caused opaque type branching in Iteration internals and it didn’t allow developers to craft their own Enumerators and control the cursor value. We made a decision to _always_ return Enumerator instance from `build_enumerator`. Now we provide explicit helpers to convert ActiveRecord Relation or an Array to Enumerator, and for more complex iteration flows developers can build their own `Enumerator` objects.
177
177
 
data/bin/test ADDED
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ def main
5
+ begin
6
+ command = create_command
7
+ rescue ArgumentError => e
8
+ abort(e.message)
9
+ end
10
+ puts "Running #{command.join(" ")}"
11
+ system(*command)
12
+ end
13
+
14
+ def create_command
15
+ case ARGV.length
16
+ when 0
17
+ ["bundle", "exec", "rake", "test"]
18
+ when 1
19
+ filename = ARGV[0]
20
+ ["bundle", "exec", "rake", "test", "TEST=#{filename}"]
21
+ when 2
22
+ filename = ARGV[0]
23
+ test_name = ARGV[1]
24
+ test_name_with_underscores = test_name.tr(" ", "_")
25
+ test_name_pattern = "/#{Regexp.escape(test_name_with_underscores)}/"
26
+ ["bundle", "exec", "rake", "test", "TEST=#{filename}", "TESTOPTS=\"--name=#{test_name_pattern} -v\""]
27
+ else
28
+ raise ArgumentError, "Too many arguments. Did you forget to put the test name in quotes?"
29
+ end
30
+ end
31
+
32
+ main
data/dev.yml CHANGED
@@ -13,7 +13,43 @@ up:
13
13
  - custom:
14
14
  name: Create Job Iteration database
15
15
  meet: mysql -uroot -h job-iteration.railgun -e "CREATE DATABASE job_iteration_test"
16
- met?: mysql -uroot -h job-iteration.railgun job_iteration_test -e "SELECT 1"
16
+ met?: mysql -uroot -h job-iteration.railgun job_iteration_test -e "SELECT 1" &> /dev/null
17
17
 
18
18
  commands:
19
- test: bundle exec rake
19
+ test:
20
+ run: bin/test "$@"
21
+ syntax:
22
+ optional: filename testnamepattern
23
+ aliases: [t]
24
+ desc: run tests
25
+ long_desc: |
26
+ {{bold:Default}}
27
+ =======
28
+ Run the entire test suite.
29
+
30
+ Examples:
31
+ {{command:dev test}}
32
+ {{command:dev t}}
33
+
34
+ {{bold:Run all tests in a file}}
35
+ ========================
36
+ Include the file path.
37
+
38
+ Example:
39
+ {{command:dev test test/unit/iteration_test.rb}}
40
+
41
+ {{bold:Run a single test in a given file}}
42
+ ========================
43
+ Include the file path and the name of the test you'd like to run.
44
+
45
+ Example:
46
+ {{command:dev test test/unit/iteration_test.rb test_that_it_has_a_version_number}}
47
+
48
+ {{bold:Run all tests in a given file whose name contains a string}}
49
+ ========================
50
+ Include the file path and the string that the test names should contain.
51
+
52
+ Example:
53
+ {{command:dev test test/unit/iteration_test.rb version_number}}
54
+ style:
55
+ run: bundle exec rubocop -a
@@ -5,9 +5,10 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
5
  require "job-iteration/version"
6
6
 
7
7
  Gem::Specification.new do |spec|
8
+ spec.required_ruby_version = ">= 2.6"
8
9
  spec.name = "job-iteration"
9
10
  spec.version = JobIteration::VERSION
10
- spec.authors = %w(Shopify)
11
+ spec.authors = ["Shopify"]
11
12
  spec.email = ["ops-accounts+shipit@shopify.com"]
12
13
 
13
14
  spec.summary = "Makes your background jobs interruptible and resumable."
@@ -20,7 +21,7 @@ Gem::Specification.new do |spec|
20
21
  end
21
22
  spec.bindir = "exe"
22
23
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
- spec.require_paths = %w(lib)
24
+ spec.require_paths = ["lib"]
24
25
 
25
26
  spec.metadata["changelog_uri"] = "https://github.com/Shopify/job-iteration/blob/master/CHANGELOG.md"
26
27
  spec.metadata["allowed_push_host"] = "https://rubygems.org"
@@ -13,7 +13,7 @@ module JobIteration
13
13
  def initialize
14
14
  super(
15
15
  "The relation cannot use ORDER BY or LIMIT due to the way how iteration with a cursor is designed. " \
16
- "You can use other ways to limit the number of rows, e.g. a WHERE condition on the primary key column."
16
+ "You can use other ways to limit the number of rows, e.g. a WHERE condition on the primary key column."
17
17
  )
18
18
  end
19
19
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require_relative "./active_record_cursor"
3
4
  module JobIteration
4
5
  # Builds Enumerator based on ActiveRecord Relation. Supports enumerating on rows and batches.
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require_relative "./active_record_batch_enumerator"
3
4
  require_relative "./active_record_enumerator"
4
5
  require_relative "./csv_enumerator"
@@ -6,6 +6,13 @@ module JobIteration
6
6
  module Iteration
7
7
  extend ActiveSupport::Concern
8
8
 
9
+ attr_accessor(
10
+ :cursor_position,
11
+ :start_time,
12
+ :times_interrupted,
13
+ :total_time,
14
+ )
15
+
9
16
  class CursorError < ArgumentError
10
17
  attr_reader :cursor
11
18
 
@@ -29,13 +36,6 @@ module JobIteration
29
36
  end
30
37
 
31
38
  included do |_base|
32
- attr_accessor(
33
- :cursor_position,
34
- :start_time,
35
- :times_interrupted,
36
- :total_time,
37
- )
38
-
39
39
  define_callbacks :start
40
40
  define_callbacks :shutdown
41
41
  define_callbacks :complete
@@ -159,7 +159,7 @@ module JobIteration
159
159
 
160
160
  logger.info(
161
161
  "[JobIteration::Iteration] Enumerator found nothing to iterate! " \
162
- "times_interrupted=#{times_interrupted} cursor_position=#{cursor_position}"
162
+ "times_interrupted=#{times_interrupted} cursor_position=#{cursor_position}"
163
163
  ) unless found_record
164
164
 
165
165
  adjust_total_time
@@ -167,10 +167,8 @@ module JobIteration
167
167
  true
168
168
  end
169
169
 
170
- def record_unit_of_work
171
- ActiveSupport::Notifications.instrument("each_iteration.iteration", iteration_instrumentation_tags) do
172
- yield
173
- end
170
+ def record_unit_of_work(&block)
171
+ ActiveSupport::Notifications.instrument("each_iteration.iteration", iteration_instrumentation_tags, &block)
174
172
  end
175
173
 
176
174
  def reenqueue_iteration_job
@@ -210,7 +208,7 @@ module JobIteration
210
208
 
211
209
  raise CursorError.new(
212
210
  "Cursor must be composed of objects capable of built-in (de)serialization: " \
213
- "Strings, Integers, Floats, Arrays, Hashes, true, false, or nil.",
211
+ "Strings, Integers, Floats, Arrays, Hashes, true, false, or nil.",
214
212
  cursor: cursor,
215
213
  )
216
214
  end
@@ -227,7 +225,7 @@ module JobIteration
227
225
  parameters = method_parameters(:build_enumerator)
228
226
  unless valid_cursor_parameter?(parameters)
229
227
  raise ArgumentError, "Iteration job (#{self.class}) #build_enumerator " \
230
- "expects the keyword argument `cursor`"
228
+ "expects the keyword argument `cursor`"
231
229
  end
232
230
  else
233
231
  raise ArgumentError, "Iteration job (#{self.class}) must implement #build_enumerator " \
@@ -1,5 +1,6 @@
1
1
  # typed: true
2
2
  # frozen_string_literal: true
3
+
3
4
  module JobIteration
4
5
  # ThrottleEnumerator allows you to throttle iterations
5
6
  # based on external signal (e.g. database health).
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JobIteration
4
- VERSION = "1.1.14"
4
+ VERSION = "1.3.2"
5
5
  end
data/lib/job-iteration.rb CHANGED
@@ -22,6 +22,7 @@ module JobIteration
22
22
 
23
23
  # Used internally for hooking into job processing frameworks like Sidekiq and Resque.
24
24
  attr_accessor :interruption_adapter
25
+
25
26
  self.interruption_adapter = -> { false }
26
27
 
27
28
  # Set if you want to use your own enumerator builder instead of default EnumeratorBuilder.
@@ -33,21 +34,20 @@ module JobIteration
33
34
  #
34
35
  # JobIteration.enumerator_builder = MyOwnBuilder
35
36
  attr_accessor :enumerator_builder
37
+
36
38
  self.enumerator_builder = JobIteration::EnumeratorBuilder
37
39
 
38
40
  def load_integrations
39
41
  loaded = nil
40
42
  INTEGRATIONS.each do |integration|
41
- begin
42
- load_integration(integration)
43
- if loaded
44
- raise IntegrationLoadError,
45
- "#{loaded} integration has already been loaded, but #{integration} is also available. " \
43
+ load_integration(integration)
44
+ if loaded
45
+ raise IntegrationLoadError,
46
+ "#{loaded} integration has already been loaded, but #{integration} is also available. " \
46
47
  "Iteration will only work with one integration."
47
- end
48
- loaded = integration
49
- rescue LoadError
50
48
  end
49
+ loaded = integration
50
+ rescue LoadError
51
51
  end
52
52
  end
53
53
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: job-iteration
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.14
4
+ version: 1.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shopify
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-05-28 00:00:00.000000000 Z
11
+ date: 2021-11-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -45,6 +45,7 @@ executables: []
45
45
  extensions: []
46
46
  extra_rdoc_files: []
47
47
  files:
48
+ - ".github/dependabot.yml"
48
49
  - ".github/workflows/ci.yml"
49
50
  - ".gitignore"
50
51
  - ".rubocop.yml"
@@ -52,10 +53,12 @@ files:
52
53
  - CHANGELOG.md
53
54
  - CODE_OF_CONDUCT.md
54
55
  - Gemfile
56
+ - Gemfile.lock
55
57
  - LICENSE.txt
56
58
  - README.md
57
59
  - Rakefile
58
60
  - bin/setup
61
+ - bin/test
59
62
  - dev.yml
60
63
  - gemfiles/rails_5_2.gemfile
61
64
  - gemfiles/rails_6_0.gemfile
@@ -92,14 +95,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
92
95
  requirements:
93
96
  - - ">="
94
97
  - !ruby/object:Gem::Version
95
- version: '0'
98
+ version: '2.6'
96
99
  required_rubygems_version: !ruby/object:Gem::Requirement
97
100
  requirements:
98
101
  - - ">="
99
102
  - !ruby/object:Gem::Version
100
103
  version: '0'
101
104
  requirements: []
102
- rubygems_version: 3.2.17
105
+ rubygems_version: 3.2.20
103
106
  signing_key:
104
107
  specification_version: 4
105
108
  summary: Makes your background jobs interruptible and resumable.