sqewer 6.4.1 → 8.0.1

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: be8cc2c0e7ee4782db12019bd4590c661434be3c96987c3f3c79c052999dadaa
4
- data.tar.gz: b9f0e5c17f48e7d8ca7eed9cd7b7386c29d389d0178ee2fa7a3e5a077746a0e4
3
+ metadata.gz: db608a0341020c8df0186ebc598164f5efef1c75051b4661aabb6e8c7e5c4406
4
+ data.tar.gz: 3823ade5a67c8e66844eed3e267f5d5c4f0b6f3626c53c645d22ebb57b25ed12
5
5
  SHA512:
6
- metadata.gz: e9ec134731dd5df33666b9b75d1745549f68c33856c6a466a3e2485c046c2b936898065df5dc7ed6ad3b1286bf23db585df2603b65be9f19756f62e0782529fa
7
- data.tar.gz: 58ab8dbecd3bf578c358142c922775afa8333f777f2d79cdeeb4e50ebad0fd9839fca8bffa2e543346091196ce9ca855994fb618268aaf13cb14d748d334f563
6
+ metadata.gz: 2c549285412294f21ec5a8af1d5614e949941996c687b91478c5a1ed4553995e888aa56443dde5c6eace899b24d44d5a02e21dfb61d391408e126c01b001e3dd
7
+ data.tar.gz: 49b1d928b6786d38911db7803498a42fd4860c8c7723ec09a477b464103eded17a752d3f900c01f20af5cd0d6d79360d260d78a7636a0a8c1f7e6e322554e209
data/.travis.yml CHANGED
@@ -1,11 +1,7 @@
1
1
  gemfile:
2
- - gemfiles/Gemfile.rails-4.2.x
3
2
  - gemfiles/Gemfile.rails-5.0.x
4
3
  - gemfiles/Gemfile.rails-5.1.x
5
4
  rvm:
6
- - 2.3.7
7
- - 2.4.5
8
- - 2.5.1
9
5
  - 2.6.5
10
6
  - 2.7.0
11
7
  cache: bundler
@@ -13,15 +9,3 @@ sudo: false
13
9
  env:
14
10
  global:
15
11
  - AWS_REGION=eu-central-1
16
- matrix:
17
- exclude:
18
- - rvm: 2.3.7
19
- gemfile: gemfiles/Gemfile.rails-5.0.x
20
- - rvm: 2.3.7
21
- gemfile: gemfiles/Gemfile.rails-5.1.x
22
- - rvm: 2.5.1
23
- gemfile: gemfiles/Gemfile.rails-4.2.x
24
- - rvm: 2.6.5
25
- gemfile: gemfiles/Gemfile.rails-4.2.x
26
- - rvm: 2.7.0
27
- gemfile: gemfiles/Gemfile.rails-4.2.x
data/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
+ ### 8.0.1
2
+ - Release the singleton SQS client when AWS raises credentials error to be able to use a new credential next time
3
+
4
+ ### 8.0.0
5
+ - Remove method `Sqewer.client=`
6
+ - Change `Sqewer::Connection` to use a singleton SQS client by default
7
+
8
+ ### 7.0.0
9
+ - Remove support of Ruby 2.3, 2.4 and 2.5
10
+ - Remove support of Rails 4
11
+ - Change `Sqewer::Connection` to preferentially use a singleton instance of `Aws::SQS::Client`, which can be set using `Sqewer.client=`. This avoids many HTTP requests to the AWS metadata endpoint when getting credentials.
12
+
13
+ ### 6.5.1
14
+ - Also retry on `Aws::SQS::Errors::InternalError` exception when receiving/sending messages. This will make
15
+ the receiving thread more resilient to sudden SQS failures. By the time SQS recovers the receiving thread
16
+ should stay alive.
17
+
18
+ ### 6.5.0
19
+ - Adds `$stdout.sync = true` to CLI to flush the logs to STDOUT
20
+
1
21
  ### 6.4.1
2
- - Retrieve attributes when receiving messages from SQS
22
+ - Retrieve attributes when receiving messages from SQS
3
23
 
4
24
  ### 6.4.0
5
25
  - Raise an exception in submit! if the job serializes to a message that is
data/README.md CHANGED
@@ -20,14 +20,14 @@ and to start processing, in your commandline handler:
20
20
 
21
21
  #!/usr/bin/env ruby
22
22
  require 'my_applicaion'
