Skip to content

Commit 452adcf

Browse files
authored
refactor(s3_backup): simplify S3 backup service by removing error handling and logging helpers (#196)
1 parent cf39f4f commit 452adcf

File tree

2 files changed

+46
-153
lines changed

2 files changed

+46
-153
lines changed

app/services/concerns/s3_helpers.rb

Lines changed: 3 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -6,47 +6,15 @@ module S3Helpers
66
private
77

88
def ensure_s3_enabled
9-
return if ENV["USE_S3_STORAGE"] == "true"
10-
11-
error_msg = "S3 storage is not enabled. Set USE_S3_STORAGE=true in your .env file"
12-
raise error_msg
9+
raise "S3 storage is not enabled" unless ENV["USE_S3_STORAGE"] == "true"
1310
end
1411

1512
def validate_s3_config
1613
required_vars = %w[S3_ENDPOINT S3_ACCESS_KEY_ID S3_SECRET_ACCESS_KEY S3_BUCKET]
1714
missing_vars = required_vars.select { |var| ENV[var].blank? }
1815

19-
if missing_vars.any?
20-
error_msg = "Missing required S3 environment variables: #{missing_vars.join(", ")}"
21-
22-
Sentry.capture_message(error_msg, level: "error", extra: {
23-
missing_vars: missing_vars,
24-
context: self.class.name,
25-
environment: Rails.env
26-
})
27-
28-
raise error_msg
29-
end
30-
end
31-
32-
def get_s3_service
33-
service = ActiveStorage::Blob.service
34-
35-
unless service.is_a?(ActiveStorage::Service::S3Service)
36-
error_msg = "Active Storage is not configured to use S3. Current service: #{service.class.name}"
37-
raise error_msg
38-
end
39-
40-
service
16+
raise "Missing S3 config: #{missing_vars.join(", ")}" if missing_vars.any?
4117
end
4218

43-
def handle_s3_errors
44-
yield
45-
rescue Aws::S3::Errors::ServiceError => e
46-
Sentry.capture_exception(e)
47-
raise
48-
rescue => e
49-
Sentry.capture_exception(e)
50-
raise
51-
end
19+
def get_s3_service = ActiveStorage::Blob.service
5220
end

app/services/s3_backup_service.rb

Lines changed: 43 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -9,124 +9,49 @@ def perform
99
ensure_s3_enabled
1010
validate_s3_config
1111

12-
handle_s3_errors do
13-
# Capture backup context for better error reporting
14-
Sentry.with_scope do |scope|
15-
scope.set_context("backup", {
16-
service: "S3BackupService",
17-
rails_env: Rails.env,
18-
timestamp: Time.current.iso8601
19-
})
20-
end
21-
22-
service = get_s3_service
23-
24-
# Create temp directory
25-
FileUtils.mkdir_p(temp_dir)
26-
27-
# Generate backup filename
28-
timestamp = Time.current.strftime("%Y-%m-%d")
29-
backup_filename = "database-#{timestamp}.sqlite3"
30-
temp_backup_path = temp_dir.join(backup_filename)
31-
s3_key = "#{backup_dir}/database-#{timestamp}.tar.gz"
32-
33-
begin
34-
# Create SQLite backup
35-
log_info "Creating database backup..."
36-
37-
# Check if database path exists and is valid
38-
unless database_path && File.exist?(database_path)
39-
error_msg = "Database file not found at: #{database_path}"
40-
log_error error_msg
41-
Sentry.capture_message(error_msg, level: "error")
42-
raise error_msg
43-
end
44-
45-
# Check if sqlite3 command is available
46-
unless system("which sqlite3 > /dev/null 2>&1")
47-
error_msg = "sqlite3 command not found - required for database backup"
48-
log_error error_msg
49-
Sentry.capture_message(error_msg, level: "error", extra: {
50-
environment: Rails.env,
51-
path: ENV["PATH"]
52-
})
53-
raise error_msg
54-
end
55-
56-
# Use array form to prevent command injection
57-
unless system("sqlite3", database_path.to_s, ".backup '#{temp_backup_path}'", exception: true)
58-
error_msg = "Failed to create SQLite backup"
59-
Sentry.capture_message(error_msg, level: "error", extra: {
60-
command: "sqlite3 #{database_path} .backup '#{temp_backup_path}'",
61-
database_path: database_path.to_s,
62-
temp_backup_path: temp_backup_path.to_s
63-
})
64-
raise error_msg
65-
end
66-
log_info "Database backup created successfully"
67-
68-
# Compress the backup
69-
log_info "Compressing backup..."
70-
temp_compressed_path = create_tar_gz(timestamp)
71-
log_info "Backup compressed successfully"
72-
73-
# Upload to S3
74-
log_info "Uploading to S3 (#{s3_key})..."
75-
File.open(temp_compressed_path, "rb") do |file|
76-
service.upload(s3_key, file)
77-
end
78-
log_info "Backup uploaded to S3 successfully"
79-
80-
# Clean up old backups
81-
log_info "Cleaning up old backups..."
82-
deleted_count = cleanup_old_backups(service)
83-
log_info "Deleted #{deleted_count} old backups" if deleted_count.positive?
84-
85-
backup_size_mb = (File.size(temp_compressed_path) / 1024.0 / 1024.0).round(2)
86-
log_info "Database backup completed successfully!"
87-
log_info "Backup location: #{s3_key}"
88-
log_info "Backup size: #{backup_size_mb} MB"
89-
90-
# Return summary for callers
91-
{
92-
success: true,
93-
location: s3_key,
94-
size_mb: backup_size_mb,
95-
deleted_count: deleted_count
96-
}
97-
rescue => e
98-
# Report any unexpected errors to Sentry with full context
99-
Sentry.capture_exception(e, extra: {
100-
database_path: database_path.to_s,
101-
backup_filename: backup_filename,
102-
s3_key: s3_key,
103-
step: "backup_process"
104-
})
105-
raise # Re-raise to let handle_s3_errors deal with it
106-
ensure
107-
# Clean up temp files
108-
FileUtils.rm_f(temp_backup_path)
109-
FileUtils.rm_f(temp_dir.join("database-#{timestamp}.tar.gz"))
110-
end
111-
end
112-
end
113-
114-
private
115-
116-
# Logging helpers that work for both rake tasks and jobs
117-
def log_info(message)
118-
if defined?(Rails.logger)
119-
Rails.logger.info message
120-
else
121-
warn message
122-
end
123-
end
124-
125-
def log_error(message)
126-
if defined?(Rails.logger)
127-
Rails.logger.error message
128-
else
129-
warn "❌ #{message}"
12+
service = get_s3_service
13+
FileUtils.mkdir_p(temp_dir)
14+
15+
timestamp = Time.current.strftime("%Y-%m-%d")
16+
backup_filename = "database-#{timestamp}.sqlite3"
17+
temp_backup_path = temp_dir.join(backup_filename)
18+
s3_key = "#{backup_dir}/database-#{timestamp}.tar.gz"
19+
20+
# Create SQLite backup
21+
Rails.logger.info "Creating database backup..."
22+
system("sqlite3", database_path.to_s, ".backup '#{temp_backup_path}'", exception: true)
23+
Rails.logger.info "Database backup created successfully"
24+
25+
# Compress the backup
26+
Rails.logger.info "Compressing backup..."
27+
temp_compressed_path = create_tar_gz(timestamp)
28+
Rails.logger.info "Backup compressed successfully"
29+
30+
# Upload to S3
31+
Rails.logger.info "Uploading to S3 (#{s3_key})..."
32+
File.open(temp_compressed_path, "rb") do |file|
33+
service.upload(s3_key, file)
13034
end
35+
Rails.logger.info "Backup uploaded to S3 successfully"
36+
37+
# Clean up old backups
38+
Rails.logger.info "Cleaning up old backups..."
39+
deleted_count = cleanup_old_backups(service)
40+
Rails.logger.info "Deleted #{deleted_count} old backups" if deleted_count.positive?
41+
42+
backup_size_mb = (File.size(temp_compressed_path) / 1024.0 / 1024.0).round(2)
43+
Rails.logger.info "Database backup completed successfully!"
44+
Rails.logger.info "Backup location: #{s3_key}"
45+
Rails.logger.info "Backup size: #{backup_size_mb} MB"
46+
47+
{
48+
success: true,
49+
location: s3_key,
50+
size_mb: backup_size_mb,
51+
deleted_count: deleted_count
52+
}
53+
ensure
54+
FileUtils.rm_f(temp_backup_path) if defined?(temp_backup_path)
55+
FileUtils.rm_f(temp_dir.join("database-#{timestamp}.tar.gz")) if defined?(timestamp)
13156
end
13257
end

0 commit comments

Comments
 (0)