From 3808192fd816138add854f8a6ce392c3d8be1621 Mon Sep 17 00:00:00 2001 From: "ken.li" Date: Mon, 2 Feb 2026 14:40:46 +0800 Subject: [PATCH] =?UTF-8?q?feat(mysql):=20=E6=B7=BB=E5=8A=A0MySQL=E5=85=A8?= =?UTF-8?q?=E9=87=8F=E5=A4=87=E4=BB=BD=E4=B8=8E=E8=BF=98=E5=8E=9F=E8=84=9A?= =?UTF-8?q?=E6=9C=AC=E5=8F=8A=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增备份脚本backup-sync.sh和还原脚本restore-sync.sh,用于处理严重复制冲突场景 更新故障分析文档,补充全量备份还原的操作流程和注意事项 添加测试流程模板文件_TEST_FLOW_TEMPLATE.md 更新SKILL.md文档,增加测试流程分析相关内容 --- skills/g3fo-docs/SKILL.md | 20 +- .../business_flows/_TEST_FLOW_TEMPLATE.md | 15 ++ .../middleware/mysql/backup-sync.sh | 132 ++++++++++ .../middleware/mysql/fault-analysis.md | 119 +++++++++ .../middleware/mysql/restore-sync.sh | 227 ++++++++++++++++++ 5 files changed, 511 insertions(+), 2 deletions(-) create mode 100644 skills/g3fo-docs/references/business_flows/_TEST_FLOW_TEMPLATE.md create mode 100644 skills/g3fo-docs/references/middleware/mysql/backup-sync.sh create mode 100644 skills/g3fo-docs/references/middleware/mysql/restore-sync.sh diff --git a/skills/g3fo-docs/SKILL.md b/skills/g3fo-docs/SKILL.md index c8748a9..4b47176 100644 --- a/skills/g3fo-docs/SKILL.md +++ b/skills/g3fo-docs/SKILL.md @@ -11,6 +11,7 @@ description: g3fo 系统相关文档。管理业务流程说明、服务职责 - **查业务流程**:查阅 `references/business_flows/` (如订单生命周期、用户入金等跨服务流程)。 - **查服务职责**:查阅 `references/services/` (如 `g3fo-trade-service.md` 定义的具体功能)。 - **查架构概览**:查阅 `references/architecture/domain_overview.md`。 +- **分析测试流程**:参考 `references/business_flows/_TEST_FLOW_TEMPLATE.md`。 - **查标准运维流程**:去 `references/middleware/` 或 `references/system-init/`。 - **查具体环境/客户资产**: - 内部环境(Dev/UAT):查阅 `references/inventory/internal.md`。 @@ -19,7 +20,7 @@ description: g3fo 系统相关文档。管理业务流程说明、服务职责 ## 2. 问答逻辑 (AI 引导) ### A. 业务逻辑问答 -1. **识别范围**:判断用户问题涉及哪些业务流程或服务。 +1. **识别范围**:判断用户问题涉及哪些业务流程 or 服务。 2. **加载文档**: - 优先查找 `business_flows/` 下的相关流程文档。 - 结合 `services/` 下涉及的服务文档,深入了解具体职责。 @@ -45,14 +46,29 @@ description: g3fo 系统相关文档。管理业务流程说明、服务职责 - **格式 A (Docker 模式)**:完整的 `docker exec -it mysql mysql -uroot -pafe123456 -h127.0.0.1 -e "SQL_CONTENT"`。 - **格式 B (纯 SQL 模式)**:仅包含 SQL 语句本身,方便在 GUI 工具中执行。 +### C. 测试流程分析 (New) +1. **需求解析**:识别用户提出的业务关键词(如“下单”、“撤单”、“充值”)。 +2. **文档检索**: + - 查找 `business_flows/` 下的业务流程文档。 + - 查找 `services/` 下涉及的服务定义。 +3. **代码溯源**: + - 根据文档中提到的服务,在项目中搜索对应的 Controller、Service 或核心逻辑代码。 + - **重点搜索路径**: + - REST 接口:`com.afe.g3fo.*.controller` 或 `com.afe.g3fo.*.api`。 + - Dubbo 接口:`com.afe.g3fo.*.facade`(定义)及 `com.afe.g3fo.*.facade.impl`(实现)。 + - **重点搜索注解**:`@RequestMapping`, `@PostMapping`, `@GetMapping`, `@Service`, `@DubboService` 等。 +4. **表格生成**:汇总信息,输出包含“步骤”、“涉及服务”、“关键接口/代码逻辑”、“测试内容/预期结果”的表格。 + - **格式参考**:`references/business_flows/_TEST_FLOW_TEMPLATE.md`。 + ## 3. 示例 Prompt (用户可参考) - **业务咨询**: + - “我想测试下单业务流程,请分析代码并列出详细的测试步骤表格。” - “g3fo 系统中,一个订单从下单到成交会经过哪些服务?请结合 `references/business_flows/` 下的相关文档回答。” - “`g3fo-margin-service` 负责哪些核心逻辑?它的上下游依赖是谁?” - **部署配置**: - “请结合 `references/inventory/internal.md` 中的 `UAT` 环境变量,参考 `references/middleware/keepalived/deploy.md` 部署文档,为我生成 Node A 的 `keepalived.conf` 配置文件。” - **故障排查**: - - “我的 MySQL 出现了复制冲突,报错 `Duplicate entry`,请根据 `references/middleware/mysql/fault-analysis.md` 提供排查脚本和修复建议。” + - “我的 MySQL 出现了复制冲突,报错 `Duplicate entry`,请根据 `references/middleware/mysql/fault-analysis.md` 提供排查脚本 and 修复建议。” ## 4. 资源地图 - **业务与服务**: diff --git a/skills/g3fo-docs/references/business_flows/_TEST_FLOW_TEMPLATE.md b/skills/g3fo-docs/references/business_flows/_TEST_FLOW_TEMPLATE.md new file mode 100644 index 0000000..6e9a4da --- /dev/null +++ b/skills/g3fo-docs/references/business_flows/_TEST_FLOW_TEMPLATE.md @@ -0,0 +1,15 @@ +# Test Flow Template + +## Business Flow: [Flow Name] + +| 步骤 | 涉及服务 | 关键接口/代码逻辑 | 测试内容/预期结果 | +| :--- | :--- | :--- | :--- | +| 1. [步骤名称] | `[服务名]` | `[类名.方法名()]` | [测试要点及预期行为描述] | +| 2. [步骤名称] | `[服务名]` | `[类名.方法名()]` | [测试要点及预期行为描述] | + +### Testing Notes +- **Prerequisites**: [e.g., User must be logged in, Balance > 0] +- **Key Data Points**: [e.g., OrderID, TransactionID] +- **Edge Cases to Cover**: + - [Edge Case 1] + - [Edge Case 2] diff --git a/skills/g3fo-docs/references/middleware/mysql/backup-sync.sh b/skills/g3fo-docs/references/middleware/mysql/backup-sync.sh new file mode 100644 index 0000000..61cd238 --- /dev/null +++ b/skills/g3fo-docs/references/middleware/mysql/backup-sync.sh @@ -0,0 +1,132 @@ +#!/bin/bash + +TAG="" +while getopts "t:" opt; do + case $opt in + t) TAG="$OPTARG" ;; + *) echo "Invalid option: -$OPTARG" >&2; exit 1 ;; + esac +done +shift $((OPTIND-1)) + +cd /data/backup/mysql || exit 1 +if [ -n "$TAG" ]; then + BACKUP_DIR="./$(date +%Y%m%d%H%M%S)_${TAG}" +else + BACKUP_DIR="./$(date +%Y%m%d%H%M%S)" +fi + +mkdir -p "$BACKUP_DIR" + +MYSQL_USER="root" +MYSQL_PASSWORD="ENC(VN13kACOdhyl7xYqbgDMTMP0fK4dwoCkFe0AQW0VEt7/tpwx96GzRVY6eIpwlASM)" +MYSQL_HOST="127.0.0.1" + +if [[ "$MYSQL_PASSWORD" =~ ^ENC\(.*\)$ ]]; then + encrypted_pwd="${MYSQL_PASSWORD:4:-1}" + if ! MYSQL_PASSWORD=$(java -cp /data/secure/jasypt-agent.jar com.jasypt.agent.DecryptionCLI $encrypted_pwd \ + 2>&1); then + echo -e "\033[31m[Fatal Error] Password decryption failed. Reason:" + echo "$MYSQL_PASSWORD" | grep -iE "error|exception" + exit 1 + fi +fi + +# Function to execute MySQL command +mysql_exec() { + docker exec -i mysql mysql -u "$MYSQL_USER" -p"$MYSQL_PASSWORD" -h "$MYSQL_HOST" -N -e "$1" 2>/dev/null +} + +# Function to restore MySQL state (called on exit) +restore_mysql_state() { + if [ "$READ_ONLY_SET" = "true" ]; then + echo "Restoring MySQL state..." + mysql_exec "SET GLOBAL read_only = OFF;" || echo "[Warning] Failed to set read_only = OFF" + echo "MySQL state restored" + fi +} + +# Set trap to ensure MySQL state is restored even on error +trap restore_mysql_state EXIT ERR + +# Pre-backup: Set read-only and stop slave +echo "Setting MySQL to read-only mode..." +if ! mysql_exec "SET GLOBAL read_only = ON;"; then + echo "[Critical Error] Failed to set read_only = ON" + exit 1 +fi +READ_ONLY_SET="true" + +echo "Stopping slave replication..." +if ! mysql_exec "STOP REPLICA;"; then + echo "[Warning] Failed to stop replica (may not be a slave server or already stopped)" +fi + +# Get GTID position using SELECT @@global.gtid_executed; +echo "Getting GTID position..." +GTID_EXECUTED=$(mysql_exec "SELECT @@global.gtid_executed;") +if [ -n "$GTID_EXECUTED" ]; then + # Remove newlines and literal \n strings + GTID_EXECUTED=$(echo "$GTID_EXECUTED" | tr -d '\n' | sed 's/\\n//g' | tr -s ' ') + echo "GTID Executed: $GTID_EXECUTED" + # Write GTID to file for restore use + echo "$GTID_EXECUTED" > "$BACKUP_DIR/gtid_executed.txt" +else + echo "[Warning] Failed to get GTID executed information" +fi + +# Get database list for backup +if [ $# -eq 0 ]; then + # When no arguments, get all non-system databases + databases=$(mysql_exec "SHOW DATABASES;" | grep -Ev "(Database|information_schema|performance_schema|mysql|skywalking|sys)") + + # Convert to array and check empty values + readarray -t db_list <<< "$databases" || { + echo "ERROR: No valid databases found or failed to retrieve database list" + restore_mysql_state + exit 1 + } +else + # Use arguments as database list when provided + db_list=("$@") +fi + +# Check if database list is empty +if [ ${#db_list[@]} -eq 0 ]; then + echo "ERROR: No databases to backup" + restore_mysql_state + exit 1 +fi + +backup_list_str="$BACKUP_DIR.7z" +# Perform backup +for db in "${db_list[@]}"; do + echo "Backing up database: $db" + backup_list_str="$backup_list_str $db" + if ! docker exec -i mysql mysqldump -u "$MYSQL_USER" -p"$MYSQL_PASSWORD" -h "$MYSQL_HOST" --set-gtid-purged=OFF --single-transaction --databases "$db" > "$BACKUP_DIR/$db.sql"; then + echo "[Critical Error] Failed to backup database $db, aborting" + restore_mysql_state + exit 1 + fi +done + +# Post-backup: Restore MySQL state (before compression) +echo "Restoring MySQL to read-write mode..." +mysql_exec "SET GLOBAL read_only = OFF;" || echo "[Warning] Failed to set read_only = OFF" + +# Clear the trap since we've successfully restored +READ_ONLY_SET="false" +trap - EXIT ERR + +# Compress backup files +if ! 7zz a "$BACKUP_DIR.7z" "$BACKUP_DIR" >/dev/null; then + echo "Compression failed. Please check disk space or 7z installation" + exit 1 +fi + +# Cleanup temporary files +rm -rf "$BACKUP_DIR" +echo "Backup completed: $BACKUP_DIR.7z" +echo $backup_list_str >> backup_list.txt + +echo "Backup process completed successfully" \ No newline at end of file diff --git a/skills/g3fo-docs/references/middleware/mysql/fault-analysis.md b/skills/g3fo-docs/references/middleware/mysql/fault-analysis.md index 17e113a..ecd76ac 100644 --- a/skills/g3fo-docs/references/middleware/mysql/fault-analysis.md +++ b/skills/g3fo-docs/references/middleware/mysql/fault-analysis.md @@ -99,3 +99,122 @@ BEGIN; COMMIT; SET GTID_NEXT = 'AUTOMATIC'; START REPLICA; ``` + +## 2. 全量备份还原解决冲突(适用于严重冲突场景) + +当复制冲突过多、数据不一致严重,或手动修复成本过高时,可以使用全量备份还原的方式彻底解决冲突问题。此方法通过从主库(或备份库)全量备份数据,然后在从库上完全还原,实现数据一致性。 + +### 2.1 相关脚本 + +- **备份脚本**: [`backup-sync.sh`](./backup-sync.sh) - 在主库或备份库上执行全量备份 +- **还原脚本**: [`restore-sync.sh`](./restore-sync.sh) - 在从库上执行全量还原 + +### 2.2 操作流程 + +#### 步骤 1: 在备份机上执行备份 + +在需要备份的 MySQL 服务器(通常是主库或数据一致的节点)上执行备份脚本: + +```bash +# 备份所有数据库(默认) +cd /data/backup/mysql +./backup-sync.sh + +# 或指定标签备份 +./backup-sync.sh -t conflict_resolve + +# 或备份指定数据库 +./backup-sync.sh database1 database2 +``` + +备份脚本会自动: +- 设置 MySQL 为只读模式 +- 停止复制(如果是从库) +- 记录当前 GTID 位置 +- 执行全量备份并压缩为 `.7z` 文件 +- 恢复 MySQL 为读写模式 + +备份完成后,会在 `/data/backup/mysql/` 目录下生成 `YYYYMMDDHHMMSS.7z` 或 `YYYYMMDDHHMMSS_TAG.7z` 格式的备份文件。 + +#### 步骤 2: 传输备份文件到还原机 + +使用 `scp` 将备份文件推送到需要还原的 DB 机的 `/data/backup/mysql/` 目录: + +```bash +# 从备份机执行(将备份文件推送到还原机) +scp /data/backup/mysql/YYYYMMDDHHMMSS.7z user@restore-server:/data/backup/mysql/ + +# 示例 +scp /data/backup/mysql/20250127120000.7z root@192.168.1.100:/data/backup/mysql/ +``` + +**注意**: 确保还原机的 `/data/backup/mysql/` 目录存在且有写入权限。 + +#### 步骤 3: 在还原机上执行还原 + +在需要还原的 MySQL 服务器(从库)上执行还原脚本: + +```bash +cd /data/backup/mysql + +# 还原所有数据库 +./restore-sync.sh YYYYMMDDHHMMSS.7z + +# 或仅还原指定数据库 +./restore-sync.sh YYYYMMDDHHMMSS.7z database1.sql +``` + +还原脚本会自动: +- 设置 MySQL 为只读模式 +- 停止复制 +- 重置二进制日志和 GTID +- 导入备份数据(导入时禁用 binlog,避免循环复制) +- 设置 GTID purged(从备份时的 GTID 位置) +- 恢复 MySQL 为读写模式 +- **自动启动复制并检查状态** + +#### 步骤 4: 在备份机上启动同步(重要) + +还原完成后,还原脚本会自动启动复制并显示同步状态。确认看到同步成功后,**需要在备份机(主库)上执行以下操作**: + +**A. Docker 执行模式 (包含 -h127.0.0.1):** +```bash +docker exec -it mysql mysql -uroot -pafe123456 -h127.0.0.1 -e " +START REPLICA; +" +``` + +**B. 纯 SQL 模式:** +```sql +START REPLICA; +``` + +等待 3 秒后,检查同步状态: + +**A. Docker 执行模式 (包含 -h127.0.0.1):** +```bash +sleep 3 +docker exec -it mysql mysql -uroot -pafe123456 -h127.0.0.1 -e "SHOW REPLICA STATUS\G" +``` + +**B. 纯 SQL 模式:** +```sql +-- 等待 3 秒后执行 +SHOW REPLICA STATUS\G; +``` + +检查关键指标: +- `Replica_IO_Running`: 应为 `Yes` +- `Replica_SQL_Running`: 应为 `Yes` +- `Last_IO_Error`: 应为空 +- `Last_SQL_Error`: 应为空 +- `Seconds_Behind_Source`: 延迟秒数(应逐渐减小) + +### 2.3 注意事项 + +1. **备份时机**: 建议在业务低峰期执行备份,减少对业务的影响 +2. **网络传输**: 确保备份机和还原机之间网络畅通,备份文件较大时传输可能需要较长时间 +3. **磁盘空间**: 确保还原机有足够的磁盘空间存放备份文件和临时解压文件 +4. **权限要求**: 脚本需要 MySQL root 权限,以及 `/data/backup/mysql/` 目录的读写权限 +5. **GTID 一致性**: 还原脚本会自动处理 GTID,确保复制能够正常继续 +6. **备份机同步**: 还原完成后务必在备份机(主库)上执行 `START REPLICA` 并检查状态,确保双向复制正常 diff --git a/skills/g3fo-docs/references/middleware/mysql/restore-sync.sh b/skills/g3fo-docs/references/middleware/mysql/restore-sync.sh new file mode 100644 index 0000000..1d4339b --- /dev/null +++ b/skills/g3fo-docs/references/middleware/mysql/restore-sync.sh @@ -0,0 +1,227 @@ +#!/bin/bash + +# Define variables +MYSQL_USER="root" +MYSQL_PASSWORD="ENC(VN13kACOdhyl7xYqbgDMTMP0fK4dwoCkFe0AQW0VEt7/tpwx96GzRVY6eIpwlASM)" +MYSQL_HOST="127.0.0.1" +BACKUP_DIR="/data/backup/mysql" +TEMP_DIR="/tmp/mysql_restore_temp" + +if [[ "$MYSQL_PASSWORD" =~ ^ENC\(.*\)$ ]]; then + encrypted_pwd="${MYSQL_PASSWORD:4:-1}" + # Decrypt password + if ! MYSQL_PASSWORD=$(java -cp /data/secure/jasypt-agent.jar com.jasypt.agent.DecryptionCLI $encrypted_pwd \ + 2>&1); then + echo -e "\033[31m[Fatal Error] Password decryption failed. Reason:" + echo "$MYSQL_PASSWORD" | grep -iE "error|exception" + exit 1 + fi +fi + +# Function to execute MySQL command +mysql_exec() { + docker exec -i mysql mysql -u "$MYSQL_USER" -p"$MYSQL_PASSWORD" -h "$MYSQL_HOST" -N -e "$1" 2>/dev/null +} + +# Function to restore MySQL state (called on exit) +restore_mysql_state() { + if [ "$READ_ONLY_SET" = "true" ]; then + echo "Restoring MySQL state..." + mysql_exec "SET GLOBAL read_only = OFF;" || echo "[Warning] Failed to set read_only = OFF" + echo "MySQL state restored" + fi +} + +# Check parameters +if [ $# -lt 1 ]; then + echo "Usage: $0 [sql_file]" + echo "Example: $0 20250414105525.7z nacos.sql" + exit 1 +fi + +BACKUP_FILE="$1" +SQL_FILE="$2" + +# Verify backup file existence +if [ ! -f "$BACKUP_DIR/$BACKUP_FILE" ]; then + echo "Error: Backup file $BACKUP_DIR/$BACKUP_FILE not found" + exit 1 +fi + +# Extract base backup name +BACKUP_NAME=$(basename "$BACKUP_FILE" .7z) + +# Create temporary directory +mkdir -p "$TEMP_DIR" +echo "Creating temporary directory: $TEMP_DIR" + +# Extract backup file +echo "Extracting $BACKUP_FILE to $TEMP_DIR..." +7zz x "$BACKUP_DIR/$BACKUP_FILE" -o"$TEMP_DIR" -y + +# Check extraction status +if [ $? -ne 0 ]; then + echo "Error: Decompression failed" + rm -rf "$TEMP_DIR" + exit 1 +fi + +echo "Extraction completed" + +# Handle nested directory +NESTED_DIR="$TEMP_DIR/$BACKUP_NAME" +if [ -d "$NESTED_DIR" ]; then + echo "Detected nested directory: $NESTED_DIR" + SQL_DIR="$NESTED_DIR" +else + echo "No nested directory found, using root extraction path" + SQL_DIR="$TEMP_DIR" +fi + +# Verify SQL files existence +SQL_COUNT=$(find "$SQL_DIR" -name "*.sql" | wc -l) +if [ "$SQL_COUNT" -eq 0 ]; then + echo "Error: No SQL files found in extracted directory" + rm -rf "$TEMP_DIR" + exit 1 +fi + +# Pre-restore: Set read-only, stop replica, and reset GTID +echo "Setting MySQL to read-only mode..." +if ! mysql_exec "SET GLOBAL read_only = ON;"; then + echo "[Critical Error] Failed to set read_only = ON" + rm -rf "$TEMP_DIR" + exit 1 +fi +READ_ONLY_SET="true" + +# Set trap to ensure MySQL state is restored even on error +trap restore_mysql_state EXIT ERR + +echo "Stopping replica replication..." +if ! mysql_exec "STOP REPLICA;"; then + echo "[Warning] Failed to stop replica (may not be a replica server or already stopped)" +fi + +echo "Resetting binary logs and GTIDs..." +if ! mysql_exec "RESET BINARY LOGS AND GTIDS;"; then + echo "[Critical Error] Failed to reset binary logs and GTIDs" + restore_mysql_state + rm -rf "$TEMP_DIR" + exit 1 +fi + +# Database import logic +if [ -n "$SQL_FILE" ]; then + # Import specific SQL file + if [ -f "$SQL_DIR/$SQL_FILE" ]; then + echo "Importing $SQL_FILE..." + + db_name=$(basename "$SQL_FILE" .sql) + echo "Using database: $db_name" + + # Create database if not exists + docker exec -i mysql mysql --init-command="SET SQL_LOG_BIN=0;" -u"$MYSQL_USER" -p"$MYSQL_PASSWORD" -h"$MYSQL_HOST" --connect-expired-password -e "CREATE DATABASE IF NOT EXISTS \`$db_name\`;" + + # Import SQL file + docker exec -i mysql mysql --init-command="SET SQL_LOG_BIN=0;" -u"$MYSQL_USER" -p"$MYSQL_PASSWORD" -h"$MYSQL_HOST" --connect-expired-password "$db_name" < "$SQL_DIR/$SQL_FILE" + + if [ $? -eq 0 ]; then + echo "Successfully imported $SQL_FILE" + else + echo "Error: Failed to import $SQL_FILE" + restore_mysql_state + rm -rf "$TEMP_DIR" + exit 1 + fi + else + echo "Error: SQL file $SQL_DIR/$SQL_FILE not found" + restore_mysql_state + rm -rf "$TEMP_DIR" + exit 1 + fi +else + # Import all SQL files + echo "Importing all SQL files..." + for sql_file in $(find "$SQL_DIR" -name "*.sql" | sort); do + db_name=$(basename "$sql_file" .sql) + echo "Importing $sql_file to database $db_name..." + + # Create database if not exists + docker exec -i mysql mysql --init-command="SET SQL_LOG_BIN=0;" -u"$MYSQL_USER" -p"$MYSQL_PASSWORD" -h"$MYSQL_HOST" --connect-expired-password -e "CREATE DATABASE IF NOT EXISTS \`$db_name\`;" + + # Import SQL file + docker exec -i mysql mysql --init-command="SET SQL_LOG_BIN=0;" -u"$MYSQL_USER" -p"$MYSQL_PASSWORD" -h"$MYSQL_HOST" --connect-expired-password "$db_name" < "$sql_file" + + if [ $? -eq 0 ]; then + echo "Successfully imported $sql_file to $db_name" + else + echo "Error: Failed to import $sql_file to $db_name" + restore_mysql_state + rm -rf "$TEMP_DIR" + exit 1 + fi + done +fi + +# Post-restore: Set GTID purged from backup file +GTID_FILE="$SQL_DIR/gtid_executed.txt" +if [ -f "$GTID_FILE" ]; then + echo "Reading GTID from $GTID_FILE..." + GTID_EXECUTED=$(cat "$GTID_FILE" | tr -d '\n' | sed 's/\\n//g' | tr -s ' ' | xargs) + if [ -n "$GTID_EXECUTED" ]; then + echo "Setting GTID purged to: $GTID_EXECUTED" + if ! mysql_exec "SET GLOBAL gtid_purged = '$GTID_EXECUTED';"; then + echo "[Critical Error] Failed to set gtid_purged" + restore_mysql_state + rm -rf "$TEMP_DIR" + exit 1 + fi + echo "GTID purged set successfully" + else + echo "[Warning] GTID file is empty" + fi +else + echo "[Warning] GTID file $GTID_FILE not found, skipping GTID purged setting" +fi + +# Restore MySQL to read-write mode +echo "Restoring MySQL to read-write mode..." +mysql_exec "SET GLOBAL read_only = OFF;" || echo "[Warning] Failed to set read_only = OFF" +READ_ONLY_SET="false" + +# Start replica +echo "Starting replica replication..." +if ! mysql_exec "START REPLICA;"; then + echo "[Warning] Failed to start replica (may not be a replica server)" +else + echo "Waiting 3 seconds for replica to initialize..." + sleep 3 + + echo "Checking replica status..." + REPLICA_STATUS=$(mysql_exec "SHOW REPLICA STATUS\G") + if [ -n "$REPLICA_STATUS" ]; then + echo "Replica Status:" + echo "$REPLICA_STATUS" + + # Check for errors in replica status + ERROR_COUNT=$(echo "$REPLICA_STATUS" | grep -i "Last_Error\|Last_IO_Error\|Last_SQL_Error" | grep -v "No error" | wc -l) + if [ "$ERROR_COUNT" -gt 0 ]; then + echo "[Warning] Replica status shows errors, please check the output above" + else + echo "Replica status appears normal" + fi + else + echo "[Warning] Failed to get replica status" + fi +fi + +# Clear the trap since we've successfully restored +trap - EXIT ERR + +# Cleanup +echo "Cleaning temporary directory..." +rm -rf "$TEMP_DIR" + +echo "Database restore operation completed" +exit 0 \ No newline at end of file