Skip to content

Commit 57adc64

Browse files
committed
Initial Working Project.
1 parent 696959c commit 57adc64

33 files changed

+640
-355
lines changed

.github/workflows/main.yml

-18
This file was deleted.

.github/workflows/test.yml

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
name: Test
2+
on: [push]
3+
jobs:
4+
test:
5+
runs-on: ubuntu-latest
6+
steps:
7+
- name: Checkout
8+
uses: actions/checkout@v2
9+
- name: Bootstrap
10+
run: |
11+
./bin/bootstrap
12+
- name: Setup
13+
run: |
14+
./bin/setup
15+
- name: Test
16+
run: |
17+
./bin/test

.gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
/.bundle/
1+
/.bundle/*
2+
/vendor/bunlde/*
23
/.yardoc
34
/_yardoc/
45
/coverage/

Dockerfile

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
FROM public.ecr.aws/sam/build-ruby2.7
2+
WORKDIR /var/task

Gemfile

+2-7
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
1-
# frozen_string_literal: true
2-
31
source "https://rubygems.org"
4-
5-
# Specify your gem's dependencies in lambda_punch.gemspec
62
gemspec
73

8-
gem "rake", "~> 13.0"
9-
10-
gem "minitest", "~> 5.0"
4+
gem "rake"
5+
gem "minitest"

Gemfile.lock

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
PATH
2+
remote: .
3+
specs:
4+
lambda_punch (0.0.2)
5+
concurrent-ruby
6+
rb-inotify
7+
timeout
8+
9+
GEM
10+
remote: https://rubygems.org/
11+
specs:
12+
concurrent-ruby (1.1.9)
13+
ffi (1.15.3)
14+
minitest (5.14.4)
15+
rake (13.0.3)
16+
rb-inotify (0.10.1)
17+
ffi (~> 1.0)
18+
timeout (0.1.1)
19+
20+
PLATFORMS
21+
x86_64-linux
22+
23+
DEPENDENCIES
24+
lambda_punch!
25+
minitest
26+
rake
27+
28+
BUNDLED WITH
29+
2.2.21

README.md

+102-19
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,126 @@
1-
# LambdaPunch
1+
![LambdaPunch](https://user-images.githubusercontent.com/2381/123561512-c23fb580-d776-11eb-9780-71d606cd8f2c.png)
22

3-
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/lambda_punch`. To experiment with that code, run `bin/console` for an interactive prompt.
3+
![Test](https://github.com/customink/lambda_punch/workflows/Test/badge.svg)
44

5-
TODO: Delete this and the text above, and describe your gem
5+
# 👊 LambdaPunch
66

7-
## Installation
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>Asynchronous background job processing for AWS Lambda with Ruby using [Lambda Extensions](https://docs.aws.amazon.com/lambda/latest/dg/runtimes-extensions-api.html). Inspired by the [SuckerPunch](https://github.com/brandonhilkert/sucker_punch) gem but specifically tooled to work with Lambda's invoke model.
88

9-
Add this line to your application's Gemfile:
9+
**For a more robust background job solution, please consider using AWS SQS with the [Lambdakiq](https://github.com/customink/lambdakiq) gem. 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+
11+
## 🏗 Architecture
12+
13+
Because AWS Lambda [freezes the execution environment](https://docs.aws.amazon.com/lambda/latest/dg/runtimes-context.html) after each invoke, there is no "process" that continues to run after the handler's response. However, thanks to Lambda Extensions along with its "early return", we can do two important things. First, we leverage the [rb-inotify](https://github.com/guard/rb-inotify) gem to send the extension process a simulated `POST-INVOKE` event. We then use [Distributed Ruby](https://ruby-doc.org/stdlib-3.0.1/libdoc/drb/rdoc/DRb.html) (DRb) from the extension to signal your application to work jobs off a queue. Both of these are synchronous calls. Once complete the LambdaPunch extensions signals it is done and your function is ready for the next request.
14+
15+
<img src="https://user-images.githubusercontent.com/2381/123647632-408f6c80-d7f6-11eb-8e39-fb4ee92b1ffa.png" width="100%" alt="AWS Lambda Extensions with LambdaPunch async job queue processing." >
16+
17+
The LambdaPunch extension process is very small and lean. It only requires a few Ruby libraries and needed gems from your application's bundle. Its only job is to send signals back to your application on the runtime. It does this within a few milliseconds and adds no noticeable overhead to your function.
18+
19+
## 🎁 Installation
20+
21+
Add this line to your project's `Gemfile` and then make sure to `bundle install` afterward.
1022

1123
```ruby
1224
gem 'lambda_punch'
1325
```
1426

15-
And then execute:
27+
Now, within your application's handler file, make sure to start the LambdaPunch DRb server outside of your handler method. Within the handler method, add an `ensure` section that lets the extension process know the request is done.
1628

17-
$ bundle install
29+
```ruby
30+
LambdaPunch.start_server!
1831

19-
Or install it yourself as:
32+
def handler(event:, context:)
33+
# ...
34+
ensure
35+
LambdaPunch.handled!(context)
36+
end
37+
```
2038

21-
$ gem install lambda_punch
39+
Within your project or [Rails application's](https://lamby.custominktech.com/docs/anatomy) `Dockerfile`, after you copy your code, add this `RUN` command to install the extension within your container's `/opt/extensions` directory.
40+
41+
```dockerfile
42+
RUN bundle exec rake lambda_punch:install
43+
```
44+
45+
## 🧰 Usage
46+
47+
Anywhere in your application's code, use the `LambdaPunch.push` method to add blocks of code to your jobs queue.
48+
49+
```ruby
50+
LambdaPunch.push do
51+
# ...
52+
end
53+
```
54+
55+
For example, if you are using Rails with AWS Lambda via the [Lamby](https://lamby.custominktech.com/) gem along with [New Relic APM](https://dev.to/aws-heroes/using-new-relic-apm-with-rails-on-aws-lambda-51gi) here is how your handler function might appear to ensure their metrics are flushed after each request.
56+
57+
```ruby
58+
def handler(event:, context:)
59+
Lamby.handler $app, event, context
60+
ensure
61+
LambdaPunch.push { NewRelic::Agent.agent.flush_pipe_data }
62+
LambdaPunch.handled!(context)
63+
end
64+
```
2265

23-
## Usage
66+
### ActiveJob
2467

25-
TODO: Write usage instructions here
68+
🚧 COMING SOON 🚧 - A simple ActiveJob adapter...
2669

27-
## Development
70+
### Timeouts
2871

29-
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
72+
Your function's timeout is the max amount to handle the request and process all extension's invoke events. If your function times out, it is possible that queued jobs will not be processed until the next invoke.
73+
74+
If your application integrates with API Gateway (which has a 30 second timeout) then it is possible your function can be set with a higher timeout to perform background work. Since work is done after each invoke, the LambdaPunch queue should be empty when your function receives the `SHUTDOWN` event. If jobs are in the queue when this happens they will have two seconds max to work them down before being lost.
75+
76+
**For a more robust background job solution, please consider using AWS SQS with the [Lambdakiq](https://github.com/customink/lambdakiq) gem. 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.**
77+
78+
### Logging
79+
80+
The default log level is `fatal`, so you will not see any LambdaPunch lines in your logs. However, if you want some low level debugging information on how LambdaPunch is working, you can use this environment variable to change the log level.
81+
82+
```yaml
83+
Environment:
84+
Variables:
85+
LAMBDA_PUNCH_LOG_LEVEL: debug
86+
```
87+
88+
## 📊 CloudWatch Metrics
89+
90+
When using Extensions, your function's CloudWatch `Duration` metrics will be the sum of your response time combined with your extension's execution time. For example, if your request takes `200ms` to respond but your background task takes `1000ms` your duration will be a combined `1200ms`. For more details see the _"Performance impact and extension overhead"_ section of the [Lambda Extensions API
91+
](https://docs.aws.amazon.com/lambda/latest/dg/runtimes-extensions-api.html)
92+
93+
Thankfully, when using Lambda Extensions, CloudWatch will create a `PostRuntimeExtensionsDuration` metric that you can use to isolate your true response times `Duration` [using some metric math](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/using-metric-math.html). Here is an example where the metric math above is used in the first "Duration (response)" widget.
94+
95+
![metric-math](https://user-images.githubusercontent.com/2381/123561591-4eea7380-d777-11eb-8682-c20b9460f112.png)
96+
97+
![durations](https://user-images.githubusercontent.com/2381/123561590-4e51dd00-d777-11eb-96b2-d886c91aedb0.png)
98+
99+
## 👷🏽‍♀️ Development
100+
101+
After checking out the repo, run the following commands to build a Docker image and install dependencies.
102+
103+
```shell
104+
$ ./bin/bootstrap
105+
$ ./bin/setup
106+
```
107+
108+
Then, to run the tests use the following command.
109+
110+
```shell
111+
$ ./bin/test
112+
```
30113

31-
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
114+
You can also run the `./bin/console` command for an interactive prompt within the development Docker container. Likewise you can use `./bin/run ...` followed by any command which would be executed within the same container.
32115

33-
## Contributing
116+
## 💖 Contributing
34117

35-
Bug reports and pull requests are welcome on GitHub at https://github.com/metaskills/lambda_punch. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/metaskills/lambda_punch/blob/main/CODE_OF_CONDUCT.md).
118+
Bug reports and pull requests are welcome on GitHub at https://github.com/customink/lambda_punch. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/customink/lambda_punch/blob/main/CODE_OF_CONDUCT.md).
36119

37-
## License
120+
## 👩‍⚖️ License
38121

39122
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
40123

41-
## Code of Conduct
124+
## 🤝 Code of Conduct
42125

43-
Everyone interacting in the LambdaPunch project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/metaskills/lambda_punch/blob/main/CODE_OF_CONDUCT.md).
126+
Everyone interacting in the LambdaPunch project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/customink/lambda_punch/blob/main/CODE_OF_CONDUCT.md).

Rakefile

-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
# frozen_string_literal: true
2-
31
require "bundler/gem_tasks"
42
require "rake/testtask"
53

0 commit comments

Comments
 (0)