Skip to content

Instantly share code, notes, and snippets.

@josephbolus
Last active November 28, 2025 09:03
Show Gist options
  • Select an option

  • Save josephbolus/e306d023c13dd9db951f9c9927e18bbc to your computer and use it in GitHub Desktop.

Select an option

Save josephbolus/e306d023c13dd9db951f9c9927e18bbc to your computer and use it in GitHub Desktop.
MySQL Instance Migration Script – with --dry-run support
#!/bin/bash
# =============================================================================
# MySQL Instance Migration – fully compatible with MySQL Shell 8.0.34+ / 8.4+
# No more "Invalid options: maxRate"
# =============================================================================
set -euo pipefail
DRY_RUN=false
[[ "${1:-}" == "--dry-run" || "${1:-}" == "-n" ]] && DRY_RUN=true && shift
# ----------------------------- Config ---------------------------------
SOURCE_LOGIN_PATH="source_migration_user"
TARGET_LOGIN_PATH="target_migration_user"
SOURCE_URI=""
TARGET_URI=""
DUMP_BASE_DIR="${DUMP_BASE_DIR:-/tmp}"
THREADS=$(nproc)
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
SOURCE_DUMP_DIR="$DUMP_BASE_DIR/source_dump_$TIMESTAMP"
TARGET_BACKUP_DIR="$DUMP_BASE_DIR/target_backup_$TIMESTAMP"
LOG_FILE="$DUMP_BASE_DIR/mysql_migration_${TIMESTAMP}${DRY_RUN:+_DRYRUN}.log"
# ----------------------------- Safe logging --------------------------
mkdir -p "$(dirname "$LOG_FILE")"
touch "$LOG_FILE"
log() {
local msg="$*"
[[ "$DRY_RUN" == true ]] && msg="[DRY-RUN] $msg"
printf '%s %s\n' "$(date -u +"%Y-%m-%dT%H:%M:%SZ")" "$msg" >> "$LOG_FILE"
printf '%s %s\n' "$(date -u +"%Y-%m-%dT%H:%M:%SZ")" "$msg"
}
# ----------------------------- Cleanup -------------------------------
cleanup() {
[[ "$DRY_RUN" == true ]] && {
log "Dry-run cleanup: removing temporary directories"
rm -rf "$SOURCE_DUMP_DIR" "$TARGET_BACKUP_DIR" 2>/dev/null || true
return
}
log "Live run cleanup: removing source dump only"
rm -rf "$SOURCE_DUMP_DIR" 2>/dev/null || true
}
trap cleanup EXIT
# ----------------------------- Connection ----------------------------
build_conn_opts() {
local lp="$1" uri="$2"
[[ -n "$lp" ]] && echo "--login-path=$lp"
[[ -n "$uri" ]] && echo "--uri=$uri"
[[ -z "$lp" && -z "$uri" ]] && { log "ERROR: No credentials"; exit 1; }
}
SOURCE_CONN=$(build_conn_opts "$SOURCE_LOGIN_PATH" "$SOURCE_URI")
TARGET_CONN=$(build_conn_opts "$TARGET_LOGIN_PATH" "$TARGET_URI")
# ----------------------------- Start ---------------------------------
log "=== MySQL Migration $( [[ $DRY_RUN == true ]] && echo "DRY-RUN" || echo "LIVE" ) Started ==="
log "Log file : $LOG_FILE"
mkdir -p "$SOURCE_DUMP_DIR" "$TARGET_BACKUP_DIR"
# Step 1 – Source dump (no users)
if [[ "$DRY_RUN" == false ]]; then
log "Step 1/3: Dumping source instance (users excluded)..."
mysqlsh $SOURCE_CONN --js -e "
util.dumpInstance('$SOURCE_DUMP_DIR', {
users: false,
checksum: true,
threads: $THREADS,
compression: 'zstd',
chunking: true,
bytesPerChunk: '64M',
showProgress: true
})
" >> "$LOG_FILE" 2>&1
log "Source dump completed"
else
log "Step 1/3: [WOULD] Dump source → $SOURCE_DUMP_DIR"
fi
# Step 2 – Target backup (full)
if [[ "$DRY_RUN" == false ]]; then
log "Step 2/3: Creating full backup of target..."
mysqlsh $TARGET_CONN --js -e "
util.dumpInstance('$TARGET_BACKUP_DIR', {
users: true,
checksum: true,
threads: $THREADS,
compression: 'zstd',
chunking: true,
bytesPerChunk: '64M',
showProgress: true
})
" >> "$LOG_FILE" 2>&1
log "Target backup completed"
else
log "Step 2/3: [WOULD] Backup target → $TARGET_BACKUP_DIR"
fi
# Step 3 – Load into target
if [[ "$DRY_RUN" == false ]]; then
log "Step 3/3: Importing source dump into target..."
mysqlsh $TARGET_CONN --js -e "
util.loadDump('$SOURCE_DUMP_DIR', {
checksum: true,
threads: $THREADS,
showProgress: true
})
" >> "$LOG_FILE" 2>&1
log "=== MIGRATION SUCCESSFUL ==="
else
log "Step 3/3: [WOULD] Load dump into target"
log "Running compatibility check..."
mysqlsh $TARGET_CONN --js -e "util.checkInstanceCompatibility('$SOURCE_DUMP_DIR')" >> "$LOG_FILE" 2>&1 || true
log "=== DRY-RUN COMPLETE – NO CHANGES MADE ==="
fi
log "Target backup $( [[ $DRY_RUN == false ]] && echo "available at" || echo "would be at" ): $TARGET_BACKUP_DIR"
log "Log file kept at: $LOG_FILE"
log "=== Script finished ==="
exit 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment