From 8207e6a0ec9896e5872fbc6c1f117f77a9ac2f2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20de=20Groot?= Date: Fri, 15 Dec 2017 13:16:57 +0100 Subject: [PATCH 01/28] Make defaults the same as in bgbackujp --- bgrestore.cnf.dist | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bgrestore.cnf.dist b/bgrestore.cnf.dist index 24d644c..e9eb4f8 100644 --- a/bgrestore.cnf.dist +++ b/bgrestore.cnf.dist @@ -4,10 +4,10 @@ # MariaDB host for restore -restorehost=127.0.0.1 +restorehost=localhost # MariaDB port for restore -restoreport=40001 +restoreport=3306 # MariaDB user for restore restoreuser=bgbackup @@ -19,10 +19,10 @@ restorepass=password preppath=/backup/tmp # MariaDB host for backup history -backuphisthost=127.0.0.1 +backuphisthost=localhost # MariaDB port for backup history -backuphistport=40002 +backuphistport=3306 # MariaDB user for backup history backuphistuser=bgbackup From a7202e5066efa737a862b6c4785287d6e386556f Mon Sep 17 00:00:00 2001 From: Michael Date: Wed, 30 Oct 2019 16:58:00 +0100 Subject: [PATCH 02/28] If bgbackup is called bgbackup.sh, so should bgrestore be called. --- README.md | 2 +- bgrestore-auto.sh => bgrestore.sh | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename bgrestore-auto.sh => bgrestore.sh (100%) diff --git a/README.md b/README.md index 71ec16f..28081d9 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # bgrestore -bgrestore-auto.sh helps you automatically restore the last successful backup from bgbackup to another MariaDB server. This is very useful for verifying backups, refreshing development environments, etc. +bgrestore.sh helps you automatically restore the last successful backup from bgbackup to another MariaDB server. This is very useful for verifying backups, refreshing development environments, etc. bgrestore connects to the backup_history table in mdbutil to gather information about the last successful full backup such as whether it's encrypted, compressed, etc. This information is then used to restore on the designated MariaDB server. diff --git a/bgrestore-auto.sh b/bgrestore.sh similarity index 100% rename from bgrestore-auto.sh rename to bgrestore.sh From ab633055ef165a299557dbb5d541fbc1a56f557a Mon Sep 17 00:00:00 2001 From: Michael Date: Wed, 1 Apr 2020 13:30:30 +0200 Subject: [PATCH 03/28] First attempt to support mariabackup for restores --- bgrestore.sh | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/bgrestore.sh b/bgrestore.sh index 319ba6d..e067bc8 100644 --- a/bgrestore.sh +++ b/bgrestore.sh @@ -54,15 +54,22 @@ function preflight { fi # set logfile logfile=$logpath/bgrestore_$(date +%Y-%m-%d-%T).log # logfile - # Check for xtrabackup - if command -v innobackupex >/dev/null; then + + # Check for mariabackup or xtrabackup + if [ "$backuptool" == "1" ] && command -v mariabackup >/dev/null; then + innobackupex=$(command -v mariabackup) + # mariabackup does not have encryption support + encrypt="no" + elif [ "$backuptool" == "2" ] && command -v innobackupex >/dev/null; then innobackupex=$(command -v innobackupex) else - log_info "xtrabackup/innobackupex does not appear to be installed. Please install and try again." + echo "The backuptool does not appear to be installed. Please check that a valid backuptool is chosen in bgbackup.cnf and that it's installed." + log_info "The backuptool does not appear to be installed. Please check that a valid backuptool is chosen in bgbackup.cnf and that it's installed." log_status=FAILED mail_log exit 1 fi + if [ "$datadir" == '' ] ; then log_info "Datadir location not set correctly." log_status=FAILED @@ -208,7 +215,9 @@ trap sigint INT starttime=$(date +"%Y-%m-%d %H:%M:%S") mdate=$(date +%m/%d/%y) # Date for mail subject. Not in function so set at script start time, not when backup is finished. mysqlcommand=$(command -v mysql) -innocommand=$(command -v innobackupex) + +innocommand="$innobackupex" +if [ "$backuptool" == "2" ] ; then innocommand=$innocommand" --innobackupex"; fi # do the work preflight From 71b0eede757aa3ba085ed8599f0191007774122b Mon Sep 17 00:00:00 2001 From: Michael Date: Wed, 1 Apr 2020 13:31:49 +0200 Subject: [PATCH 04/28] Oops, backuptool 1 = mariadb --- bgrestore.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bgrestore.sh b/bgrestore.sh index e067bc8..ec9213d 100644 --- a/bgrestore.sh +++ b/bgrestore.sh @@ -217,7 +217,7 @@ mdate=$(date +%m/%d/%y) # Date for mail subject. Not in function so set at sc mysqlcommand=$(command -v mysql) innocommand="$innobackupex" -if [ "$backuptool" == "2" ] ; then innocommand=$innocommand" --innobackupex"; fi +if [ "$backuptool" == "1" ] ; then innocommand=$innocommand" --innobackupex"; fi # do the work preflight From 9fdce0d66102700b270e651dabf365fc576a0dda Mon Sep 17 00:00:00 2001 From: Michael Date: Tue, 7 Apr 2020 17:35:33 +0200 Subject: [PATCH 05/28] Implemented skipcopy and bugfixes --- bgrestore.cnf.dist | 3 +++ bgrestore.sh | 21 ++++++++++++++------- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/bgrestore.cnf.dist b/bgrestore.cnf.dist index e9eb4f8..ab92cfa 100644 --- a/bgrestore.cnf.dist +++ b/bgrestore.cnf.dist @@ -36,6 +36,9 @@ backuphistschema=mdbutil # MariaDB host which backup should be from backuphost=hostname_from_backup_history +# Skip copy step, place backup in preppath after creating it +skipcopy=no + # Datadir datadir=/var/lib/mysql diff --git a/bgrestore.sh b/bgrestore.sh index ec9213d..1532d38 100644 --- a/bgrestore.sh +++ b/bgrestore.sh @@ -70,6 +70,10 @@ function preflight { exit 1 fi + innocommand="$innobackupex" + if [ "$backuptool" == "1" ] ; then innocommand=$innocommand" --innobackupex"; fi + + if [ "$datadir" == '' ] ; then log_info "Datadir location not set correctly." log_status=FAILED @@ -127,7 +131,8 @@ function lastfullinfo { mail_log exit 2 fi - if [ ! -d "$lastfullbulocation" ] ; then + if [ ! -d "$lastfullbulocation" ] && [ "$skipcopy" != "yes" ] ; then + log_info "Error: $lastfullbulocation directory not found" log_info "The directory for the last full backup cannot be found on this server." log_status=FAILED @@ -151,9 +156,14 @@ function lastfullinfo { # Function to prepare backup for restore function prepit { - cp -R "$lastfullbulocation" "$preppath"/ - buname=$(basename "$lastfullbulocation") - bufullpath="$preppath"/"$buname" + if [ "$skipcopy" != "yes"]; then + cp -R "$lastfullbulocation" "$preppath"/ + buname=$(basename "$lastfullbulocation") + bufullpath="$preppath"/"$buname" + else + fufullpath="$preppath" + fi + if [ "$lastfullencrypted" == "yes" ] ; then log_info "Backup is encrypted." $innocommand --decrypt=AES256 --encrypt-key="$(cat "$lastfullcryptkey")" --parallel="$threads" "$bufullpath" @@ -216,9 +226,6 @@ starttime=$(date +"%Y-%m-%d %H:%M:%S") mdate=$(date +%m/%d/%y) # Date for mail subject. Not in function so set at script start time, not when backup is finished. mysqlcommand=$(command -v mysql) -innocommand="$innobackupex" -if [ "$backuptool" == "1" ] ; then innocommand=$innocommand" --innobackupex"; fi - # do the work preflight lastfullinfo From 3e1850749aaecbbb7c6261c9bbabd4e4c22487de Mon Sep 17 00:00:00 2001 From: Michael Date: Tue, 7 Apr 2020 17:39:12 +0200 Subject: [PATCH 06/28] FIxed typo --- bgrestore.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bgrestore.sh b/bgrestore.sh index 1532d38..0b7a927 100644 --- a/bgrestore.sh +++ b/bgrestore.sh @@ -161,7 +161,7 @@ function prepit { buname=$(basename "$lastfullbulocation") bufullpath="$preppath"/"$buname" else - fufullpath="$preppath" + bufullpath="$preppath" fi if [ "$lastfullencrypted" == "yes" ] ; then From 03044bf9656b7b7c6acec059b8abf0c30dc2a129 Mon Sep 17 00:00:00 2001 From: Michael Date: Wed, 8 Apr 2020 10:23:05 +0200 Subject: [PATCH 07/28] Bugfix in combination with skipcopy --- bgrestore.cnf.dist | 2 +- bgrestore.sh | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/bgrestore.cnf.dist b/bgrestore.cnf.dist index ab92cfa..e96bc1a 100644 --- a/bgrestore.cnf.dist +++ b/bgrestore.cnf.dist @@ -36,7 +36,7 @@ backuphistschema=mdbutil # MariaDB host which backup should be from backuphost=hostname_from_backup_history -# Skip copy step, place backup in preppath after creating it +# Skip copy step, place backup in preppath after creating it. This will also make bgrestore delete the contents of the folder instead of the folder itself (after succeeding) skipcopy=no # Datadir diff --git a/bgrestore.sh b/bgrestore.sh index 0b7a927..7fc51b4 100644 --- a/bgrestore.sh +++ b/bgrestore.sh @@ -209,7 +209,12 @@ function restoreit { function cleanup { if [ "$log_status" == "SUCCEEDED" ] ; then log_info "Cleaning up." - rm -Rf "${bufullpath:?}" + if [ "$skipcopy" != "yes"]; then + rm -Rf "${bufullpath:?}" + else # Clean up prep directory instead of deleting the full backup path (which is also prep directory) + rm -Rf "${preppath:?}/*" + fi + log_info "Complete." fi } From b7bf99bfb44d79912f0d23c1c6fb72e18f40864e Mon Sep 17 00:00:00 2001 From: Michael Date: Wed, 8 Apr 2020 11:09:41 +0200 Subject: [PATCH 08/28] Fixe typo --- bgrestore.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bgrestore.sh b/bgrestore.sh index 7fc51b4..61b64e1 100644 --- a/bgrestore.sh +++ b/bgrestore.sh @@ -209,7 +209,7 @@ function restoreit { function cleanup { if [ "$log_status" == "SUCCEEDED" ] ; then log_info "Cleaning up." - if [ "$skipcopy" != "yes"]; then + if [ "$skipcopy" != "yes" ]; then rm -Rf "${bufullpath:?}" else # Clean up prep directory instead of deleting the full backup path (which is also prep directory) rm -Rf "${preppath:?}/*" From dbcbf4a7d382876b66e7a6b59c1cd1aaab546d44 Mon Sep 17 00:00:00 2001 From: Michael Date: Wed, 8 Apr 2020 11:18:54 +0200 Subject: [PATCH 09/28] Fixed another typo --- bgrestore.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bgrestore.sh b/bgrestore.sh index 61b64e1..72bbc3d 100644 --- a/bgrestore.sh +++ b/bgrestore.sh @@ -212,7 +212,7 @@ function cleanup { if [ "$skipcopy" != "yes" ]; then rm -Rf "${bufullpath:?}" else # Clean up prep directory instead of deleting the full backup path (which is also prep directory) - rm -Rf "${preppath:?}/*" + rm -Rf "${preppath}/"* fi log_info "Complete." From 6428f7290acf9d66520b4a7ab876a6b55f05b113 Mon Sep 17 00:00:00 2001 From: Michael Date: Tue, 12 May 2020 11:23:07 +0200 Subject: [PATCH 10/28] Added some defensiveness in shutting down MariaDB, added some newlines in main function --- bgrestore.sh | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/bgrestore.sh b/bgrestore.sh index 72bbc3d..7f74e43 100644 --- a/bgrestore.sh +++ b/bgrestore.sh @@ -182,18 +182,28 @@ function prepit { # Function to restore function restoreit { + log_info "Shutting down MariaDB to restore. " mysqlshutdowncreate -# $mysqlshutdowncommand "shutdown" - service mysql stop + $mysqlshutdowncommand "shutdown" + + log_info "More shutdown commands to make sure MariaDB is down." + systemctl stop mariadb + pkill -9 mysqld + systemctl stop mariadb + log_info "Deleting the data directory." rm -Rf "${datadir:?}"/* + log_info "Copying the backup to the data directory." $innocommand --copy-back "$bufullpath" + log_info "Fixing privileges." chown -R "$datadirowner":"$datadirgroup" "$datadir" + log_info "Starting MariaDB." - service mysql start + systemctl start mariadb + startstatus=$? if [ "$startstatus" -eq 0 ] ; then log_status=SUCCEEDED From 9fe2965b55feae3dcacfb64c3116a8a969313ddf Mon Sep 17 00:00:00 2001 From: Michael Date: Thu, 6 Aug 2020 14:51:07 +0200 Subject: [PATCH 11/28] Made bgrestore compatible with MySQL 5.7 compatibility changes I made in bgbackup --- bgrestore.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bgrestore.sh b/bgrestore.sh index 7f74e43..2e60878 100644 --- a/bgrestore.sh +++ b/bgrestore.sh @@ -123,7 +123,7 @@ function mysqlshutdowncreate { # Function to get directory and other info from last full backup function lastfullinfo { mysqlhistcreate - lastfulluuid=$($mysqlhistcommand "select uuid from $backuphistschema.backup_history where butype = 'Full' and status = 'SUCCEEDED' and hostname = '$backuphost' and deleted_at = 0 order by end_time desc limit 1") + lastfulluuid=$($mysqlhistcommand "select uuid from $backuphistschema.backup_history where butype = 'Full' and status = 'SUCCEEDED' and hostname = '$backuphost' and (deleted_at IS NULL OR deleted_at = 0) order by end_time desc limit 1") lastfullbulocation=$($mysqlhistcommand "select bulocation from $backuphistschema.backup_history where uuid = '$lastfulluuid' ") if [ "$lastfullbulocation" == '' ] ; then log_info "Backup location not set successfully." From 2720c8a9de5ea688eefb63af32ed28d6def1375f Mon Sep 17 00:00:00 2001 From: Michael Date: Thu, 20 Aug 2020 13:31:57 +0200 Subject: [PATCH 12/28] Changed copy-back to move-back to save disk space on big installations --- bgrestore.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bgrestore.sh b/bgrestore.sh index 2e60878..4628fc3 100644 --- a/bgrestore.sh +++ b/bgrestore.sh @@ -195,8 +195,8 @@ function restoreit { log_info "Deleting the data directory." rm -Rf "${datadir:?}"/* - log_info "Copying the backup to the data directory." - $innocommand --copy-back "$bufullpath" + log_info "Moving the prepared backup to the data directory." + $innocommand --move-back "$bufullpath" log_info "Fixing privileges." chown -R "$datadirowner":"$datadirgroup" "$datadir" From 83465962f38a712a510da9256dcdca15bbb4f3e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20de=20Groot?= Date: Tue, 28 Sep 2021 16:30:36 +0200 Subject: [PATCH 13/28] Fixed syntax --- bgrestore.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bgrestore.sh b/bgrestore.sh index 4628fc3..f003975 100644 --- a/bgrestore.sh +++ b/bgrestore.sh @@ -156,7 +156,7 @@ function lastfullinfo { # Function to prepare backup for restore function prepit { - if [ "$skipcopy" != "yes"]; then + if [ "$skipcopy" != "yes" ]; then cp -R "$lastfullbulocation" "$preppath"/ buname=$(basename "$lastfullbulocation") bufullpath="$preppath"/"$buname" From 0a3948e1f08761948c79712d26d915bb54584062 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20de=20Groot?= Date: Tue, 30 Nov 2021 18:10:24 +0400 Subject: [PATCH 14/28] Re-enabled encrytion support --- bgrestore.sh | 2 -- 1 file changed, 2 deletions(-) diff --git a/bgrestore.sh b/bgrestore.sh index f003975..224fc36 100644 --- a/bgrestore.sh +++ b/bgrestore.sh @@ -58,8 +58,6 @@ function preflight { # Check for mariabackup or xtrabackup if [ "$backuptool" == "1" ] && command -v mariabackup >/dev/null; then innobackupex=$(command -v mariabackup) - # mariabackup does not have encryption support - encrypt="no" elif [ "$backuptool" == "2" ] && command -v innobackupex >/dev/null; then innobackupex=$(command -v innobackupex) else From c7aa555f9a0636f0164807c967a052a491c04837 Mon Sep 17 00:00:00 2001 From: Michael de Groot Date: Fri, 5 Apr 2024 12:48:10 +0200 Subject: [PATCH 15/28] Add feature to delete old restore first. --- bgrestore.cnf.dist | 3 +++ bgrestore.sh | 14 ++++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/bgrestore.cnf.dist b/bgrestore.cnf.dist index e96bc1a..f6c8bdd 100644 --- a/bgrestore.cnf.dist +++ b/bgrestore.cnf.dist @@ -39,6 +39,9 @@ backuphost=hostname_from_backup_history # Skip copy step, place backup in preppath after creating it. This will also make bgrestore delete the contents of the folder instead of the folder itself (after succeeding) skipcopy=no +# Delete the previous restore first (before starting to decompress the backup) +deletefirst=no + # Datadir datadir=/var/lib/mysql diff --git a/bgrestore.sh b/bgrestore.sh index 224fc36..d20153c 100644 --- a/bgrestore.sh +++ b/bgrestore.sh @@ -178,8 +178,8 @@ function prepit { log_info "Backup has been prepared for restore." } -# Function to restore -function restoreit { +# Function to delete old restore +function deleteoldrestore { log_info "Shutting down MariaDB to restore. " mysqlshutdowncreate @@ -192,6 +192,10 @@ function restoreit { log_info "Deleting the data directory." rm -Rf "${datadir:?}"/* +} + +# Function to restore +function restoreit { log_info "Moving the prepared backup to the data directory." $innocommand --move-back "$bufullpath" @@ -241,8 +245,14 @@ mysqlcommand=$(command -v mysql) # do the work preflight +if [ "$deletefirst" == "no" ] ; then + deleteoldrestore +fi lastfullinfo prepit +if [ "$deletefirst" != "no" ] ; then + deleteoldrestore +fi restoreit cleanup From a83333c3c0579a15f12e65bc16edc0f4179e594d Mon Sep 17 00:00:00 2001 From: Michael de Groot Date: Fri, 5 Apr 2024 12:50:52 +0200 Subject: [PATCH 16/28] Made decrypting and decomprsssing defensive --- bgrestore.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bgrestore.sh b/bgrestore.sh index d20153c..6dc9cdf 100644 --- a/bgrestore.sh +++ b/bgrestore.sh @@ -164,13 +164,13 @@ function prepit { if [ "$lastfullencrypted" == "yes" ] ; then log_info "Backup is encrypted." - $innocommand --decrypt=AES256 --encrypt-key="$(cat "$lastfullcryptkey")" --parallel="$threads" "$bufullpath" + $innocommand --decrypt=AES256 --encrypt-key="$(cat "$lastfullcryptkey")" --parallel="$threads" "$bufullpath" || { echo 'Decrypting failed.'; exit } for i in `find $bufullpath -iname "*\.xbcrypt"`; do rm -f $i; done log_info "Backup now decrypted." fi if [ "$lastfullcompressed" == "yes" ] ; then log_info "Backup is compressed." - $innocommand --decompress --parallel="$threads" "$bufullpath" + $innocommand --decompress --parallel="$threads" "$bufullpath" || { echo 'Decompressing failed.'; exit } for i in `find $bufullpath -iname "*\.qp"`; do rm -f $i; done log_info "Backup is now decompressed." fi From add4bd486acf578fb138f6289d0b35b159b66965 Mon Sep 17 00:00:00 2001 From: Michael de Groot Date: Fri, 5 Apr 2024 14:17:16 +0200 Subject: [PATCH 17/28] Fixed bug and made defensive --- bgrestore.sh | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/bgrestore.sh b/bgrestore.sh index 6dc9cdf..0fd3a71 100644 --- a/bgrestore.sh +++ b/bgrestore.sh @@ -36,6 +36,17 @@ function log_info() { fi } +# Error function +function log_error() { + if [ "$syslog" = yes ] ; then + logger -p local0.notice -t bgrestore "$*" + fi + printf "%s --> %s\n" "$(date +%Y-%m-%d-%T)" "$*" >>"$logfile" + printf "%s --> %s\n" "$(date +%Y-%m-%d-%T)" "$*" 1>&2 + exit 1 +} + + # Preflight checks function preflight { # find and source the config file @@ -164,13 +175,13 @@ function prepit { if [ "$lastfullencrypted" == "yes" ] ; then log_info "Backup is encrypted." - $innocommand --decrypt=AES256 --encrypt-key="$(cat "$lastfullcryptkey")" --parallel="$threads" "$bufullpath" || { echo 'Decrypting failed.'; exit } + $innocommand --decrypt=AES256 --encrypt-key="$(cat "$lastfullcryptkey")" --parallel="$threads" "$bufullpath" || log_error "Decrypt failed" for i in `find $bufullpath -iname "*\.xbcrypt"`; do rm -f $i; done log_info "Backup now decrypted." fi if [ "$lastfullcompressed" == "yes" ] ; then log_info "Backup is compressed." - $innocommand --decompress --parallel="$threads" "$bufullpath" || { echo 'Decompressing failed.'; exit } + $innocommand --decompress --parallel="$threads" "$bufullpath" || log_error "Decompress failed" for i in `find $bufullpath -iname "*\.qp"`; do rm -f $i; done log_info "Backup is now decompressed." fi From e15c36874d22467c74e6c2c40b27bfdf5d44168f Mon Sep 17 00:00:00 2001 From: Michael de Groot Date: Fri, 5 Apr 2024 14:41:09 +0200 Subject: [PATCH 18/28] Oops.... --- bgrestore.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bgrestore.sh b/bgrestore.sh index 0fd3a71..a2dbe8e 100644 --- a/bgrestore.sh +++ b/bgrestore.sh @@ -256,12 +256,12 @@ mysqlcommand=$(command -v mysql) # do the work preflight -if [ "$deletefirst" == "no" ] ; then +if [ "$deletefirst" == "yes" ] ; then deleteoldrestore fi lastfullinfo prepit -if [ "$deletefirst" != "no" ] ; then +if [ "$deletefirst" != "yes" ] ; then deleteoldrestore fi restoreit From b23e8fe528e4db80d0182ee0fd6c3a5e90390a8b Mon Sep 17 00:00:00 2001 From: Michael de Groot Date: Tue, 2 Jul 2024 18:33:28 +0200 Subject: [PATCH 19/28] Add defensiveness --- bgrestore.sh | 43 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/bgrestore.sh b/bgrestore.sh index a2dbe8e..822904b 100644 --- a/bgrestore.sh +++ b/bgrestore.sh @@ -175,17 +175,42 @@ function prepit { if [ "$lastfullencrypted" == "yes" ] ; then log_info "Backup is encrypted." - $innocommand --decrypt=AES256 --encrypt-key="$(cat "$lastfullcryptkey")" --parallel="$threads" "$bufullpath" || log_error "Decrypt failed" + $innocommand --decrypt=AES256 --encrypt-key="$(cat "$lastfullcryptkey")" --parallel="$threads" "$bufullpath" >> "$logfile" + $decryptstatus=$? + if [ "$decryptstatus" -eq 0 ] ; then + log_info "Backup decrypt was successfol." + else + log_status=FAILED + log_info "Something went wrong. Decrypt failed." + exit 1 + fi for i in `find $bufullpath -iname "*\.xbcrypt"`; do rm -f $i; done log_info "Backup now decrypted." fi if [ "$lastfullcompressed" == "yes" ] ; then log_info "Backup is compressed." - $innocommand --decompress --parallel="$threads" "$bufullpath" || log_error "Decompress failed" + $innocommand --decompress --parallel="$threads" "$bufullpath" >> "$logfile" + $decompressstatus=$? + if [ "$decompressstatus" -eq 0 ] ; then + log_info "Backup decompress was successfol." + else + log_status=FAILED + log_info "Something went wrong. Decompress failed." + exit 1 + fi + for i in `find $bufullpath -iname "*\.qp"`; do rm -f $i; done log_info "Backup is now decompressed." fi - $innocommand --apply-log "$bufullpath" + $innocommand --apply-log "$bufullpath" >> "$logfile" + $applystatus=$? + if [ "$applystatus" -eq 0 ] ; then + log_info "Backup apply log was successfol." + else + log_status=FAILED + log_info "Something went wrong. Apply log failed." + exit 1 + fi log_info "Backup has been prepared for restore." } @@ -200,6 +225,8 @@ function deleteoldrestore { systemctl stop mariadb pkill -9 mysqld systemctl stop mariadb + log_info "Current data dir contents:" + ls -al "${datadir}" >> "$logfile" log_info "Deleting the data directory." rm -Rf "${datadir:?}"/* @@ -208,8 +235,16 @@ function deleteoldrestore { # Function to restore function restoreit { - log_info "Moving the prepared backup to the data directory." + log_info "Moving the prepared backup in $bufullpath to the data directory." $innocommand --move-back "$bufullpath" + movebackstatus=$? + if [ "$movebackstatus" -eq 0 ] ; then + log_info "MariaDB succussfully restored and restarted." + else + log_status=FAILED + log_info "Something went wrong. Move back process failed." + exit 1 + fi log_info "Fixing privileges." chown -R "$datadirowner":"$datadirgroup" "$datadir" From 3b08a9967349446910df8407b9a52cf2cf20278c Mon Sep 17 00:00:00 2001 From: Michael de Groot Date: Tue, 2 Jul 2024 20:01:11 +0200 Subject: [PATCH 20/28] Corrected move back command --- bgrestore.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/bgrestore.sh b/bgrestore.sh index 822904b..c163975 100644 --- a/bgrestore.sh +++ b/bgrestore.sh @@ -176,7 +176,7 @@ function prepit { if [ "$lastfullencrypted" == "yes" ] ; then log_info "Backup is encrypted." $innocommand --decrypt=AES256 --encrypt-key="$(cat "$lastfullcryptkey")" --parallel="$threads" "$bufullpath" >> "$logfile" - $decryptstatus=$? + decryptstatus=$? if [ "$decryptstatus" -eq 0 ] ; then log_info "Backup decrypt was successfol." else @@ -190,7 +190,7 @@ function prepit { if [ "$lastfullcompressed" == "yes" ] ; then log_info "Backup is compressed." $innocommand --decompress --parallel="$threads" "$bufullpath" >> "$logfile" - $decompressstatus=$? + decompressstatus=$? if [ "$decompressstatus" -eq 0 ] ; then log_info "Backup decompress was successfol." else @@ -203,7 +203,7 @@ function prepit { log_info "Backup is now decompressed." fi $innocommand --apply-log "$bufullpath" >> "$logfile" - $applystatus=$? + applystatus=$? if [ "$applystatus" -eq 0 ] ; then log_info "Backup apply log was successfol." else @@ -226,7 +226,7 @@ function deleteoldrestore { pkill -9 mysqld systemctl stop mariadb log_info "Current data dir contents:" - ls -al "${datadir}" >> "$logfile" + ls -al "${datadir:?}" >> "$logfile" log_info "Deleting the data directory." rm -Rf "${datadir:?}"/* @@ -236,7 +236,7 @@ function deleteoldrestore { function restoreit { log_info "Moving the prepared backup in $bufullpath to the data directory." - $innocommand --move-back "$bufullpath" + $innocommand --move-back "$bufullpath" --datadir="${datadir:?}" movebackstatus=$? if [ "$movebackstatus" -eq 0 ] ; then log_info "MariaDB succussfully restored and restarted." From 294acdc8717625ac9da60b26c573e38a4486ecb3 Mon Sep 17 00:00:00 2001 From: Michael de Groot Date: Wed, 31 Jul 2024 14:50:18 +0200 Subject: [PATCH 21/28] Add log cleanup --- bgrestore.cnf.dist | 3 +++ bgrestore.sh | 14 ++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/bgrestore.cnf.dist b/bgrestore.cnf.dist index f6c8bdd..007e40e 100644 --- a/bgrestore.cnf.dist +++ b/bgrestore.cnf.dist @@ -54,6 +54,9 @@ datadirgroup=mysql # Path to keep logs logpath=/var/log +# Number of logs to keep +keeplognum=1000 + # Log to syslog syslog=no diff --git a/bgrestore.sh b/bgrestore.sh index c163975..874daf5 100644 --- a/bgrestore.sh +++ b/bgrestore.sh @@ -277,6 +277,18 @@ function cleanup { fi } +# Function to cleanup logs +function log_cleanup { + if [ $log_status = "SUCCEEDED" ]; then + delloglist=$(ls -tp "$logpath/bgbackup*" | tail -n +$((keeplognum+=1))) + for logtodelete in $delloglist; do + rm -f "$logdir"/"$logtodelete" + log_info "Deleted log file $logtodelete" + done + else + log_info "Backup failed. Not deleting any log files at this time." + fi +} ##### Begin script @@ -305,3 +317,5 @@ cleanup # email the log mail_log +# clean old log ifles +log_cleanup From 8a6122b34d642a7406f368633ad7307bda82234e Mon Sep 17 00:00:00 2001 From: Michael de Groot Date: Wed, 31 Jul 2024 16:06:02 +0200 Subject: [PATCH 22/28] Fix deleting of log files --- bgrestore.sh | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/bgrestore.sh b/bgrestore.sh index 874daf5..82f6a0e 100644 --- a/bgrestore.sh +++ b/bgrestore.sh @@ -280,17 +280,16 @@ function cleanup { # Function to cleanup logs function log_cleanup { if [ $log_status = "SUCCEEDED" ]; then - delloglist=$(ls -tp "$logpath/bgbackup*" | tail -n +$((keeplognum+=1))) + delloglist=$(ls -tp "$logpath" | grep bgrestore | tail -n +$((keeplognum+=1))) for logtodelete in $delloglist; do - rm -f "$logdir"/"$logtodelete" - log_info "Deleted log file $logtodelete" + rm -f "$logpath"/"$logtodelete" + log_info "Deleted log file $logpath/$logtodelete" done else - log_info "Backup failed. Not deleting any log files at this time." + log_info "Restore failed. Not deleting any log files at this time." fi } - ##### Begin script # we trap control-c From b38d3067d17b27b5c5113bec2c546ff4602fc735 Mon Sep 17 00:00:00 2001 From: Michael de Groot Date: Tue, 13 May 2025 10:54:59 +0200 Subject: [PATCH 23/28] Remove bktype feature --- bgrestore.sh | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/bgrestore.sh b/bgrestore.sh index 82f6a0e..a302366 100644 --- a/bgrestore.sh +++ b/bgrestore.sh @@ -148,13 +148,7 @@ function lastfullinfo { mail_log exit 1 fi - lastfullbktype=$($mysqlhistcommand "select bktype from $backuphistschema.backup_history where uuid = '$lastfulluuid' ") - if [ "$lastfullbktype" != "directory" ] ; then - log_info "$lastfullbktype not yet supported." - log_status=FAILED - mail_log - exit 2 - fi + lastfullcompressed=$($mysqlhistcommand "select compressed from $backuphistschema.backup_history where uuid = '$lastfulluuid' ") lastfullencrypted=$($mysqlhistcommand "select encrypted from $backuphistschema.backup_history where uuid = '$lastfulluuid' ") if [ "$lastfullencrypted" == "yes" ] ; then From 7e169fb80d6d0e94e67b9e78e47db3b7613c8ec9 Mon Sep 17 00:00:00 2001 From: Michael de Groot Date: Thu, 24 Jul 2025 17:06:41 +0200 Subject: [PATCH 24/28] Fix chmod --- bgrestore.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 bgrestore.sh diff --git a/bgrestore.sh b/bgrestore.sh old mode 100644 new mode 100755 From 996be61dd36f053218659780c16b18090c133caa Mon Sep 17 00:00:00 2001 From: Michael de Groot Date: Sat, 27 Sep 2025 10:47:08 +0200 Subject: [PATCH 25/28] Add author --- bgrestore.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bgrestore.sh b/bgrestore.sh index a302366..80a40e5 100755 --- a/bgrestore.sh +++ b/bgrestore.sh @@ -2,7 +2,7 @@ # bgrestore - Automate the restore of backups taken with bgbackup script. Great for backup verification, development refreshes, etc. # -# Authors: Ben Stillman +# Authors: Ben Stillman , Michaƫl de Groot # License: GNU General Public License, version 3. # Redistribution/Reuse of this code is permitted under the GNU v3 license. # As an additional term ALL code must carry the original Author(s) credit in comment form. From 9637efc0c5fad6e6af3d53daf7f8abf5c57e9602 Mon Sep 17 00:00:00 2001 From: Michael de Groot Date: Mon, 29 Sep 2025 15:16:37 +0200 Subject: [PATCH 26/28] Add work-around for MDEV-6660 --- bgrestore.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bgrestore.sh b/bgrestore.sh index 80a40e5..4f52ca5 100755 --- a/bgrestore.sh +++ b/bgrestore.sh @@ -243,6 +243,9 @@ function restoreit { log_info "Fixing privileges." chown -R "$datadirowner":"$datadirgroup" "$datadir" + echo "Fixing unfinished transactions. MDEV-6660 workaround." + sudo -u mysql mysqld --tc-heuristic-recover=ROLLBACK + log_info "Starting MariaDB." systemctl start mariadb From 725cc9d1da9002f1b8c08953ea0028fc821e3a2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20de=20Groot?= Date: Wed, 19 Aug 2026 15:02:40 +0100 Subject: [PATCH 27/28] Delegate the actual restore to fgrestore, add multi-instance support bgrestore's prepit()/restoreit() duplicated fgrestore's decrypt/decompress/ prepare/move-back logic with an older, non-chain-aware implementation. bgrestore now only does what's genuinely its own (backup_history lookup, logging, the MDEV-6660 workaround, starting the service) and hands the actual restore to fgrestore, which is chain-aware (Full/Differential/ Incremental) and checks exit status at every step. Also adds -c|--config CLI parsing and a service_name config variable (replacing a hardcoded "mariadb" service) with a matching per-service lock file, so restore tests can run against multi-instance hosts. --- README.md | 17 ++-- bgrestore.cnf.dist | 18 ++-- bgrestore.sh | 237 ++++++++++++++++++--------------------------- 3 files changed, 113 insertions(+), 159 deletions(-) diff --git a/README.md b/README.md index 28081d9..d17927f 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,14 @@ # bgrestore -bgrestore.sh helps you automatically restore the last successful backup from bgbackup to another MariaDB server. This is very useful for verifying backups, refreshing development environments, etc. - -bgrestore connects to the backup_history table in mdbutil to gather information about the last successful full backup such as whether it's encrypted, compressed, etc. This information is then used to restore on the designated MariaDB server. +bgrestore.sh helps you automatically restore the last successful full backup from bgbackup to another MariaDB server. This is very useful for verifying backups, refreshing development environments, etc. +bgrestore connects to the backup_history table in mdbutil to find the last successful full backup for a given host. It then delegates the actual restore (decrypt, decompress, prepare, move-back) to [fgrestore](../mysql-mariadb-physical-backup/files/fgrestore.sh), which needs to be installed alongside bgrestore on the restore host. bgrestore itself only keeps what's genuinely its own: the backup_history lookup, logging/mail, and stopping/starting the MariaDB service around the restore. ## How to... -This all assumes the backup was taken with bgbackup (https://github.com/bstillman/bgbackup). This is necessary for the script to gather the required information. +This all assumes the backup was taken with bgbackup (https://github.com/bstillman/bgbackup). This is necessary for the script to gather the required information. -Copy bgrestore.cnf.dist to /etc/bgrestore.cnf and configure as needed (details below). +Copy bgrestore.cnf.dist to /etc/bgrestore.cnf and configure as needed (details below). To run restore tests against a multi-instance host, copy it to a distinct path per instance instead and select it with `bgrestore -c /path/to/that/instance's/bgrestore.cnf`. Currently the script assumes the location of the backup on the source and the destination is the same. Ex: if the backup is in /backups on the server backed up, it should also reside in /backups on the server to be restored. @@ -70,13 +69,13 @@ The hostname of the server from which the backup was taken. Ex. If backing up se The full path to the data directory on the MariaDB server to be restored. -### datadirowner +### restore_my_cnf_file -The owner of the data directory. Usually mysql. +The my.cnf fgrestore should use for the restore. Parses datadir, tmpdir, innodb log/doublewrite dirs, log-error/log-bin/relay-log paths, and the owning `user=` (fgrestore chowns restored files to whatever `user=` says there, default mysql, if it's not set). -### datadirgroup +### service_name -The group of the data directory. Usually mysql. +The systemd service name to stop/start around the restore. Defaults to `mariadb`; set per-instance (via a distinct config file selected with `-c`) to run restore tests against a multi-instance host. ### logpath diff --git a/bgrestore.cnf.dist b/bgrestore.cnf.dist index 007e40e..17ed554 100644 --- a/bgrestore.cnf.dist +++ b/bgrestore.cnf.dist @@ -2,6 +2,15 @@ # bgrestore configuration file +# Path to the my.cnf fgrestore should use for the restore (parses datadir, tmpdir, +# innodb log/doublewrite dirs, log-error/log-bin/relay-log paths, and the owning +# 'user=' for chown). Same default fgrestore itself falls back to. +restore_my_cnf_file=/etc/my.cnf + +# systemd service name to stop/start around the restore. Needed to target a specific +# instance when running restore tests on a multi-instance host; select which +# bgrestore.cnf (and thus which service_name) via '-c|--config'. +service_name=mariadb # MariaDB host for restore restorehost=localhost @@ -39,18 +48,9 @@ backuphost=hostname_from_backup_history # Skip copy step, place backup in preppath after creating it. This will also make bgrestore delete the contents of the folder instead of the folder itself (after succeeding) skipcopy=no -# Delete the previous restore first (before starting to decompress the backup) -deletefirst=no - # Datadir datadir=/var/lib/mysql -# Datadir owner -datadirowner=mysql - -# Datadir group -datadirgroup=mysql - # Path to keep logs logpath=/var/log diff --git a/bgrestore.sh b/bgrestore.sh index 4f52ca5..eb39c78 100755 --- a/bgrestore.sh +++ b/bgrestore.sh @@ -49,9 +49,7 @@ function log_error() { # Preflight checks function preflight { - # find and source the config file - etccnf=$( find /etc -name bgrestore.cnf ) - scriptdir=$( cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) + # source the config file (path resolved by CLI arg parsing / default, see "Begin script") if [ -e "$etccnf" ]; then source "$etccnf" elif [ -e "$scriptdir"/bgrestore.cnf ]; then @@ -66,23 +64,6 @@ function preflight { # set logfile logfile=$logpath/bgrestore_$(date +%Y-%m-%d-%T).log # logfile - # Check for mariabackup or xtrabackup - if [ "$backuptool" == "1" ] && command -v mariabackup >/dev/null; then - innobackupex=$(command -v mariabackup) - elif [ "$backuptool" == "2" ] && command -v innobackupex >/dev/null; then - innobackupex=$(command -v innobackupex) - else - echo "The backuptool does not appear to be installed. Please check that a valid backuptool is chosen in bgbackup.cnf and that it's installed." - log_info "The backuptool does not appear to be installed. Please check that a valid backuptool is chosen in bgbackup.cnf and that it's installed." - log_status=FAILED - mail_log - exit 1 - fi - - innocommand="$innobackupex" - if [ "$backuptool" == "1" ] ; then innocommand=$innocommand" --innobackupex"; fi - - if [ "$datadir" == '' ] ; then log_info "Datadir location not set correctly." log_status=FAILED @@ -149,127 +130,21 @@ function lastfullinfo { exit 1 fi - lastfullcompressed=$($mysqlhistcommand "select compressed from $backuphistschema.backup_history where uuid = '$lastfulluuid' ") - lastfullencrypted=$($mysqlhistcommand "select encrypted from $backuphistschema.backup_history where uuid = '$lastfulluuid' ") - if [ "$lastfullencrypted" == "yes" ] ; then - lastfullcryptkey=$($mysqlhistcommand "select cryptkey from $backuphistschema.backup_history where uuid = '$lastfulluuid' ") - fi log_info "Last full backup to restore: $lastfullbulocation " } -# Function to prepare backup for restore -function prepit { - if [ "$skipcopy" != "yes" ]; then - cp -R "$lastfullbulocation" "$preppath"/ - buname=$(basename "$lastfullbulocation") - bufullpath="$preppath"/"$buname" - else - bufullpath="$preppath" - fi - - if [ "$lastfullencrypted" == "yes" ] ; then - log_info "Backup is encrypted." - $innocommand --decrypt=AES256 --encrypt-key="$(cat "$lastfullcryptkey")" --parallel="$threads" "$bufullpath" >> "$logfile" - decryptstatus=$? - if [ "$decryptstatus" -eq 0 ] ; then - log_info "Backup decrypt was successfol." - else - log_status=FAILED - log_info "Something went wrong. Decrypt failed." - exit 1 - fi - for i in `find $bufullpath -iname "*\.xbcrypt"`; do rm -f $i; done - log_info "Backup now decrypted." - fi - if [ "$lastfullcompressed" == "yes" ] ; then - log_info "Backup is compressed." - $innocommand --decompress --parallel="$threads" "$bufullpath" >> "$logfile" - decompressstatus=$? - if [ "$decompressstatus" -eq 0 ] ; then - log_info "Backup decompress was successfol." - else - log_status=FAILED - log_info "Something went wrong. Decompress failed." - exit 1 - fi - - for i in `find $bufullpath -iname "*\.qp"`; do rm -f $i; done - log_info "Backup is now decompressed." - fi - $innocommand --apply-log "$bufullpath" >> "$logfile" - applystatus=$? - if [ "$applystatus" -eq 0 ] ; then - log_info "Backup apply log was successfol." - else - log_status=FAILED - log_info "Something went wrong. Apply log failed." - exit 1 - fi - log_info "Backup has been prepared for restore." -} - -# Function to delete old restore -function deleteoldrestore { - - log_info "Shutting down MariaDB to restore. " - mysqlshutdowncreate - $mysqlshutdowncommand "shutdown" - - log_info "More shutdown commands to make sure MariaDB is down." - systemctl stop mariadb - pkill -9 mysqld - systemctl stop mariadb - log_info "Current data dir contents:" - ls -al "${datadir:?}" >> "$logfile" - - log_info "Deleting the data directory." - rm -Rf "${datadir:?}"/* -} - -# Function to restore -function restoreit { - - log_info "Moving the prepared backup in $bufullpath to the data directory." - $innocommand --move-back "$bufullpath" --datadir="${datadir:?}" - movebackstatus=$? - if [ "$movebackstatus" -eq 0 ] ; then - log_info "MariaDB succussfully restored and restarted." - else - log_status=FAILED - log_info "Something went wrong. Move back process failed." - exit 1 - fi - - log_info "Fixing privileges." - chown -R "$datadirowner":"$datadirgroup" "$datadir" - - echo "Fixing unfinished transactions. MDEV-6660 workaround." - sudo -u mysql mysqld --tc-heuristic-recover=ROLLBACK - - log_info "Starting MariaDB." - systemctl start mariadb - - startstatus=$? - if [ "$startstatus" -eq 0 ] ; then - log_status=SUCCEEDED - log_info "MariaDB succussfully restored and restarted." - else - log_status=FAILED - log_info "Something went wrong. MariaDB did not start. Check error log." - exit 1 - fi -} - # Cleanup the decompressed/decrypted backup copy +# Regardless of skipcopy, fgrestore always ends up with the prepared backup flattened +# directly into $preppath (skipcopy=yes: prepared in place there via '-I'; skipcopy=no: +# fgrestore itself copies into it via '-D') -- so cleanup is now the same either way. +# Also sweeps fgrestore's chain-staging dirs ('.inc.*'); in practice bgrestore +# only ever restores Full backups (see lastfullinfo) so these shouldn't exist, but -M's +# --move-back already moved everything of substance out, so sweeping is a safe no-op. function cleanup { - if [ "$log_status" == "SUCCEEDED" ] ; then + if [ "$log_status" == "SUCCEEDED" ] ; then log_info "Cleaning up." - if [ "$skipcopy" != "yes" ]; then - rm -Rf "${bufullpath:?}" - else # Clean up prep directory instead of deleting the full backup path (which is also prep directory) - rm -Rf "${preppath}/"* - fi - + rm -Rf "${preppath:?}"/* + rm -Rf "${preppath:?}".inc.* log_info "Complete." fi } @@ -292,6 +167,35 @@ function log_cleanup { # we trap control-c trap sigint INT +scriptdir=$( cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +etccnf="/etc/bgrestore.cnf" + +# Function to display usage +usage() { + echo "Usage: $0 [-c config_file | --config config_file]" + exit 1 +} + +# Parse command line arguments +while [[ "$#" -gt 0 ]]; do + case "$1" in + -c|--config) + if [[ -n "${2:-}" ]]; then + etccnf="$2" + shift + else + echo "Error: --config requires a non-empty option argument." + usage + fi + ;; + *) + echo "Error: Unknown option $1" + usage + ;; + esac + shift +done + # Set some specific variables starttime=$(date +"%Y-%m-%d %H:%M:%S") mdate=$(date +%m/%d/%y) # Date for mail subject. Not in function so set at script start time, not when backup is finished. @@ -299,15 +203,66 @@ mysqlcommand=$(command -v mysql) # do the work preflight -if [ "$deletefirst" == "yes" ] ; then - deleteoldrestore + +# Check that we are not already running. Scoped per service_name so separately +# configured instances on the same restore host (multi-instance restore testing) can +# run concurrently, while two runs against the same instance still can't overlap. +lockfile=/tmp/bgrestore +[ -n "$service_name" ] && lockfile=$lockfile"-$service_name" +lockfile=$lockfile".lock" + +if [ -f $lockfile ] +then + log_error "Another instance of $lockfile is already running. Exiting." fi +trap 'rm -f $lockfile' 0 +touch $lockfile + lastfullinfo -prepit -if [ "$deletefirst" != "yes" ] ; then - deleteoldrestore + +log_info "Shutting down MariaDB to restore." +mysqlshutdowncreate +$mysqlshutdowncommand "shutdown" + +# Decrypt/decompress/prepare/move-back are all handled by fgrestore, chain-aware +# (Full/Differential/Incremental). '-r' always removes compressed originals after +# decompression. skipcopy=yes means copy-last-backup.sh already rsynced the backup +# straight into preppath, so '-I' (in-place) prepares it there directly -- a second +# copy would double disk usage. Otherwise fgrestore copies from lastfullbulocation +# into preppath itself via '-D'. +if [ "$skipcopy" == "yes" ]; then + fgrestore -S "$preppath" -C "$restore_my_cnf_file" -M -N -r -I \ + $( [ "$run_restorecon" == "yes" ] && echo -R ) >> "$logfile" 2>&1 +else + fgrestore -S "$lastfullbulocation" -D "$preppath" -C "$restore_my_cnf_file" -M -N -r \ + $( [ "$run_restorecon" == "yes" ] && echo -R ) >> "$logfile" 2>&1 fi -restoreit +fgrestorestatus=$? +if [ "$fgrestorestatus" -eq 0 ] ; then + log_info "fgrestore completed successfully." +else + log_status=FAILED + log_info "Something went wrong. fgrestore failed. See $logfile for details." + mail_log + exit 1 +fi + +log_info "Fixing unfinished transactions. MDEV-6660 workaround." +sudo -u mysql mysqld --tc-heuristic-recover=ROLLBACK + +log_info "Starting MariaDB." +systemctl start "$service_name" +startstatus=$? +if [ "$startstatus" -eq 0 ] ; then + log_status=SUCCEEDED + log_info "MariaDB succussfully restored and restarted." +else + log_status=FAILED + log_info "Something went wrong. MariaDB did not start. Check error log." + mail_log + exit 1 +fi + cleanup # email the log From df8f72c2e93982136d1b03d817184e65bf65ed51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20de=20Groot?= Date: Wed, 19 Aug 2026 15:32:47 +0100 Subject: [PATCH 28/28] Restore the latest backup of any type, not just the latest Full fgrestore is chain-aware now, so there's no reason to wait for the next Full when the latest successful backup is a Differential or Incremental -- fgrestore trickles back to the Full on its own. Drops the butype = 'Full' filter from the backup_history lookup and renames lastfullinfo()/ lastfulluuid/lastfullbulocation accordingly. --- bgrestore.sh | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/bgrestore.sh b/bgrestore.sh index eb39c78..21af1a5 100755 --- a/bgrestore.sh +++ b/bgrestore.sh @@ -110,36 +110,38 @@ function mysqlshutdowncreate { mysqlshutdowncommand=$mysqlshutdowncommand" -Bse " } -# Function to get directory and other info from last full backup -function lastfullinfo { +# Function to get directory and other info from the last backup (any type -- fgrestore +# is chain-aware, so a Differential/Incremental gets trickled back to its Full automatically) +function lastbackupinfo { mysqlhistcreate - lastfulluuid=$($mysqlhistcommand "select uuid from $backuphistschema.backup_history where butype = 'Full' and status = 'SUCCEEDED' and hostname = '$backuphost' and (deleted_at IS NULL OR deleted_at = 0) order by end_time desc limit 1") - lastfullbulocation=$($mysqlhistcommand "select bulocation from $backuphistschema.backup_history where uuid = '$lastfulluuid' ") - if [ "$lastfullbulocation" == '' ] ; then + lastbuuuid=$($mysqlhistcommand "select uuid from $backuphistschema.backup_history where status = 'SUCCEEDED' and hostname = '$backuphost' and (deleted_at IS NULL OR deleted_at = 0) order by end_time desc limit 1") + lastbulocation=$($mysqlhistcommand "select bulocation from $backuphistschema.backup_history where uuid = '$lastbuuuid' ") + if [ "$lastbulocation" == '' ] ; then log_info "Backup location not set successfully." log_status=FAILED mail_log exit 2 fi - if [ ! -d "$lastfullbulocation" ] && [ "$skipcopy" != "yes" ] ; then + if [ ! -d "$lastbulocation" ] && [ "$skipcopy" != "yes" ] ; then - log_info "Error: $lastfullbulocation directory not found" - log_info "The directory for the last full backup cannot be found on this server." + log_info "Error: $lastbulocation directory not found" + log_info "The directory for the last backup cannot be found on this server." log_status=FAILED mail_log exit 1 fi - log_info "Last full backup to restore: $lastfullbulocation " + log_info "Last backup to restore: $lastbulocation " } # Cleanup the decompressed/decrypted backup copy # Regardless of skipcopy, fgrestore always ends up with the prepared backup flattened # directly into $preppath (skipcopy=yes: prepared in place there via '-I'; skipcopy=no: # fgrestore itself copies into it via '-D') -- so cleanup is now the same either way. -# Also sweeps fgrestore's chain-staging dirs ('.inc.*'); in practice bgrestore -# only ever restores Full backups (see lastfullinfo) so these shouldn't exist, but -M's -# --move-back already moved everything of substance out, so sweeping is a safe no-op. +# Also sweeps fgrestore's chain-staging dirs ('.inc.*') -- bgrestore now +# restores the latest backup of any type, so a Differential/Incremental chain of more +# than one member does create these; -M's --move-back only moves $preppath itself back, +# not the per-member staging dirs. function cleanup { if [ "$log_status" == "SUCCEEDED" ] ; then log_info "Cleaning up." @@ -218,7 +220,7 @@ fi trap 'rm -f $lockfile' 0 touch $lockfile -lastfullinfo +lastbackupinfo log_info "Shutting down MariaDB to restore." mysqlshutdowncreate @@ -228,13 +230,13 @@ $mysqlshutdowncommand "shutdown" # (Full/Differential/Incremental). '-r' always removes compressed originals after # decompression. skipcopy=yes means copy-last-backup.sh already rsynced the backup # straight into preppath, so '-I' (in-place) prepares it there directly -- a second -# copy would double disk usage. Otherwise fgrestore copies from lastfullbulocation +# copy would double disk usage. Otherwise fgrestore copies from lastbulocation # into preppath itself via '-D'. if [ "$skipcopy" == "yes" ]; then fgrestore -S "$preppath" -C "$restore_my_cnf_file" -M -N -r -I \ $( [ "$run_restorecon" == "yes" ] && echo -R ) >> "$logfile" 2>&1 else - fgrestore -S "$lastfullbulocation" -D "$preppath" -C "$restore_my_cnf_file" -M -N -r \ + fgrestore -S "$lastbulocation" -D "$preppath" -C "$restore_my_cnf_file" -M -N -r \ $( [ "$run_restorecon" == "yes" ] && echo -R ) >> "$logfile" 2>&1 fi fgrestorestatus=$?