backy_rb 0.2.0 → 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: a7cc1e70142f6fa0c98d12c5e1187e05596a847e58ff13060b49d713bea3189c
4
- data.tar.gz: 702727d108936670bcfa86a203cd67c672eb0dfb2a3f84d4f22e538962c2e151
3
+ metadata.gz: 592cedc95670f4512c65fffebd96358d351153b2821ad2f237be4c32460060ca
4
+ data.tar.gz: 7055c420d1d2e382abde9f7049de3241648387acaaf124e5d2b8be92de32c9b1
5
5
  SHA512:
6
- metadata.gz: 16e3cc5cecf30e88be20ed71685cd5c615ace3ad960f0bcca92c03cda64f0c586da5d6905072e99c3fac2b334e598e46588eb438a0406dd10956a73fb1d4ec78
7
- data.tar.gz: 55e8e14e5edb195cc882abe7cf4568cbb986d1e39b955c198fea5f25091b0adc00e42136b8ccd6df5a522ddf61662a4ee0ff617ee877221fc6540f91b7813aee
6
+ metadata.gz: 9d7416f6c12e79d74202e98dca6f66cebb73d5b08cd7615c1966100aa88f288c4d0724361ec0661783124c281618ddba2bb4d65fd51dbab08ad5ddef4906c7a7
7
+ data.tar.gz: b2e58e94802831d2915c05e77d94dd87f46a789cc841e679083227d6dd0f6be3d9f20c98b374b5efc7a6f6fddfa525255b76e86b7a632388f84727f0e4ae4a53
data/CHANGELOG.md CHANGED
@@ -3,6 +3,15 @@
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
+
11
+ ## [0.2.1] - 2024-06-24
12
+ ### Fixed
13
+ - Fix bug where `push` is not working because of missing filename
14
+
6
15
  ## [0.2.0] - 2024-06-24
7
16
  ### Added
8
17
  - Support for turning off replication
data/Gemfile.lock CHANGED
@@ -1,10 +1,11 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- backy_rb (0.2.0)
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/README.md CHANGED
@@ -68,18 +68,19 @@ Backy can be configured through a .backyrc YAML file. Place this file in your ho
68
68
  Example `.backyrc`:
69
69
 
70
70
  ```yaml
71
- shared:
71
+ defaults:
72
72
  use_parallel: true
73
- pause_replication: true
74
- s3_access_key_id: YOUR_ACCESS_KEY
75
- s3_secret_access_key: YOUR_SECRET_KEY
76
- s3_region: YOUR_REGION
77
- s3_bucket: YOUR_BUCKET
78
- pg_host: DB_HOST
79
- pg_port: DB_PORT
80
- pg_username: DB_USERNAME
81
- pg_password: DB_PASSWORD
82
- pg_database: DB_NAME
73
+ s3:
74
+ access_key_id: YOUR_ACCESS_KEY
75
+ secret_access_key: YOUR_SECRET_KEY
76
+ region: YOUR_REGION
77
+ bucket: YOUR_BUCKET
78
+ database:
79
+ host: DB_HOST
80
+ port: DB_PORT
81
+ username: DB_USERNAME
82
+ password: DB_PASSWORD
83
+ database_name: DB_NAME
83
84
  ```
84
85
 
85
86
  ## Development
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
@@ -14,15 +14,19 @@ module Backy
14
14
  setup_backup_directory
15
15
  log_start
16
16
 
17
+ dump_file = nil
18
+ @replication_resumed = false
19
+
17
20
  begin
18
- handle_replication { backup }
21
+ handle_replication { dump_file = backup }
19
22
  rescue => e
20
23
  Logger.error("An error occurred during backup: #{e.message}")
21
24
  ensure
22
- if replica? && pause_replication?
23
- log_replication_resume
24
- end
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
28
+
29
+ dump_file
26
30
  end
27
31
 
28
32
  private
@@ -61,6 +65,7 @@ module Backy
61
65
  def log_replication_resume
62
66
  if resume_replication
63
67
  Logger.log("Replication resumed.")
68
+ @replication_resumed = true
64
69
  else
65
70
  Logger.error("Failed to resume replication. Manual intervention required.")
66
71
  end
@@ -68,13 +73,48 @@ module Backy
68
73
 
69
74
  def plain_text_backup
70
75
  timestamp = current_timestamp
71
- dump_file = "#{DUMP_DIR}/#{database}_#{whoami}@#{hostname}_#{timestamp}.sql.gz"
72
-
73
- cmd = "(#{pg_password_env}pg_dump #{pg_credentials} #{database} #{DUMP_CMD_OPTS} | gzip -9 > #{dump_file}) 2>&1 >> #{log_file}"
74
-
75
- Logger.log("Saving to #{dump_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
76
116
 
77
- execute_command(cmd, "error. See #{log_file}")
117
+ dump_file
78
118
  end
79
119
 
80
120
  def parallel_backup
@@ -91,10 +131,16 @@ module Backy
91
131
  execute_command(cleanup_cmd, "Cleanup failed. See #{log_file} for details.")
92
132
 
93
133
  Logger.success("Backup process completed. Output file: #{dump_file}")
134
+
135
+ dump_file
94
136
  end
95
137
 
96
138
  def execute_command(cmd, error_message)
97
- Logger.error(error_message) unless system(cmd)
139
+ if system(cmd)
140
+ Logger.success("done")
141
+ else
142
+ Logger.error(error_message)
143
+ end
98
144
  end
99
145
 
100
146
  def current_timestamp
@@ -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.0"
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.0
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