job-iteration 1.3.3 → 1.3.6

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: d715ff6962fc9ff1081d5db93be694b0f0ed3e9fc2935b0d9f252971ea8a40d0
4
- data.tar.gz: 60571b6b588816ad637a0eaaee238adb423c230102d4a73745e0fcc3b9e41b17
3
+ metadata.gz: 2f1dea81ba8267470285bbd858ace091200060cc0695dfa8a986293139806a4d
4
+ data.tar.gz: 23affca4d929d7bfafbf9989eb152a65fe3831d380b79b877a31469854768871
5
5
  SHA512:
6
- metadata.gz: 587952d2f628c6d43c982ee8a4fe85d409d4d39044b34aa436bf97a2dd2679d92bfcb098bb2b35380b90297359dbefddc805a188501bd464665e8199a21f673e
7
- data.tar.gz: dc172c2fa8172ee47d2af5ed7d47513b8e1131121f6b4f8deebf534a1c7f7e38e8b01f421bf3cc1e419aec9d1c5469020e690757be1e87c35e6d6dc0db8a437c
6
+ metadata.gz: 163fd0b5034c8e0f06fc6f3e8c39f31a8b02286a7d51cc7602ead690cf8696299883c334bf5c57414dc7ba07f48faec157fdc7c602e61cbc2a42b398e23e39aa
7
+ data.tar.gz: 74634403a068cbccdce59b9ca526cbb97a83dbef2f34dad535385e3d4984d1967dd43748ee32ac0c80fa520ce2a137c33be15e32c2ed0a05e3c7957f85173445
@@ -1,6 +1,6 @@
1
1
  name: CI
2
2
 
3
- on: [push]
3
+ on: [push, pull_request]
4
4
 
5
5
  jobs:
6
6
  build:
@@ -13,18 +13,27 @@ jobs:
13
13
  - 6379:6379
14
14
  strategy:
15
15
  matrix:
16
- ruby: [2.6, 2.7, 3.0]
17
- gemfile: [rails_5_2, rails_6_0, rails_edge]
16
+ ruby: ["2.6", "2.7", "3.0", "3.1"]
17
+ gemfile: [rails_5_2, rails_6_0, rails_6_1, rails_7_0, rails_edge]
18
18
  exclude:
19
- - ruby: 2.6
19
+ - ruby: "2.6"
20
+ gemfile: rails_7_0
21
+ - ruby: "2.6"
20
22
  gemfile: rails_edge
21
- - ruby: 3.0
23
+ - ruby: "3.0"
22
24
  gemfile: rails_5_2
25
+ - ruby: "3.1"
26
+ gemfile: rails_5_2
27
+ - ruby: "3.1"
28
+ gemfile: rails_6_0
29
+ include:
30
+ - ruby: head
31
+ gemfile: rails_edge
23
32
  env:
24
33
  BUNDLE_GEMFILE: gemfiles/${{ matrix.gemfile }}.gemfile
25
34
  steps:
26
35
  - name: Check out code
27
- uses: actions/checkout@v2
36
+ uses: actions/checkout@v3
28
37
  - name: Set up Ruby ${{ matrix.ruby }}
29
38
  uses: ruby/setup-ruby@v1
30
39
  with:
data/CHANGELOG.md CHANGED
@@ -1,6 +1,18 @@
1
1
  ### Master (unreleased)
2
2
 
