Skip to content

Commit

Permalink
Add rake task for cleaning up sidekiq batch keys
Browse files Browse the repository at this point in the history
Apply suggestions from code review

Co-authored-by: Aleksandar N. Kostadinov <akostadinov@gmail.com>
  • Loading branch information
mayorova and akostadinov committed Oct 11, 2024
1 parent 09e62e3 commit 52504ea
Show file tree
Hide file tree
Showing 7 changed files with 88 additions and 7 deletions.
8 changes: 1 addition & 7 deletions app/lib/backend/storage_rewrite.rb
Original file line number Diff line number Diff line change
Expand Up @@ -130,14 +130,8 @@ def process(collection)
rewrite(scope: collection)
end

# This logger just prints out a message to STDOUT, with new line before and after.
# New line before is to make progress log look better
def logger
@logger ||= begin
log = ActiveSupport::Logger.new($stdout)
log.formatter = ->(_, _, _, msg) { "\n#{msg.is_a?(String) ? msg : msg.inspect}\n" }
log
end
@logger ||= ProgressCounter.stdout_logger
end

# All accounts eligible for backend sync
Expand Down
2 changes: 2 additions & 0 deletions app/services/synchronization/nowait_lock_service.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# frozen_string_literal: true

require 'three_scale/patterns/service'

class Synchronization::NowaitLockService < ThreeScale::Patterns::Service
# @param str_resource [String] a lock key
# @param timeout [Integer] milliseconds lock timeout
Expand Down
2 changes: 2 additions & 0 deletions app/services/synchronization/unsafe_unlock_service.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# frozen_string_literal: true

require 'three_scale/patterns/service'

class Synchronization::UnsafeUnlockService < ThreeScale::Patterns::Service
# unconditional lock release, dangerous to create race conditions based on a redlock key
# should only be used for manual intervention in case of extraordinary circumstances
Expand Down
10 changes: 10 additions & 0 deletions lib/progress_counter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,16 @@
class ProgressCounter
attr_reader :total, :index

class << self
# This logger just prints out a message to STDOUT, with new line before and after.
# New line before is to make progress log look better
def stdout_logger
log = ActiveSupport::Logger.new($stdout)
log.formatter = ->(_, _, _, msg) { "\n#{msg.is_a?(String) ? msg : msg.inspect}\n" }
log
end
end

def initialize(total)
@index = 0
@total = total
Expand Down
15 changes: 15 additions & 0 deletions lib/tasks/sidekiq/cleanup.rake
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# frozen_string_literal: true

require 'three_scale/sidekiq_batch_cleanup_service'

namespace :sidekiq do
desc 'cleanup BID-* keys from sidekiq-batch, specify the max age in seconds as an argument'
task :cleanup_batches, [:max_age_seconds] => :environment do |task, args|
params = args[:max_age_seconds] ? { max_age_seconds: Integer(args[:max_age_seconds]) } : {}

message = params[:max_age_seconds] ? "#{params[:max_age_seconds].seconds.in_hours} hours" : "the default age"
puts "Cleaning up the sidekiq-batch BID-* keys older than #{message}"

ThreeScale::SidekiqBatchCleanupService.call(**params)
end
end
File renamed without changes.
58 changes: 58 additions & 0 deletions lib/three_scale/sidekiq_batch_cleanup_service.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
require 'progress_counter'

require 'three_scale/patterns/service'

module ThreeScale
class SidekiqBatchCleanupService < ThreeScale::Patterns::Service
MAX_FETCH_COUNT = 1000

BID_EXPIRE_TTL = 30.days.seconds
DEFAULT_MAX_AGE_SECONDS = 3.hours.seconds

# `max_age_seconds` specifies the maximum age of the keys (in seconds)
# all keys that are older will be deleted, calculated by the TTL that is still left, compared with the default expire value
def initialize(max_age_seconds: DEFAULT_MAX_AGE_SECONDS)
@now = Time.zone.now
@redis = System.redis

@bid_max_ttl = BID_EXPIRE_TTL - max_age_seconds
end

attr_reader :now, :redis, :bid_max_ttl

def call
total = redis.dbsize
logger.info "Total number of keys: #{total}, will delete BID-* keys with TTL less than #{bid_max_ttl.seconds.in_hours} hours"

scan_enum = System.redis.scan_each(match: 'BID-*', type: 'hash', count: MAX_FETCH_COUNT)

each_with_progress_counter(scan_enum, total) do |key|
next if key =~ /-(success|complete|failed|jids)$/
bid = key.remove(/^BID-/)
perform(bid)
end
end

def perform(bid)
# TODO: ensure there is no task running still
batch_key = "BID-#{bid}"
ttl = redis.ttl(batch_key)

Sidekiq::Batch.cleanup_redis(bid) if ttl <= bid_max_ttl
end

private

def each_with_progress_counter(enumerable, count)
progress = ProgressCounter.new(count)
enumerable.each do |element|
yield element
progress.call
end
end

def logger
@logger ||= ProgressCounter.stdout_logger
end
end
end

0 comments on commit 52504ea

Please sign in to comment.