Saturday, April 12, 2025
#!/bin/bash
# Usage: ./rac_resize_redologs.sh
# Example: ./rac_resize_redologs.sh rac_config.txt 1G
# Input file format:
# primary_scan primary_db
# standby_scan standby_db
# Check input parameters
if [ $# -ne 2 ]; then
echo "Usage: $0 "
exit 1
fi
INPUT_FILE="$1"
NEW_SIZE="$2"
PRIMARY_HOST=$(awk 'NR==1 {print $1}' "$INPUT_FILE")
PRIMARY_DB=$(awk 'NR==1 {print $2}' "$INPUT_FILE")
STANDBY_HOST=$(awk 'NR==2 {print $1}' "$INPUT_FILE")
STANDBY_DB=$(awk 'NR==2 {print $2}' "$INPUT_FILE")
DG_USER="sys"
LOG_FILE="rac_redolog_resize_$(date +%Y%m%d_%H%M).log"
# Validate input
if [[ -z "$PRIMARY_HOST" || -z "$PRIMARY_DB" ||
-z "$STANDBY_HOST" || -z "$STANDBY_DB" || -z "$NEW_SIZE" ]]; then
echo "Invalid input file format. Expected:"
echo "primary_scan primary_db"
echo "standby_scan standby_db"
exit 1
fi
# Get SYS password
read -s -p "Enter SYS password for ${PRIMARY_HOST}/${PRIMARY_DB}: " SYS_PASSWORD
echo
# Function to log messages
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE"
}
# Verify database role
verify_role() {
local host=$1
local db=$2
local expected_role=$3
role=$(sqlplus -S "${DG_USER}/${SYS_PASSWORD}@//${host}:1521/${db} as sysdba" << EOF
SET HEADING OFF FEEDBACK OFF PAGESIZE 0
SELECT database_role FROM v\$database;
EXIT;
EOF
)
if [[ "$role" != "$expected_role" ]]; then
log "Error: ${host}/${db} is not a ${expected_role} (Role: ${role})"
exit 1
fi
log "${expected_role} database confirmed"
}
# Enhanced online redo log resize with active log handling
resize_online_redologs() {
local host=$1
local db=$2
log "Resizing ONLINE redo logs on ${db} (RAC) to ${NEW_SIZE}..."
sqlplus -S "${DG_USER}/${SYS_PASSWORD}@//${host}:1521/${db} as sysdba" << EOF >> "$LOG_FILE" 2>&1
SET SERVEROUTPUT ON
DECLARE
TYPE thread_t IS TABLE OF NUMBER;
threads thread_t;
groups_per_thread NUMBER;
max_retries NUMBER := 5;
retry_interval NUMBER := 10; -- seconds
-- Procedure to drop old logs with retries
PROCEDURE drop_old_logs IS
CURSOR old_groups_cur IS
SELECT group#, thread#, status
FROM v\$log
WHERE bytes < (SELECT MAX(bytes) FROM v\$log);
BEGIN
FOR oldgrp IN old_groups_cur LOOP
DECLARE
retries NUMBER := 0;
current_status VARCHAR2(10);
BEGIN
current_status := oldgrp.status;
WHILE current_status != 'INACTIVE' AND retries < max_retries LOOP
DBMS_OUTPUT.PUT_LINE('Thread ' || oldgrp.thread# ||
' Group ' || oldgrp.group# || ' still ' || current_status ||
' - forcing log switch (attempt ' || (retries+1) || ')');
-- Force log switch for specific thread
BEGIN
EXECUTE IMMEDIATE 'ALTER SYSTEM SWITCH LOGFILE THREAD ' || oldgrp.thread#;
EXCEPTION
WHEN OTHERS THEN NULL;
END;
-- Wait for status change
DBMS_LOCK.SLEEP(retry_interval);
-- Check current status
BEGIN
SELECT status INTO current_status
FROM v\$log
WHERE group# = oldgrp.group#;
EXCEPTION
WHEN OTHERS THEN
current_status := 'UNKNOWN';
END;
retries := retries + 1;
END LOOP;
IF current_status = 'INACTIVE' THEN
BEGIN
EXECUTE IMMEDIATE 'ALTER DATABASE DROP LOGFILE GROUP ' || oldgrp.group#;
DBMS_OUTPUT.PUT_LINE('Dropped group ' || oldgrp.group#);
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Error dropping group ' || oldgrp.group# || ': ' || SQLERRM);
END;
ELSE
DBMS_OUTPUT.PUT_LINE('Failed to drop group ' || oldgrp.group# ||
' - still ' || current_status || ' after ' || max_retries || ' attempts');
END IF;
END;
END LOOP;
END drop_old_logs;
BEGIN
-- Get active threads
SELECT thread# BULK COLLECT INTO threads FROM v\$thread;
-- Get current online log groups per thread
SELECT COUNT(*)/COUNT(DISTINCT thread#) INTO groups_per_thread FROM v\$log;
-- Add new log groups for each thread
FOR i IN 1..threads.COUNT LOOP
FOR j IN 1..groups_per_thread LOOP
EXECUTE IMMEDIATE 'ALTER DATABASE ADD LOGFILE THREAD ' || threads(i) ||
' SIZE ${NEW_SIZE}';
END LOOP;
END LOOP;
-- Force log switches across all threads
FOR thread_rec IN (SELECT thread# FROM v\$thread) LOOP
FOR k IN 1..(groups_per_thread * 2) LOOP
EXECUTE IMMEDIATE 'ALTER SYSTEM SWITCH LOGFILE THREAD ' || thread_rec.thread#;
END LOOP;
END LOOP;
-- Drop old logs with retry mechanism
drop_old_logs;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Error: ' || SQLERRM);
RAISE;
END;
/
EXIT;
EOF
if grep -q "ORA-" "$LOG_FILE"; then
log "Error resizing online redo logs on ${db}"
return 1
fi
log "Online redo logs resized successfully on ${db} (RAC)"
}
# Other functions remain unchanged (resize_standby_redologs, manage_mrp, etc.)
# Main execution
{
log "Starting RAC Redo Log Resize Operation"
verify_role "$PRIMARY_HOST" "$PRIMARY_DB" "PRIMARY"
verify_role "$STANDBY_HOST" "$STANDBY_DB" "PHYSICAL STANDBY"
# Process standby
manage_mrp "OFF"
manage_standby_file_management "$STANDBY_HOST" "$STANDBY_DB" "MANUAL"
resize_online_redologs "$STANDBY_HOST" "$STANDBY_DB"
resize_standby_redologs "$STANDBY_HOST" "$STANDBY_DB"
manage_standby_file_management "$STANDBY_HOST" "$STANDBY_DB" "AUTO"
manage_mrp "ON"
# Process primary
resize_online_redologs "$PRIMARY_HOST" "$PRIMARY_DB"
resize_standby_redologs "$PRIMARY_HOST" "$PRIMARY_DB"
# Verification steps
log "Operation completed successfully"
} | tee -a "$LOG_FILE"
echo "Detailed log saved to: ${LOG_FILE}"