{"id":19719739,"url":"https://github.com/resque/resque-unique_by_arity","last_synced_at":"2026-03-05T10:02:25.556Z","repository":{"id":20360385,"uuid":"89759019","full_name":"resque/resque-unique_by_arity","owner":"resque","description":"Magic hacks which allow fine tuning of job uniqueness (arity, queue-time and run-time)","archived":false,"fork":false,"pushed_at":"2026-01-17T22:45:25.000Z","size":109,"stargazers_count":9,"open_issues_count":1,"forks_count":2,"subscribers_count":3,"default_branch":"main","last_synced_at":"2026-02-19T17:17:13.362Z","etag":null,"topics":["arity","resque","resque-jobs","resque-plugin","uniqueness"],"latest_commit_sha":null,"homepage":"http://railsbling.com/resque-unique_by_arity/","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/resque.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2017-04-29T02:34:38.000Z","updated_at":"2026-01-17T22:45:29.000Z","dependencies_parsed_at":"2024-03-23T00:03:50.929Z","dependency_job_id":null,"html_url":"https://github.com/resque/resque-unique_by_arity","commit_stats":null,"previous_names":["pboling/resque-unique_by_arity"],"tags_count":21,"template":false,"template_full_name":null,"purl":"pkg:github/resque/resque-unique_by_arity","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/resque%2Fresque-unique_by_arity","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/resque%2Fresque-unique_by_arity/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/resque%2Fresque-unique_by_arity/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/resque%2Fresque-unique_by_arity/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/resque","download_url":"https://codeload.github.com/resque/resque-unique_by_arity/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/resque%2Fresque-unique_by_arity/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30118932,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-05T09:35:22.236Z","status":"ssl_error","status_checked_at":"2026-03-05T09:35:20.028Z","response_time":93,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["arity","resque","resque-jobs","resque-plugin","uniqueness"],"created_at":"2024-11-11T23:09:10.109Z","updated_at":"2026-03-05T10:02:25.522Z","avatar_url":"https://github.com/resque.png","language":"Ruby","readme":"# Resque::UniqueByArity\n\nBecause some jobs have parameters that you do not want to consider for\ndetermination of uniqueness.\n\nNOTE:\n\nI rewrote, and renamed, both `resque_solo` and `resque-lonely_job`, because they\n can't be used together.  Why?  Their `redis_key` methods directly conflict,\n among other more subtle issues.\n\nThis gem requires use of my rewritten gems for uniqueness enforcement:\n\n  - [`resque-unique_at_runtime`](https://github.com/pboling/resque-unique_at_runtime)\n  - [`resque-unique_in_queue`](https://github.com/pboling/resque-unique_in_queue)\n\n| Project                 |  Resque::UniqueByArity |\n|------------------------ | ----------------------- |\n| gem name                |  [resque-unique_by_arity](https://rubygems.org/gems/resque-unique_by_arity) |\n| license                 |  [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT) |\n| download rank           |  [![Downloads Today](https://img.shields.io/gem/rd/resque-unique_by_arity.svg)](https://github.com/pboling/resque-unique_by_arity) |\n| version                 |  [![Version](https://img.shields.io/gem/v/resque-unique_by_arity.svg)](https://rubygems.org/gems/resque-unique_by_arity) |\n| dependencies            |  [![Depfu](https://badges.depfu.com/badges/25c6e1e4c671926e9adea898f2df9a47/count.svg)](https://depfu.com/github/pboling/resque-unique_by_arity?project_id=2729) |\n| continuous integration  |  [![Build Status](https://travis-ci.org/pboling/resque-unique_by_arity.svg?branch=master)](https://travis-ci.org/pboling/resque-unique_by_arity) |\n| test coverage           |  [![Test Coverage](https://api.codeclimate.com/v1/badges/7520df3968eb146c8894/test_coverage)](https://codeclimate.com/github/pboling/resque-unique_by_arity/test_coverage) |\n| maintainability         |  [![Maintainability](https://api.codeclimate.com/v1/badges/7520df3968eb146c8894/maintainability)](https://codeclimate.com/github/pboling/resque-unique_by_arity/maintainability) |\n| code triage             |  [![Open Source Helpers](https://www.codetriage.com/pboling/resque-unique_by_arity/badges/users.svg)](https://www.codetriage.com/pboling/resque-unique_by_arity) |\n| homepage                |  [on Github.com][homepage], [on Railsbling.com][blogpage] |\n| documentation           |  [on RDoc.info][documentation] |\n| Spread ~♡ⓛⓞⓥⓔ♡~      |  [🌏](https://about.me/peter.boling), [👼](https://angel.co/peter-boling), [:shipit:](http://coderwall.com/pboling), [![Tweet Peter](https://img.shields.io/twitter/follow/galtzo.svg?style=social\u0026label=Follow)](http://twitter.com/galtzo) |\n\n## Important Note\n\nSee `lib/resque/unique_by_arity/configuration.rb` for all config options.  Only\n a smattering of what is available is documented in this README.\n\n## Most Important Note\n\nYou must configure this gem *after* you define the perform class method in your\n job or an error will be raised thanks to `perform` not having been defined yet.\n\nExample:\n\n```ruby\nclass MyJob\n  def self.perform(arg)\n    # do stuff\n  end\n  include Resque::Plugins::UniqueByArity.new(\n    arity_for_uniqueness: 1,\n    lock_after_execution_period: 60,\n    runtime_lock_timeout: 60 * 60 * 24 * 5, # 5 days\n    unique_at_runtime: true,\n    unique_in_queue: true\n  )\nend\n```\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'resque-unique_by_arity'\n```\n\nAnd then execute:\n\n    $ bundle\n\nOr install it yourself as:\n\n    $ gem install resque-unique_by_arity\n\n## Usage\n\n### Global Configuration\n\nThe following is showing the default values.  These global configs are copied into each per-class config unless they are overridden by the class config.\n\n\nCreate an initializer (e.g. `config/initializers/resque-unique_by_arity.rb` for rails) and customize the following:\n\n```ruby\n  Resque::UniqueByArity.configure do |config|\n    config.logger = nil\n    config.log_level = :debug\n    config.arity_for_uniqueness = 0\n    config.unique_at_runtime = false\n    config.unique_in_queue = false\n    # No need to do the following if keeping default values\n    config.runtime_lock_timeout = 60 * 60 * 24 * 5\n    config.runtime_requeue_interval = 1\n    config.unique_at_runtime_key_base = 'r-uar'.freeze\n    config.lock_after_execution_period = 0\n    config.ttl = -1\n    config.unique_in_queue_key_base = 'r-uiq'.freeze\n    # Debug Mode is preferably set via an environment variable:\n    #   to one of 'true', 'arity', or 'arity,queue,runtime' for all three tools:\n    #     ENV['RESQUE_DEBUG'] = 'true'\n    # config.debug_mode = true\n  end\n```\n\n### Per Job Class Configuration\n\nThis gem will take care to set the class instance variables (similar to the\n familiar `@queue` class instance variable) that are utilized by\n `resque-unique_in_queue` and `resque-unique_at_runtime` (default values shown):\n\n ```ruby\n# For resque-unique_at_runtime\n@runtime_lock_timeout = 60 * 60 * 24 * 5\n@runtime_requeue_interval = 1\n@unique_at_runtime_key_base = 'r-uar'.freeze\n\n# For resque-unique_in_queue\n@lock_after_execution_period = 0\n@ttl = -1\n@unique_in_queue_key_base = 'r-uiq'.freeze\n```\n\nAll you need to do is configure this gem accordingly:\n```ruby\n  include Resque::Plugins::UniqueByArity.new(\n    arity_for_uniqueness: 1,\n    # Turn on one or both of the following:\n    unique_at_runtime: false,\n    unique_in_queue: false,\n    # No need to do the following if keeping default values\n    runtime_lock_timeout: 60 * 60 * 24 * 5,\n    runtime_requeue_interval: 1,\n    # would override the global setting, probably a bad idea.\n    # unique_at_runtime_key_base: 'r-uar'.freeze,\n    lock_after_execution_period: 0,\n    ttl: -1,\n    # would override the global setting, probably a bad idea.\n    # unique_in_queue_key_base: 'r-uiq'.freeze\n  )\n```\n\n### Arity For Uniqueness\n\nSome jobs have parameters that you do not want to consider for determination of\n uniqueness.  Resque jobs should use simple parameters, **not named parameters**,\n  so you can just specify the number of parameters, counting from the left, you\n   want to be considered for uniqueness.\n\n```ruby\nclass MyJob\n  def self.perform(my, cat, is, the, best, opts = {})\n    # Only the first 3: [my, cat, is] will be considered for determination of uniqueness\n  end\n  include Resque::Plugins::UniqueByArity.new(\n    arity_for_uniqueness: 3,\n    unique_at_runtime: true\n  )\nend\n```\n\n#### Arity For Uniqueness Validation\n\nWant this gem to tell you when it is misconfigured?  It can.\n\n```ruby\nclass MyJob\n  def self.perform(my, cat, opts = {})\n    # Because the third argument is optional the arity valdiation will not approve.\n    # Arguments to be considered for uniqueness should be required arguments.\n    # The warning log might look like:\n    #\n    #    MyJob.perform has the following required parameters: [:my, :cat], which is not enough to satisfy the configured arity_for_uniqueness of 3\n  end\n  include Resque::Plugins::UniqueByArity.new(\n    arity_for_uniqueness: 3,\n    arity_validation: :warning, # or :skip, :error, or an error class to be raised, e.g. RuntimeError\n    unique_at_runtime: true\n  )\nend\n```\n\n\n### Lock After Execution\n\nGive the job a break after it finishes running, and don't allow another of the\n same, with matching args @ configured arity, to start within X seconds.\n\n```ruby\nclass MyJob\n  def self.perform(arg1)\n    # do stuff\n  end\n  include Resque::Plugins::UniqueByArity.new(\n    arity_for_uniqueness: 1,\n    lock_after_execution_period: 60,\n    unique_at_runtime: true\n  )\nend\n```\n\n### Runtime Lock Timeout\n\nIf runtime lock keys get stale, they will expire on their own after some period.\n  You can set the expiration period on a per class basis.\n\n```ruby\nclass MyJob\n  def self.perform(arg1)\n    # do stuff\n  end\n  include Resque::Plugins::UniqueByArity.new(\n    arity_for_uniqueness: 1,\n    runtime_lock_timeout: 60 * 60 * 24 * 5, # 5 days\n    unique_at_runtime: true\n  )\nend\n```\n\n### Unique At Runtime (across all queues)\n\nPrevent your app from running a job that is already running.\n\n```ruby\nclass MyJob\n  def self.perform(arg1)\n    # do stuff\n  end\n  include Resque::Plugins::UniqueByArity.new(\n    arity_for_uniqueness: 1,\n    unique_at_runtime: true\n  )\nend\n```\n\n#### Oops, I have stale runtime uniqueness keys for MyJob stored in Redis...\n\nPreventing jobs with matching signatures from running, and they never get\ndequeued because there is no actual corresponding job to dequeue.\n\n*How to deal?*\n\n```ruby\nMyJob.purge_unique_at_runtime_redis_keys\n```\n\n### Unique At Queue Time\n\n#### Unique In Job's Specific Queue\n\nPrevent your app from queueing a job that is already queued in the same queue.\n\n```ruby\nclass MyJob\n  def self.perform(arg1)\n    # do stuff\n  end\n  include Resque::Plugins::UniqueByArity.new(\n    arity_for_uniqueness: 1,\n    unique_in_queue: true\n  )\nend\n```\n\n#### Unique Across All Queues\n\nPrevent your app from queueing a job that is already queued in *any* queue.\n\n```ruby\nclass MyJob\n  def self.perform(arg1)\n    # do stuff\n  end\n  include Resque::Plugins::UniqueByArity.new(\n    arity_for_uniqueness: 1,\n    unique_across_queues: true\n  )\nend\n```\n\n#### Oops, I have stale Queue Time uniqueness keys...\n\nPreventing jobs with matching signatures from being queued, and they never get\ndequeued because there is no actual corresponding job to dequeue.\n\n*How to deal?*\n\nOption: Rampage\n\n```ruby\n# Delete *all* queued jobs in the queue, and\n#   delete *all* uniqueness keys for the queue.\nRedis.remove_queue('queue_name')\n```\n\nOption: Butterfly\n\n```ruby\n# Delete *no* queued jobs at all, and\n#   delete *all* uniqueness keys for the queue (might then allow duplicates).\nResque::UniqueInQueue::Queue.cleanup('queue_name')\n```\n\n### All Together Now\n\n#### Unique At Runtime (across all queues) AND Unique In Job's Specific Queue\n\nPrevent your app from running a job that is already running, **and**\nprevent your app from queueing a job that is already queued in the same queue.\n\n```ruby\nclass MyJob\n  def self.perform(arg1)\n    # do stuff\n  end\n  include Resque::Plugins::UniqueByArity.new(\n    arity_for_uniqueness: 1,\n    unique_at_runtime: true,\n    runtime_lock_timeout: 60 * 60 * 24 * 5, # 5 days\n    unique_in_queue: true\n  )\nend\n```\n\n#### Unique At Runtime (across all queues) AND Unique Across All Queues\n\nPrevent your app from running a job that is already running, **and**\nprevent your app from queueing a job that is already queued in *any* queue.\n\n```ruby\nclass MyJob\n  def self.perform(arg1)\n    # do stuff\n  end\n  include Resque::Plugins::UniqueByArity.new(\n    arity_for_uniqueness: 1,\n    unique_at_runtime: true,\n    runtime_lock_timeout: 60 * 60 * 24 * 5, # 5 days\n    unique_across_queues: true\n  )\nend\n```\n\n### Debugging\n\nRun your worker with `RESQUE_DEBUG=true` to see payloads printed before they are\n used to determine uniqueness, as well as a lot of other debugging output.\n\n### Customize Unique Keys Per Job\n\nRedefine methods to customize all the things.  Warning: This might be crazy-making.\n\n```ruby\nclass MyJob\n  def self.perform(arg1)\n    # do stuff\n  end\n  include Resque::Plugins::UniqueByArity.new(\n    #...\n  )\n\n  # Core hashing algorithm for a job used for *all 3 types* of uniqueness\n  # @return [Array\u003cString, arguments\u003e], where the string is the unique digest, and arguments are the specific args that were used to calculate the digest\n  def self.redis_unique_hash(payload, arity_for_uniqueness = 1)\n    #       for how the built-in version works\n    # uniqueness_args = payload[\"args\"] # over simplified \u0026 ignoring arity\n    # args = { class: job, args: uniqueness_args }\n    # return [Digest::MD5.hexdigest(Resque.encode(args)), uniqueness_args]\n  end\n\n  def self.unique_in_queue_redis_key_prefix\n    # \"unique_job:#{self}\" # \u003c= default value\n  end\n\n  def self.unique_in_queue_redis_key(queue, payload)\n    # arity_for_uniqueness = determine_arity # over simplified \u0026 ignoring context-specific arity determination\n    # unique_hash, _args_for_uniqueness = redis_unique_hash(payload, arity_for_uniqueness)\n    # \"#{unique_in_queue_key_namespace(queue)}:#{unique_in_queue_redis_key_prefix}:#{unique_hash}\"\n  end\n\n  def self.unique_in_queue_key_namespace(queue = nil)\n    # definition depends on which type of uniqueness is chosen, be careful if you customize\n    # \"r-uiq:queue:#{queue}:job\" # \u003c= is for unique within queue at queue time\n    # \"r-uiq:across_queues:job\" # \u003c= is for unique across all queues at queue time\n  end\n\n  def self.runtime_key_namespace\n    # \"unique_at_runtime:#{self}\"\n  end\n\n  def self.unique_at_runtime_redis_key(*args)\n    # payload = {\"class\" =\u003e self.to_s, \"args\" =\u003e args}\n    # unique_hash, _args_for_uniqueness = redis_unique_hash(payload, configuration.arity_for_uniqueness_at_runtime)\n    # key = \"#{runtime_key_namespace}:#{unique_hash}\" # \u003c= simplified default\n  end\nend\n```\n\n## Development\n\nAfter checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.\n\nTo 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 tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).\n\n## Contributing\n\nBug reports and pull requests are welcome on GitHub at https://github.com/pboling/resque-unique_by_arity. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.\n\n1. Fork it\n2. Create your feature branch (`git checkout -b my-new-feature`)\n3. Commit your changes (`git commit -am 'Added some feature'`)\n4. Push to the branch (`git push origin my-new-feature`)\n5. Create new Pull Request\n\n## Code of Conduct\n\nEveryone interacting in the Resque::UniqueByArity project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/pboling/resque-unique_by_arity/blob/master/CODE_OF_CONDUCT.md).\n\n## Versioning\n\nThis library aims to adhere to [Semantic Versioning 2.0.0][semver].\nViolations of this scheme should be reported as bugs. Specifically,\nif a minor or patch version is released that breaks backward\ncompatibility, a new version should be immediately released that\nrestores compatibility. Breaking changes to the public API will\nonly be introduced with new major versions.\n\nAs a result of this policy, you can (and should) specify a\ndependency on this gem using the [Pessimistic Version Constraint][pvc] with two digits of precision.\n\nFor example:\n\n```ruby\nspec.add_dependency 'resque-unique_by_arity', '~\u003e 0.0'\n```\n\n\n## License\n\n* Copyright (c) 2017 - 2018 [Peter H. Boling][peterboling] of [Rails Bling][railsbling]\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT)\n\n[license]: LICENSE\n[semver]: http://semver.org/\n[pvc]: http://guides.rubygems.org/patterns/#pessimistic-version-constraint\n[railsbling]: http://www.railsbling.com\n[peterboling]: http://www.peterboling.com\n[documentation]: http://rdoc.info/github/pboling/resque-unique_by_arity/frames\n[homepage]: https://github.com/pboling/resque-unique_by_arity/\n[blogpage]: http://www.railsbling.com/tags/resque-unique_by_arity/\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fresque%2Fresque-unique_by_arity","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fresque%2Fresque-unique_by_arity","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fresque%2Fresque-unique_by_arity/lists"}