3
- ## v.1.3.3 (Nov 17, 2021)
3
+ ## v1.3.6 (Mar 9, 2022)
4
+
5
+ - [190](https://github.com/Shopify/job-iteration/pull/190) - Fix updating `times_interrupted` and `total_time` when job is throttled
6
+
7
+ ## v1.3.5 (Feb 10, 2022)
8
+
9
+ - [183](https://github.com/Shopify/job-iteration/pull/183) - Add `JobIteration::EnumeratorBuilder#build_csv_enumerator`
10
+
11
+ ## v1.3.4 (Jan 18, 2022)
12
+
13
+ - [174](https://github.com/Shopify/job-iteration/pull/174) - Fix Ruby 3.2 compatibility
14
+
15
+ ## v1.3.3 (Nov 17, 2021)
4
16
  - [153](https://github.com/Shopify/job-iteration/pull/153) - Re-enqueue jobs only after shutdown hooks have run
5
17
 
6
18
  ## v1.3.2 (Nov 12, 2021)
data/Gemfile CHANGED
@@ -11,7 +11,7 @@ gemspec
11
11
  gem "sidekiq"
12
12
  gem "resque"
13
13
 
14
- gem "mysql2", "~> 0.5"
14
+ gem "mysql2", github: "brianmario/mysql2"
15
15
  gem "globalid"
16
16
  gem "i18n"
17
17
  gem "redis"
data/Gemfile.lock CHANGED
@@ -1,21 +1,27 @@
1
+ GIT
2
+ remote: https://github.com/brianmario/mysql2
3
+ revision: 8193dc412c6a266045b9d13a9da36c16750939a4
4
+ specs:
5
+ mysql2 (0.5.3)
6
+
1
7
  PATH
2
8
  remote: .
3
9
  specs:
4
- job-iteration (1.3.3)
10
+ job-iteration (1.3.6)
5
11
  activejob (>= 5.2)
6
12
 
7
13
  GEM
8
14
  remote: https://rubygems.org/
9
15
  specs:
10
- activejob (6.1.4.1)
11
- activesupport (= 6.1.4.1)
16
+ activejob (6.1.4.6)
17
+ activesupport (= 6.1.4.6)
12
18
  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
+ activemodel (6.1.4.6)
20
+ activesupport (= 6.1.4.6)
21
+ activerecord (6.1.4.6)
22
+ activemodel (= 6.1.4.6)
23
+ activesupport (= 6.1.4.6)
24
+ activesupport (6.1.4.6)
19
25
  concurrent-ruby (~> 1.0, >= 1.0.2)
20
26
  i18n (>= 1.6, < 2)
21
27
  minitest (>= 5.1)
@@ -31,20 +37,19 @@ GEM
31
37
  activerecord (>= 5.a)
32
38
  database_cleaner-core (~> 2.0.0)
33
39
  database_cleaner-core (2.0.1)
34
- globalid (0.5.2)
40
+ globalid (1.0.0)
35
41
  activesupport (>= 5.0)
36
- i18n (1.8.11)
42
+ i18n (1.10.0)
37
43
  concurrent-ruby (~> 1.0)
38
44
  method_source (1.0.0)
39
- minitest (5.14.4)
45
+ minitest (5.15.0)
40
46
  mocha (1.13.0)
41
47
  mono_logger (1.1.1)
42
48
  multi_json (1.15.0)
43
49
  mustermann (1.1.1)
44
50
  ruby2_keywords (~> 0.0.1)
45
- mysql2 (0.5.3)
46
51
  parallel (1.21.0)
47
- parser (3.0.2.0)
52
+ parser (3.1.1.0)
48
53
  ast (~> 2.4.1)
49
54
  pry (0.14.1)
50
55
  coderay (~> 1.1)
@@ -52,12 +57,12 @@ GEM
52
57
  rack (2.2.3)
53
58
  rack-protection (2.1.0)
54
59
  rack
55
- rainbow (3.0.0)
60
+ rainbow (3.1.1)
56
61
  rake (13.0.6)
57
- redis (4.5.1)
62
+ redis (4.6.0)
58
63
  redis-namespace (1.8.1)
59
64
  redis (>= 3.0.4)
60
- regexp_parser (2.1.1)
65
+ regexp_parser (2.2.1)
61
66
  resque (2.2.0)
62
67
  mono_logger (~> 1.0)
63
68
  multi_json (~> 1.0)
@@ -65,22 +70,22 @@ GEM
65
70
  sinatra (>= 0.9.2)
66
71
  vegas (~> 0.1.2)
67
72
  rexml (3.2.5)
68
- rubocop (1.22.1)
73
+ rubocop (1.25.1)
69
74
  parallel (~> 1.10)
70
- parser (>= 3.0.0.0)
75
+ parser (>= 3.1.0.0)
71
76
  rainbow (>= 2.2.2, < 4.0)
72
77
  regexp_parser (>= 1.8, < 3.0)
73
78
  rexml
74
- rubocop-ast (>= 1.12.0, < 2.0)
79
+ rubocop-ast (>= 1.15.1, < 2.0)
75
80
  ruby-progressbar (~> 1.7)
76
81
  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)
82
+ rubocop-ast (1.16.0)
83
+ parser (>= 3.1.1.0)
84
+ rubocop-shopify (2.5.0)
85
+ rubocop (~> 1.25)
81
86
  ruby-progressbar (1.11.0)
82
87
  ruby2_keywords (0.0.5)
83
- sidekiq (6.3.1)
88
+ sidekiq (6.4.1)
84
89
  connection_pool (>= 2.2.2)
85
90
  rack (~> 2.0)
86
91
  redis (>= 4.2.0)
@@ -89,15 +94,17 @@ GEM
89
94
  rack (~> 2.2)
90
95
  rack-protection (= 2.1.0)
91
96
  tilt (~> 2.0)
92
- sorbet-runtime (0.5.9219)
97
+ sorbet-runtime (0.5.9724)
93
98
  tilt (2.0.10)
94
99
  tzinfo (2.0.4)
95
100
  concurrent-ruby (~> 1.0)
96
101
  unicode-display_width (2.1.0)
97
102
  vegas (0.1.11)
98
103
  rack (>= 1.0.0)
99
- yard (0.9.26)
100
- zeitwerk (2.4.2)
104
+ webrick (1.7.0)
105
+ yard (0.9.27)
106
+ webrick (~> 1.7.0)
107
+ zeitwerk (2.5.4)
101
108
 
102
109
  PLATFORMS
103
110
  ruby
@@ -109,7 +116,7 @@ DEPENDENCIES
109
116
  i18n
110
117
  job-iteration!
111
118
  mocha
112
- mysql2 (~> 0.5)
119
+ mysql2!
113
120
  pry
114
121
  rake
115
122
  redis
@@ -120,4 +127,4 @@ DEPENDENCIES
120
127
  yard
121
128
 
122
129
  BUNDLED WITH
123
- 2.2.22
130
+ 2.3.5
data/LICENSE.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2018 Shopify
3
+ Copyright (c) 2018-present Shopify
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
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
- 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).
5
+ Meet Iteration, an extension for [ActiveJob](https://github.com/rails/rails/tree/main/activejob) that makes your jobs interruptible and resumable, saving all progress that the job has made (aka checkpoint for jobs).
6
6
 
7
7
  ## Background
8
8
 
@@ -123,7 +123,7 @@ class CsvJob < ApplicationJob
123
123
 
124
124
  def build_enumerator(import_id, cursor:)
125
125
  import = Import.find(import_id)
126
- JobIteration::CsvEnumerator.new(import.csv).rows(cursor: cursor)
126
+ enumerator_builder.csv(import.csv, cursor: cursor)
127
127
  end
128
128
 
129
129
  def each_iteration(csv_row)
@@ -223,4 +223,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
223
223
 
224
224
  ## Code of Conduct
225
225
 
226
- Everyone interacting in the Job::Iteration project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/Shopify/job-iteration/blob/master/CODE_OF_CONDUCT.md).
226
+ Everyone interacting in the Job::Iteration project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/Shopify/job-iteration/blob/main/CODE_OF_CONDUCT.md).
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ eval_gemfile "../Gemfile"
4
+
5
+ if Gem::Version.new(RUBY_VERSION.dup) >= Gem::Version.new("3.1")
6
+ gem "net-imap", require: false
7
+ gem "net-pop", require: false
8
+ gem "net-smtp", require: false
9
+ end
10
+
11
+ gem "activejob", "~> 6.1.0"
12
+ gem "activerecord", "~> 6.1.0"
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ eval_gemfile "../Gemfile"
4
+
5
+ gem "activejob", "~> 7.0.0"
6
+ gem "activerecord", "~> 7.0.0"
@@ -41,6 +41,14 @@ ActiveSupport::Notifications.subscribe('interrupted.iteration') do |_, _, _, _,
41
41
  tags: { job_class: tags[:job_class]&.underscore }
42
42
  )
