修复(08-23-isolated-backup-restore-drill): 消除影子库就绪竞态

这个提交包含在:
2026-08-25 17:21:41 +08:00
父节点 18233d3433
当前提交 6d7dfbb1eb
共修改 6 个文件,包含 328 行新增和 78 行删除
@@ -44,6 +44,11 @@ TARGET_CONFIRMATION_STATUS="NOT_RUN"
RESTORE_APPLY_STATUS="NOT_RUN"
RESTORE_EXECUTION_PATH="NOT_RUN"
DUMP_LIST_EXIT_CODE="NOT_RUN"
TARGET_CONFIRMATION_EXIT_CODE="NOT_RUN"
TARGET_NAME_MATCH="NOT_RUN"
TARGET_CONFIRMATION_ATTEMPT_COUNT="0"
TARGET_CONFIRMATION_SUCCESS_STREAK="0"
TARGET_CONFIRMATION_FAILURE_REASON="NOT_APPLICABLE"
RESTORE_SCRIPT_EXIT_CODE="NOT_RUN"
PG_RESTORE_EXIT_CODE="NOT_RUN"
PG_RESTORE_ERROR_COUNT="NOT_RUN"
@@ -61,6 +66,47 @@ require_command() {
command -v "$1" >/dev/null 2>&1 || fail "required_command_missing"
}
confirm_postgres_target() {
local actual_database=""
local query_exit_code=0
local attempt=0
TARGET_CONFIRMATION_EXIT_CODE="NOT_RUN"
TARGET_NAME_MATCH="NOT_RUN"
TARGET_CONFIRMATION_ATTEMPT_COUNT="0"
TARGET_CONFIRMATION_SUCCESS_STREAK="0"
TARGET_CONFIRMATION_FAILURE_REASON="TARGET_QUERY_NONZERO"
for attempt in $(seq 1 60); do
TARGET_CONFIRMATION_ATTEMPT_COUNT="$attempt"
set +e
actual_database="$(PGPASSWORD="$postgres_password" docker exec -e PGPASSWORD \
"$CONTAINER_NAME" psql -h 127.0.0.1 -v ON_ERROR_STOP=1 \
-U "$SHADOW_DB_USER" -d "$SHADOW_DB_NAME" \
-Atqc 'SELECT current_database()' 2>>"$private_log")"
query_exit_code="$?"
set -e
TARGET_CONFIRMATION_EXIT_CODE="$query_exit_code"
if [[ "$query_exit_code" -ne 0 ]]; then
TARGET_NAME_MATCH="NOT_RUN"
TARGET_CONFIRMATION_SUCCESS_STREAK="0"
[[ "$attempt" -eq 60 ]] || sleep 1
continue
fi
if [[ "$actual_database" != "$SHADOW_DB_NAME" ]]; then
TARGET_NAME_MATCH="no"
TARGET_CONFIRMATION_FAILURE_REASON="TARGET_NAME_MISMATCH"
return 1
fi
TARGET_NAME_MATCH="yes"
TARGET_CONFIRMATION_SUCCESS_STREAK="$((TARGET_CONFIRMATION_SUCCESS_STREAK + 1))"
if [[ "$TARGET_CONFIRMATION_SUCCESS_STREAK" -ge 2 ]]; then
TARGET_CONFIRMATION_FAILURE_REASON="NONE"
return 0
fi
sleep 1
done
return 1
}
cleanup_runtime() {
local cleanup_ok=1
local container_suffix=""
@@ -126,6 +172,10 @@ emit_restore_diagnostics() {
echo "dump_list=$DUMP_LIST_STATUS"
echo "dump_list_exit_code=$DUMP_LIST_EXIT_CODE"
echo "target_confirmation=$TARGET_CONFIRMATION_STATUS"
echo "target_confirmation_exit_code=$TARGET_CONFIRMATION_EXIT_CODE"
echo "target_name_match=$TARGET_NAME_MATCH"
echo "target_confirmation_attempt_count=$TARGET_CONFIRMATION_ATTEMPT_COUNT"
echo "target_confirmation_success_streak=$TARGET_CONFIRMATION_SUCCESS_STREAK"
echo "restore_execution_path=$RESTORE_EXECUTION_PATH"
echo "restore_apply=$RESTORE_APPLY_STATUS"
echo "restore_script_exit_code=$RESTORE_SCRIPT_EXIT_CODE"
@@ -170,7 +220,7 @@ finish() {
}
trap finish EXIT HUP INT TERM
for command_name in docker python3 sha256sum tar find stat df cp openssl visudo systemctl; do
for command_name in docker python3 sha256sum tar find stat df cp openssl visudo systemctl seq; do
require_command "$command_name"
done
@@ -379,15 +429,6 @@ chmod 0600 "$env_file"
printf '%s\n' '[stage=postgres_container_start]' >>"$private_log"
docker run -d --rm --name "$CONTAINER_NAME" --network none --env-file "$env_file" \
"$POSTGRES_IMAGE" >>"$private_log" 2>&1 || fail "postgres_container_start_failed"
for _ in $(seq 1 60); do
if docker exec "$CONTAINER_NAME" pg_isready -U "$SHADOW_DB_USER" -d "$SHADOW_DB_NAME" \
>/dev/null 2>&1; then
break
fi
sleep 1
done
docker exec "$CONTAINER_NAME" pg_isready -U "$SHADOW_DB_USER" -d "$SHADOW_DB_NAME" \
>/dev/null 2>&1 || fail "postgres_not_ready"
PHASE="restore_database"
DUMP_LIST_STATUS="FAIL"
@@ -406,15 +447,8 @@ DUMP_LIST_STATUS="PASS"
TARGET_CONFIRMATION_STATUS="FAIL"
printf '%s\n' '[stage=target_confirmation]' >>"$private_log"
set +e
actual_database="$(PGPASSWORD="$postgres_password" docker exec -e PGPASSWORD \
"$CONTAINER_NAME" psql -U "$SHADOW_DB_USER" -d "$SHADOW_DB_NAME" \
-Atqc 'SELECT current_database()' 2>>"$private_log")"
target_confirmation_exit_code="$?"
set -e
if [[ "$target_confirmation_exit_code" -ne 0 \
|| "$actual_database" != "$SHADOW_DB_NAME" ]]; then
RESTORE_ERROR_CATEGORY="TARGET_CONFIRMATION_FAILED"
if ! confirm_postgres_target; then
RESTORE_ERROR_CATEGORY="$TARGET_CONFIRMATION_FAILURE_REASON"
fail "restore_target_confirmation_failed"
fi
TARGET_CONFIRMATION_STATUS="PASS"