Created
August 16, 2024 14:57
-
-
Save wellington1993/9cd3a8a87c38fa964c28d11e7597b11e to your computer and use it in GitHub Desktop.
This file contains hidden or 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
namespace :cleanup do | |
desc "Cleanup old versions and keep only the last year of data / Limpeza de versões antigas e manter apenas os dados do último ano" | |
task versions: :environment do | |
require 'benchmark' | |
class Version < ApplicationRecord | |
self.table_name = 'versions' | |
end | |
class TempVersion < ApplicationRecord | |
self.table_name = 'temp_versions' | |
end | |
class Maintenance < ApplicationRecord | |
self.table_name = 'maintenances' | |
end | |
ActiveRecord::Schema.define do | |
create_table :maintenances do |t| | |
t.string :maintenance_type | |
t.string :status | |
t.datetime :started_at | |
t.datetime :ended_at | |
t.decimal :total_time | |
t.decimal :create_table_time | |
t.decimal :copy_time | |
t.decimal :rename_time | |
t.decimal :drop_time | |
t.timestamps | |
end unless ActiveRecord::Base.connection.table_exists?('maintenances') | |
end | |
memcached_available = false | |
client = ActionDispatch::Session::CacheStore.new(Rails.application.config.session_options[:cache]) | |
memcached_available = client.cache.instance_variable_get(:@data)&.alive? | |
if !memcached_available || client.cache.write('cleanup_versions_lock', 1, unless_exist: true, expires_in: 1.hour) | |
begin | |
maintenance = Maintenance.create!( | |
maintenance_type: 'cleanup_versions', | |
status: 'in_progress', | |
started_at: Time.current | |
) | |
puts "Creating temporary table... / Criando tabela temporária..." | |
create_table_time = Benchmark.realtime do | |
ActiveRecord::Base.transaction do | |
# Verifica se a tabela já existe | |
unless ActiveRecord::Base.connection.table_exists?('temp_versions') | |
# Se a tabela não existir, cria-a com particionamento inicial | |
ActiveRecord::Base.connection.execute <<-SQL.squish | |
CREATE TABLE temp_versions ( | |
id BIGINT AUTO_INCREMENT PRIMARY KEY, | |
item_type VARCHAR(255), | |
item_id BIGINT, | |
event VARCHAR(255), | |
whodunnit VARCHAR(255), | |
object TEXT, | |
created_at DATETIME, | |
object_changes TEXT, | |
transaction_id BIGINT, | |
INDEX index_temp_versions_on_created_at (created_at) | |
) | |
PARTITION BY RANGE (YEAR(created_at)) ( | |
PARTITION p#{Date.current.year} VALUES LESS THAN (#{Date.current.year + 1}), | |
PARTITION p#{Date.current.year + 1} VALUES LESS THAN (#{Date.current.year + 2}) | |
); | |
SQL | |
else | |
# Se a tabela já existir, ajusta os particionamentos conforme necessário | |
current_year = Date.current.year | |
next_year = current_year + 1 | |
# Verifica os particionamentos existentes | |
partitions = ActiveRecord::Base.connection.select_values("SHOW PARTITIONS FROM temp_versions") | |
# Cria particionamento para o ano atual se não existir | |
unless partitions.include?("p#{current_year}") | |
ActiveRecord::Base.connection.execute <<-SQL.squish | |
ALTER TABLE temp_versions | |
ADD PARTITION ( | |
PARTITION p#{current_year} VALUES LESS THAN (#{next_year}) | |
); | |
SQL | |
end | |
# Cria particionamento para o próximo ano se não existir | |
unless partitions.include?("p#{next_year}") | |
ActiveRecord::Base.connection.execute <<-SQL.squish | |
ALTER TABLE temp_versions | |
ADD PARTITION ( | |
PARTITION p#{next_year} VALUES LESS THAN (#{next_year + 1}) | |
); | |
SQL | |
end | |
end | |
end | |
end | |
batch_size = 100_000 | |
total_copied = 0 | |
copy_time = 0 | |
Version.where('created_at >= ?', 1.year.ago).find_in_batches(batch_size: batch_size) do |batch| | |
copy_time += Benchmark.realtime do | |
TempVersion.transaction do | |
batch.each do |version| | |
TempVersion.create!(version.attributes.except('id')) | |
version.destroy | |
end | |
end | |
end | |
total_copied += batch.size | |
puts "Copied #{total_copied} records. Copy time: #{copy_time.round(2)} seconds. / Copiados #{total_copied} registros. Tempo de cópia: #{copy_time.round(2)} segundos." | |
row_count = ActiveRecord::Base.connection.execute(<<-SQL.squish).first[0] | |
SELECT COUNT(*) | |
FROM versions | |
WHERE created_at >= DATE_SUB(NOW(), INTERVAL 1 YEAR) | |
SQL | |
break if row_count == 0 | |
end | |
rename_time = Benchmark.realtime do | |
puts "Renaming tables... / Renomeando tabelas..." | |
ActiveRecord::Base.connection.execute <<-SQL | |
RENAME TABLE versions TO versions_old, temp_versions TO versions; | |
SQL | |
end | |
drop_time = Benchmark.realtime do | |
puts "Dropping old table... / Excluindo tabela antiga..." | |
ActiveRecord::Base.connection.execute <<-SQL | |
DROP TABLE versions_old; | |
SQL | |
end | |
maintenance.update!( | |
status: 'completed', | |
ended_at: Time.current, | |
total_time: (Time.current - maintenance.started_at).round(2), | |
create_table_time: create_table_time.round(2), | |
copy_time: copy_time.round(2), | |
rename_time: rename_time.round(2), | |
drop_time: drop_time.round(2) | |
) | |
puts "Process completed successfully! Total records copied: #{total_copied}. Rename time: #{rename_time.round(2)} seconds. Drop time: #{drop_time.round(2)} seconds. / Processo concluído com sucesso! Total de registros copiados: #{total_copied}. Tempo de renomeação: #{rename_time.round(2)} segundos. Tempo de exclusão: #{drop_time.round(2)} segundos." | |
ensure | |
client.cache.delete('cleanup_versions_lock') if memcached_available | |
end | |
else | |
puts "Another process is running. Exiting... / Outro processo está em execução. Saindo..." | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment