sqewer 6.4.0 → 8.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +0 -16
- data/CHANGELOG.md +20 -0
- data/README.md +17 -13
- data/bin/console +8 -0
- data/lib/sqewer.rb +5 -0
- data/lib/sqewer/cli.rb +1 -0
- data/lib/sqewer/connection.rb +30 -16
- data/lib/sqewer/simple_job.rb +8 -1
- data/lib/sqewer/version.rb +1 -1
- data/lib/sqewer/worker.rb +6 -3
- data/sqewer.gemspec +2 -1
- metadata +8 -7
- data/gemfiles/Gemfile.rails-4.2.x +0 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7bef1bf31a141c142252a3ff3a898351808d378f0538197769091fabcfdf7e0e
|
4
|
+
data.tar.gz: 6eeffee2b8ec4009507ad192aede407c5ab0bb929e694f89f7bf2394ea3f3301
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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.
|
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.
|
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.
|
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
|
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
data/lib/sqewer.rb
CHANGED
data/lib/sqewer/cli.rb
CHANGED
data/lib/sqewer/connection.rb
CHANGED
@@ -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:
|
58
|
-
response = client.receive_message(
|
59
|
-
|
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:
|
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
|
data/lib/sqewer/simple_job.rb
CHANGED
@@ -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)
|
data/lib/sqewer/version.rb
CHANGED
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
|
-
|
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
|
-
|
237
|
-
|
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", "~>
|
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:
|
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:
|
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: '
|
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: '
|
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:
|
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.
|
289
|
+
rubygems_version: 3.1.6
|
289
290
|
signing_key:
|
290
291
|
specification_version: 4
|
291
292
|
summary: Process jobs from SQS
|