backy_rb 0.2.1 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b7bb1c2b1110d5fe15106b8bb78e8e07201e2318156aa6f67a4349eb92720a06
4
- data.tar.gz: 5a8ae6f47b04ac3480cecedf77987166d5d630cc511496a734cddf5f7659e871
3
+ metadata.gz: 592cedc95670f4512c65fffebd96358d351153b2821ad2f237be4c32460060ca
4
+ data.tar.gz: 7055c420d1d2e382abde9f7049de3241648387acaaf124e5d2b8be92de32c9b1
5
5
  SHA512:
6
- metadata.gz: 956ae711507992a7c75a3340d108b2df9e8da541ce9e90d346b1c0249bd22a414a6bf41f5e78f901f8b81ec68fd626a6f4cafbc8833d02a6eaea5f55563a0796
7
- data.tar.gz: ca0b9c66ff07d33bd204df31e400690e53ca3a7108d8267eed734e9f5bc3d280046d1033856fbf13f89c297c38821d212ae573f4ea1d30b49eda95eb135bb624
6
+ metadata.gz: 9d7416f6c12e79d74202e98dca6f66cebb73d5b08cd7615c1966100aa88f288c4d0724361ec0661783124c281618ddba2bb4d65fd51dbab08ad5ddef4906c7a7
7
+ data.tar.gz: b2e58e94802831d2915c05e77d94dd87f46a789cc841e679083227d6dd0f6be3d9f20c98b374b5efc7a6f6fddfa525255b76e86b7a632388f84727f0e4ae4a53
data/CHANGELOG.md CHANGED
@@ -3,6 +3,11 @@
3
3
  All notable changes to `Backy` will be documented in this file.
4
4
 
5
5
  ## [Unreleased]
6
+
7
+ ## [0.2.2] - 2025-01-25
8
+ ### Fixed
9
+ - Add Ruby 3.4 compatibility fix for ActiveSupport < 7.1
10
+
6
11
  ## [0.2.1] - 2024-06-24
7
12
  ### Fixed
8
13
  - Fix bug where `push` is not working because of missing filename
data/Gemfile.lock CHANGED
@@ -1,10 +1,11 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- backy_rb (0.2.1)
4
+ backy_rb (0.2.2)
5
5
  activerecord (>= 4.0)
6
6
  activesupport (>= 4.0)
7
7
  aws-sdk-s3 (>= 1.117)
8
+ concurrent-ruby (~> 1.3)
8
9
  pg (~> 1.5)
9
10
  thor (~> 1.2)
10
11
 
@@ -95,7 +96,7 @@ GEM
95
96
  aws-eventstream (~> 1, >= 1.0.2)
96
97
  brakeman (5.4.1)
97
98
  builder (3.2.4)
98
- concurrent-ruby (1.2.2)
99
+ concurrent-ruby (1.3.5)
99
100
  crass (1.0.6)
100
101
  date (3.3.3)
101
102
  diff-lcs (1.5.0)
@@ -120,6 +121,7 @@ GEM
120
121
  marcel (1.0.2)
121
122
  method_source (1.0.0)
122
123
  mini_mime (1.1.2)
124
+ mini_portile2 (2.8.9)
123
125
  minitest (5.18.0)
124
126
  net-imap (0.3.6)
125
127
  date
@@ -131,9 +133,8 @@ GEM
131
133
  net-smtp (0.3.3)
132
134
  net-protocol
133
135
  nio4r (2.5.9)
134
- nokogiri (1.15.2-arm64-darwin)
135
- racc (~> 1.4)
136
- nokogiri (1.15.2-x86_64-linux)
136
+ nokogiri (1.15.2)
137
+ mini_portile2 (~> 2.8.2)
137
138
  racc (~> 1.4)
138
139
  parallel (1.22.1)
139
140
  parser (3.2.1.1)
@@ -231,6 +232,7 @@ GEM
231
232
  PLATFORMS