23
- Sqewer::CLI.run
23
+ Sqewer::CLI.start
24
24
 
25
25
  To add arguments to the job
26
26
 
27
27
  class JobWithArgs
28
28
  include Sqewer::SimpleJob
29
29
  attr_accessor :times
30
-
30
+
31
31
  def run
32
32
  ...
33
33
  end
@@ -48,7 +48,7 @@ The messages will only be deleted from SQS once the job execution completes with
48
48
 
49
49
  ## Requirements
50
50
 
51
- Ruby 2.1+, version 2 of the AWS SDK. You can also run Sqewer backed by a SQLite database file, which can be handy for development situations.
51
+ Ruby 2.6+, version 2 of the AWS SDK. You can also run Sqewer backed by a SQLite database file, which can be handy for development situations.
52
52
 
53
53
  ## Job storage
54
54
 
@@ -88,6 +88,10 @@ Note that at this point only arguments that are raw JSON types are supported:
88
88
 
89
89
  If you need marshalable Ruby types there instead, you might need to implement a custom `Serializer.`
90
90
 
91
+ ### Sqewer::SimpleJob
92
+
93
+ The module `Sqewer::SimpleJob` can be included to a job class to add some features, specially dealing with attributes, see more details [here](https://github.com/WeTransfer/sqewer/blob/master/lib/sqewer/simple_job.rb).
94
+
91
95
  ## Jobs spawning dependent jobs
92
96
 
93
97
  If your `run` method on the job object accepts arguments (has non-zero `arity` ) the `ExecutionContext` will
@@ -119,11 +123,11 @@ include all the keyword arguments needed to instantiate the job when executing.
119
123
  def initialize(to:, body:)
120
124
  ...
121
125
  end
122
-
126
+
123
127
  def run()
124
128
  ...
125
129
  end
126
-
130
+
127
131
  def to_h
128
132
  {to: @to, body: @body}
129
133
  end
@@ -151,7 +155,7 @@ conform to the job serialization format used internally. For example, you can ha
151
155
  message = JSON.load(message_blob)
152
156
  return if message['Service'] # AWS test
153
157
  return HandleS3Notification.new(message) if message['Records']
154
-
158
+
155
159
  super # as default
156
160
  end
157
161
  end
@@ -176,7 +180,7 @@ The very minimal executable for running jobs would be this:
176
180
 
177
181
  #!/usr/bin/env ruby
178
182
  require 'my_applicaion'
179
- Sqewer::CLI.run
183
+ Sqewer::CLI.start
180
184
 
181
185
  This will connect to the queue at the URL set in the `SQS_QUEUE_URL` environment variable, and
182
186
  use all the default parameters. The `CLI` module will also set up a signal handler to terminate
@@ -233,9 +237,9 @@ you generate. For example, you could use a pipe. But in a more general case some
233
237
  ActiveRAMGobbler.fetch_stupendously_many_things.each do |...|
234
238
  end
235
239
  end
236
-
240
+
237
241
  _, status = Process.wait2(pid)
238
-
242
+
239
243
  # Raise an error in the parent process to signal Sqewer that the job failed
240
244
  # if the child exited with a non-0 status
241
245
  raise "Child process crashed" unless status.exitstatus && status.exitstatus.zero?
@@ -252,7 +256,7 @@ You can wrap job processing in middleware. A full-featured middleware class look
252
256
  # msg_id is the receipt handle, msg_payload is the message body string, msg_attributes are the message's attributes
253
257
  yield
254
258
  end
255
-
259
+
256
260
  # Surrounds the actual job execution
257
261
  def around_execution(job, context)
258
262
  # job is the actual job you will be running, context is the ExecutionContext.
@@ -284,7 +288,7 @@ and traceable (make good use of logging).
284
288
 
285
289
  # Usage with Rails via ActiveJob
286
290
 
287
- This gem includes a queue adapter for usage with ActiveJob in Rails 4.2+. The functionality
291
+ This gem includes a queue adapter for usage with ActiveJob in Rails 5+. The functionality
288
292
  is well-tested and should function for any well-conforming ActiveJob subclasses.
289
293
 
290
294
  To run the default `sqewer` worker setup against your Rails application, first set it as the
@@ -378,7 +382,7 @@ products using this library, was a very bad idea (more workload for deployment).
378
382
 
379
383
  ## Why so many configurable components?
380
384
 
381
- Because sometimes your requirements differ just-a-little-bit from what is provided, and you have to swap your
385
+ Because sometimes your requirements differ just-a-little-bit from what is provided, and you have to swap your
382
386
  implementation in instead. One product needs foreign-submitted SQS jobs (S3 notifications). Another product
383
387
  needs a custom Logger subclass. Yet another product needs process-based concurrency on top of threads.
384
388
  Yet another process needs to manage database connections when running the jobs. Have 3-4 of those, and a
@@ -398,7 +402,7 @@ Because I found that a producer-consumer model with a thread pool works quite we
398
402
  the Ruby standard library alone.
399
403
 
400
404
  ## Contributing to the library
401
-
405
+
402
406
  * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
403
407
  * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
404
408
  * Fork the project.
data/bin/console ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "sqewer"
6
+
7
+ require "irb"
8
+ IRB.start(__FILE__)
data/lib/sqewer.rb CHANGED
@@ -11,6 +11,11 @@ module Sqewer
11
11
  end
12
12
  end
13
13
 
14
+ # Returns a singleton of Aws::SQS::Client
15
+ def self.client
16
+ Sqewer::Connection.client
17
+ end
18
+
14
19
  # Loads a particular Sqewer extension that is not loaded
15
20
  # automatically during the gem require.
16
21
  #
data/lib/sqewer/cli.rb CHANGED
@@ -1,3 +1,4 @@
1
+ $stdout.sync = true
1
2
  # Wraps a Worker object in a process-wide commanline handler. Once the `start` method is
2
3
  # called, signal handlers will be installed for the following signals:
3
4
  #
@@ -39,12 +39,25 @@ class Sqewer::Connection
39
39
  raise "SQS_QUEUE_URL not set in the environment. This is the queue URL Sqewer uses by default."
40
40
  end
41
41
 
42
+ # Returns a singleton of Aws::SQS::Client
43
+ def self.client
44
+ # It's better using a singleton client to prevent making a lot of HTTP
45
+ # requests to the AWS metadata endpoint when getting credentials.
46
+ @client ||= begin
47
+ require 'aws-sdk-sqs'
48
+ ::Aws::SQS::Client.new(
49
+ instance_profile_credentials_timeout: 1,
50
+ instance_profile_credentials_retries: 5,
51
+ )
52
+ end
53
+ end
54
+
42
55
  # Initializes a new adapter, with access to the SQS queue at the given URL.
43
56
  #
44
57
  # @param queue_url[String] the SQS queue URL (the URL can be copied from your AWS console)
45
- def initialize(queue_url)
46
- require 'aws-sdk-sqs'
58
+ def initialize(queue_url, client: self.class.client)
47
59
  @queue_url = queue_url
60
+ @client = client
48
61
  end
49
62
 
50
63
  # Receive at most 10 messages from the queue, and return the array of Message objects. Retries for at
@@ -52,10 +65,10 @@ class Sqewer::Connection
52
65
  # even after 15 minutes it is either down or the server is misconfigured. Either way it makes no sense to
53
66
  # continue.
54
67
  #
55
- # @return [Array<Message>] an array of Message objects
68
+ # @return [Array<Message>] an array of Message objects
56
69
  def receive_messages
57
- Retriable.retriable on: Seahorse::Client::NetworkingError, tries: MAX_RANDOM_RECEIVE_FAILURES do
58
- response = client.receive_message(
70
+ Retriable.retriable on: network_and_aws_sdk_errors, tries: MAX_RANDOM_RECEIVE_FAILURES do
71
+ response = @client.receive_message(
59
72
  queue_url: @queue_url,
60
73
  attribute_names: ['All'],
61
74
  wait_time_seconds: DEFAULT_TIMEOUT_SECONDS,
@@ -63,13 +76,21 @@ class Sqewer::Connection
63
76
  )
64
77
  response.messages.map {|message| Message.new(message.receipt_handle, message.body, message.attributes) }
65
78
  end
79
+ rescue Aws::Errors::MissingCredentialsError
80
+ # We noticed cases where errors related to AWS credentials started to happen suddenly.
81
+ # We don't know the root cause yet, but what we can do is release the
82
+ # singleton @client instance because it contains a cache of credentials that in most
83
+ # cases is no longer valid.
84
+ self.class.release_client
85
+
86
+ raise
66
87
  end
67
88
 
68
89
  # Send a message to the backing queue
69
90
  #
70
91
  # @param message_body[String] the message to send
71
92
  # @param kwargs_for_send[Hash] additional arguments for the submit (such as `delay_seconds`).
72
- # Passes the arguments to the AWS SDK.
93
+ # Passes the arguments to the AWS SDK.
73
94
  # @return [void]
74
95
  def send_message(message_body, **kwargs_for_send)
75
96
  send_multiple_messages {|via| via.send_message(message_body, **kwargs_for_send) }
@@ -189,11 +210,21 @@ class Sqewer::Connection
189
210
  buffer.each_batch {|batch| handle_batch_with_retries(:delete_message_batch, batch) }
190
211
  end
191
212
 
213
+ protected
214
+
215
+ def self.release_client
216
+ @client = nil
217
+ end
218
+
192
219
  private
193
220
 
221
+ def network_and_aws_sdk_errors
222
+ [NotOurFaultAwsError, Seahorse::Client::NetworkingError, Aws::SQS::Errors::InternalError]
223
+ end
224
+
194
225
  def handle_batch_with_retries(method, batch)
195
- Retriable.retriable on: [NotOurFaultAwsError, Seahorse::Client::NetworkingError], tries: MAX_RANDOM_FAILURES_PER_CALL do
196
- resp = client.send(method, queue_url: @queue_url, entries: batch)
226
+ Retriable.retriable on: network_and_aws_sdk_errors, tries: MAX_RANDOM_FAILURES_PER_CALL do
227
+ resp = @client.send(method, queue_url: @queue_url, entries: batch)
197
228
  wrong_messages, aws_failures = resp.failed.partition {|m| m.sender_fault }
198
229
  if wrong_messages.any?
199
230
  err = wrong_messages.inspect + ', ' + resp.inspect
@@ -204,12 +235,13 @@ class Sqewer::Connection
204
235
  raise NotOurFaultAwsError
205
236
  end
206
237
  end
207
- end
208
-
209
- def client
210
- @client ||= Aws::SQS::Client.new(
211
- instance_profile_credentials_timeout: 1, # defaults to 1 second
212
- instance_profile_credentials_retries: 5, # defaults to 0 retries
213
- )
238
+ rescue Aws::Errors::MissingCredentialsError
239
+ # We noticed cases where errors related to AWS credentials started to happen suddenly.
240
+ # We don't know the root cause yet, but what we can do is release the
241
+ # singleton @client instance because it contains a cache of credentials that in most
242
+ # cases is no longer valid.
243
+ self.class.release_client
244
+
245
+ raise
214
246
  end
215
247
  end
@@ -4,6 +4,13 @@
4
4
  # * initialize() will have keyword access to all accessors, and will ensure you have called each one of them
5
5
  # * to_h() will produce a symbolized Hash with all the properties defined using attr_accessor, and the job_class_name
6
6
  # * inspect() will provide a sensible default string representation for logging
7
+ #
8
+ # This module validates if the attributes defined in the job class are the same as
9
+ # those persisted in the queue. More details on `Sqewer::SimpleJob#initialize`.
10
+ # Because of this, it's required to create a new job class when adding or removing
11
+ # an attribute.
12
+ # This mechanism guarantees strong consistency. Without it, a new deployed job class
13
+ # could process old incompatible payloads.
7
14
  module Sqewer::SimpleJob
8
15
  UnknownJobAttribute = Class.new(Sqewer::Error)
9
16
  MissingAttribute = Class.new(Sqewer::Error)
@@ -53,7 +60,7 @@ module Sqewer::SimpleJob
53
60
  accessor = "#{k}="
54
61
  touched_attributes << k
55
62
  unless respond_to?(accessor)
56
- raise UnknownJobAttribute, "Unknown attribute #{k.inspect} for #{self.class}"
63
+ raise UnknownJobAttribute, "Unknown attribute #{k.inspect} for #{self.class}"
57
64
  end
58
65
 
59
66
  send("#{k}=", v)
@@ -1,3 +1,3 @@
1
1
  module Sqewer
2
- VERSION = '6.4.1'
2
+ VERSION = '8.0.1'
3
3
  end
data/lib/sqewer/worker.rb CHANGED
@@ -91,7 +91,7 @@ class Sqewer::Worker
91
91
 
92
92
  consumers = (1..@num_threads).map do
93
93
  Thread.new do
94
- catch(:goodbye) { loop {take_and_execute} }
94
+ loop { take_and_execute }
95
95
  end
96
96
  end
97
97
 
@@ -233,8 +233,11 @@ class Sqewer::Worker
233
233
  message = @execution_queue.pop(nonblock=true)
234
234
  handle_message(message)
235
235
  rescue ThreadError # Queue is empty
236
- throw :goodbye if stopping?
237
- sleep SLEEP_SECONDS_ON_EMPTY_QUEUE
236
+ if stopping?
237
+ Thread.current.exit
238
+ else
239
+ sleep SLEEP_SECONDS_ON_EMPTY_QUEUE
240
+ end
238
241
  rescue => e # anything else, at or below StandardError that does not need us to quit
239
242
  @logger.error { '[worker] Failed "%s..." with %s: %s' % [message.inspect[0..64], e.class, e.message] }
240
243
  e.backtrace.each { |s| @logger.debug{"\t#{s}"} }
data/sqewer.gemspec CHANGED
@@ -12,6 +12,7 @@ Gem::Specification.new do |spec|
12
12
  spec.summary = %q{Process jobs from SQS}
13
13
  spec.description = %q{A full-featured library for all them SQS worker needs}
14
14
  spec.homepage = "https://github.com/WeTransfer/sqewer"
15
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.6.0")
15
16
 
16
17
  # Prevent pushing this gem to RubyGems.org. To allow pushes either set the "allowed_push_host"
17
18
  # to allow pushing to a single host or delete this section to allow pushing to any host.
@@ -49,4 +50,5 @@ Gem::Specification.new do |spec|
49
50
  spec.add_development_dependency "dotenv"
50
51
  spec.add_development_dependency "simplecov"
51
52
  spec.add_development_dependency "appsignal", '~> 2'
53
+ spec.add_development_dependency "pry-byebug"
52
54
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sqewer
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.4.1
4
+ version: 8.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Julik Tarkhanov
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2020-05-22 00:00:00.000000000 Z
12
+ date: 2021-07-12 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: aws-sdk-sqs
@@ -221,11 +221,26 @@ dependencies:
221
221
  - - "~>"
222
222
  - !ruby/object:Gem::Version
223
223
  version: '2'
224
+ - !ruby/object:Gem::Dependency
225
+ name: pry-byebug
226
+ requirement: !ruby/object:Gem::Requirement
227
+ requirements:
228
+ - - ">="
229
+ - !ruby/object:Gem::Version
230
+ version: '0'
231
+ type: :development
232
+ prerelease: false
233
+ version_requirements: !ruby/object:Gem::Requirement
234
+ requirements:
235
+ - - ">="
236
+ - !ruby/object:Gem::Version
237
+ version: '0'
224
238
  description: A full-featured library for all them SQS worker needs
225
239
  email:
226
240
  - me@julik.nl
227
241
  - linkyndy@gmail.com
228
242
  executables:
243
+ - console
229
244
  - sqewer
230
245
  - sqewer_rails
231
246
  extensions: []
@@ -239,10 +254,10 @@ files:
239
254
  - Gemfile
240
255
  - README.md
241
256
  - Rakefile
257
+ - bin/console
242
258
  - bin/sqewer
243
259
  - bin/sqewer_rails
244
260
  - example.env
245
- - gemfiles/Gemfile.rails-4.2.x
246
261
  - gemfiles/Gemfile.rails-5.0.x
247
262
  - gemfiles/Gemfile.rails-5.1.x
248
263
  - lib/sqewer.rb
@@ -278,14 +293,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
278
293
  requirements:
279
294
  - - ">="
280
295
  - !ruby/object:Gem::Version
281
- version: '0'
296
+ version: 2.6.0
282
297
  required_rubygems_version: !ruby/object:Gem::Requirement
283
298
  requirements:
284
299
  - - ">="
285
300
  - !ruby/object:Gem::Version
286
301
  version: '0'
287
302
  requirements: []
288
- rubygems_version: 3.1.2
303
+ rubygems_version: 3.1.6
289
304
  signing_key:
290
305
  specification_version: 4
291
306
  summary: Process jobs from SQS
@@ -1,8 +0,0 @@
1
- source "http://rubygems.org"
2
-
3
- # Gemspec as base dependency set
4
- gemspec path: __dir__ + '/..'
5
-
6
- gem 'sqlite3', '~> 1.3.6'
7
- gem 'activejob', "~> 4.2.0"
8
- gem 'activerecord', "~> 4.2.0"