[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-7941":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":16,"subscribersCount":16,"size":16,"stars1d":16,"stars7d":16,"stars30d":16,"stars90d":16,"forks30d":16,"starsTrendScore":16,"compositeScore":17,"rankGlobal":10,"rankLanguage":10,"license":18,"archived":19,"fork":19,"defaultBranch":20,"hasWiki":19,"hasPages":19,"topics":21,"createdAt":10,"pushedAt":10,"updatedAt":22,"readmeContent":23,"aiSummary":24,"trendingCount":16,"starSnapshotCount":16,"syncStatus":15,"lastSyncTime":25,"discoverSource":26},7941,"sucker_punch","brandonhilkert\u002Fsucker_punch","brandonhilkert","Sucker Punch is a Ruby asynchronous processing library using concurrent-ruby, heavily influenced by Sidekiq and girl_friday.","",null,"Ruby",2633,109,34,2,0,28.12,"MIT License",false,"master",[],"2026-06-12 02:01:46","# Sucker Punch\n\n[![Build](https:\u002F\u002Fgithub.com\u002Fbrandonhilkert\u002Fsucker_punch\u002Factions\u002Fworkflows\u002Fbuild.yml\u002Fbadge.svg)](https:\u002F\u002Fgithub.com\u002Fbrandonhilkert\u002Fsucker_punch\u002Factions\u002Fworkflows\u002Fbuild.yml)\n[![Code Climate](https:\u002F\u002Fcodeclimate.com\u002Fgithub\u002Fbrandonhilkert\u002Fsucker_punch.svg)](https:\u002F\u002Fcodeclimate.com\u002Fgithub\u002Fbrandonhilkert\u002Fsucker_punch)\n\nSucker Punch is a single-process Ruby asynchronous processing library.\nThis reduces costs\nof hosting on a service like Heroku along with the memory footprint of\nhaving to maintain additional jobs if hosting on a dedicated server.\nAll queues can run within a single application (eg. Rails, Sinatra, etc.) process.\n\nSucker Punch is perfect for asynchronous processes like emailing, data\ncrunching, or social platform manipulation. No reason to hold up a\nuser when you can do these things in the background within the same\nprocess as your web application...\n\nSucker Punch is built on top of [concurrent-ruby](https:\u002F\u002Fgithub.com\u002Fruby-concurrency\u002Fconcurrent-ruby). Each job is setup as\na pool, which equates to its own queue with individual workers working against\nthe jobs. Unlike most other background processing libraries, Sucker\nPunch's jobs are stored in memory. The benefit to this is there is no\nadditional infrastructure requirement (ie. database, redis, etc.). However,\nif the web processes are restarted with jobs remaining in the queue,\nthey will be lost. For this reason, Sucker Punch is generally\nrecommended for jobs that are fast and non-mission critical (ie. logs, emails,\netc.).\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n    gem 'sucker_punch', '~> 3.0'\n\nAnd then execute:\n\n    $ bundle\n\nYou can also run (same as two steps above):\n\n    bundle add sucker_punch\n\nOr install it yourself as:\n\n    $ gem install sucker_punch\n\n## Backwards Compatibility\n\nNo breaking changes were introduced in version 3.x.\n\nIn version `~> 2.0.0`, the syntax to enqueue an asynchronous background job was changed from:\n\n```ruby\nLogJob.new.async.perform(...)\n```\n\nto:\n\n\n```ruby\nLogJob.perform_async(...)\n```\n\nIf you're upgrading from a pre-`2.0.0` release and want to retain the old\nsyntax `LogJob.new.async.perform(...)`, you can include\n`sucker_punch\u002Fasync_syntax` in your application.\n\nFor Rails, you could add an initializer:\n\n```ruby\n# config\u002Finitializers\u002Fsucker_punch.rb\n\nrequire 'sucker_punch\u002Fasync_syntax'\n```\n\n## Usage\n\nEach job acts as its own queue and should be a separate Ruby class that:\n\n* includes `SuckerPunch::Job`\n* defines the `perform` instance method that includes the code the job will run when enqueued\n\n\n```Ruby\n# app\u002Fjobs\u002Flog_job.rb\n\nclass LogJob\n  include SuckerPunch::Job\n\n  def perform(event)\n    Log.new(event).track\n  end\nend\n```\n\n#### Synchronous\n\n```Ruby\nLogJob.new.perform(\"login\")\n```\n\n#### Asynchronous\n\n```Ruby\nLogJob.perform_async(\"login\")\n```\n\n#### Configure the # of the Workers\n\nThe default number of workers (threads) running against your job is `2`. If\nyou'd like to configure this manually, the number of workers can be\nset on the job using the `workers` class method:\n\n```Ruby\nclass LogJob\n  include SuckerPunch::Job\n  workers 4\n\n  def perform(event)\n    Log.new(event).track\n  end\nend\n```\n\n#### Configure the Queue Size\n\nThe default number of jobs that can be queued is unlimited. If you wish to restrict this you can set\nmax\\_jobs as follows:\n\n```Ruby\nclass LogJob\n  include SuckerPunch::Job\n  max_jobs 10\n\n  def perform(event)\n    Log.new(event).track\n  end\nend\n```\n\n#### Executing Jobs in the Future\n\nMany background processing libraries have methods to perform operations after a\ncertain amount of time and Sucker Punch is no different. Use the `perform_in`\nwith an argument of the number of seconds in the future you would like the job\nto job to run.\n\n``` ruby\nclass DataJob\n  include SuckerPunch::Job\n\n  def perform(data)\n    puts data\n  end\nend\n\nDataJob.perform_async(\"asdf\") # immediately perform asynchronously\nDataJob.perform_in(60, \"asdf\") # `perform` will be executed 60 sec. later\n```\n\n#### `ActiveRecord` Connection Pool Connections\n\nJobs interacting with `ActiveRecord` should take special precaution not to\nexhaust connections in the pool. This can be done\nwith `ActiveRecord::Base.connection_pool.with_connection`, which ensures\nthe connection is returned back to the pool when completed.\n\n```Ruby\n# app\u002Fjobs\u002Fawesome_job.rb\n\nclass AwesomeJob\n  include SuckerPunch::Job\n\n  def perform(user_id)\n    ActiveRecord::Base.connection_pool.with_connection do\n      user = User.find(user_id)\n      user.update(is_awesome: true)\n    end\n  end\nend\n```\n\nWe can create a job from within another job:\n\n```Ruby\nclass AwesomeJob\n  include SuckerPunch::Job\n\n  def perform(user_id)\n    ActiveRecord::Base.connection_pool.with_connection do\n      user = User.find(user_id)\n      user.update_attributes(is_awesome: true)\n      LogJob.perform_async(\"User #{user.id} became awesome!\")\n    end\n  end\nend\n```\n\n#### Logger\n\n```Ruby\nSuckerPunch.logger = Logger.new('sucker_punch.log')\nSuckerPunch.logger # => #\u003CLogger:0x007fa1f28b83f0>\n```\n\n_Note: If Sucker Punch is being used within a Rails application, Sucker Punch's logger\nis set to Rails.logger by default._\n\n#### Exceptions\n\nYou can customize how to handle uncaught exceptions that are raised by your jobs.\n\nFor example, using Rails and the ExceptionNotification gem,\nadd a new initializer `config\u002Finitializers\u002Fsucker_punch.rb`:\n\n```Ruby\n# ex    => The caught exception object\n# klass => The job class\n# args  => An array of the args passed to the job\n\nSuckerPunch.exception_handler = -> (ex, klass, args) { ExceptionNotifier.notify_exception(ex) }\n```\n\nOr, using Airbrake:\n\n```Ruby\nSuckerPunch.exception_handler = -> (ex, klass, args) { Airbrake.notify(ex) }\n```\n\n#### Shutdown Timeout\n\nSucker Punch goes through a series of checks to attempt to shut down the queues\nand their threads. A \"shutdown\" command is issued to the queues, which gives\nthem notice but allows them to attempt to finish all remaining jobs.\nSubsequently enqueued jobs are discarded at this time.\n\nThe default `shutdown_timeout` (the # of seconds to wait before forcefully\nkilling the threads) is 8 sec. This is to allow applications hosted on Heroku\nto attempt to shutdown prior to the 10 sec. they give an application to\nshutdown with some buffer.\n\nTo configure something other than the default 8 sec.:\n\n```ruby\n  SuckerPunch.shutdown_timeout = 15 # # of sec. to wait before killing threads\n```\n\n#### Queue Class Methods\n\nSucker Punch leverages concurrent-friendly queues per-process. These class methods operating on all queues might be helpful in some edge cases.\n\n- `SuckerPunch::Queue.stats` - Returns a hash keyed by each queue's name with total, busy, and timeout statistics for each queue's `workers` and `jobs`.\n- `SuckerPunch::Queue.clear` - Calls `clear` for each queue. Susceptible to race conditions. Only use in testing.\n- `SuckerPunch::Queue.shutdown_all` - Used with SuckerPunch's `at_exit` hook. Waits for all queues to be idle using the [shutdown timeout](#shutdown-timeout) configuration above.\n- `SuckerPunch::Queue.wait` - Waits for all queues to become idle with no timeout. \n\n#### Timeouts\n\nUsing `Timeout` causes persistent connections to\n[randomly get corrupted](http:\u002F\u002Fwww.mikeperham.com\u002F2015\u002F05\u002F08\u002Ftimeout-rubys-most-dangerous-api).\nDo not use timeouts as control flow, use built-in connection timeouts.\nIf you decide to use Timeout, only use it as last resort to know something went very wrong and\nideally restart the worker process after every timeout.\n\n## Testing\n\nRequiring this library causes your jobs to run everything inline.\nSo a call to the following will actually be SYNCHRONOUS:\n\n```Ruby\n# spec\u002Fspec_helper.rb\nrequire 'sucker_punch\u002Ftesting\u002Finline'\n```\n\n```Ruby\nLogJob.perform_async(\"login\") # => Will be synchronous and block until job is finished\n```\n\n## Rails\n\nIf you're using Sucker Punch with Rails, there's a built-in generator task:\n\n```ruby\n$ rails g sucker_punch:job logger\n```\n\nwould create the file `app\u002Fjobs\u002Flogger_job.rb` with a unimplemented `#perform`\nmethod.\n\n## Sinatra\n\nIf you're using Sucker Punch with Sinatra, you must require Sucker Punch before Sinatra:\n\n```ruby\n# app.rb\n\nrequire 'sucker_punch'\nrequire 'sinatra'\n```\n\nThis will ensure Sucker Punch's `at_exit()` handler to clean up and shutdown queues does not happen **before** Sinatra *starts up* via its own `at_exit()` handler.\n\n## Active Job\n\nSucker Punch has been added as an Active Job adapter in Rails 4.2.\nSee the [guide](http:\u002F\u002Fedgeguides.rubyonrails.org\u002Factive_job_basics.html) for\nconfiguration and implementation.\n\nAdd Sucker Punch to your `Gemfile`:\n\n```Ruby\ngem 'sucker_punch'\n```\n\nAnd then configure the backend to use Sucker Punch:\n\n```Ruby\n# config\u002Fapplication.rb\nclass Application \u003C Rails::Application\n  # ...\n  config.active_job.queue_adapter = :sucker_punch\nend\n```\n\nIf you want to use Sucker Punch version `2.0.0+` with Rails `\u003C 5.0.0`, be sure\nto include the backwards compatibility module in an initializer:\n\n\n```ruby\n# config\u002Finitializers\u002Fsucker_punch.rb\n\nrequire 'sucker_punch\u002Fasync_syntax'\n```\n\n## FAQ\n\n**What is the difference between `sucker_punch` and `ActiveJob::QueueAdapters::AsyncAdapter`?**\n\nNot much at this point. SuckerPunch existed before ActiveJob, but ultimately uses similar code under the covers. I'd recommend using `AsyncAdapter` if you're using Rails.\n\n\n## Troubleshooting\n\n### Initializers for forking servers (Unicorn, Passenger, etc.)\n\nPreviously, Sucker Punch required an initializer and that posed problems for\nservers that fork (ie. Unicorn and Passenger). Version 1 was rewritten to\nnot require any special code to be executed after forking occurs. Please remove\n if you're using version `>= 1.0.0`\n\n### Cleaning test data transactions\n\nIf you're running tests in transactions (using Database Cleaner or a native solution), Sucker Punch jobs may have trouble finding database records that were created during test setup because the job class is running in a separate thread and the Transaction operates on a different thread so it clears out the data before the job can do its business. The best thing to do is cleanup data created for tests jobs through a truncation strategy by tagging the rspec tests as jobs and then specifying the strategy in `spec_helper` like below. And do not forget to turn off transactional fixtures (delete, comment or set it to `false`).\n\n```Ruby\n# spec\u002Fspec_helper.rb\nRSpec.configure do |config|\n\n  # Turn off transactional fixtures (delete, comment or set it to `false`)\n  # config.use_transactional_fixtures = true\n\n  config.before(:each) do\n    DatabaseCleaner.strategy = :transaction\n  end\n\n  # Clean up all jobs specs with truncation\n  config.before(:each, job: true) do\n    DatabaseCleaner.strategy = :truncation\n  end\n\n  config.before(:each) do\n    DatabaseCleaner.start\n  end\n\n  config.after(:each) do\n    DatabaseCleaner.clean\n  end\nend\n\n# spec\u002Fjobs\u002Femail_job_spec.rb\nrequire 'spec_helper'\n\n# Tag the spec as a job spec so data is persisted long enough for the test\ndescribe EmailJob, job: true do\n  describe \"#perform\" do\n    let(:user) { FactoryGirl.create(:user) }\n\n    it \"delivers an email\" do\n      expect {\n        EmailJob.new.perform(user.id)\n      }.to change{ ActionMailer::Base.deliveries.size }.by(1)\n    end\n  end\nend\n```\n\n## Contributing\n\n1. Fork it\n2. Create your feature branch (`git checkout -b my-new-feature`)\n3. Commit your changes (`git commit -am 'Add some feature'`)\n4. Push to the branch (`git push origin my-new-feature`)\n5. Create new Pull Request\n","Sucker Punch 是一个基于 Ruby 的单进程异步处理库，使用 concurrent-ruby 实现。其核心功能包括在内存中存储任务队列，无需额外的基础设施如数据库或 Redis 支持，从而减少了部署成本和内存占用。适合用于执行非关键性且快速完成的任务，例如发送邮件、数据处理或社交媒体操作等场景，在这些情况下不需要阻塞用户等待。由于任务存储在内存中，如果应用重启未完成的任务将会丢失，因此推荐用于那些可以容忍短暂中断的服务。","2026-06-11 03:15:10","top_language"]