Skip to content

Instantly share code, notes, and snippets.

@pavanshekar
Last active April 28, 2026 21:20
Show Gist options
  • Select an option

  • Save pavanshekar/f181a2846fe1be38bdc7909d38cd42c6 to your computer and use it in GitHub Desktop.

Select an option

Save pavanshekar/f181a2846fe1be38bdc7909d38cd42c6 to your computer and use it in GitHub Desktop.
performance_test_errata_migration.rake
# Performance test for errata applications migration
# Run with: bundle exec rake katello:dev:performance_test_migration
namespace :katello do
namespace :dev do
desc "Performance test for errata applications migration"
task :performance_test_migration => :environment do
require 'benchmark'
puts "=" * 80
puts "Errata Applications Migration Performance Test"
puts "Started at: #{Time.current}"
puts "=" * 80
# Set user context
User.current = User.anonymous_api_admin
# Count existing data before migration
puts "\nPre-migration state:"
existing_applications = Katello::ErrataApplication.count
puts " Existing ErrataApplication records: #{existing_applications}"
# Count tasks that will be processed
puts "\nCounting tasks to be processed..."
count_time = Benchmark.realtime do
@task_count = ForemanTasks::Task
.joins(template_invocation: { template: :remote_execution_features })
.where(label: 'Actions::RemoteExecution::RunHostJob')
.where('remote_execution_features.label': ['katello_errata_install', 'katello_errata_install_by_search'])
.where.not(started_at: nil)
.distinct
.count
end
puts " Tasks to process: #{@task_count}"
puts " Count query took: #{count_time.round(2)} seconds"
if @task_count.zero?
puts "\nNo tasks found to migrate. Run the seed script first:"
puts " bundle exec rake katello:dev:seed_errata_tasks[50000,100]"
exit 0
end
# Run the migration and time it
puts "\n" + "=" * 80
puts "Running migration..."
puts "=" * 80
migration_time = Benchmark.realtime do
system("bundle exec rake katello:upgrades:4.21:populate_errata_applications")
unless $?.success?
puts "\nERROR: Migration failed with exit code #{$?.exitstatus}"
exit 1
end
end
puts "\n" + "=" * 80
puts "Migration completed in: #{migration_time.round(2)} seconds (#{(migration_time / 60).round(2)} minutes)"
puts "=" * 80
# Verify results
puts "\nPost-migration state:"
new_applications = Katello::ErrataApplication.count
created_count = new_applications - existing_applications
puts " Total ErrataApplication records: #{new_applications}"
puts " New records created: #{created_count}"
if created_count > 0
puts " Average time per record: #{(migration_time / created_count * 1000).round(2)} ms"
puts " Records per second: #{(created_count / migration_time).round(2)}"
elsif @task_count > 0
puts " WARNING: No records created despite #{@task_count} tasks found"
end
# Sample some records
puts "\nSample records created:"
Katello::ErrataApplication.order(created_at: :desc).limit(3).each do |app|
puts " - Host: #{app.host.name}, Errata: #{app.errata_ids.count}, Status: #{app.status}, Date: #{app.applied_at}"
end
# Test report generation if we have data
if new_applications > 100
puts "\n" + "=" * 80
puts "Testing report generation..."
puts "=" * 80
# Create a test scope object
test_scope_class = Class.new do
include Katello::Concerns::BaseTemplateScopeExtensions
end
test_scope = test_scope_class.new
# Test 1: Simple report (last 7 days)
puts "\nTest 1: Report for last 7 days"
report_time_1 = Benchmark.realtime do
@results_1 = test_scope.load_errata_applications_from_db(
filter_errata_type: 'all',
include_last_reboot: 'no',
since: 7.days.ago.to_s,
up_to: Time.current.to_s,
status: 'all',
host_filter: nil
)
end
puts " Results: #{@results_1.count} rows"
puts " Time: #{report_time_1.round(2)} seconds"
# Test 2: Report with host filter (single host)
if @results_1.any?
sample_host = @results_1.first[:hostname]
puts "\nTest 2: Report for single host (#{sample_host})"
report_time_2 = Benchmark.realtime do
@results_2 = test_scope.load_errata_applications_from_db(
filter_errata_type: 'all',
include_last_reboot: 'no',
status: 'all',
host_filter: "name = #{sample_host}"
)
end
puts " Results: #{@results_2.count} rows"
puts " Time: #{report_time_2.round(2)} seconds"
end
# Test 3: Large report (all data with last_reboot)
puts "\nTest 3: Full report with last_reboot data"
report_time_3 = Benchmark.realtime do
@results_3 = test_scope.load_errata_applications_from_db(
filter_errata_type: 'all',
include_last_reboot: 'yes',
status: 'all',
host_filter: nil
)
end
puts " Results: #{@results_3.count} rows"
puts " Time: #{report_time_3.round(2)} seconds"
# Show query efficiency metrics
if defined?(report_time_3) && @results_3&.count&.positive?
puts "\nReport generation metrics:"
puts " Average time per result row: #{(report_time_3 / @results_3.count * 1000).round(2)} ms"
puts " Rows per second: #{(@results_3.count / report_time_3).round(2)}"
end
# Sample report data
if @results_3.any?
puts "\nSample report rows:"
@results_3.take(3).each do |row|
puts " - #{row[:hostname]}: #{row[:erratum_id]} (#{row[:erratum_type]}) - #{row[:status]}, still_applicable: #{row[:still_applicable]}"
end
end
else
puts "\nSkipping report generation test (insufficient data)"
end
# Database statistics
puts "\n" + "=" * 80
puts "Database Statistics"
puts "=" * 80
# Table size
result = ActiveRecord::Base.connection.execute(
"SELECT pg_size_pretty(pg_total_relation_size('katello_errata_applications')) as size"
)
puts " Table size: #{result.first['size']}"
# Index usage
index_stats = ActiveRecord::Base.connection.execute(<<-SQL)
SELECT
indexrelname as index_name,
idx_scan as times_used,
pg_size_pretty(pg_relation_size(indexrelid)) as index_size
FROM pg_stat_user_indexes
WHERE schemaname = 'public'
AND relname = 'katello_errata_applications'
ORDER BY idx_scan DESC
SQL
if index_stats.any?
puts "\n Index statistics:"
index_stats.each do |stat|
puts " #{stat['index_name']}: used #{stat['times_used']} times, size: #{stat['index_size']}"
end
end
# Summary
puts "\n" + "=" * 80
puts "SUMMARY"
puts "=" * 80
puts " Tasks processed: #{@task_count}"
puts " Records created: #{created_count}"
puts " Migration time: #{migration_time.round(2)} seconds (#{(migration_time / 60).round(2)} minutes)"
if created_count > 0
puts " Throughput: #{(created_count / migration_time).round(2)} records/second"
end
if defined?(report_time_3) && defined?(@results_3)
puts "\n Report generation (full dataset):"
puts " Rows: #{@results_3.count}"
puts " Time: #{report_time_3.round(2)} seconds"
if @results_3.count > 0
puts " Throughput: #{(@results_3.count / report_time_3).round(2)} rows/second"
end
end
puts "\nCompleted at: #{Time.current}"
puts "=" * 80
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment