backy_rb 0.1.7 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 00ab5005d9a22cf24c18c8fb7a9a5fa0a202eb1a19ee137cca3d51f41999bfc6
4
- data.tar.gz: 1a9e412072b4d6f57e9c1d32362eddcbecd5f700ae8f612d4c67a5604be3e176
3
+ metadata.gz: a7cc1e70142f6fa0c98d12c5e1187e05596a847e58ff13060b49d713bea3189c
4
+ data.tar.gz: 702727d108936670bcfa86a203cd67c672eb0dfb2a3f84d4f22e538962c2e151
5
5
  SHA512:
6
- metadata.gz: 5440e8a4dbd149b823ebe27115277a5fafd96cdafec5f4a6269bb7d8832b02cf206feac6f673cef89e84d325b97c0fc75e9698e0e002c5eed974dada5fcbdb16
7
- data.tar.gz: 6f9534e4a3a33e1281a85d926bd6b4b1a348ea95061ba22828b2b24d4a8404bd831c51d6cb5e3766a094f66b7dad2f6d4cfd45019f638e2731cb3308dad7f169
6
+ metadata.gz: 16e3cc5cecf30e88be20ed71685cd5c615ace3ad960f0bcca92c03cda64f0c586da5d6905072e99c3fac2b334e598e46588eb438a0406dd10956a73fb1d4ec78
7
+ data.tar.gz: 55e8e14e5edb195cc882abe7cf4568cbb986d1e39b955c198fea5f25091b0adc00e42136b8ccd6df5a522ddf61662a4ee0ff617ee877221fc6540f91b7813aee
data/.backyrc.example CHANGED
@@ -1,17 +1,33 @@
1
1
  # ~/.backyrc
2
- defaults:
2
+ shared:
3
3
  use_parallel: true
4
- s3:
5
- access_key_id: "YOUR_AWS_ACCESS_KEY_ID"
6
- secret_access_key: "YOUR_AWS_SECRET_ACCESS_KEY"
7
- region: "eu-central-1"
8
- bucket: "your-s3-bucket-name"
9
- database:
10
- adapter: "postgresql"
11
- host: "localhost"
12
- port: 5432
13
- username: "your-db-username"
14
- password: "your-db-password"
15
- database_name: "your-database-name"
16
- local:
17
- backup_path: "/path/to/your/local/backup/directory"
4
+ pause_replication: true
5
+ s3_access_key: "YOUR_AWS_ACCESS_KEY_ID"
6
+ s3_secret: "YOUR_AWS_SECRET_ACCESS_KEY"
7
+ s3_region: "eu-central-1"
8
+ s3_bucket: "your-s3-bucket-name"
9
+ s3_prefix: "./db/dump/"
10
+ pg_host: "localhost"
11
+ pg_port: 5432
12
+ pg_username: "your-db-username"
13
+ pg_password: "your-db-password"
14
+ pg_database: "your-database-name"
15
+ app_name: "backy"
16
+ environment: "development"
17
+ log_file: "./log/backy.log"
18
+ local_backup_path: "/path/to/your/local/backup/directory"
19
+
20
+ production:
21
+ pg_host: "production-host"
22
+ s3_bucket: "production-s3-bucket-name"
23
+ log_file: "./log/production_backy.log"
24
+
25
+ staging:
26
+ pg_host: "staging-host"
27
+ s3_bucket: "staging-s3-bucket-name"
28
+ log_file: "./log/staging_backy.log"
29
+
30
+ development:
31
+ pg_host: "localhost"
32
+ s3_bucket: "development-s3-bucket-name"
33
+ log_file: "./log/development_backy.log"
data/CHANGELOG.md CHANGED
@@ -3,6 +3,18 @@
3
3
  All notable changes to `Backy` will be documented in this file.
4
4
 
5
5
  ## [Unreleased]
6
+ ## [0.2.0] - 2024-06-24
7
+ ### Added
8
+ - Support for turning off replication
9
+ - Added support for config by environment in .backyrc
10
+
11
+ ### Changed
12
+ - Breaking change; Change in config keys
13
+ - Internal refactoring
14
+
15
+ ## [0.1.8] - 2024-06-22
16
+ ### Fixed
17
+ - Ensure that the dependencies are installed
6
18
 