232
233
  arm64-darwin-22
233
234
  arm64-darwin-23
235
+ arm64-darwin-24
234
236
  x86_64-linux
235
237
 
236
238
  DEPENDENCIES
data/backy_rb.gemspec CHANGED
@@ -12,7 +12,7 @@ Gem::Specification.new do |spec|
12
12
  spec.summary = "Backy is a powerful and user-friendly database backup gem designed specifically for Ruby on Rails applications. It streamlines the backup process, ensuring your data is safe, secure, and easily retrievable. With its versatile features and easy integration, Backy is the go-to solution for Rails developers looking to protect their valuable information."
13
13
  spec.description = "Backy is a comprehensive database backup solution for Ruby on Rails applications, created to help developers manage and safeguard their data with ease. This robust gem offers a wide range of features"
14
14
  spec.homepage = "https://rubynor.com"
15
- spec.required_ruby_version = ">= 2.2.0"
15
+ spec.required_ruby_version = ">= 2.5.0"
16
16
 
17
17
  spec.metadata["homepage_uri"] = spec.homepage
18
18
  spec.metadata["source_code_uri"] = "https://github.com/rubynor/backy"
@@ -43,9 +43,13 @@ Gem::Specification.new do |spec|
43
43
 
44
44
  spec.add_dependency "activerecord", ">= 4.0"
45
45
  spec.add_dependency "activesupport", ">= 4.0"
46
+
47
+ # Note: For Ruby 3.4+ compatibility with ActiveSupport < 7.1,
48
+ # we include a compatibility fix in lib/backy_rb.rb
46
49
  spec.add_dependency "aws-sdk-s3", ">= 1.117"
47
50
  spec.add_dependency "pg", "~> 1.5"
48
51
  spec.add_dependency "thor", "~> 1.2"
52
+ spec.add_dependency "concurrent-ruby", "~> 1.3"
49
53
 
50
54
  # For more information and examples about making a new gem, check out our
51
55
  # guide at: https://bundler.io/guides/creating_gem.html
data/lib/backy/pg_dump.rb CHANGED
@@ -15,13 +15,15 @@ module Backy
15
15
  log_start
16
16
 
17
17
  dump_file = nil
18
+ @replication_resumed = false
18
19
 
19
20
  begin
20
21
  handle_replication { dump_file = backup }
21
22
  rescue => e
22
23
  Logger.error("An error occurred during backup: #{e.message}")
23
24
  ensure
24
- log_replication_resume if replica? && pause_replication?
25
+ # Only resume if not already resumed (single core path resumes early)
26
+ log_replication_resume if replica? && pause_replication? && !@replication_resumed
25
27
  end
26
28
 
27
29
  dump_file
@@ -63,6 +65,7 @@ module Backy
63
65
  def log_replication_resume
64
66
  if resume_replication
65
67
  Logger.log("Replication resumed.")
68
+ @replication_resumed = true
66
69
  else
67
70
  Logger.error("Failed to resume replication. Manual intervention required.")
68
71
  end
@@ -70,13 +73,46 @@ module Backy
70
73
 
71
74
  def plain_text_backup
72
75
  timestamp = current_timestamp
