lambdakiq 1.0.1 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +13 -2
- data/Gemfile.lock +1 -1
- data/README.md +73 -49
- data/lib/lambdakiq/error.rb +0 -10
- data/lib/lambdakiq/job.rb +9 -4
- data/lib/lambdakiq/metrics.rb +13 -2
- data/lib/lambdakiq/version.rb +1 -1
- data/lib/lambdakiq/worker.rb +4 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 94804599e9c2e4f8f701c34cee61a7d18db8f9a20fee85a3e24681e2bd555076
|
4
|
+
data.tar.gz: 7550236dacfc649a36468bbc4b3a7116a03b90d5943df4cdb0c08f7521ad505f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e7592aaa326a0ed6fa6202c0877b61ed67f00d156078d37840dda13ee4a455ff4de1ac35a377dd95230288fdc6ae054b974ef990190c0a46b59a45eb81c10ee5
|
7
|
+
data.tar.gz: 2f8efaa743a20710fd57ad2aa1b0cf8983828100b6ec5880f0815320f186216a5420e362d24b1b21b4e0ae78187c75e517afc4e27ddf5f9d3c3b295803101c16
|
data/CHANGELOG.md
CHANGED
@@ -1,10 +1,21 @@
|
|
1
|
-
|
2
1
|
# Keep A Changelog!
|
3
2
|
|
4
3
|
See this http://keepachangelog.com link for information on how we want this documented formatted.
|
5
4
|
|
5
|
+
## v2.0.0
|
6
|
+
|
7
|
+
#### Changed
|
8
|
+
|
9
|
+
- Leverage new `ReportBatchItemFailures` feature of SQS.
|
10
|
+
|
11
|
+
## v1.0.2, v1.0.3, v1.0.4
|
12
|
+
|
13
|
+
#### Fixed
|
14
|
+
|
15
|
+
- Rails v5.2 compatibility. Metrics logging is are safe for non-Lambdakiq jobs
|
16
|
+
|
6
17
|
## v1.0.0
|
7
18
|
|
8
19
|
#### Added
|
9
20
|
|
10
|
-
|
21
|
+
- Initial release.
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
![Test](https://github.com/customink/lambdakiq/workflows/Test/badge.svg)
|
2
|
-
|
3
1
|
![Lambdakiq: ActiveJob on SQS & Lambda](images/Lambdakiq.png)
|
4
2
|
|
3
|
+
![Test](https://github.com/customink/lambdakiq/workflows/Test/badge.svg)
|
4
|
+
|
5
5
|
# Lambdakiq
|
6
6
|
|
7
7
|
<a href="https://lamby.custominktech.com"><img src="https://user-images.githubusercontent.com/2381/59363668-89edeb80-8d03-11e9-9985-2ce14361b7e3.png" alt="Lamby: Simple Rails & AWS Lambda Integration using Rack." align="right" width="300" /></a>A drop-in replacement for [Sidekiq](https://github.com/mperham/sidekiq) when running Rails in AWS Lambda using the [Lamby](https://lamby.custominktech.com) gem.
|
@@ -10,19 +10,18 @@ Lambdakiq allows you to leverage AWS' managed infrastructure to the fullest exte
|
|
10
10
|
|
11
11
|
## Key Features
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
13
|
+
- Distinct web & jobs Lambda functions.
|
14
|
+
- AWS fully managed polling. Event-driven.
|
15
|
+
- Maximum 12 retries. Per job configurable.
|
16
|
+
- Mirror Sidekiq's retry [backoff](https://github.com/mperham/sidekiq/wiki/Error-Handling#automatic-job-retry) timing.
|
17
|
+
- Last retry is at 11 hours 30 minutes.
|
18
|
+
- Supports ActiveJob's wait/delay. Up to 15 minutes.
|
19
|
+
- Dead messages are stored for up to 14 days.
|
20
20
|
|
21
21
|
## Project Setup
|
22
22
|
|
23
23
|
This gem assumes your Rails application is on AWS Lambda, ideally with our [Lamby](https://lamby.custominktech.com) gem. It could be using Lambda's traditional zip package type or the newer [container](https://dev.to/aws-heroes/lambda-containers-with-rails-a-perfect-match-4lgb) format. If Rails on Lambda is new to you, consider following our [quick start](https://lamby.custominktech.com/docs/quick_start) guide to get your first application up and running. From there, to use Lambdakiq, here are steps to setup your project
|
24
24
|
|
25
|
-
|
26
25
|
### Bundle & Config
|
27
26
|
|
28
27
|
Add the Lambdakiq gem to your `Gemfile`.
|
@@ -31,7 +30,7 @@ Add the Lambdakiq gem to your `Gemfile`.
|
|
31
30
|
gem 'lambdakiq'
|
32
31
|
```
|
33
32
|
|
34
|
-
Open `config/
|
33
|
+
Open `config/environments/production.rb` and set Lambdakiq as your ActiveJob queue adapter.
|
35
34
|
|
36
35
|
```ruby
|
37
36
|
config.active_job.queue_adapter = :lambdakiq
|
@@ -46,6 +45,33 @@ class ApplicationJob < ActiveJob::Base
|
|
46
45
|
end
|
47
46
|
```
|
48
47
|
|
48
|
+
Using ActionMailer's built-in deliver job with ActiveJob? Make sure to include the Lambdakiq worker and set the queue name depending on your Rails version. You can do this in a newly created `config/initializers//action_mailer.rb` or another initializer of your choice.
|
49
|
+
|
50
|
+
```ruby
|
51
|
+
# Rails 5.x
|
52
|
+
ActionMailer::DeliveryJob.include Lambdakiq::Worker
|
53
|
+
ActionMailer::DeliveryJob.queue_as ENV['JOBS_QUEUE_NAME']
|
54
|
+
# Rails 6.x
|
55
|
+
ActionMailer::MailDeliveryJob.include Lambdakiq::Worker
|
56
|
+
ActionMailer::MailDeliveryJob.queue_as ENV['JOBS_QUEUE_NAME']
|
57
|
+
```
|
58
|
+
|
59
|
+
The same Docker image will be used for both your `web` and `jobs` functions (example setup in following sections) which means the same `app.rb` handler would be used. The [Lamby](https://lamby.custominktech.com) gem automatically detects if Lambdakiq is being used so the following handler works as is.
|
60
|
+
|
61
|
+
```ruby
|
62
|
+
def handler(event:, context:)
|
63
|
+
Lamby.handler $app, event, context
|
64
|
+
end
|
65
|
+
```
|
66
|
+
|
67
|
+
You can use the Lambdakiq handler directly in cases where your handler is a different method. Likewise there is a `Lambdakiq.jobs?(event)` helper function which returns true if the `messageAttributes` has a `lambdakiq` attribute.
|
68
|
+
|
69
|
+
```ruby
|
70
|
+
def jobs_handler(event:, context:)
|
71
|
+
Lambdakiq.handler(event)
|
72
|
+
end
|
73
|
+
```
|
74
|
+
|
49
75
|
### SQS Resources
|
50
76
|
|
51
77
|
Open up your project's SAM [`template.yaml`](https://lamby.custominktech.com/docs/anatomy#file-template-yaml) file and make the following additions and changes. First, we need to create your [SQS queues](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-sqs-queues.html) under the `Resources` section.
|
@@ -91,7 +117,7 @@ Both functions will need capabilities to access the SQS jobs queue. We can add o
|
|
91
117
|
|
92
118
|
```yaml
|
93
119
|
Policies:
|
94
|
-
- Version:
|
120
|
+
- Version: "2012-10-17"
|
95
121
|
Statement:
|
96
122
|
- Effect: Allow
|
97
123
|
Action:
|
@@ -100,6 +126,8 @@ Policies:
|
|
100
126
|
- !Sub arn:aws:sqs:${AWS::Region}:${AWS::AccountId}:${JobsQueue.QueueName}
|
101
127
|
```
|
102
128
|
|
129
|
+
### Overview
|
130
|
+
|
103
131
|
Now we can duplicate our `RailsLambda` resource YAML (except for the `Events` property) to a new `JobsLambda` one. This gives us a distinct Lambda function to process jobs whose events, memory, timeout, and more can be independently tuned. However, both the `web` and `jobs` functions will use the same ECR container image!
|
104
132
|
|
105
133
|
```yaml
|
@@ -116,10 +144,12 @@ JobsLambda:
|
|
116
144
|
Properties:
|
117
145
|
Queue: !GetAtt JobsQueue.Arn
|
118
146
|
BatchSize: 1
|
147
|
+
FunctionResponseTypes:
|
148
|
+
- ReportBatchItemFailures
|
119
149
|
MemorySize: 1792
|
120
150
|
PackageType: Image
|
121
151
|
Policies:
|
122
|
-
- Version:
|
152
|
+
- Version: "2012-10-17"
|
123
153
|
Statement:
|
124
154
|
- Effect: Allow
|
125
155
|
Action:
|
@@ -131,10 +161,10 @@ JobsLambda:
|
|
131
161
|
|
132
162
|
Here are some key aspects of our `JobsLambda` resource above:
|
133
163
|
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
164
|
+
- The `Events` property uses the [SQS Type](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-sqs.html).
|
165
|
+
- The [BatchSize](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-sqs.html#sam-function-sqs-batchsize) can be any number you like. Less means more Lambda concurrency, more means some jobs could take longer. The jobs function `Timeout` must be lower than the `JobsQueue`'s `VisibilityTimeout` property. When the batch size is one, the queue's visibility is generally one second more.
|
166
|
+
- You must use `ReportBatchItemFailures` response types. Lambdakiq assumes we are [reporting batch item failures](https://docs.aws.amazon.com/lambda/latest/dg/with-sqs.html#services-sqs-batchfailurereporting). This is a new feature of SQS introduced in [November 2021](https://docs.aws.amazon.com/lambda/latest/dg/with-sqs.html#services-sqs-batchfailurereporting).
|
167
|
+
- The `Metadata`'s Docker properties must be the same as our web function except for the `DockerTag`. This is needed for the image to be shared. This works around a known [SAM issue](https://github.com/aws/aws-sam-cli/issues/2466) vs using the `ImageConfig` property.
|
138
168
|
|
139
169
|
🎉 Deploy your application and have fun with ActiveJob on SQS & Lambda.
|
140
170
|
|
@@ -148,9 +178,9 @@ Most general Lambdakiq configuration options are exposed via the Rails standard
|
|
148
178
|
config.lambdakiq
|
149
179
|
```
|
150
180
|
|
151
|
-
|
152
|
-
|
153
|
-
|
181
|
+
- `max_retries=` - Retries for all jobs. Default is the Lambdakiq maximum of `12`.
|
182
|
+
- `metrics_namespace=` - The CloudWatch Embedded Metrics namespace. Default is `Lambdakiq`.
|
183
|
+
- `metrics_logger=` - Set to the Rails logger which is STDOUT via Lamby/Lambda.
|
154
184
|
|
155
185
|
### ActiveJob Configs
|
156
186
|
|
@@ -162,13 +192,7 @@ class OrderProcessorJob < ApplicationJob
|
|
162
192
|
end
|
163
193
|
```
|
164
194
|
|
165
|
-
|
166
|
-
|
167
|
-
## Concurrency & Limits
|
168
|
-
|
169
|
-
AWS SQS is highly scalable with [few limits](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-quotas.html). As your jobs in SQS increases so should your concurrent functions to process that work. However, as this article, ["Why isn't my Lambda function with an Amazon SQS event source scaling optimally?"](https://aws.amazon.com/premiumsupport/knowledge-center/lambda-sqs-scaling/) describes it is possible that errors will effect your concurrency.
|
170
|
-
|
171
|
-
To help keep your queue and workers scalable, reduce the errors raised by your jobs. You an also reduce the retry count.
|
195
|
+
- `retry` - Overrides the default Lambdakiq `max_retries` for this one job.
|
172
196
|
|
173
197
|
## Observability with CloudWatch
|
174
198
|
|
@@ -180,42 +204,43 @@ Metrics are published under the `Lambdakiq` namespace. This is configurable usin
|
|
180
204
|
|
181
205
|
### Metric Dimensions
|
182
206
|
|
183
|
-
|
184
|
-
|
185
|
-
|
207
|
+
- `AppName` - This is the name of your Rails application. Ex: `MyApp`
|
208
|
+
- `JobEvent` - Name of the ActiveSupport Notification. Ex: `*.active_job`.
|
209
|
+
- `JobName` - The class name of the ActiveSupport job. Ex: `NotificationJob`
|
186
210
|
|
187
211
|
### ActiveJob Event Names
|
212
|
+
|
188
213
|
For reference, here are the `JobEvent` names published by ActiveSupport. A few of these are instrumented by Lambdakiq since we use custom retry logic like Sidekiq. These event/metrics are found in the Rails application CloudWatch logs because they publish/enqueue jobs.
|
189
214
|
|
190
|
-
|
191
|
-
|
215
|
+
- `enqueue.active_job`
|
216
|
+
- `enqueue_at.active_job`
|
192
217
|
|
193
218
|
While these event/metrics can be found in the jobs function's log.
|
194
219
|
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
220
|
+
- `perform_start.active_job`
|
221
|
+
- `perform.active_job`
|
222
|
+
- `enqueue_retry.active_job`
|
223
|
+
- `retry_stopped.active_job`
|
199
224
|
|
200
225
|
### Metric Properties
|
201
226
|
|
202
227
|
These are the properties published with each metric. Remember, properties can not be used as metric data in charts but can be searched using [CloudWatch Logs Insights](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/AnalyzingLogData.html).
|
203
228
|
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
229
|
+
- `JobId` - ActiveJob Unique ID. Ex: `9f3b6977-6afc-4769-aed6-bab1ad9a0df5`
|
230
|
+
- `QueueName` - SQS Queue Name. Ex: `myapp-JobsQueue-14F18LG6XFUW5.fifo`
|
231
|
+
- `MessageId` - SQS Message ID. Ex: `5653246d-dc5e-4c95-9583-b6b83ec78602`
|
232
|
+
- `ExceptionName` - Class name of error raised. Present in perform and retry events.
|
233
|
+
- `EnqueuedAt` - When ActiveJob enqueued the message. Ex: `2021-01-14T01:43:38Z`
|
234
|
+
- `Executions` - The number of current executions. Counts from `1` and up.
|
235
|
+
- `JobArg#{n}` - Enumerated serialized arguments.
|
211
236
|
|
212
237
|
### Metric Data
|
213
238
|
|
214
239
|
And finally, here are the metrics which each dimension can chart using [CloudWatch Metrics & Dashboards](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Dashboards.html).
|
215
240
|
|
216
|
-
|
217
|
-
|
218
|
-
|
241
|
+
- `Duration` - Of the job event in milliseconds.
|
242
|
+
- `Count` - Of the event.
|
243
|
+
- `ExceptionCount` - Of the event. Useful with `ExceptionName`.
|
219
244
|
|
220
245
|
### CloudWatch Dashboard Examples
|
221
246
|
|
@@ -223,15 +248,14 @@ Please share how you are using CloudWatch to monitor and/or alert on your Active
|
|
223
248
|
|
224
249
|
💬 https://github.com/customink/lambdakiq/discussions/3
|
225
250
|
|
226
|
-
|
227
251
|
## Common Questions
|
228
252
|
|
229
253
|
**Are Scheduled Jobs Supported?** - No. If you need a scheduled job please use the [SAM Schedule](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-schedule.html) event source which invokes your function with an [Eventbridege AWS::Events::Rule](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-events-rule.html).
|
230
254
|
|
231
255
|
**Are FIFO Queues Supported?** - Yes. When you create your [AWS::SQS::Queue](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-sqs-queues.html) resources you can set the [FifoQueue](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-sqs-queues.html#aws-sqs-queue-fifoqueue) property to `true`. Remember that both your jobs queue and the redrive queue must be the same. When using FIFO we:
|
232
256
|
|
233
|
-
|
234
|
-
|
257
|
+
- Simulate `delay_seconds` for ActiveJob's wait by using visibility timeouts under the hood. We still cap it to non-FIFO's 15 minutes.
|
258
|
+
- Set both the messages `message_group_id` and `message_deduplication_id` to the unique job id provided by ActiveJob.
|
235
259
|
|
236
260
|
**Can I Use Multiple Queues?** - Yes. Nothing is stopping you from creating any number of queues and/or functions to process them. Your subclasses can use ActiveJob's `queue_as` method as needed. This is an easy way to handle job priorities too.
|
237
261
|
|
data/lib/lambdakiq/error.rb
CHANGED
@@ -2,16 +2,6 @@ module Lambdakiq
|
|
2
2
|
class Error < StandardError
|
3
3
|
end
|
4
4
|
|
5
|
-
class JobError < Error
|
6
|
-
attr_reader :original_exception, :job
|
7
|
-
|
8
|
-
def initialize(error)
|
9
|
-
@original_exception = error
|
10
|
-
super(error.message)
|
11
|
-
set_backtrace Rails.backtrace_cleaner.clean(error.backtrace)
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
5
|
class FifoDelayError < Error
|
16
6
|
def initialize(error)
|
17
7
|
super
|
data/lib/lambdakiq/job.rb
CHANGED
@@ -9,9 +9,9 @@ module Lambdakiq
|
|
9
9
|
records = Event.records(event)
|
10
10
|
jobs = records.map { |record| new(record) }
|
11
11
|
jobs.each(&:perform)
|
12
|
-
|
13
|
-
|
14
|
-
|
12
|
+
failed_jobs = jobs.select { |j| j.error }
|
13
|
+
item_failures = failed_jobs.map { |j| { itemIdentifier: j.provider_job_id } }
|
14
|
+
{ batchItemFailures: item_failures }
|
15
15
|
end
|
16
16
|
|
17
17
|
end
|
@@ -40,10 +40,14 @@ module Lambdakiq
|
|
40
40
|
active_job.executions
|
41
41
|
end
|
42
42
|
|
43
|
+
def provider_job_id
|
44
|
+
active_job.provider_job_id
|
45
|
+
end
|
46
|
+
|
43
47
|
def perform
|
44
48
|
if fifo_delay?
|
45
49
|
fifo_delay
|
46
|
-
|
50
|
+
return
|
47
51
|
end
|
48
52
|
execute
|
49
53
|
end
|
@@ -104,6 +108,7 @@ module Lambdakiq
|
|
104
108
|
end
|
105
109
|
|
106
110
|
def fifo_delay
|
111
|
+
@error = FifoDelayError.new(active_job.job_id)
|
107
112
|
params = client_params.merge visibility_timeout: record.fifo_delay_visibility_timeout
|
108
113
|
client.change_message_visibility(params)
|
109
114
|
end
|
data/lib/lambdakiq/metrics.rb
CHANGED
@@ -16,6 +16,7 @@ module Lambdakiq
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def log
|
19
|
+
return unless lambdakiq?
|
19
20
|
logger.info JSON.dump(message)
|
20
21
|
end
|
21
22
|
|
@@ -29,6 +30,16 @@ module Lambdakiq
|
|
29
30
|
job.class.name
|
30
31
|
end
|
31
32
|
|
33
|
+
def adapter_name
|
34
|
+
event.payload[:adapter].class.name
|
35
|
+
end
|
36
|
+
|
37
|
+
def lambdakiq?
|
38
|
+
adapter_name.include?('Lambdakiq') &&
|
39
|
+
job.respond_to?(:lambdakiq?) &&
|
40
|
+
job.lambdakiq?
|
41
|
+
end
|
42
|
+
|
32
43
|
def logger
|
33
44
|
Lambdakiq.config.metrics_logger
|
34
45
|
end
|
@@ -51,6 +62,7 @@ module Lambdakiq
|
|
51
62
|
end
|
52
63
|
|
53
64
|
def instrument!
|
65
|
+
return unless lambdakiq?
|
54
66
|
put_metric 'Duration', event.duration.to_i, 'Milliseconds'
|
55
67
|
put_metric 'Count', 1, 'Count'
|
56
68
|
put_metric 'ExceptionCount', 1, 'Count' if exception_name
|
@@ -59,7 +71,7 @@ module Lambdakiq
|
|
59
71
|
set_property 'QueueName', job.queue_name
|
60
72
|
set_property 'MessageId', job.provider_job_id if job.provider_job_id
|
61
73
|
set_property 'ExceptionName', exception_name if exception_name
|
62
|
-
set_property 'EnqueuedAt', job.enqueued_at if job.enqueued_at
|
74
|
+
set_property 'EnqueuedAt', job.enqueued_at if job.respond_to?(:enqueued_at) && job.enqueued_at
|
63
75
|
set_property 'Executions', job.executions if job.executions
|
64
76
|
job.arguments.each_with_index do |argument, index|
|
65
77
|
set_property "JobArg#{index+1}", argument
|
@@ -107,4 +119,3 @@ module Lambdakiq
|
|
107
119
|
|
108
120
|
end
|
109
121
|
end
|
110
|
-
|
data/lib/lambdakiq/version.rb
CHANGED
data/lib/lambdakiq/worker.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lambdakiq
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ken Collins
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-11-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activejob
|