7
19
  ## [0.1.7] - 2024-06-21
8
20
  ### Added
data/Gemfile CHANGED
@@ -11,10 +11,4 @@ gem "rspec", "~> 3.0"
11
11
 
12
12
  gem "standard", "~> 1.3"
13
13
 
14
- gem "aws-sdk-s3", "~> 1.117"
15
-
16
- gem "pg"
17
-
18
- gem "thor", "~> 1.2"
19
-
20
14
  gem "puma"
data/Gemfile.lock CHANGED
@@ -1,10 +1,12 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- backy_rb (0.1.3)
4
+ backy_rb (0.2.0)
5
5
  activerecord (>= 4.0)
6
6
  activesupport (>= 4.0)
7
7
  aws-sdk-s3 (>= 1.117)
8
+ pg (~> 1.5)
9
+ thor (~> 1.2)
8
10
 
9
11
  GEM
10
12
  remote: https://rubygems.org/
@@ -136,7 +138,7 @@ GEM
136
138
  parallel (1.22.1)
137
139
  parser (3.2.1.1)
138
140
  ast (~> 2.4.1)
139
- pg (1.5.3)
141
+ pg (1.5.6)
140
142
  puma (6.3.0)
141
143
  nio4r (~> 2.0)
142
144
  racc (1.7.1)
@@ -228,14 +230,13 @@ GEM
228
230
 
229
231
  PLATFORMS
230
232
  arm64-darwin-22
233
+ arm64-darwin-23
231
234
  x86_64-linux
232
235
 
233
236
  DEPENDENCIES
234
- aws-sdk-s3 (~> 1.117)
235
237
  backy_rb!
236
238
  brakeman (~> 5.4)
237
239
  faker (>= 3.1)
238
- pg
239
240
  puma
240
241
  rails (>= 4.0)
241
242
  rake (~> 13.0)
@@ -245,7 +246,6 @@ DEPENDENCIES
245
246
  rubocop-rake (~> 0.6.0)
246
247
  rubocop-rspec (~> 2.18)
247
248
  standard (~> 1.3)
248
- thor (~> 1.2)
249
249
  timecop (~> 0.9.6)
250
250
  zlib (~> 3.0)
251
251
 
