我的应用程序中的用户创建了Transactions
,我需要这些交易(以及为在用户在一定时间内没有响应时将交易状态更改为ignored
而创建的关联作业)除非用户执行pay
操作,否则请自行取消。
在一个示例中,我使用的方法在状态更改为perform_async
之后使用approved
进行以下调用,然后在未及时响应的情况下取消:
Class Transaction < ApplicationRecord
#when approved
def create_worker
MyWorker.perform_async(self.id)
end
#if user responds in time, cancel the jobs and update the record to `paid` etc
def cancel_worker
jid = MyWorker.perform_async(self.id)
MyWorker.cancel! jid
end
end
根据here和here的建议,我在工作人员内部添加了有关何时取消的其他功能。看起来像这样:
class MyWorker
include Sidekiq::Worker
def perform(transaction_id)
return if paid?
transaction = Transaction.find transaction_id
self.class.perform_in(1.minutes, transaction.ignore!)
end
def paid?
Sidekiq.redis { |c| c.exists("paid-#{jid}") }
end
def self.cancel! jid
Sidekiq.redis { |c| c.setex("paid-#{jid}", 86400, 1) }
end
end
此代码将产生以下终端输出:
2018-12-16T01:40:50.645Z 30530 TID-oxm547nes MyWorker JID-6c97e448fe30998235dee95d INFO: start
Changing transaction 4 approved to ignored (event: ignore!)
2018-12-16T01:40:50.884Z 30530 TID-oxm547nes MyWorker JID-6c97e448fe30998235dee95d INFO: done: 0.239 sec
2018-12-16T01:41:56.122Z 30530 TID-oxm547oag MyWorker JID-b46bb3b002e00f480a04be16 INFO: start
2018-12-16T01:41:56.125Z 30530 TID-oxm547oag MyWorker JID-b46bb3b002e00f480a04be16 INFO: fail: 0.003 sec
2018-12-16T01:41:56.126Z 30530 TID-oxm547oag WARN: {"context":"Job raised exception","job":{"class":"MyWorker","args":[true],"retry":true,"queue":"default","jid":"b46bb3b002e00f480a04be16","created_at":1544924450.884224,"enqueued_at":1544924516.107598,"error_message":"Couldn't find Transaction with 'id'=true","error_class":"ActiveRecord::RecordNotFound","failed_at":1544924516.125679,"retry_count":0},"jobstr":"{\"class\":\"MyWorker\",\"args\":[true],\"retry\":true,\"queue\":\"default\",\"jid\":\"b46bb3b002e00f480a04be16\",\"created_at\":1544924450.884224,\"enqueued_at\":1544924516.107598}"}
因此,这将创建两个作业-一个作业的别名为6c97e448fe30998235dee95d
,然后立即将Transaction设置为ignored
,然后另一个作业的别名为b46bb3b002e00f480a04be16
,该作业会在早期通过返回工作者的perform
函数(因为它没有使用与第一个工作相同的jid)。
我可以推测为什么这无法达到我的预期目的的一个原因是,对MyWorker.cancel!
的调用无法得到我想要取消的工作人员的通知,而无需先创建一个db迁移持有说吉德。
创建数据库迁移以包含工作人员的jid是确保操作之间可访问jid的首选方法吗? id=true
如何到达那里?如上面的错误所述:Couldn't find Transaction with 'id'=true"
答案 0 :(得分:2)
好,让我们一步一步走。
此代码:
self.class.perform_in(1.minute, transaction.ignore!)
传递ignore!
方法的返回值(在本例中为true
)作为作业的参数,这会导致异常。
您应确保传递正确的参数:
self.class.perform_in(1.minute, transaction.tap(&:ignore!).id)
每次调用MyWorker.perform_async
(或任何其他执行类方法)时,您都在创建新工作,因此得到相同的jid
也就不足为奇了。
您应该按照建议将初始jid
存储在交易表中,然后在付款时取回它以将其取消。否则,作业ID将丢失。一种替代方法是实际使用相同的Redis存储已付款标志,但由交易键代替。 c.exists("paid-#{transaction.id}")
您的代码不会等待1分钟以忽略该事务,它只是立即忽略该事务并将其自身设置为在1分钟后再次执行。
您可能想打电话
jid = MyWorker.perform_in(1.minute, transaction.id)
直接来自create_worker
方法。
更新
如果按照我的想象,如果您正在使用某种持久状态机,那么“忽略完成就忽略”而忘记取消作业甚至更容易
class Transaction
# I'm inventing a DSL here
include SomeStateMachine
state :accepted do
event :ignore, to: :ignored
event :confirm, to: :confirmed
end
state :ignored
state :confirmed
def create_worker
# no need to track it
MyWorker.perform_in(1.minute, id)
end
end
class MyWorker
include Sidekiq::Worker
def perform(id)
transaction = Transaction.find(id)
transaction.ignore! if transaction.can_ignore?
end
end
您可以让您的工作运行,它会很乐意跳过任何不可忽略的交易。