73
- dump_file = "#{DUMP_DIR}/#{database}_#{whoami}@#{hostname}_#{timestamp}.sql.gz"
74
-
75
- cmd = "(#{pg_password_env}pg_dump #{pg_credentials} #{database} #{DUMP_CMD_OPTS} | gzip -9 > #{dump_file}) 2>&1 >> #{log_file}"
76
-
77
- Logger.log("Saving to #{dump_file} ... ")
78
-
79
- execute_command(cmd, "error. See #{log_file}")
76
+ temp_dump_file = "#{DUMP_DIR}/#{database}_#{whoami}@#{hostname}_#{timestamp}.sql"
77
+ dump_file = "#{temp_dump_file}.gz"
78
+
79
+ # First, dump the database without compression
80
+ dump_cmd = "(#{pg_password_env}pg_dump #{pg_credentials} #{database} #{DUMP_CMD_OPTS} > #{temp_dump_file}) 2>&1 >> #{log_file}"
81
+
82
+ Logger.log("Dumping database to #{temp_dump_file} ... ")
83
+
84
+ if system(dump_cmd)
85
+ Logger.success("Database dump completed")
86
+
87
+ # Resume replication immediately after dump completes
88
+ if replica? && pause_replication?
89
+ log_replication_resume
90
+ end
91
+
92
+ # Now compress the dump file
93
+ Logger.log("Compressing dump file ... ")
94
+
95
+ # Check if pigz is available for single-threaded but faster compression
96
+ if system("which pigz > /dev/null 2>&1")
97
+ compression_cmd = "pigz -1 < #{temp_dump_file} > #{dump_file}"
98
+ Logger.log("Using pigz for faster compression")
99
+ else
100
+ compression_cmd = "gzip -1 < #{temp_dump_file} > #{dump_file}"
101
+ end
102
+
103
+ if system(compression_cmd)
104
+ Logger.success("Compression completed")
105
+ # Remove the uncompressed file
106
+ FileUtils.rm_f(temp_dump_file)
107
+ else
108
+ Logger.error("Compression failed. See #{log_file}")
109
+ FileUtils.rm_f(temp_dump_file)
110
+ return nil
111
+ end
112
+ else
113
+ Logger.error("Database dump failed. See #{log_file}")
114
+ return nil
115
+ end
80
116
 
81
117
  dump_file
82
118
  end
@@ -45,7 +45,7 @@ module Backy
45
45
  FileUtils.mkdir_p(dump_dir)
46
46
 
47
47
  decompress_cmd = "pigz -p #{Etc.nprocessors} -dc #{file_name} | tar -C #{dump_dir} --strip-components 3 -xf -"
48
- restore_cmd = "pg_restore -j #{Etc.nprocessors} -Fd -O -d #{database} #{dump_dir}"
48
+ restore_cmd = "pg_restore -j #{Etc.nprocessors} -Fd -O #{pg_credentials} -d #{database} #{dump_dir}"
49
49
 
50
50
  # Terminate connections and drop/create database
51
51
  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}"
data/lib/backy/s3_save.rb CHANGED
@@ -3,6 +3,8 @@ module Backy
3
3
  include S3
4
4
 
5
5
  DEFAULT_EXPIRE_AFTER = 1.month
6
+ PART_SIZE = 50 * 1024 * 1024
7
+ MAX_THREADS = 5
6
8
 
7
9
  def initialize(file_name:, key: nil, expire_after: nil)
8
10
  @file_name = file_name
@@ -19,8 +21,12 @@ module Backy
19
21
  return
20
22
  end
21
23
 
22
- File.open(file_name, "rb") do |body|
23
- s3.put_object(key: key, body: body, bucket: bucket, expires: expires)
24
+ file_size = File.size(file_name)
25
+
26
+ if file_size < 5 * 1024 * 1024 * 1024
27
+ upload_simple
28
+ else
29
+ upload_multipart
24
30
  end
25
31
 
26
32
  puts "done"
@@ -29,5 +35,68 @@ module Backy
29
35
  private
30
36
 
31
37
  attr_reader :file_name, :key, :expires