43
43
  end
44
+
45
+ # If you're using ThrottleEnumerator
46
+ ActiveSupport::Notifications.subscribe('throttled.iteration') do |_, _, _, _, tags|
47
+ StatsD.increment(
48
+ "iteration.throttled",
49
+ tags: { job_class: tags[:job_class]&.underscore }
50
+ )
51
+ end
44
52
  ```
45
53
 
46
54
  ## Max iteration time
@@ -23,7 +23,7 @@ Gem::Specification.new do |spec|
23
23
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
24
24
  spec.require_paths = ["lib"]
25
25
 
26
- spec.metadata["changelog_uri"] = "https://github.com/Shopify/job-iteration/blob/master/CHANGELOG.md"
26
+ spec.metadata["changelog_uri"] = "https://github.com/Shopify/job-iteration/blob/main/CHANGELOG.md"
27
27
  spec.metadata["allowed_push_host"] = "https://rubygems.org"
28
28
 
29
29
  spec.add_development_dependency("activerecord")
@@ -34,6 +34,7 @@ module JobIteration
34
34
 
35
35
  def each
36
36
  return to_enum { size } unless block_given?
37
+
37
38
  while (relation = next_batch)
38
39
  yield relation, cursor_value
39
40
  end
@@ -65,7 +66,8 @@ module JobIteration
65
66
  @cursor = Array.wrap(cursor)
66
67
 
67
68
  # Yields relations by selecting the primary keys of records in the batch.
68
- # Post.where(published: nil) results in an enumerator of relations like: Post.where(ids: batch_of_ids)
69
+ # Post.where(published: nil) results in an enumerator of relations like:
70
+ # Post.where(published: nil, ids: batch_of_ids)
69
71
  @base_relation.where(@primary_key => ids)
70
72
  end
71
73
 
@@ -85,6 +87,7 @@ module JobIteration
85
87
 
86
88
  def cursor_value
87
89
  return @cursor.first if @cursor.size == 1
90
+
88
91
  @cursor
89
92
  end
90
93
 
@@ -45,6 +45,7 @@ module JobIteration
45
45
 
46
46
  def position=(position)
47
47
  raise "Cursor position cannot contain nil values" if position.any?(&:nil?)
48
+
48
49
  @position = position
49
50
  end
50
51
 
@@ -45,6 +45,7 @@ module JobIteration
45
45
  column_value(record, attribute_name)
46
46
  end
47
47
  return positions.first if positions.size == 1
48
+
48
49
  positions
49
50
  end
50
51
 
@@ -43,6 +43,7 @@ module JobIteration
43
43
  # Builds Enumerator objects that iterates N times and yields number starting from zero.
44
44
  def build_times_enumerator(number, cursor:)
45
45
  raise ArgumentError, "First argument must be an Integer" unless number.is_a?(Integer)
46
+
46
47
  wrap(self, build_array_enumerator(number.times.to_a, cursor: cursor))
47
48
  end
48
49
 
@@ -54,6 +55,7 @@ module JobIteration
54
55
  if enumerable.any? { |i| defined?(ActiveRecord) && i.is_a?(ActiveRecord::Base) }
55
56
  raise ArgumentError, "array cannot contain ActiveRecord objects"
56
57
  end
58
+
57
59
  drop =
58
60
  if cursor.nil?
59
61
  0
@@ -119,12 +121,14 @@ module JobIteration
119
121
 
120
122
  # Builds Enumerator from Active Record Relation and enumerates on batches, yielding Active Record Relations.
121
123
  # See documentation for #build_active_record_enumerator_on_batches.
122
- def build_active_record_enumerator_on_batch_relations(scope, cursor:, **args)
123
- JobIteration::ActiveRecordBatchEnumerator.new(
124
+ def build_active_record_enumerator_on_batch_relations(scope, wrap: true, cursor:, **args)
125
+ enum = JobIteration::ActiveRecordBatchEnumerator.new(
124
126
  scope,
125
127
  cursor: cursor,
126
128
  **args
127
129
  ).each
130
+ enum = wrap(self, enum) if wrap
131
+ enum
128
132
  end
129
133
 
130
134
  def build_throttle_enumerator(enum, throttle_on:, backoff:)
@@ -136,6 +140,10 @@ module JobIteration
136
140
  ).to_enum
137
141
  end
138
142
 
143
+ def build_csv_enumerator(enumerable, cursor:)
144
+ CsvEnumerator.new(enumerable).rows(cursor: cursor)
145
+ end
146
+
139
147
  alias_method :once, :build_once_enumerator
140
148
  alias_method :times, :build_times_enumerator
141
149
  alias_method :array, :build_array_enumerator
@@ -143,6 +151,7 @@ module JobIteration
143
151
  alias_method :active_record_on_batches, :build_active_record_enumerator_on_batches
144
152
  alias_method :active_record_on_batch_relations, :build_active_record_enumerator_on_batch_relations
145
153
  alias_method :throttle, :build_throttle_enumerator
154
+ alias_method :csv, :build_csv_enumerator
146
155
 
147
156
  private
148
157
 
@@ -10,8 +10,14 @@ module JobIteration
10
10
  super
11
11
  end
12
12
  end
13
- # The patch is required in order to call shutdown? on a Resque::Worker instance
14
- Resque::Worker.prepend(ResqueIterationExtension)
13
+
14
+ # @private
15
+ module ::Resque
16
+ class Worker
17
+ # The patch is required in order to call shutdown? on a Resque::Worker instance
18
+ prepend(ResqueIterationExtension)
19
+ end
20
+ end
15
21
 
16
22
  JobIteration.interruption_adapter = -> { $resque_worker.try!(:shutdown?) }
17
23
  end
@@ -68,6 +68,7 @@ module JobIteration
68
68
 
69
69
  def initialize(*arguments)
70
70
  super
71
+ @job_iteration_retry_backoff = nil
71
72
  @needs_reenqueue = false
72
73
  self.times_interrupted = 0
73
74
  self.total_time = 0.0
@@ -132,10 +133,11 @@ module JobIteration
132
133
  end
133
134
 
134
135
  run_callbacks(:shutdown)
136
+ completed = handle_completed(completed)
135
137
 
136
138
  if @needs_reenqueue
137
139
  reenqueue_iteration_job
138
- elsif run_complete_callbacks?(completed)
140
+ elsif completed
139
141
  run_callbacks(:complete)
140
142
  output_interrupt_summary
141
143
  end
@@ -157,6 +159,7 @@ module JobIteration
157
159
  end
158
160
 
159
161
  next unless job_should_exit?
162
+
160
163
  self.executions -= 1 if executions > 1
161
164
  @needs_reenqueue = true
162
165
  return false
@@ -184,7 +187,7 @@ module JobIteration
184
187
  self.times_interrupted += 1
185
188
 
186
189
  self.already_in_queue = true if respond_to?(:already_in_queue=)
187
- retry_job
190
+ retry_job(wait: @job_iteration_retry_backoff)
188
191
  end
189
192
 
190
193
  def adjust_total_time
@@ -266,24 +269,29 @@ module JobIteration
266
269
  JobIteration.interruption_adapter.call || (defined?(super) && super)
267
270
  end
268
271
 
269
- def run_complete_callbacks?(completed)
270
- # nil means that someone aborted the job but want to call the on_complete callback
271
- if completed.nil?
272
- completed = :finished
273
- end
274
-
272
+ def handle_completed(completed)
275
273
  case completed
276
- when :finished, true then true
277
- # skip_complete_callbacks is returning from ThrottleEnumeratorand we do not want the on_complete callback to
278
- # be executed
279
- when false, :skip_complete_callbacks then false
274
+ when nil # someone aborted the job but wants to call the on_complete callback
275
+ return true
276
+ when true
277
+ return true
278
+ when false, :skip_complete_callbacks
279
+ return false
280
+ when Array # used by ThrottleEnumerator
281
+ reason, backoff = completed
282
+ raise "Unknown reason: #{reason}" unless reason == :retry
283
+
284
+ @job_iteration_retry_backoff = backoff
285
+ @needs_reenqueue = true
286
+ return false
280
287
  end
288
+ raise "Unexpected thrown value: #{completed.inspect}"
281
289
  end
282
290
 
283
291
  def valid_cursor_parameter?(parameters)
284
292
  # this condition is when people use the splat operator.
285
293
  # def build_enumerator(*)
286
- return true if parameters == [[:rest]]
294
+ return true if parameters == [[:rest]] || parameters == [[:rest, :*]]
287
295
 
288
296
  parameters.each do |parameter_type, parameter_name|
289
297
  next unless parameter_name == :cursor
@@ -31,8 +31,7 @@ module JobIteration
31
31
  @enum.each do |*val|
32
32
  if should_throttle?
33
33
  ActiveSupport::Notifications.instrument("throttled.iteration", job_class: @job.class.name)
34
- @job.retry_job(wait: @backoff)
35
- throw(:abort, :skip_complete_callbacks)
34
+ throw(:abort, [:retry, @backoff])
36
35
  end
37
36
 
38
37
  yielder.yield(*val)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JobIteration
4
- VERSION = "1.3.3"
4
+ VERSION = "1.3.6"
5
5
  end
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.3.3
4
+ version: 1.3.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shopify
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-11-17 00:00:00.000000000 Z
11
+ date: 2022-03-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -62,6 +62,8 @@ files:
62
62
  - dev.yml
63
63
  - gemfiles/rails_5_2.gemfile
64
64
  - gemfiles/rails_6_0.gemfile
65
+ - gemfiles/rails_6_1.gemfile
66
+ - gemfiles/rails_7_0.gemfile
65
67
  - gemfiles/rails_edge.gemfile
66
68
  - guides/best-practices.md
67
69
  - guides/custom-enumerator.md
@@ -85,7 +87,7 @@ homepage: https://github.com/shopify/job-iteration
85
87
  licenses:
86
88
  - MIT
87
89
  metadata:
88
- changelog_uri: https://github.com/Shopify/job-iteration/blob/master/CHANGELOG.md
90
+ changelog_uri: https://github.com/Shopify/job-iteration/blob/main/CHANGELOG.md
89
91
  allowed_push_host: https://rubygems.org
90
92
  post_install_message:
91
93
  rdoc_options: []