sqewer 6.4.0 → 8.0.0

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: b0251b1700672c6dc8722779c0865d3757aff9de9ea747d4739863521931cc0c
4
- data.tar.gz: 0533f556c4cad614cf7973818126996c8a8bea51d1791ca22df059ab65a5202a
3
+ metadata.gz: 7bef1bf31a141c142252a3ff3a898351808d378f0538197769091fabcfdf7e0e
4
+ data.tar.gz: 6eeffee2b8ec4009507ad192aede407c5ab0bb929e694f89f7bf2394ea3f3301
5
5
  SHA512:
6
- metadata.gz: 7d9d8f211697dc585517fd6c431e32f7500711888a3b97cf94aa7d4afbd043da2790407242a81a1ba37bca905cf6b454f73ee98a5015550768c948fe53eaa315
7
- data.tar.gz: 38369a4c3a74e3bdb5616705744ad732a363757c3eeadb04dde327f77d9251633047851e446454a0126895acbaf194d44081c3afd0192a509992d592e164a2a5
6
+ metadata.gz: 1b7a6faf35f31cc8fa8f33d063964553308cfef543a2e35c26dccfc55e9819a7754eaff9a77c35233d6e230269f2adc06c8ee23902fda5f41e8c4d0708b6ff02
7
+ data.tar.gz: 9aeedcf29cabcb941037df8c51712d908901ab7700c75649ca40986418fc7462ef17218a7431d798cdf43da6ba545b8e3d87bc56735c34e9dac7181579f63560
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,3 +1,23 @@
1
+ ### 8.0.0
2
+ - Remove method `Sqewer.client=`
3
+ - Change `Sqewer::Connection` to use a singleton SQS client by default
4
+
5
+ ### 7.0.0
6
+ - Remove support of Ruby 2.3, 2.4 and 2.5
7
+ - Remove support of Rails 4
8
+ - 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.
9
+
10
+ ### 6.5.1
11
+ - Also retry on `Aws::SQS::Errors::InternalError` exception when receiving/sending messages. This will make
12
+ the receiving thread more resilient to sudden SQS failures. By the time SQS recovers the receiving thread
13
+ should stay alive.
14
+
15
+ ### 6.5.0
16
+ - Adds `$stdout.sync = true` to CLI to flush the logs to STDOUT
17
+
18
+ ### 6.4.1
19
+ - Retrieve attributes when receiving messages from SQS
20
+
1
21
  ### 6.4.0
2
22
  - Raise an exception in submit! if the job serializes to a message that is
3
23
  above the native SQS limit for message size.
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,11 +65,15 @@ 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(queue_url: @queue_url,
59
- wait_time_seconds: DEFAULT_TIMEOUT_SECONDS, max_number_of_messages: BATCH_RECEIVE_SIZE)
70
+ Retriable.retriable on: network_and_aws_sdk_errors, tries: MAX_RANDOM_RECEIVE_FAILURES do
71
+ response = @client.receive_message(
72
+ queue_url: @queue_url,
73
+ attribute_names: ['All'],
74
+ wait_time_seconds: DEFAULT_TIMEOUT_SECONDS,
75
+ max_number_of_messages: BATCH_RECEIVE_SIZE
76
+ )
60
77
  response.messages.map {|message| Message.new(message.receipt_handle, message.body, message.attributes) }
61
78
  end
62
79
  end
@@ -65,7 +82,7 @@ class Sqewer::Connection
65
82
  #
66
83
  # @param message_body[String] the message to send
67
84
  # @param kwargs_for_send[Hash] additional arguments for the submit (such as `delay_seconds`).
68
- # Passes the arguments to the AWS SDK.
85
+ # Passes the arguments to the AWS SDK.
69
86
  # @return [void]
70
87
  def send_message(message_body, **kwargs_for_send)
71
88
  send_multiple_messages {|via| via.send_message(message_body, **kwargs_for_send) }
@@ -187,9 +204,13 @@ class Sqewer::Connection
187
204
 
188
205
  private
189
206
 