38
+
39
+ def upload_simple
40
+ puts "Uploading #{file_name} to S3 ... "
41
+ File.open(file_name, "rb") do |body|
42
+ s3.put_object(key: key, body: body, bucket: bucket, expires: expires)
43
+ end
44
+ end
45
+
46
+ def upload_multipart
47
+ puts "Uploading #{file_name} to S3 (multipart) ... "
48
+ upload_id = s3.create_multipart_upload(bucket: bucket, key: key, expires: expires).upload_id
49
+ parts = []
50
+ mutex = Mutex.new
51
+
52
+ file_size = File.size(file_name)
53
+ total_parts = (file_size.to_f / PART_SIZE).ceil
54
+
55
+ part_numbers = (1..total_parts).to_a
56
+
57
+ part_numbers.each_slice(MAX_THREADS) do |batch|
58
+ threads = batch.map do |part_number|
59
+ Thread.new do
60
+ start_pos = (part_number - 1) * PART_SIZE
61
+ end_pos = [start_pos + PART_SIZE, file_size].min
62
+
63
+ part_data = nil
64
+ File.open(file_name, "rb") do |file|
65
+ file.seek(start_pos)
66
+ part_data = file.read(end_pos - start_pos)
67
+ end
68
+
69
+ resp = s3.upload_part(
70
+ bucket: bucket,
71
+ key: key,
72
+ upload_id: upload_id,
73
+ part_number: part_number,
74
+ body: part_data
75
+ )
76
+
77
+ mutex.synchronize do
78
+ parts << {etag: resp.etag, part_number: part_number}
79
+ end
80
+ end
81
+ end
82
+
83
+ threads.each(&:join)
84
+ end
85
+
86
+ sorted_parts = parts.sort_by { |p| p[:part_number] }
87
+
88
+ s3.complete_multipart_upload(
89
+ bucket: bucket,
90
+ key: key,
91
+ upload_id: upload_id,
92
+ multipart_upload: {
93
+ parts: sorted_parts
94
+ }
95
+ )
96
+ rescue => e
97
+ puts "\nError during multipart upload: #{e.message}"
98
+ s3.abort_multipart_upload(bucket: bucket, key: key, upload_id: upload_id)
99
+ raise e
100
+ end
32
101
  end
33
102
  end
data/lib/backy/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Backy
2
- VERSION = "0.2.1"
2
+ VERSION = "0.2.2"
3
3
  end
data/lib/backy_rb.rb CHANGED
@@ -1,5 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Ruby 3.4+ compatibility fix for ActiveSupport < 7.1
4
+ require 'logger' if RUBY_VERSION >= '3.4.0'
5
+
3
6
  require "active_support"
4
7
  require "active_support/core_ext/integer/time"
5
8
  require "active_support/core_ext/object/blank"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: backy_rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexey Kharchenko
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2024-06-24 00:00:00.000000000 Z
13
+ date: 2025-08-25 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rspec
@@ -236,6 +236,20 @@ dependencies:
236
236
  - - "~>"
237
237
  - !ruby/object:Gem::Version
238
238
  version: '1.2'
239
+ - !ruby/object:Gem::Dependency
240
+ name: concurrent-ruby
241
+ requirement: !ruby/object:Gem::Requirement
242
+ requirements:
243
+ - - "~>"
244
+ - !ruby/object:Gem::Version
245
+ version: '1.3'
246
+ type: :runtime
247
+ prerelease: false
248
+ version_requirements: !ruby/object:Gem::Requirement
249
+ requirements:
250
+ - - "~>"
251
+ - !ruby/object:Gem::Version
252
+ version: '1.3'
239
253
  description: Backy is a comprehensive database backup solution for Ruby on Rails applications,
240
254
  created to help developers manage and safeguard their data with ease. This robust
241
255
  gem offers a wide range of features
@@ -294,14 +308,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
294
308
  requirements:
295
309
  - - ">="
296
310
  - !ruby/object:Gem::Version
297
- version: 2.2.0
311
+ version: 2.5.0
298
312
  required_rubygems_version: !ruby/object:Gem::Requirement
299
313
  requirements:
300
314
  - - ">="
301
315
  - !ruby/object:Gem::Version
302
316
  version: '0'
303
317
  requirements: []
304
- rubygems_version: 3.4.22
318
+ rubygems_version: 3.5.22
305
319
  signing_key:
306
320
  specification_version: 4
307
321
  summary: Backy is a powerful and user-friendly database backup gem designed specifically