lib/backy/pg_restore.rb in backy_rb-0.1.3 vs lib/backy/pg_restore.rb in backy_rb-0.1.4

- old
+ new

@@ -1,27 +1,93 @@ module Backy class PgRestore include Db include AppConfig + DUMP_DIR = "db/dump" + def initialize(file_name:) @file_name = file_name end def call - terminate_connection_sql = "SELECT pg_terminate_backend(pg_stat_activity.pid) FROM pg_stat_activity WHERE pg_stat_activity.datname = '#{database}' AND pid <> pg_backend_pid();" - cmd = "(#{pg_password_env}psql -c \"#{terminate_connection_sql};\" #{pg_credentials} #{database}; #{pg_password_env}dropdb #{pg_credentials} #{database}; #{pg_password_env}createdb #{pg_credentials} #{database}; gunzip -c #{file_name} | #{pg_password_env}psql #{pg_credentials} -q -d #{database}) 2>&1 >> #{log_file}" + pigz_installed = system('which pigz > /dev/null 2>&1') + multicore = Etc.nprocessors > 1 + use_multicore = ENV["BACKY_USE_PARALLEL"] == "true" - print "Restoring #{database} from #{file_name} ... " - - if system(cmd) - puts "done" + if pigz_installed && multicore && use_multicore + log_message('Using parallel restore with pigz') + parallel_restore else - puts "error. See #{log_file}" + log_message("Pigz not installed or system is not multicore") + log_message('Using plain text restore') + plain_text_restore end end private attr_reader :file_name + + def plain_text_restore + cmd = "(#{pg_password_env}psql -c \"#{terminate_connection_sql};\" #{pg_credentials} #{database}; #{pg_password_env}dropdb #{pg_credentials} #{database}; #{pg_password_env}createdb #{pg_credentials} #{database}; gunzip -c #{file_name} | #{pg_password_env}psql #{pg_credentials} -q -d #{database}) 2>&1 >> #{log_file}" + + log_message("Restoring #{database} from #{file_name} ...") + if system(cmd) + log_message("Database restoration completed successfully.") + else + log_message("Database restoration failed. See #{log_file} for details.") + end + end + + def parallel_restore + timestamp = Time.now.strftime("%Y%m%d_%H%M%S") + dump_dir = "#{DUMP_DIR}/#{database}_dump_#{timestamp}" + FileUtils.mkdir_p(dump_dir) + + decompress_cmd = "pigz -p #{Etc.nprocessors} -dc #{file_name} | tar -C #{dump_dir} --strip-components 3 -xf -" + restore_cmd = "pg_restore -j #{Etc.nprocessors} -Fd -O -d #{database} #{dump_dir}" + + # Terminate connections and drop/create database + terminate_and_recreate_db = "(#{pg_password_env}psql -c \"#{terminate_connection_sql};\" #{pg_credentials} #{database}; #{pg_password_env}dropdb #{pg_credentials} #{database}; #{pg_password_env}createdb #{pg_credentials} #{database}) 2>&1 >> #{log_file}" + + log_message("Terminating connections to #{database}") + if system(terminate_and_recreate_db) + log_message("Database connections terminated and database recreated.") + else + log_message("Error during database termination and recreation. See #{log_file}") + return + end + + # Decompress and restore + log_message("Decompressing #{file_name} into #{dump_dir} ...") + if system(decompress_cmd) + log_message("Decompression completed successfully.") + + # Check the expected file + unless File.exist?("#{dump_dir}/toc.dat") + log_message("toc.dat not found in #{dump_dir}.") + return + end + else + log_message("Decompression failed. See #{log_file} for details.") + return + end + + log_message("Restoring database from #{dump_dir} ...") + if system(restore_cmd) + log_message("Database restoration completed successfully.") + else + log_message("Database restoration failed. See #{log_file} for details.") + return + end + + log_message("Cleanup: Removing #{dump_dir} ...") + FileUtils.rm_rf(dump_dir) + log_message("Cleanup completed.") + end + + def terminate_connection_sql + "SELECT pg_terminate_backend(pg_stat_activity.pid) FROM pg_stat_activity WHERE pg_stat_activity.datname = '#{database}' AND pid <> pg_backend_pid();" + end end end