data/README.md CHANGED
@@ -68,19 +68,18 @@ 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
- defaults:
71
+ shared:
72
72
  use_parallel: true
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
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
84
83
  ```
85
84
 
86
85
  ## Development
data/backy_rb.gemspec CHANGED
@@ -7,6 +7,7 @@ Gem::Specification.new do |spec|
7
7
  spec.version = Backy::VERSION
8
8
  spec.authors = ["Alexey Kharchenko", "Martin Ulleberg", "Pål André Sundt"]
9
9
  spec.email = ["akharchenko@gmail.com", "martin.ulleberg@gmail.com", "pal@rubynor.com"]
10
+ spec.license = "MIT"
10
11
 
11
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."
12
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"
@@ -43,6 +44,9 @@ Gem::Specification.new do |spec|
43
44
  spec.add_dependency "activerecord", ">= 4.0"
44
45
  spec.add_dependency "activesupport", ">= 4.0"
45
46
  spec.add_dependency "aws-sdk-s3", ">= 1.117"
47
+ spec.add_dependency "pg", "~> 1.5"
48
+ spec.add_dependency "thor", "~> 1.2"
49
+
46
50
  # For more information and examples about making a new gem, check out our
47
51
  # guide at: https://bundler.io/guides/creating_gem.html
48
52
  end
@@ -1,4 +1,4 @@
1
- require 'forwardable'
1
+ require "forwardable"
2
2
 
3
3
  module Backy
4
4
  module AppConfig
@@ -1,29 +1,44 @@
1
+ require "fileutils"
2
+ require "etc"
3
+ require "open3"
4
+ require "yaml"
5
+ require "uri"
6
+
1
7
  module Backy
2
8
  class Configuration
3
- attr_writer(
4
- :pg_host,
5
- :pg_port,
6
- :pg_database,
7
- :pg_username,
8
- :pg_password,
9
- :s3_region,
10
- :s3_access_key,
11
- :s3_secret,
12
- :s3_bucket,
13
- :s3_prefix,
14
- :app_name,
15
- :environment,
16
- :use_parallel,
17
- :log_file
18
- )
9
+ DEFAULTS = {
10
+ pg_host: nil,
11
+ pg_port: nil,
12
+ pg_database: nil,
13
+ pg_username: nil,
14
+ pg_password: nil,
15
+ s3_region: nil,
16
+ s3_access_key: nil,
17
+ s3_secret: nil,
18
+ s3_bucket: nil,
19
+ s3_prefix: "./db/dump/",
20
+ app_name: "backy",
21
+ environment: "development",
22
+ use_parallel: false,
23
+ pause_replication: true,
24
+ log_file: "./log/backy.log",
25
+ local_backup_path: nil
26
+ }.freeze
27
+
28
+ CONFIG_FILE_NAME = ".backyrc"
29
+
30
+ attr_accessor(*DEFAULTS.keys)
31
+
32
+ def initialize
33
+ DEFAULTS.each do |key, value|
34
+ instance_variable_set("@#{key}", value)
35
+ end
36
+ end
19
37
 
20
38
  def load
21
- local_config_file = File.join(Dir.pwd, '.backyrc')
22
- global_config_file = File.join(Dir.home, '.backyrc')
39
+ load_config_file
23
40
 
24
- config_file = File.exist?(local_config_file) ? local_config_file : global_config_file
25
- Logger.log("Loading configuration from #{config_file}...") if File.exist?(config_file)
26
- load_from_file(config_file) if File.exist?(config_file)
41
+ load_from_env
27
42
  end
28
43
 
29
44
  def pg_url=(url)
@@ -40,54 +55,14 @@ module Backy
40
55
  @pg_url ||= ENV["PG_URL"]
41
56
  end
42
57
 
43
- def pg_host
44
- @pg_host ||= ENV["PG_HOST"]
45
- end
46
-
47
- def pg_port
48
- @pg_port ||= ENV["PG_PORT"]
49
- end
50
-
51
- def pg_database
52
- @pg_database ||= ENV["PG_DATABASE"]
53
- end
54
-
55
- def pg_username
56
- @pg_username ||= ENV["PG_USERNAME"]
57
- end
58
-
59
- def pg_password
60
- @pg_password ||= ENV["PG_PASSWORD"]
61
- end
62
-
63
- def s3_region
64
- @s3_region ||= ENV["S3_REGION"]
65
- end
66
-
67
- def s3_access_key
68
- @s3_access_key ||= ENV["S3_ACCESS_KEY"]
69
- end
70
-
71
- def s3_secret
72
- @s3_secret ||= ENV["S3_SECRET"]
73
- end
74
-
75
- def s3_bucket
76
- @s3_bucket ||= ENV["S3_BUCKET"]
77
- end
78
-
79
- def s3_prefix
80
- @s3_prefix ||= ENV["S3_PREFIX"].presence || "/db/dump/"
81
- end
82
-
83
- def use_parallel
84
- @use_parallel ||= ENV["BACKY_USE_PARALLEL"] == "true"
85
- end
86
-
87
58
  def use_parallel?
88
59
  use_parallel && pigz_installed && multicore
89
60
  end
90
61
 
62
+ def pause_replication?
63
+ pause_replication
64
+ end
65
+
91
66
  # Detect if pigz binary is available
92
67
  # If it is, use it to speed up the dump
93
68
  # pigz is a parallel gzip implementation
@@ -101,14 +76,6 @@ module Backy
101
76
  @multicore ||= Etc.nprocessors > 1
102
77
  end
103
78
 
104
- def app_name
105
- @app_name ||= ENV["APP_NAME"].presence || "backy"
106
- end
107
-
108
- def environment
109
- @environment ||= "development"
110
- end
111
-
112
79
  def log_file
113
80
  @log_file ||= default_log_file
114
81
  end
@@ -118,37 +85,62 @@ module Backy
118
85
  def default_log_file
119
86
  if Gem.win_platform?
120
87
  # Windows default path
121
- File.join(Dir.home, "AppData", "Local", "#{app_name}", "log", "#{app_name}.log")
88
+ File.join(Dir.home, "AppData", "Local", app_name, "log", app_name, ".log")
122
89
  else
123
90
  # Unix-like systems default path
124
- File.join(Dir.home, ".local", "share", "#{app_name}", "log", "#{app_name}.log")
91
+ File.join(Dir.home, ".local", "share", app_name, "log", app_name, ".log")
92
+ end
93
+ end
94
+
95
+ def load_config_file
96
+ local_config_file = File.join(Dir.pwd, CONFIG_FILE_NAME)
97
+ global_config_file = File.join(Dir.home, CONFIG_FILE_NAME)
98
+
99
+ config_file = File.exist?(local_config_file) ? local_config_file : global_config_file
100
+ if File.exist?(config_file)
101
+ Logger.log("Loading configuration from #{config_file}...")
102
+ load_from_file(config_file)
125
103
  end
126
104
  end
127
105
 
128
106
  def load_from_file(file_path)
129
107
  configuration = YAML.load_file(file_path)
130
108
 
131
- @s3_access_key = configuration.dig("defaults", "s3", "access_key_id")
132
- @s3_secret = configuration.dig("defaults", "s3", "secret_access_key")
133
- @s3_region = configuration.dig("defaults", "s3", "region")
134
- @s3_bucket = configuration.dig("defaults", "s3", "bucket")
135
- @s3_prefix = configuration.dig("defaults", "s3", "prefix") || s3_prefix
109
+ shared_config = configuration.fetch("shared", {})
110
+ environment_config = configuration.fetch(environment, {})
136
111
 
137
- @pg_url = configuration.dig("defaults", "database", "pg_url")
138
- if @pg_url
139
- self.pg_url = @pg_url
140
- else
141
- @pg_host = configuration.dig("defaults", "database", "host")
142
- @pg_port = configuration.dig("defaults", "database", "port")
143
- @pg_username = configuration.dig("defaults", "database", "username")
144
- @pg_password = configuration.dig("defaults", "database", "password")
145
- @pg_database = configuration.dig("defaults", "database", "database_name")
112
+ merged_config = deep_merge(shared_config, environment_config)
113
+
114
+ apply_config(merged_config)
115
+ end
116
+
117
+ def apply_config(config)
118
+ config.each do |key, value|
119
+ instance_variable_set("@#{key}", value) if respond_to?("#{key}=")
146
120
  end
147
121
 
148
- @app_name = configuration.dig("defaults", "app_name") || "backy"
149
- @environment = configuration.dig("defaults", "environment") || "development"
150
- @log_file = configuration.dig("defaults", "log", "file") || default_log_file
151
- @use_parallel = configuration.dig("defaults", "use_parallel") || false
122
+ self.pg_url = @pg_url if @pg_url
123
+ end
124
+
125
+ def load_from_env
126
+ ENV.each do |key, value|
127
+ case key
128
+ when "PG_HOST" then @pg_host = value
129
+ when "PG_PORT" then @pg_port = value
130
+ when "PG_DATABASE" then @pg_database = value
131
+ when "PG_USERNAME" then @pg_username = value
132
+ when "PG_PASSWORD" then @pg_password = value
133
+ when "S3_REGION" then @s3_region = value
134
+ when "S3_ACCESS_KEY" then @s3_access_key = value
135
+ when "S3_SECRET" then @s3_secret = value
136
+ when "S3_BUCKET" then @s3_bucket = value
137
+ when "S3_PREFIX" then @s3_prefix = value
138
+ when "APP_NAME" then @app_name = value
139
+ when "BACKY_USE_PARALLEL" then @use_parallel = value == "true"
140
+ when "BACKY_PAUSE_REPLICATION" then @pause_replication = value == "true"
141
+ when "LOCAL_BACKUP_PATH" then @local_backup_path = value
142
+ end
143
+ end
152
144
  end
153
145
 
154
146
  def parse_postgres_uri(uri)
@@ -163,5 +155,17 @@ module Backy
163
155
  database_name: parsed_uri.path[1..]
164
156
  }
165
157
  end
158
+
159
+ def deep_merge(hash1, hash2)
160
+ merged = hash1.dup
161
+ hash2.each do |key, value|
162
+ merged[key] = if value.is_a?(Hash) && hash1[key].is_a?(Hash)
163
+ deep_merge(hash1[key], value)
164
+ else
165
+ value
166
+ end
167
+ end
168
+ merged
169
+ end
166
170
  end
167
171
  end
data/lib/backy/db.rb CHANGED
@@ -1,4 +1,4 @@
1
- require 'forwardable'
1
+ require "forwardable"
2
2
 
3
3
  module Backy
4
4
  module Db
@@ -12,6 +12,7 @@ module Backy
12
12
  def_delegator "Backy.configuration", :pg_username, :username
13
13
  def_delegator "Backy.configuration", :pg_password, :password
14
14
  def_delegator "Backy.configuration", :use_parallel?, :use_parallel?
15
+ def_delegator "Backy.configuration", :pause_replication?, :pause_replication?
15
16
 
16
17
  def pg_password_env
17
18
  password.present? ? "PGPASSWORD='#{password}' " : ""
data/lib/backy/logger.rb CHANGED
@@ -1,11 +1,13 @@
1
- require 'thor'
1
+ require "thor"
2
2
 
3
3
  module Backy
4
4
  class Logger
5
+ @log_messages = []
6
+
5
7
  # Logs a message with the specified color using Thor's shell
6
8
  def self.log(message, color = nil)
7
- thor_shell = Thor::Base.shell.new
8
- thor_shell.say("[#{Time.now.strftime("%Y-%m-%d %H:%M:%S")}] #{message}", color)
9
+ @log_messages << message
10
+ say("[#{Time.now.strftime("%Y-%m-%d %H:%M:%S")}] #{message}\n", color)
9
11
  end
10
12
 
11
13
  def self.success(message)
@@ -23,5 +25,17 @@ module Backy
23
25
  def self.error(message)
24
26
  log(message, :red)
25
27
  end
28
+
29
+ def self.say(message, color = nil)
30
+ thor_shell.say(message, color)
31
+ end
32
+
33
+ def self.log_messages
34
+ @log_messages
35
+ end
36
+
37
+ private_class_method def self.thor_shell
38
+ @thor_shell ||= Thor::Base.shell.new
39
+ end
26
40
  end
27
41
  end
data/lib/backy/pg_dump.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require "fileutils"
2
2
  require "etc"
3
+ require "open3"
3
4
 
4
5
  module Backy
5
6
  class PgDump
@@ -10,9 +11,44 @@ module Backy
10
11
  DUMP_CMD_OPTS = "--no-acl --no-owner --no-subscriptions --no-publications"
11
12
 
12
13
  def call
14
+ setup_backup_directory
15
+ log_start
16
+
17
+ begin
18
+ handle_replication { backup }
19
+ rescue => e
20
+ Logger.error("An error occurred during backup: #{e.message}")
21
+ ensure
22
+ if replica? && pause_replication?
23
+ log_replication_resume
24
+ end
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def setup_backup_directory
13
31
  FileUtils.mkdir_p(DUMP_DIR)
32
+ end
33
+
34
+ def log_start
14
35
  Logger.log("Starting backy for #{database}")
36
+ end
37
+
38
+ def handle_replication
39
+ if replica? && pause_replication?
40
+ if pause_replication
41
+ Logger.log("Replication paused.")
42
+ yield
43
+ else
44
+ Logger.error("Failed to pause replication. Aborting backup.")
45
+ end
46
+ else
47
+ yield
48
+ end
49
+ end
15
50
 
51
+ def backup
16
52
  if use_parallel?
17
53
  Logger.log("Using multicore dump with pigz")
18
54
  parallel_backup
@@ -22,64 +58,47 @@ module Backy
22
58
  end
23
59
  end
24
60
 
25
- private
61
+ def log_replication_resume
62
+ if resume_replication
63
+ Logger.log("Replication resumed.")
64
+ else
65
+ Logger.error("Failed to resume replication. Manual intervention required.")
66
+ end
67
+ end
26
68
 
27
69
  def plain_text_backup
28
- timestamp = Time.now.strftime("%Y%m%d_%H%M%S")
29
- dump_file = "#{DUMP_DIR}/#{database}_#{whoami}@#{hostname}_single_#{timestamp}.sql.gz"
70
+ timestamp = current_timestamp
71
+ dump_file = "#{DUMP_DIR}/#{database}_#{whoami}@#{hostname}_#{timestamp}.sql.gz"
30
72
 
31
73
  cmd = "(#{pg_password_env}pg_dump #{pg_credentials} #{database} #{DUMP_CMD_OPTS} | gzip -9 > #{dump_file}) 2>&1 >> #{log_file}"
32
74
 
33
- print "Saving to #{dump_file} ... "
75
+ Logger.log("Saving to #{dump_file} ... ")
34
76
 
35
- if system(cmd)
36
- Logger.success("done")
37
- else
38
- Logger.error("error. See #{log_file}")
39
- return
40
- end
41
-
42
- dump_file
77
+ execute_command(cmd, "error. See #{log_file}")
43
78
  end
44
79
 
45
80
  def parallel_backup
46
- timestamp = Time.now.strftime("%Y%m%d_%H%M%S")
81
+ timestamp = current_timestamp
47
82
  dump_dir = "#{DUMP_DIR}/#{database}_dump_parallel_#{timestamp}"
48
83
  dump_file = "#{dump_dir}.tar.gz"
49
84
 
50
- pg_dump_cmd = "pg_dump -Z0 -j #{Etc.nprocessors} -Fd #{database} -f #{dump_dir} #{pg_credentials} #{DUMP_CMD_OPTS}"
85
+ pg_dump_cmd = "#{pg_password_env}pg_dump -Z0 -j #{Etc.nprocessors} -Fd #{database} -f #{dump_dir} #{pg_credentials} #{DUMP_CMD_OPTS}"
51
86
  tar_cmd = "tar -cf - #{dump_dir} | pigz -p #{Etc.nprocessors} > #{dump_file}"
52
87
  cleanup_cmd = "rm -rf #{dump_dir}"
53
88
 
54
- Logger.log("Running pg_dump #{database}")
55
- if system("#{pg_password_env}#{pg_dump_cmd} 2>&1 >> #{log_file}")
56
- Logger.log("pg_dump completed successfully.")
57
- else
58
- Logger.error("pg_dump failed. See #{log_file} for details.")
59
- return
60
- end
61
-
62
- # Execute tar command
63
- Logger.log("Compressing #{dump_dir}")
64
- if system(tar_cmd)
65
- Logger.log("Compression completed successfully.")
66
- else
67
- Logger.error("Compression failed. See #{log_file} for details.")
68
- return
69
- end
70
-
71
- # Execute cleanup command
72
- Logger.log("Cleaning up #{dump_dir}")
73
- if system(cleanup_cmd)
74
- Logger.log("Cleanup completed successfully.")
75
- else
76
- Logger.error("Cleanup failed. See #{log_file} for details.")
77
- return
78
- end
89
+ execute_command("#{pg_password_env}#{pg_dump_cmd} 2>&1 >> #{log_file}", "pg_dump failed. See #{log_file} for details.")
90
+ execute_command(tar_cmd, "Compression failed. See #{log_file} for details.")
91
+ execute_command(cleanup_cmd, "Cleanup failed. See #{log_file} for details.")
79
92
 
80
93
  Logger.success("Backup process completed. Output file: #{dump_file}")
94
+ end
95
+
96
+ def execute_command(cmd, error_message)
97
+ Logger.error(error_message) unless system(cmd)
98
+ end
81
99
 
82
- dump_file # Return the name of the dump file
100
+ def current_timestamp
101
+ Time.now.strftime("%Y%m%d_%H%M%S")
83
102
  end
84
103
 
85
104
  def hostname
@@ -89,5 +108,45 @@ module Backy
89
108
  def whoami
90
109
  @whoami ||= `whoami`.strip
91
110
  end
111
+
112
+ def pause_replication
113
+ query = "SELECT pg_wal_replay_pause();"
114
+ success, _output = execute_sql(query)
115
+ success
116
+ end
117
+
118
+ def resume_replication
119
+ query = "SELECT pg_wal_replay_resume();"
120
+ success, _output = execute_sql(query)
121
+ success
122
+ end
123
+
124
+ def execute_sql(query)
125
+ command = %(#{pg_password_env}psql #{pg_credentials} -d #{database} -c "#{query}")
126
+ output = ""
127
+ Open3.popen3(command) do |_stdin, stdout, stderr, wait_thr|
128
+ while (line = stdout.gets)
129
+ output << line
130
+ end
131
+ while (line = stderr.gets)
132
+ puts "Error: #{line}"
133
+ end
134
+ exit_status = wait_thr.value
135
+ [exit_status.success?, output]
136
+ end
137
+ end
138
+
139
+ def replica?
140
+ @is_replica ||= begin
141
+ query = "SELECT pg_is_in_recovery();"
142
+ success, output = execute_sql(query)
143
+ if success && output.include?("t")
144
+ Logger.log("Database is a replica.")
145
+ true
146
+ else
147
+ false
148
+ end
149
+ end
150
+ end
92
151
  end
93
152
  end
@@ -10,16 +10,16 @@ module Backy
10
10
  end
11
11
 
12
12
  def call
13
- pigz_installed = system('which pigz > /dev/null 2>&1')
13
+ pigz_installed = system("which pigz > /dev/null 2>&1")
14
14
  multicore = Etc.nprocessors > 1
15
15
  use_multicore = ENV["BACKY_USE_PARALLEL"] == "true"
16
16
 
17
17
  if pigz_installed && multicore && use_multicore
18
- Logger.log('Using parallel restore with pigz')
18
+ Logger.log("Using parallel restore with pigz")
19
19
  parallel_restore
20
20
  else
21
21
  Logger.log("Pigz not installed or system is not multicore")
22
- Logger.log('Using plain text restore')
22
+ Logger.log("Using plain text restore")
23
23
  plain_text_restore
24
24
  end
25
25
  end
data/lib/backy/s3.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  require "aws-sdk-s3"
2
- require 'forwardable'
2
+ require "forwardable"
3
3
 
4
4
  module Backy
5
5
  module S3
data/lib/backy/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Backy
2
- VERSION = "0.1.7"
2
+ VERSION = "0.2.0"
3
3
  end
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.1.7
4
+ version: 0.2.0
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-21 00:00:00.000000000 Z
13
+ date: 2024-06-24 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rspec
@@ -208,6 +208,34 @@ dependencies:
208
208
  - - ">="
209
209
  - !ruby/object:Gem::Version
210
210
  version: '1.117'
211
+ - !ruby/object:Gem::Dependency
212
+ name: pg
213
+ requirement: !ruby/object:Gem::Requirement
214
+ requirements:
215
+ - - "~>"
216
+ - !ruby/object:Gem::Version
217
+ version: '1.5'
218
+ type: :runtime
219
+ prerelease: false
220
+ version_requirements: !ruby/object:Gem::Requirement
221
+ requirements:
222
+ - - "~>"
223
+ - !ruby/object:Gem::Version
224
+ version: '1.5'
225
+ - !ruby/object:Gem::Dependency
226
+ name: thor
227
+ requirement: !ruby/object:Gem::Requirement
228
+ requirements:
229
+ - - "~>"
230
+ - !ruby/object:Gem::Version
231
+ version: '1.2'
232
+ type: :runtime
233
+ prerelease: false
234
+ version_requirements: !ruby/object:Gem::Requirement
235
+ requirements:
236
+ - - "~>"
237
+ - !ruby/object:Gem::Version
238
+ version: '1.2'
211
239
  description: Backy is a comprehensive database backup solution for Ruby on Rails applications,
212
240
  created to help developers manage and safeguard their data with ease. This robust
213
241
  gem offers a wide range of features
@@ -252,7 +280,8 @@ files:
252
280
  - lib/backy_rb.rb
253
281
  - lib/tasks/backy_tasks.rake
254
282
  homepage: https://rubynor.com
255
- licenses: []
283
+ licenses:
284
+ - MIT
256
285
  metadata:
257
286
  homepage_uri: https://rubynor.com
258
287
  source_code_uri: https://github.com/rubynor/backy
@@ -272,7 +301,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
272
301
  - !ruby/object:Gem::Version
273
302
  version: '0'
274
303
  requirements: []
275
- rubygems_version: 3.5.5
304
+ rubygems_version: 3.4.22
276
305
  signing_key:
277
306
  specification_version: 4
278
307
  summary: Backy is a powerful and user-friendly database backup gem designed specifically