veeqo/activejob-uniqueness

GitHub: veeqo/activejob-uniqueness

为 Rails ActiveJob 提供基于 Redis 分布式锁的任务唯一性保障,支持多种锁定策略以防止队列中出现重复或并发任务。

Stars: 302 | Forks: 40

# ActiveJob 的任务唯一性 [![构建状态](https://static.pigsec.cn/wp-content/uploads/repos/cas/45/45c1010596e099d79c3d0cf019e3eeb50095fdc69b60dc47e92aa24a477d189a.svg)](https://github.com/veeqo/activejob-uniqueness/actions/workflows/main.yml) [![Gem 版本](https://badge.fury.io/rb/activejob-uniqueness.svg)](https://badge.fury.io/rb/activejob-uniqueness) 该 gem 允许使用以下策略来保护任务唯一性: | 策略 | 任务锁定 | 任务解锁 | |-|-|-| | `until_executing` | 当**推入**队列时 | 当**开始处理**时 | | `until_executed` | 当**推入**队列时 | 当任务**处理成功**时 | | `until_expired` | 当**推入**队列时 | 当锁**过期**时 | | `until_and_while_executing` | 当**推入**队列时 | 当**开始处理**时
获取运行时锁以**防止并发任务**
*包含额外选项:`runtime_lock_ttl`、`on_runtime_conflict`* | | `while_executing` | 当**开始处理**时 | 当任务**处理完成**时
包括发生错误在内的任何结果 | 受 [SidekiqUniqueJobs](https://github.com/mhenrixon/sidekiq-unique-jobs) 启发,底层使用了 [Redlock](https://github.com/leandromoreira/redlock-rb)。

## 安装说明 将 `activejob-uniqueness` gem 添加到你的 Gemfile 中。 ``` gem 'activejob-uniqueness' ``` 如果你希望为 Sidekiq Web UI 解锁任务,需要显式地 require 该补丁。[**队列清理会变慢!**](#sidekiq-api-support) ``` gem 'activejob-uniqueness', require: 'active_job/uniqueness/sidekiq_patch' ``` 然后运行 `bundle install` 命令。 ## 配置 ActiveJob::Uniqueness 无需任何配置即可直接使用。它会使用 `REDIS_URL` 来连接 Redis 实例。 要覆盖默认配置,请使用以下命令创建初始化文件 `config/initializers/active_job_uniqueness.rb`: ``` rails generate active_job:uniqueness:install ``` 该 gem 依赖 `redlock` 进行 Redis 连接,这意味着**它不会继承 `Sidekiq` 的全局配置**。要配置连接,你可以使用 `config.redlock_servers`,例如为了禁用 Redis/Key-Value 云服务商的 SSL 验证: ``` ActiveJob::Uniqueness.configure do |config| config.redlock_servers = [ RedisClient.new( url: ENV['REDIS_URL'], ssl_params: { verify_mode: OpenSSL::SSL::VERIFY_NONE } ) ] end ``` ## 用法 ### 使任务唯一 ``` class MyJob < ActiveJob::Base # new jobs with the same args will raise error until existing one is executed unique :until_executed def perform(args) # work end end ``` ### 为每个任务微调唯一性设置 ``` class MyJob < ActiveJob::Base # new jobs with the same args will be logged within 3 hours or until existing one is being executing unique :until_executing, lock_ttl: 3.hours, on_conflict: :log def perform(args) # work end end ``` 你可以通过[配置](#configuration)进行全局默认设置 ### 控制锁冲突 ``` class MyJob < ActiveJob::Base # Proc gets the job instance including its arguments unique :until_executing, on_conflict: ->(job) { job.logger.info "Oops: #{job.arguments}" } def perform(args) # work end end ``` ### 控制 redis 连接错误 ``` class MyJob < ActiveJob::Base # Proc gets the job instance including its arguments, and as keyword arguments the resource(lock key) `resource` and the original error `error` unique :until_executing, on_redis_connection_error: ->(job, resource: _, error: _) { job.logger.info "Oops: #{job.arguments}" } def perform(args) # work end end ``` ### 控制锁 key 参数 ``` class MyJob < ActiveJob::Base unique :until_executed def perform(foo, bar, baz) # work end def lock_key_arguments arguments.first(2) # baz is ignored end end ``` ### 控制锁 key ``` class MyJob < ActiveJob::Base unique :until_executed def perform(foo, bar, baz) # work end def lock_key 'qux' # completely custom lock key end def runtime_lock_key 'quux' # completely custom runtime lock key for :until_and_while_executing end end ``` ### 手动解锁任务 选定的策略会自动解锁任务,但在某些情况下(例如队列被清空时),手动解锁任务会非常方便。 ``` # 移除特定参数的 lock: MyJob.unlock!(foo: 'bar') # 或 ActiveJob::Uniqueness.unlock!(job_class_name: 'MyJob', arguments: [{foo: 'bar'}]) # 移除 MyJob 的所有 lock MyJob.unlock! # 或 ActiveJob::Uniqueness.unlock!(job_class_name: 'MyJob') # 移除所有 lock ActiveJob::Uniqueness.unlock! ``` ## 测试模式 大多数情况下,你可能不希望任务在测试中被锁定。将下面这行代码添加到你的测试套件(`rails_helper.rb`)中: ``` ActiveJob::Uniqueness.test_mode! ``` ## 日志记录 ActiveJob::Uniqueness 通过 `ActiveSupport::Notifications` 监测以下事件: * `lock.active_job_uniqueness` * `runtime_lock.active_job_uniqueness` * `unlock.active_job_uniqueness` * `runtime_unlock.active_job_uniqueness` * `conflict.active_job_uniqueness` * `runtime_conflict.active_job_uniqueness` 然后将其写入 `ActiveJob::Base.logger`。 **在 `6.1` 版本之前的 ActiveJob 会始终记录 `Enqueued MyJob (Job ID) ...`,即使回调链已被中断也是如此。[详情](https://github.com/rails/rails/pull/37830)** ## 测试 启动 redis 服务器(在单独的控制台中): ``` docker run --rm -p 6379:6379 redis ``` 运行测试: ``` bundle rake ``` ## Sidekiq API 支持 ActiveJob::Uniqueness 支持 Sidekiq API,以便在清理队列时(例如通过 Sidekiq Web UI)取消任务锁。从 Sidekiq 5.1 开始,任务死亡也会触发锁清理。 请注意,**[大型队列的清理会变得慢得多](https://github.com/veeqo/activejob-uniqueness/issues/16)**,因为每个任务都会被单独解锁。为了激活 Sidekiq API 补丁,请在你的 Gemfile 中显式地 require 它: ``` gem 'activejob-uniqueness', require: 'active_job/uniqueness/sidekiq_patch' ``` ## 开源许可证 该 gem 作为开源软件发布,基于 [MIT License](https://opensource.org/licenses/MIT) 条款。 ## 关于 [Veeqo](https://www.veeqo.com) 在 Veeqo,我们的工程团队致力于打造一个世界级的库存与发货平台,并以最高标准的编码最佳实践来构建。我们是一个不断壮大的团队,正在寻找充满激情的开发者与我们[一同前行](https://veeqo-ltd.breezy.hr/)。如果你想在电子商务领域最激动人心的科技公司之一谋求职业发展,我们期待你的回音。 [Veeqo 开发者博客](https://devs.veeqo.com)
标签:搜索引擎查询