Thursday, April 17, 2025
#!/bin/bash
# Oracle RAC Redo Log Management Library
# Requires: sqlplus, dgmgrl
declare -A RL_CONF
rl_load_config() {
source redolog.conf || return 1
}
rl_init_logging() {
local LOG_FILE="$1"
exec > >(tee -a "$LOG_FILE") 2>&1
}
rl_verify_role() {
local host="$1"
local db="$2"
local expected_role="$3"
local sys_password="$4"
local role=$(sqlplus -S "sys/${sys_password}@//${host}:1521/${db} as sysdba" << EOF
SET HEADING OFF FEEDBACK OFF PAGESIZE 0
SELECT database_role FROM v\$database;
EXIT;
EOF
)
[[ "$role" == "$expected_role" ]] || return 1
}
rl_add_logs() {
local host="$1"
local db="$2"
local log_type="$3"
local sys_password="$4"
sqlplus -S "sys/${sys_password}@//${host}:1521/${db} as sysdba" << EOF >> ${RL_CONF[LOG_FILE]} 2>&1
SET SERVEROUTPUT ON
DECLARE
TYPE thread_t IS TABLE OF NUMBER;
threads thread_t;
BEGIN
SELECT thread# BULK COLLECT INTO threads FROM v\$thread;
FOR i IN 1..threads.COUNT LOOP
FOR j IN 1..${RL_CONF[${log_type}_GROUPS_PER_THREAD]} LOOP
EXECUTE IMMEDIATE 'ALTER DATABASE ADD ${log_type} LOGFILE THREAD ' ||
threads(i) || ' SIZE ${RL_CONF[${log_type}_REDO_SIZE]}';
END LOOP;
END LOOP;
END;
/
EXIT;
EOF
}
rl_drop_old_logs() {
local host="$1"
local db="$2"
local log_type="$3"
local sys_password="$4"
sqlplus -S "sys/${sys_password}@//${host}:1521/${db} as sysdba" << EOF >> ${RL_CONF[LOG_FILE]} 2>&1
SET SERVEROUTPUT ON
DECLARE
CURSOR c_logs IS SELECT group#, thread#, status FROM v\$${log_type}_log;
db_role VARCHAR2(16);
BEGIN
SELECT database_role INTO db_role FROM v\$database;
FOR rec IN c_logs LOOP
BEGIN
IF db_role = 'PRIMARY' AND rec.status IN ('CURRENT','ACTIVE') THEN
FOR i IN 1..${RL_CONF[MAX_RETRIES]} LOOP
BEGIN
EXECUTE IMMEDIATE 'ALTER SYSTEM SWITCH LOGFILE THREAD ' || rec.thread#;
DBMS_LOCK.SLEEP(${RL_CONF[RETRY_INTERVAL]});
EXIT WHEN rec.status = 'INACTIVE';
EXCEPTION WHEN OTHERS THEN NULL;
END;
END LOOP;
END IF;
EXECUTE IMMEDIATE 'ALTER DATABASE DROP ${log_type} LOGFILE GROUP ' || rec.group#;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Error dropping group ' || rec.group# || ': ' || SQLERRM);
END;
END LOOP;
END;
/
EXIT;
EOF
}
###
# Add this function to redolog_lib.sh
rl_count_logs() {
local host="$1"
local db="$2"
local sys_password="$3"
echo "Current Log Group Configuration for ${db}:"
# Count online redo logs
sqlplus -S "sys/${sys_password}@//${host}:1521/${db} as sysdba" << EOF
SET LINES 200 PAGES 1000
COLUMN "Thread#" FORMAT 999
COLUMN "Online Groups" FORMAT 999
COLUMN "Standby Groups" FORMAT 999
PROMPT Online Redo Log Groups per Thread:
SELECT thread# "Thread#", COUNT(*) "Online Groups"
FROM v\$log
GROUP BY thread#
ORDER BY thread#;
PROMPT Standby Redo Log Groups per Thread:
SELECT thread# "Thread#", COUNT(*) "Standby Groups"
FROM v\$standby_log
GROUP BY thread#
ORDER BY thread#;
EXIT;
EOF
}
###
rl_manage_mrp() {
local primary_db="$1"
local standby_db="$2"
local action="$3"
local sys_password="$4"
dgmgrl -silent / <> ${RL_CONF[LOG_FILE]} 2>&1
connect sys/${sys_password}@${primary_db}
edit database ${standby_db} set state='APPLY-${action^^}';
show database ${standby_db}
exit
DGCMDS
return $?
}
# Data Guard Synchronization Check
rl_check_dg_sync() {
local primary_db="$1"
local standby_db="$2"
local sys_password="$3"
echo "Checking Data Guard Synchronization Status..."
dgmgrl -silent / <&1 | tee -a "${RL_CONF[LOG_FILE]}"
connect sys/${sys_password}@${primary_db}
show database ${standby_db}
exit
DGCMDS
# Additional SQL check for comprehensive status
sqlplus -S "sys/${sys_password}@//${primary_db} as sysdba" << EOF | tee -a "${RL_CONF[LOG_FILE]}"
SET LINES 200 PAGES 1000
COLUMN name FORMAT A30
COLUMN value FORMAT A50
COLUMN time_computed FORMAT A30
SELECT
name,
value,
TO_CHAR(time_computed, 'YYYY-MM-DD HH24:MI:SS') AS time_computed
FROM
v\$dataguard_stats
WHERE
name IN ('apply lag', 'transport lag', 'apply finish time', 'transport status');
SELECT
protection_mode,
protection_level,
remote_archive,
dataguard_broker,
guard_status
FROM
v\$database;
EXIT;
EOF
}
####
# Oracle Redo Log Configuration
declare -A RL_CONF=(
[LOG_FILE]="redolog_operation.log" # Main log file
[ONLINE_REDO_SIZE]="512M" # New online redo log size
[STANDBY_REDO_SIZE]="512M" # New standby redo log size
[ONLINE_GROUPS_PER_THREAD]=4 # Online log groups per thread
[STANDBY_GROUPS_PER_THREAD]=5 # Standby log groups per thread
[MAX_RETRIES]=5 # Max log switch attempts
[RETRY_INTERVAL]=5 # Seconds between retries
)
###
#!/bin/bash
# Oracle RAC Redo Log Management Script
# Usage: ./main_redolog.sh
# Load configuration and library
source redolog.conf || exit 1
source redolog_lib.sh || exit 1
# Initialize environment
INPUT_FILE="$1"
[[ -f "$INPUT_FILE" ]] || { echo "Input file missing"; exit 1; }
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")
rl_load_config || exit 1
rl_init_logging "${RL_CONF[LOG_FILE]}"
# Get SYS password
read -sp "Enter SYS password: " SYS_PASSWORD
echo
# Verify database roles
if ! rl_verify_role "$PRIMARY_HOST" "$PRIMARY_DB" "PRIMARY" "$SYS_PASSWORD"; then
echo "Primary database verification failed" | tee -a "${RL_CONF[LOG_FILE]}"
exit 1
fi
if ! rl_verify_role "$STANDBY_HOST" "$STANDBY_DB" "PHYSICAL STANDBY" "$SYS_PASSWORD"; then
echo "Standby database verification failed" | tee -a "${RL_CONF[LOG_FILE]}"
exit 1
fi
# Main operation sequence
{
echo "=== Starting Redo Log Management Operation ==="
date
# Process standby database
echo "Stopping MRP on standby..."
rl_manage_mrp "$PRIMARY_DB" "$STANDBY_DB" "OFF" "$SYS_PASSWORD" || exit 1
echo "Adding new logs to standby..."
rl_add_logs "$STANDBY_HOST" "$STANDBY_DB" "ONLINE" "$SYS_PASSWORD"
rl_add_logs "$STANDBY_HOST" "$STANDBY_DB" "STANDBY" "$SYS_PASSWORD"
echo "Removing old logs from standby..."
rl_drop_old_logs "$STANDBY_HOST" "$STANDBY_DB" "LOG" "$SYS_PASSWORD"
rl_drop_old_logs "$STANDBY_HOST" "$STANDBY_DB" "STANDBY" "$SYS_PASSWORD"
echo "Restarting MRP on standby..."
rl_manage_mrp "$PRIMARY_DB" "$STANDBY_DB" "ON" "$SYS_PASSWORD" || exit 1
# Process primary database
echo "Updating primary database logs..."
rl_add_logs "$PRIMARY_HOST" "$PRIMARY_DB" "ONLINE" "$SYS_PASSWORD"
rl_add_logs "$PRIMARY_HOST" "$PRIMARY_DB" "STANDBY" "$SYS_PASSWORD"
echo "Removing old logs from primary..."
rl_drop_old_logs "$PRIMARY_HOST" "$PRIMARY_DB" "LOG" "$SYS_PASSWORD"
rl_drop_old_logs "$PRIMARY_HOST" "$PRIMARY_DB" "STANDBY" "$SYS_PASSWORD"
# Add after MRP restart in main operations section
echo "Verifying Data Guard Synchronization..."
rl_check_dg_sync "$PRIMARY_DB" "$STANDBY_DB" "$SYS_PASSWORD"
{
echo "=== Final Data Guard Status Check ==="
rl_check_dg_sync "$PRIMARY_DB" "$STANDBY_DB" "$SYS_PASSWORD"
} | tee -a "${RL_CONF[LOG_FILE]}"
echo "=== Operation Completed Successfully ==="
date
} | tee -a "${RL_CONF[LOG_FILE]}"
echo "Detailed log saved to: ${RL_CONF[LOG_FILE]}"
####
# Add this function to redolog_lib.sh
rl_count_logs() {
local host="$1"
local db="$2"
local sys_password="$3"
echo "Current Log Group Configuration for ${db}:"
# Count online redo logs
sqlplus -S "sys/${sys_password}@//${host}:1521/${db} as sysdba" << EOF
SET LINES 200 PAGES 1000
COLUMN "Thread#" FORMAT 999
COLUMN "Online Groups" FORMAT 999
COLUMN "Standby Groups" FORMAT 999
PROMPT Online Redo Log Groups per Thread:
SELECT thread# "Thread#", COUNT(*) "Online Groups"
FROM v\$log
GROUP BY thread#
ORDER BY thread#;
PROMPT Standby Redo Log Groups per Thread:
SELECT thread# "Thread#", COUNT(*) "Standby Groups"
FROM v\$standby_log
GROUP BY thread#
ORDER BY thread#;
EXIT;
EOF
}
###
# Add these lines before the final echo statement
{
echo "=== Final Log Group Verification ==="
echo "Primary Database:"
rl_count_logs "$PRIMARY_HOST" "$PRIMARY_DB" "$SYS_PASSWORD"
echo "Standby Database:"
rl_count_logs "$STANDBY_HOST" "$STANDBY_DB" "$SYS_PASSWORD"
} | tee -a "${RL_CONF[LOG_FILE]}"