An RMAN active duplicate that fails outright is easy. It throws an error, you read it, you fix it. The one that quietly does everything right and then simply stops is the one that costs you an afternoon, because nothing is broken enough to tell you where to look.
Everything worked, right up until nothing did
The duplicate progressed through the same sequence cleanly every time. The password file copied. The spfile restored. The parameter overrides applied. The auxiliary shut down and restarted. The control file restored. The standby mounted. Every step reported success, in the same order, on every attempt.
Then it stalled completely.
The oradata directory on the auxiliary stayed empty. No datafile copy activity ever started. All the auxiliary channels sat in INACTIVE state, waiting on SQL*Net message from client for several minutes, until RMAN finally gave up and errored out. The most telling part was the consistency. The behavior was identical across every restart, no matter what else I changed between attempts. That is the signature of state, not configuration. When a run fails differently each time, you are chasing a moving target. When it fails in exactly the same place every time regardless of what you touch, something is being remembered between runs.
The thing that gets remembered lives in dbs
The auxiliary had been through several earlier duplicate attempts that were killed mid-run. Each of those killed runs left residue behind in $ORACLE_HOME/dbs, and that residue was quietly poisoning every attempt that followed. Three files were doing the damage.
The instance lock file, lk followed by the database unique name, is Oracle’s way of asserting that an instance with that SID is already alive. When RMAN performs its internal shutdown and restart of the auxiliary during the duplicate, a leftover lock file convinces Oracle that another instance is already running under that SID, which produces exactly the kind of unpredictable behavior that shows up as a hang rather than a clean error.
The RMAN duplicate state tracking file, named with the _rm_dup prefix and the database unique name, is the real culprit for the stall right after mount. When a new duplicate starts and finds this file from a previous run, RMAN does not start fresh. It tries to reconcile against the recorded state of the run that was killed, and that reconciliation is where everything went dead immediately after the mount completed. The channels were not idle because they had nothing to do. They were idle because RMAN was waiting on a state that no longer existed.
The health check file, hc followed by the database unique name, carries its own stale state from the prior run and rounds out the set. On its own it is minor, but it belongs to the same family of leftover state and there is no reason to leave it in place.
The fix is to stop starting from a dirty room
The correction is to clear that residue before every fresh attempt, not after a failure. Remove the lock file, the duplicate state file, and the health check file, and while you are at it clear the leftover spfile and the broker configuration files so the auxiliary truly starts from nothing.
rm -f $ORACLE_HOME/dbs/lk<DB_UNIQUE_NAME>
rm -f $ORACLE_HOME/dbs/_rm_dup_<DB_UNIQUE_NAME>_*.dat
rm -f $ORACLE_HOME/dbs/hc_<DB_UNIQUE_NAME>.dat
rm -f $ORACLE_HOME/dbs/spfile<DB_UNIQUE_NAME>.ora
rm -f $ORACLE_HOME/dbs/dr1<DB_UNIQUE_NAME>.dat
rm -f $ORACLE_HOME/dbs/dr2<DB_UNIQUE_NAME>.dat
Once those were gone and the auxiliary was restarted to nomount, the duplicate ran clean and the datafile copy phase began immediately. Same command, same environment, same everything, except the auxiliary was no longer being asked to reconcile against a ghost.
What I take away from this
Every killed RMAN duplicate leaves a footprint, and the next attempt inherits it. The runbooks tell you how to start a duplicate. They rarely tell you to clean up after a killed one, so the residue accumulates silently until it changes the behavior of the next run in a way that looks like a brand new problem. It is not a new problem. It is the last problem, still sitting in the dbs directory.
Now I treat a killed duplicate as unfinished business, not a closed door. Before I relaunch, I clear the auxiliary’s dbs directory back to a known clean state, because a fresh attempt on top of stale state is not actually a fresh attempt. The hardest failures to diagnose are almost never the ones where something is broken. They are the ones where something is being remembered, and the fix is to make the environment forget.
If you have chased an RMAN duplicate that stalled without a real error, I would bet the answer was hiding in a directory you assumed was clean. Where did yours turn out to be?
Comments
Comments are powered by GitHub Discussions. Sign in with a GitHub account to join the conversation.