-
-
Notifications
You must be signed in to change notification settings - Fork 279
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix numerous small issues with locking (#616)
* Update readme * Prefer explicit use of locksmith over rarely used methods. Not idea why I added those methods. * Fix a number of minor issues with locking * Sort reflections alphabetically * Add note about upgrading * Bump version
- Loading branch information
Showing
23 changed files
with
250 additions
and
209 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
# Upgrading | ||
|
||
## v7.1.0 | ||
|
||
### Reflection API | ||
|
||
SidekiqUniqueJobs do not log by default anymore. Instead I have a reflection API that I shamelessly borrowed from Rpush. | ||
|
||
To use the new notification/reflection system please define them as follows in an initializer of your choosing. | ||
|
||
```ruby | ||
SidekiqUniqueJobs.reflect do |on| | ||
# Only raised when you have defined such a callback | ||
on.after_unlock_callback_failed do |job_hash| | ||
logger.warn(job_hash.merge(message: "Unlock callback failed")) | ||
end | ||
|
||
# This job is skipped because it is a duplicate | ||
on.duplicate do |job_hash| | ||
logger.warn(job_hash.merge(message: "Duplicate Job")) | ||
end | ||
|
||
# This means your code broke and we caught the execption to provide this reflection for you. It allows your to gather metrics and details about the error. Those details allow you to act on it as you see fit. | ||
on.execution_failed do |job_hash| | ||
logger.warn(job_hash.merge(message: "Execution failed")) | ||
end | ||
|
||
# Failed to acquire lock in a timely fashion | ||
on.lock_failed do |job_hash| | ||
logger.warn(job_hash.merge(message: "Lock failed")) | ||
end | ||
|
||
# In case you want to collect metrics | ||
on.locked do |job_hash| | ||
logger.debug(job_hash.merge(message: "Lock success")) | ||
end | ||
|
||
# When your conflict strategy is to reschedule and it failed | ||
on.reschedule_failed do |job_hash| | ||
logger.debug(job_hash.merge(message: "Reschedule failed")) | ||
end | ||
|
||
# When your conflict strategy is to reschedule and it failed | ||
# Mostly for metrics I guess | ||
on.rescheduled do |job_hash| | ||
logger.debug(job_hash.merge(message: "Reschedule success")) | ||
end | ||
|
||
# You asked to wait for a lock to be achieved but we timed out waiting | ||
on.timeout do |job_hash| | ||
logger.warn(job_hash.merge(message: "Oh no! Timeout!! Timeout!!")) | ||
end | ||
|
||
# The current worker isn't part of this sidekiq servers workers | ||
on.unknown_sidekiq_worker do |job_hash| | ||
logger.warn(job_hash.merge(message: "WAT!? Why? What is this worker?")) | ||
end | ||
|
||
# Unlock failed! Not good | ||
on.unlock_failed do |job_hash| | ||
logger.warn(job_hash.merge(message: "Unlock failed")) | ||
end | ||
|
||
# Unlock was successful, perhaps mostly interesting for metrics | ||
on.unlocked do |job_hash| | ||
logger.warn(job_hash.merge(message: "Unlock success")) | ||
end | ||
``` | ||
|
||
You don't need to configure them all. Some of them are just informational, some of them more for metrics and a couple of them (failures, timeouts) might be of real interest. | ||
|
||
I leave it up to you to decided what to do about it. | ||
|
||
### Reaper Resurrector | ||
|
||
In [#604](https://github.com/mhenrixon/sidekiq-unique-jobs/pull/604) a reaper resurrector was added. This is configured by default so that if the current reaper process dies, another one kicks off again. | ||
|
||
With the recent fixes in [#616](https://github.com/mhenrixon/sidekiq-unique-jobs/pull/616) there should be even less need for reaping. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,6 +7,8 @@ class Lock | |
# @abstract | ||
# @author Mikael Henriksson <[email protected]> | ||
class BaseLock | ||
extend Forwardable | ||
|
||
# includes "SidekiqUniqueJobs::Logging" | ||
# @!parse include SidekiqUniqueJobs::Logging | ||
include SidekiqUniqueJobs::Logging | ||
|
@@ -26,6 +28,10 @@ def self.validate_options(options = {}) | |
Validator.validate(options) | ||
end | ||
|
||
# NOTE: Mainly used for a clean testing API | ||
# | ||
def_delegators :locksmith, :locked? | ||
|
||
# @param [Hash] item the Sidekiq job hash | ||
# @param [Proc] callback the callback to use after unlock | ||
# @param [Sidekiq::RedisConnection, ConnectionPool] redis_pool the redis connection | ||
|
@@ -57,31 +63,6 @@ def execute | |
raise NotImplementedError, "##{__method__} needs to be implemented in #{self.class}" | ||
end | ||
|
||
# Unlocks the job from redis | ||
# @return [String] sidekiq job id when successful | ||
# @return [false] when unsuccessful | ||
def unlock | ||
locksmith.unlock # Only signal to release the lock | ||
end | ||
|
||
# Deletes the job from redis if it is locked. | ||
def delete | ||
locksmith.delete # Soft delete (don't forcefully remove when expiration is set) | ||
end | ||
|
||
# Forcefully deletes the job from redis. | ||
# This is good for jobs when a previous lock was not unlocked | ||
def delete! | ||
locksmith.delete! # Force delete the lock | ||
end | ||
|
||
# Checks if the item has achieved a lock | ||
# @return [true] when this jid has locked the job | ||
# @return [false] when this jid has not locked the job | ||
def locked? | ||
locksmith.locked? | ||
end | ||
|
||
# | ||
# The lock manager/client | ||
# | ||
|
@@ -118,15 +99,22 @@ def prepare_item | |
SidekiqUniqueJobs::Job.prepare(item) | ||
end | ||
|
||
def lock_failed | ||
# | ||
# Handle when lock failed | ||
# | ||
# @param [Symbol] location: :client or :server | ||
# | ||
# @return [void] | ||
# | ||
def lock_failed(origin: :client) | ||
reflect(:lock_failed, item) | ||
call_strategy(of: :client) | ||
call_strategy(origin: origin) | ||
end | ||
|
||
def call_strategy(of:) # rubocop:disable Naming/MethodParameterName | ||
def call_strategy(origin:) | ||
@attempt += 1 | ||
|
||
case of | ||
case origin | ||
when :client | ||
client_strategy.call { lock if replace? } | ||
when :server | ||
|
@@ -141,6 +129,12 @@ def replace? | |
client_strategy.replace? && attempt < 2 | ||
end | ||
|
||
def unlock_and_callback | ||
return callback_safely if locksmith.unlock | ||
|
||
reflect(:unlock_failed, item) | ||
end | ||
|
||
def callback_safely | ||
callback&.call | ||
item[JID] | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,6 +8,27 @@ class Lock | |
# @author Mikael Henriksson <[email protected]> | ||
# | ||
class UntilExpired < UntilExecuted | ||
# | ||
# Locks a sidekiq job | ||
# | ||
# @note Will call a conflict strategy if lock can't be achieved. | ||
# | ||
# @return [String, nil] the locked jid when properly locked, else nil. | ||
# | ||
# @yield to the caller when given a block | ||
# | ||
def lock | ||
return lock_failed unless (job_id = locksmith.lock) | ||
return yield job_id if block_given? | ||
|
||
job_id | ||
end | ||
|
||
# Executes in the Sidekiq server process | ||
# @yield to the worker class perform method | ||
def execute(&block) | ||
locksmith.execute(&block) | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.