207
+ def network_and_aws_sdk_errors
208
+ [NotOurFaultAwsError, Seahorse::Client::NetworkingError, Aws::SQS::Errors::InternalError]
209
+ end
210
+
190
211
  def handle_batch_with_retries(method, batch)
191
- Retriable.retriable on: [NotOurFaultAwsError, Seahorse::Client::NetworkingError], tries: MAX_RANDOM_FAILURES_PER_CALL do
192
- resp = client.send(method, queue_url: @queue_url, entries: batch)
212
+ Retriable.retriable on: network_and_aws_sdk_errors, tries: MAX_RANDOM_FAILURES_PER_CALL do
213
+ resp = @client.send(method, queue_url: @queue_url, entries: batch)
193
214
  wrong_messages, aws_failures = resp.failed.partition {|m| m.sender_fault }
194
215
  if wrong_messages.any?
195
216
  err = wrong_messages.inspect + ', ' + resp.inspect
@@ -201,11 +222,4 @@ class Sqewer::Connection
201
222
  end
202
223
  end
203
224
  end
204
-
205
- def client
206
- @client ||= Aws::SQS::Client.new(
207
- instance_profile_credentials_timeout: 1, # defaults to 1 second
208
- instance_profile_credentials_retries: 5, # defaults to 0 retries
209
- )
210
- end
211
225
  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.0'
2
+ VERSION = '8.0.0'
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.
@@ -36,7 +37,7 @@ Gem::Specification.new do |spec|
36
37
  spec.add_runtime_dependency "retriable"
37
38
 
38
39
  spec.add_development_dependency "bundler"
39
- spec.add_development_dependency "rake", "~> 12.3"
40
+ spec.add_development_dependency "rake", "~> 13.0"
40
41
  spec.add_development_dependency "rspec", "~> 3.0"
41
42
 
42
43
  # The Rails deps can be relaxed, they are specified more exactly in the gemfiles/
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.0
4
+ version: 8.0.0
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-14 00:00:00.000000000 Z
12
+ date: 2021-05-26 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: aws-sdk-sqs
@@ -101,14 +101,14 @@ dependencies:
101
101
  requirements:
102
102
  - - "~>"
103
103
  - !ruby/object:Gem::Version
104
- version: '12.3'
104
+ version: '13.0'
105
105
  type: :development
106
106
  prerelease: false
107
107
  version_requirements: !ruby/object:Gem::Requirement
108
108
  requirements:
109
109
  - - "~>"
110
110
  - !ruby/object:Gem::Version
111
- version: '12.3'
111
+ version: '13.0'
112
112
  - !ruby/object:Gem::Dependency
113
113
  name: rspec
114
114
  requirement: !ruby/object:Gem::Requirement
@@ -226,6 +226,7 @@ email:
226
226
  - me@julik.nl
227
227
  - linkyndy@gmail.com
228
228
  executables:
229
+ - console
229
230
  - sqewer
230
231
  - sqewer_rails
231
232
  extensions: []
@@ -239,10 +240,10 @@ files:
239
240
  - Gemfile
240
241
  - README.md
241
242
  - Rakefile
243
+ - bin/console
242
244
  - bin/sqewer
243
245
  - bin/sqewer_rails
244
246
  - example.env
245
- - gemfiles/Gemfile.rails-4.2.x
246
247
  - gemfiles/Gemfile.rails-5.0.x
247
248
  - gemfiles/Gemfile.rails-5.1.x
248
249
  - lib/sqewer.rb
@@ -278,14 +279,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
278
279
  requirements:
279
280
  - - ">="
280
281
  - !ruby/object:Gem::Version
281
- version: '0'
282
+ version: 2.6.0
282
283
  required_rubygems_version: !ruby/object:Gem::Requirement
283
284
  requirements:
284
285
  - - ">="
285
286
  - !ruby/object:Gem::Version
286
287
  version: '0'
287
288
  requirements: []
288
- rubygems_version: 3.0.3
289
+ rubygems_version: 3.1.6
289
290
  signing_key:
290
291
  specification_version: 4
291
292
  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"