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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5c2216c121ae2ba5e013c8852bc71d8e3e84342cd67053e4d9933a88562c543a
4
- data.tar.gz: f1c48212d2f14307fbb7a09ac10748d108b0d705270df8e3a5b54047bdcee758
3
+ metadata.gz: 94804599e9c2e4f8f701c34cee61a7d18db8f9a20fee85a3e24681e2bd555076
4
+ data.tar.gz: 7550236dacfc649a36468bbc4b3a7116a03b90d5943df4cdb0c08f7521ad505f
5
5
  SHA512:
6
- metadata.gz: 687c1792d5f9e15ce30db4226a0248b46a874e2c8b55e81327a871977ccbff375ec67eb3c8279a2ff565e7e1c9f05f5147f70a0bec1e6308e8a8fbc2863440f3
7
- data.tar.gz: 0e88a280f225db1dfb8251b0622e99bf3d781dba6c817fe8f70b8345725613421578da31cc5bac37d8664fd0390431518a741f92d71e8da4087506c742719e10
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
- * Initial release.
21
+ - Initial release.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- lambdakiq (1.0.1)
4
+ lambdakiq (2.0.0)
5
5
  activejob
6
6
  aws-sdk-sqs
7
7
  concurrent-ruby
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
- * 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.
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/initializers/production.rb` and set Lambdakiq as your ActiveJob queue adapter.
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: '2012-10-17'
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: '2012-10-17'
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
- * The `Events` property uses the [SQS Type](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-sqs.html).
135
- * Our [BatchSize](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-function-sqs.html#sam-function-sqs-batchsize) is set to one so we can handle retrys more easily without worrying about idempotency in larger batches.
136
- * 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.
137
- * 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.
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
- * `max_retries=` - Retries for all jobs. Default is the Lambdakiq maximum of `12`.
152
- * `metrics_namespace=` - The CloudWatch Embedded Metrics namespace. Default is `Lambdakiq`.
153
- * `metrics_logger=` - Set to the Rails logger which is STDOUT via Lamby/Lambda.
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
- * `retry` - Overrides the default Lambdakiq `max_retries` for this one job.
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
- * `AppName` - This is the name of your Rails application. Ex: `MyApp`
184
- * `JobEvent` - Name of the ActiveSupport Notification. Ex: `*.active_job`.
185
- * `JobName` - The class name of the ActiveSupport job. Ex: `NotificationJob`
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
- * `enqueue.active_job`
191
- * `enqueue_at.active_job`
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
- * `perform_start.active_job`
196
- * `perform.active_job`
197
- * `enqueue_retry.active_job`
198
- * `retry_stopped.active_job`
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
- * `JobId` - ActiveJob Unique ID. Ex: `9f3b6977-6afc-4769-aed6-bab1ad9a0df5`
205
- * `QueueName` - SQS Queue Name. Ex: `myapp-JobsQueue-14F18LG6XFUW5.fifo`
206
- * `MessageId` - SQS Message ID. Ex: `5653246d-dc5e-4c95-9583-b6b83ec78602`
207
- * `ExceptionName` - Class name of error raised. Present in perform and retry events.
208
- * `EnqueuedAt` - When ActiveJob enqueued the message. Ex: `2021-01-14T01:43:38Z`
209
- * `Executions` - The number of current executions. Counts from `1` and up.
210
- * `JobArg#{n}` - Enumerated serialized arguments.
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
- * `Duration` - Of the job event in milliseconds.
217
- * `Count` - Of the event.
218
- * `ExceptionCount` - Of the event. Useful with `ExceptionName`.
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
- * Simulate `delay_seconds` for ActiveJob's wait by using visibility timeouts under the hood. We still cap it to non-FIFO's 15 minutes.
234
- * Set both the messages `message_group_id` and `message_deduplication_id` to the unique job id provided by ActiveJob.
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
 
@@ -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
- jwerror = jobs.detect{ |j| j.error }
13
- return unless jwerror
14
- raise JobError.new(jwerror.error)
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
- raise FifoDelayError, active_job.job_id
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
@@ -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
-
@@ -1,3 +1,3 @@
1
1
  module Lambdakiq
2
- VERSION = '1.0.1'
2
+ VERSION = '2.0.0'
3
3
  end
@@ -16,6 +16,10 @@ module Lambdakiq
16
16
 
17
17
  end
18
18
 
19
+ def lambdakiq?
20
+ true
21
+ end
22
+
19
23
  def lambdakiq_retry
20
24
  lambdakiq_options_hash[:retry]
21
25
  end
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: 1.0.1
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-02-01 00:00:00.000000000 Z
11
+ date: 2021-11-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activejob