{"id":26163486,"url":"https://github.com/rails-lambda/lambda_punch","last_synced_at":"2025-07-18T17:35:07.190Z","repository":{"id":40010654,"uuid":"379589772","full_name":"rails-lambda/lambda_punch","owner":"rails-lambda","description":"🐑👊 Asynchronous background job processing for AWS Lambda with Ruby using Lambda Extensions. Inspired by the SuckerPunch gem but specifically tooled to work with Lambda's invoke model.","archived":false,"fork":false,"pushed_at":"2024-12-11T01:35:57.000Z","size":972,"stargazers_count":24,"open_issues_count":3,"forks_count":0,"subscribers_count":10,"default_branch":"main","last_synced_at":"2025-07-08T22:06:07.958Z","etag":null,"topics":["activejob","background-jobs","jobs","lambda-extensions","rails","ruby","serverless"],"latest_commit_sha":null,"homepage":"https://lamby.cloud","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rails-lambda.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2021-06-23T12:06:56.000Z","updated_at":"2025-06-13T05:51:10.000Z","dependencies_parsed_at":"2025-04-13T06:40:49.585Z","dependency_job_id":null,"html_url":"https://github.com/rails-lambda/lambda_punch","commit_stats":null,"previous_names":["customink/lambda_punch"],"tags_count":17,"template":false,"template_full_name":null,"purl":"pkg:github/rails-lambda/lambda_punch","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rails-lambda%2Flambda_punch","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rails-lambda%2Flambda_punch/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rails-lambda%2Flambda_punch/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rails-lambda%2Flambda_punch/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rails-lambda","download_url":"https://codeload.github.com/rails-lambda/lambda_punch/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rails-lambda%2Flambda_punch/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265802018,"owners_count":23830506,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["activejob","background-jobs","jobs","lambda-extensions","rails","ruby","serverless"],"created_at":"2025-03-11T14:27:16.836Z","updated_at":"2025-07-18T17:35:07.153Z","avatar_url":"https://github.com/rails-lambda.png","language":"Ruby","readme":"![LambdaPunch](https://user-images.githubusercontent.com/2381/123561512-c23fb580-d776-11eb-9780-71d606cd8f2c.png)\n\n[![Test](https://github.com/rails-lambda/lambda_punch/actions/workflows/test.yml/badge.svg)](https://github.com/rails-lambda/lambda_punch/actions/workflows/test.yml)\n\n# 👊 LambdaPunch\n\n\u003ca href=\"https://lamby.cloud\"\u003e\u003cimg src=\"https://raw.githubusercontent.com/rails-lambda/lamby/master/images/social2.png\" alt=\"Lamby: Simple Rails \u0026 AWS Lambda Integration using Rack.\" align=\"right\" width=\"450\" style=\"margin-left:1rem;margin-bottom:1rem;\" /\u003e\u003c/a\u003eAsynchronous 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.\n\n**For a more robust background job solution, please consider using AWS SQS with the [Lambdakiq](https://github.com/rails-lambda/lambdakiq) gem. A drop-in replacement for [Sidekiq](https://github.com/mperham/sidekiq) when running Rails in AWS Lambda using the [Lamby](https://lamby.cloud/) gem.**\n\n## 🏗 Architecture\n\nBecause 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.\n\n\u003cimg 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.\" \u003e\n\nThe 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.\n\n## 🎁 Installation\n\nAdd this line to your project's `Gemfile` and then make sure to `bundle install` afterward. It is only needed in the `production` group.\n\n```ruby\ngem 'lambda_punch'\n```\n\nWithin your project or [Rails application's](https://lamby.cloud/docs/anatomy) `Dockerfile`, add the following. Make sure you do this before you `COPY` your code. The idea is to implicitly use the default `USER root` since it needs permission to create an `/opt/extensions` directory.\n\n```dockerfile\nRUN gem install lambda_punch \u0026\u0026 lambda_punch install\n```\n\nLambdaPunch uses the `LAMBDA_TASK_ROOT` environment variable to find your project's Gemfile. If you are using a provided AWS Runtime container, this should be set for you to `/var/task`. However, if you are using your own base image, make sure to set this to your project directory.\n\n```dockerfile\nENV LAMBDA_TASK_ROOT=/app\n```\n\nInstallation with AWS Lambda via the [Lamby](https://lamby.cloud/) v4 (or higher) gem can be done using Lamby's `handled_proc` config. For example, appends these to your `config/environments/production.rb` file. Here we are ensuring that the LambdaPunch DRb server is running and that after each Lamby request we notify LambdaPunch.\n\n```ruby\nconfig.to_prepare { LambdaPunch.start_server! }\nconfig.lamby.handled_proc = Proc.new do |_event, context|\n  LambdaPunch.handled!(context)\nend\n```\n\nIf you are using an older version of Lamby or a simple Ruby project with your own handler method, the installation would look something like this:\n\n```ruby\nLambdaPunch.start_server!\ndef handler(event:, context:)\n  # ...\nensure\n  LambdaPunch.handled!(context)\nend\n```\n\n## 🧰 Usage\n\nAnywhere in your application's code, use the `LambdaPunch.push` method to add blocks of code to your jobs queue.\n\n```ruby\nLambdaPunch.push do\n  # ...\nend\n```\n\nA common use case would be to ensure the [New Relic APM](https://dev.to/aws-heroes/using-new-relic-apm-with-rails-on-aws-lambda-51gi) flushes its data after each request. Using Lamby in your `config/environments/production.rb`  file would look like this:\n\n```ruby\nconfig.to_prepare { LambdaPunch.start_server! }\nconfig.lamby.handled_proc = Proc.new do |_event, context|\n  LambdaPunch.push { NewRelic::Agent.agent.flush_pipe_data }\n  LambdaPunch.handled!(context)\nend\n```\n\n### ActiveJob\n\nYou can use LambdaPunch with Rails' ActiveJob. **For a more robust background job solution, please consider using AWS SQS with the [Lambdakiq](https://github.com/rails-lambda/lambdakiq) gem.**\n\n```ruby\nconfig.active_job.queue_adapter = :lambda_punch\n```\n\n### Timeouts\n\nYour 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.\n\nIf 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.\n\n**For a more robust background job solution, please consider using AWS SQS with the [Lambdakiq](https://github.com/rails-lambda/lambdakiq) gem.**\n\n### Logging\n\nThe default log level is `error`, 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.\n\n```yaml\nEnvironment:\n  Variables:\n    LAMBDA_PUNCH_LOG_LEVEL: debug\n```\n\n### Errors\n\nAs jobs are worked off the queue, all job errors are simply logged. If you want to customize this, you can set your own error handler.\n\n```ruby\nLambdaPunch.error_handler = lambda { |e| ... }\n```\n\n## 📊 CloudWatch Metrics\n\nWhen 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\n](https://docs.aws.amazon.com/lambda/latest/dg/runtimes-extensions-api.html)\n\nThankfully, 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.\n\n![metric-math](https://user-images.githubusercontent.com/2381/123561591-4eea7380-d777-11eb-8682-c20b9460f112.png)\n\n![durations](https://user-images.githubusercontent.com/2381/123561590-4e51dd00-d777-11eb-96b2-d886c91aedb0.png)\n\n## 👷🏽‍♀️ Development\n\nAfter checking out the repo, run the following commands to build a Docker image and install dependencies.\n\n```shell\n$ ./bin/bootstrap\n$ ./bin/setup\n```\n\nThen, to run the tests use the following command.\n\n```shell\n$ ./bin/test\n```\n\nYou 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.\n\n## 💖 Contributing\n\nBug reports and pull requests are welcome on GitHub at https://github.com/rails-lambda/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/rails-lambda/lambda_punch/blob/main/CODE_OF_CONDUCT.md).\n\n## 👩‍⚖️ License\n\nThe gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).\n\n## 🤝 Code of Conduct\n\nEveryone 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/rails-lambda/lambda_punch/blob/main/CODE_OF_CONDUCT.md).\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frails-lambda%2Flambda_punch","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frails-lambda%2Flambda_punch","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frails-lambda%2Flambda_punch/lists"}