Module: TinyBackup
- Defined in:
- lib/version.rb,
lib/configure.rb,
lib/tiny_backup.rb
Defined Under Namespace
Classes: Config
Constant Summary collapse
- VERSION =
'0.1.0'
Class Method Summary collapse
-
.configure {|@config ||= Config.new| ... } ⇒ Object
Set global settings for TinyBackup like TinyBackup.configure {|config| config.max_versions = 100 }.
Instance Method Summary collapse
-
#backup_now ⇒ Object
Create a backup of the current database and choose automatically to create a .zip or .diff file.
-
#compact_all ⇒ Object
Merge into the .zip file and delete all the .diff files to clear the space.
-
#config ⇒ Object
Global settings for TinyBackup.
-
#restore_db(version_number, just_temporary = true, with_backup = true) ⇒ Object
Change the database to match the selected integer
version_number
.
Class Method Details
Instance Method Details
#backup_now ⇒ Object
Create a backup of the current database and choose automatically to create a .zip or .diff file.
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 |
# File 'lib/tiny_backup.rb', line 19 def backup_now # if the resource is locked, we skip to ensure block lock locked_by_this_method = true tmp_files = [] nvf = new_version_filename stream = StringIO.new ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream) schema_rb = stream.string if nvf.split(".").last == "zip" # there is no .zip file so we must create one and add schema.rb and .csv file for each table # TODO: use a much better compression like Zlib::BEST_COMPRESSION to reduce the zip size, but this will consume processing power ZIPLIB.open("#{config.backup_folder}/#{nvf}", ZIPLIB::CREATE) do |f| t_benchmark = Benchmark.ms do f.get_output_stream("schema.rb") do |ff| ff.write schema_rb tmp_files << ff if ZIPOLD end end puts "-- backup_schema\n -> #{'%.4f' % (t_benchmark/1000)}s\n" if !config.silent ActiveRecord::Base.connection.tables.each do |table| next if table == "schema_migrations" query_count = ActiveRecord::Base.connection.execute("SELECT COUNT(*) FROM #{table}").first.first t_benchmark = Benchmark.ms do if query_count > 0 rows = [] query_index = 0 loop do break if query_index >= query_count query = ActiveRecord::Base.connection.execute("SELECT * FROM #{table} LIMIT #{config.per_page} OFFSET #{query_index}") rows << add_query(query.fields) if query_index == 0 query.each { |row| rows << add_query(row) } query_index += config.per_page end f.get_output_stream("#{table}.csv") do |ff| ff.write rows.join tmp_files << ff if ZIPOLD end end end puts "-- backup_table(\"#{table}\")\n -> #{'%.4f' % (t_benchmark/1000)}s\n" if !config.silent end end else # a new .diff file is created with the diff between origin_zip and tmp_origin_zip(made by merging all the versions into origin) tmp_origin_zip = "#{config.backup_folder}/#{compact_original(:all)}" tables = ActiveRecord::Base.connection.tables is_empty = true File.open("#{config.backup_folder}/#{nvf}", "wb") do |f| ZIPLIB.open(tmp_origin_zip) do |zf| zf.entries.each do |zf_entry| if zf_entry.name == "schema.rb" t_benchmark = Benchmark.ms do tables.delete "schema_migrations" this_diff = diff_files zf.read(zf_entry.name), schema_rb if this_diff.present? is_empty = false f.write "***************\n" f.write "*** schema.rb \n" f.write "\n" this_diff.each { |i| f.write i } f.write "\n\n" end end puts "-- backup_schema\n -> #{'%.4f' % (t_benchmark/1000)}s\n" if !config.silent else table = zf_entry.name.split(".").first tables.delete table begin query_count = ActiveRecord::Base.connection.execute("SELECT COUNT(*) FROM #{table}").first.first rescue ActiveRecord::StatementInvalid next end rows = [] query_index = 0 t_benchmark = Benchmark.ms do loop do break if query_index >= query_count query = ActiveRecord::Base.connection.execute("SELECT * FROM #{table} LIMIT #{config.per_page} OFFSET #{query_index}") rows << add_query(query.fields) if query_index == 0 query.each { |row| rows << add_query(row) } query_index += config.per_page end this_diff = diff_files zf.read(zf_entry.name), rows.join if this_diff.present? is_empty = false f.write "***************\n" f.write "*** #{zf_entry.name} \n" f.write "\n" this_diff.each { |i| f.write i } f.write "\n\n" end end puts "-- backup_table(\"#{table}\")\n -> #{'%.4f' % (t_benchmark/1000)}s\n" if !config.silent end end end # tables that are created recently and doesn't have a .csv file in the tmp_origin_zip tables.each do |table| begin query_count = ActiveRecord::Base.connection.execute("SELECT COUNT(*) FROM #{table}").first.first rescue ActiveRecord::StatementInvalid next end rows = [] query_index = 0 t_benchmark = Benchmark.ms do loop do break if query_index >= query_count query = ActiveRecord::Base.connection.execute("SELECT * FROM #{table} LIMIT #{config.per_page} OFFSET #{query_index}") rows << add_query(query.fields) if query_index == 0 query.each { |row| rows << add_query(row) } query_index += config.per_page end this_diff = diff_files "", rows.join if this_diff.present? is_empty = false f.write "***************\n" f.write "*** #{table}.csv \n" f.write "\n" this_diff.each { |i| f.write i } f.write "\n\n" end end puts "-- backup_table(\"#{table}\")\n -> #{'%.4f' % (t_benchmark/1000)}s\n" if !config.silent end end File.delete tmp_origin_zip File.delete("#{config.backup_folder}/#{nvf}") if is_empty end # keep max versions version_files = Dir.glob("#{config.backup_folder}/#{config.version_prefix}*").sort if config.max_versions < version_files.length # throw files to garbage tmp_files << Dir.glob("#{config.backup_folder}/#{config.zip_prefix}*").first tmp_files << version_files.first File.rename "#{config.backup_folder}/#{compact_original(1)}", dup_file("#{config.backup_folder}/#{config.zip_prefix}_#{Time.now.strftime(config.date_format)}.zip") end # delete temporary files before method exit rescue => e @method_error = e ensure unlock if locked_by_this_method tmp_files.each { |i| File.delete(i) rescue nil } if tmp_files.present? raise @method_error if @method_error.present? return true end |
#compact_all ⇒ Object
Merge into the .zip file and delete all the .diff files to clear the space.
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 |
# File 'lib/tiny_backup.rb', line 195 def compact_all lock # if the resource is locked, we skip to ensure block locked_by_this_method = true tmp_files = [] tmp_files += Dir.glob("#{config.backup_folder}/#{config.zip_prefix}*") tmp_files += Dir.glob("#{config.backup_folder}/#{config.version_prefix}*") # make the temporary zip be the original and apply the updated_at time-stamp File.rename "#{config.backup_folder}/#{compact_original(:all)}", dup_file("#{config.backup_folder}/#{config.zip_prefix}_#{Time.now.strftime(config.date_format)}.zip") # delete temporary files before method exit rescue => e @method_error = e ensure unlock if locked_by_this_method tmp_files.each { |i| File.delete(i) rescue nil } if tmp_files.present? raise @method_error if @method_error.present? return true end |
#config ⇒ Object
Global settings for TinyBackup
8 9 10 |
# File 'lib/configure.rb', line 8 def config @config ||= Config.new end |
#restore_db(version_number, just_temporary = true, with_backup = true) ⇒ Object
Change the database to match the selected integer version_number
.
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 |
# File 'lib/tiny_backup.rb', line 220 def restore_db version_number, just_temporary=true, with_backup=true # do this before deleting the database and lose data backup_now if just_temporary && with_backup lock # if the resource is locked, we skip to ensure block locked_by_this_method = true tmp_files = [] if just_temporary && version_number != :all puts "you want to restore just temporary: DO NOT start a backup BEFORE calling TinyBackup.restore_db(:all)\n" if !config.silent end version_files = Dir.glob("#{config.backup_folder}/#{config.version_prefix}*") if version_number == :all version_count = version_files.length else good_versions = version_files.find_all { |i| i.gsub("#{config.backup_folder}/#{config.version_prefix}", "").split("_").first.to_i <= version_number.to_i } version_count = good_versions.length tmp_files += version_files - good_versions if !just_temporary end tmp_origin_zip = compact_original version_count tmp_files << "#{config.backup_folder}/#{tmp_origin_zip}" tmp_files << "#{config.backup_folder}/schema_tmp.rb" db_name = Rails.configuration.database_configuration[Rails.env]["database"] db_collation = ActiveRecord::Base.connection.collation ActiveRecord::Base.connection.drop_database db_name ActiveRecord::Base.connection.create_database db_name, collation: db_collation ActiveRecord::Base.connection.reconnect! # prepare the structure ZIPLIB.open("#{config.backup_folder}/#{tmp_origin_zip}") do |zf| zf.entries.each do |zf_entry| if zf_entry.name == "schema.rb" File.open("#{config.backup_folder}/schema_tmp.rb", "wb") { |f| f.write zf.read(zf_entry.name) } break end end end schema_verbose = ActiveRecord::Schema.verbose ActiveRecord::Schema.verbose = !config.silent ActiveRecord::Schema.load("#{config.backup_folder}/schema_tmp.rb") ActiveRecord::Schema.verbose = schema_verbose # add the data ZIPLIB.open("#{config.backup_folder}/#{tmp_origin_zip}") do |zf| zf.entries.each do |zf_entry| next if zf_entry.name == "schema.rb" table_rows = zf.read(zf_entry.name).split("\n") table_header = table_rows.shift table_name = zf_entry.name.split(".").first t_benchmark = Benchmark.ms do table_rows.in_groups_of(config.per_page, false) do |tr_group| ActiveRecord::Base.connection.execute insert_row(table_name, table_header, tr_group) end end puts "-- insert_data(\"#{table_name}\")\n -> #{'%.4f' % (t_benchmark/1000)}s\n" if !config.silent end end # delete temporary files before method exit rescue => e @method_error = e ensure unlock if locked_by_this_method tmp_files.each { |i| File.delete(i) rescue nil } if tmp_files.present? raise @method_error if @method_error.present? return true end |