Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add rake task for cleaning up sidekiq batch keys #3912

Merged
merged 3 commits into from
Oct 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
1 change: 1 addition & 0 deletions config/application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@ def cache_store_config
require 'three_scale/domain_substitution'
require 'three_scale/middleware/multitenant'
require 'three_scale/middleware/cors'
require 'three_scale/patterns/service'

config.middleware.use ThreeScale::Middleware::Multitenant, :tenant_id unless ENV["DEBUG_DISABLE_TENANT_CHECK"] == "1"
config.middleware.insert_before Rack::Runtime, Rack::UTF8Sanitizer
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
12 changes: 12 additions & 0 deletions lib/tasks/sidekiq/cleanup.rake
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# 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]) } : {}

ThreeScale::SidekiqBatchCleanupService.call(**params)
end
end
60 changes: 60 additions & 0 deletions lib/three_scale/sidekiq_batch_cleanup_service.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# frozen_string_literal: true

require 'progress_counter'

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
super()
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 /-(success|complete|failed|jids)$/.match?(key)

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