lambdakiq 1.0.1 → 2.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: 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