Monday, December 8, 2014

Insert slows down with cell single block physical read and read by other session events.

Recently I got chance to work on performance tuning issue with one of the application where performance suddenly degraded on database running on Exadata machine. On investigation I found that there are lot of concurrent sessions from application trying to insert into same table but spending lot of time on 'read by other session' and 'cell single block physical read' events. The strange thing was that the insert was simple statement with no underlying select query or triggers on the table being inserted. So what would have caused insert to perform poor suddenly which was earlier running in fractions of seconds.

I did some comparison for good and bad performance AWR snapshots. One thing was clear that during the day when we started having performance issue, application was trying to do lot many concurrent inserts on same table. But should oracle really limit the concurrency of application? Yes, under certain scenarios.

1.) If you have primary key on the table and application trying to insert same record, oracle would acquire row lock contention for period of time until blocking session commits and the blocked session would immediately get 'Primary or Unique Key Violation' message. This was not the case in the issue we had as there were no symptoms of such row lock contentions.

2.) Index leaf block becomes hot due to right handed growing index. This normally happens where you have index defined on sequences and such contention can be reduced by use of hash partitioning the index as the whole index tree would be divided into separate multiple trees due to hash algorithm and would minimize such contention. Even that was not in our case as there were no such symptoms.

3.) Other thing in Exadata environment, you would like to check whether storage cell disk is performing fine. We examined same and there was no issue with the Exadata Storage.

4.) GC Buffer busy waits in RAC environment could cause severe performance degradation if multiple sessions from different nodes request for a block. In RAC if an instance doesn't find the buffer in its local SGA, then block needs to be transferred from holding instance SGA through cache fusion. Now if the instance holding the block is busy, or if the block is already in use, then requesting instance has to wait and GC buffer busy will show up.  I did not find any such high waits during the time when we had performance issue.

Now we need to drill down and go into some details regarding how oracle works when there is request for inserting record in a data block. Basically each time when a session tries to insert a record, it has to find a block having enough free space where that record can fit. Oracle maintains list of free blocks in bitmap format in ASSM (freelists in MSSM) in the datafile header blocks. Further to speed up this searching process of free blocks, oracle also maintains this information in space search cache.

But what if block marked as free doesn't contain enough free space to hold the record being inserted? User process in this case would spend time in finding the right block having enough space and all other sessions which are also trying to insert record for the same table would also result in scanning these many blocks. And what if the block is not already available in buffer cache, they will have to perform unnecessary physical reads. And what if block is already being read into buffer cache by some session, other sessions will have to wait until the block is read into buffer cache and this shows up with read by other session wait. In our case we finally found that issue happening sporadically matches with 'INSERT slow on ASSM (Doc ID 1263496.1)'.

On performing below steps I was able to relate that the issue occurred because of bulk load and subsequent inserts from multiple sessions slowed down as they had to spend large time in finding the right free block having enough space.

  1.)     Find all the database sessions and instances for which the insert is slow. We need to enable the trace for 2-3 such sessions for collecting more diagnostic information and rule out any possibility of oracle bug. All below commands need to be executed using sys user. For finding such sessions below command can be used or same can be retrieved using OEM:
SELECT sess.inst_id, SID, SERIAL#, sess.sql_id,last_call_ET,to_char(logon_time,'YYYY-MM-DD HH24:MI:SS') logon_since,
         event,
         status,
         blocking_session,
         BLOCKING_INSTANCE,
         sess.program,
         sess.module,
         sess.machine 
    FROM gv$session sess, gv$sql sq
   WHERE status = 'ACTIVE' AND USERNAME = <owner> and 
         sess.sql_id = sq.sql_id and
         sess.inst_id = sq.inst_id and
         upper(sql_text) like '%object_name%' and
         sess.last_call_et > 1;

2.)    Enable trace for one of the session undergoing poor performance (having lot of read by other sessions and cell single physical read block events)

select spid from gv$process where addr in (select paddr from gv$session where sid = <session_id> and inst_id = <instance_number>);
Login to the node as sysdba where above session is currently running and enable ASSM trace using below command:
ORADEBUG SETOSPID <spid value from above query>;
ORADEBUG UNLIMIT;
ORADEBUG EVENT 10320 TRACE NAME CONTEXT FOREVER, LEVEL 3;
             ORADEBUG EVENT 10612 TRACE NAME CONTEXT FOREVER, LEVEL 1;
             ORADEBUG tracefile_name;
             Keep it active for around 2-3 minutes and then disable the trace using below command:
             ORADEBUG EVENT 10320 TRACE NAME CONTEXT OFF;
             ORADEBUG EVENT 10612 TRACE NAME CONTEXT OFF;
             Exit;
             Download the tracefile name as appeared using oradebug tracefile_name.

3.)    Also collect error stack for the session for which insert is running slow. Login to the instance as sysdba where insert is slow and run below commands:

sqlplus '/ as sysdba'
oradebug setospid <spid value>
oradebug unlimit
oradebug dump errorstack 3
!sleep 30
oradebug dump errorstack 1
!sleep 30
oradebug dump errorstack 1
!sleep 30
oradebug dump errorstack 1
!sleep 30
oradebug dump errorstack 1
!sleep 30
oradebug dump errorstack 1
!sleep 30
oradebug dump errorstack 1
!sleep 30
oradebug dump errorstack 3
!sleep 30
oradebug dump errorstack 1
!sleep 30
oradebug dump errorstack 1
!sleep 30
oradebug dump errorstack 1
!sleep 30
oradebug dump errorstack 1
!sleep 30
oradebug dump errorstack 1
!sleep 30
oradebug dump errorstack 1
!sleep 30
oradebug dump errorstack 3 
             oradebug tracefile_name;
             quit;

4.)    Run below as sysdba from any instance and capture the output:
set serveroutput on size unlimited
declare
total_bl number;
total_by number;
unused_bl number;
unused_by number;
last_used_extent_file_id number;
last_used_extent_block_id number;
last_used_block number;
unformatted_blocks number;
unformatted_bytes number;
fs1_bl number;
fs1_by number;
fs2_bl number;
fs2_by number;
fs3_bl number;
fs3_by number;
fs4_bl number;
fs4_by number;
full_bl number;
full_by number;
i number;
BEGIN
dbms_space.unused_space('PDA01PRD','DM_SYSOBJECT_S', 'TABLE',total_bl , total_by , unused_bl , unused_by , last_used_extent_file_id ,last_used_extent_block_id , last_used_block );
dbms_space.space_usage('PDA01PRD','DM_SYSOBJECT_S', 'TABLE',unformatted_blocks , unformatted_bytes , fs1_bl , fs1_by , fs2_bl , fs2_by , fs3_bl , fs3_by, fs4_bl , fs4_by , full_bl , full_by );
dbms_output.put_line('| total_bl| total_by| unused_bl| unused_by| last_used_extent_file_id|last_used_extent_block_id| last_used_block');
dbms_output.put_line('| '||lpad(total_bl,8)||'|'||
lpad(total_by,9)||'|'||
lpad(unused_bl,10)||'|'||
lpad(unused_by,10)||'|'||
lpad(last_used_extent_file_id,25)||'|'||
lpad(last_used_extent_block_id,26)||'|'||
lpad(last_used_block,16));
dbms_output.put_line(' .');
dbms_output.put_line('| unformatted_blocks| unformatted_bytes| fs1_bl| fs1_by| fs2_bl|fs2_by| fs3_bl| fs3_by| fs4_bl| fs4_by| full_bl| full_by');
dbms_output.put_line('| '||lpad(unformatted_blocks,18)||'|'||
lpad(unformatted_bytes,18)||'|'||
lpad(fs1_bl,7)||'|'||
lpad(fs1_by,7)||'|'||
lpad(fs2_bl,7)||'|'||
lpad(fs2_by,7)||'|'||
lpad(fs3_bl,7)||'|'||
lpad(fs3_by,7)||'|'||
lpad(fs4_bl,7)||'|'||
lpad(fs4_by,7)||'|'||
lpad(full_bl,8)||'|'||
lpad(full_by,8));
end;
/
prompt FS1 means 025%
free space within a block
prompt FS2 means 2550%
free space within a block
prompt FS3 means 5075%
free space within a block
prompt FS4 means 75100%
free space within a block

55.)    If value of FS1 is higher than (FS2, FS3 and FS4), then the reason of slowness could be due to large number of blocks having not enough space to accommodate inserts. In this case, explicitly marking the block to be full and increasing PCTFREE should resolve the issue. Increasing PCTFREE results in blocks to be marked as FULL at higher rate but this could result in segment to grow faster and wastage of space. So it can be increased to optimum value after considering performance and space usage.

 EXECUTE DBMS_REPAIR.SEGMENT_FIX_STATUS('PDA01PRD','DM_SYSOBJECT_S');

Same can also be confirmed after examining ASSM trace file where blocks from FS1 category moving to FULL can be clearly seen :
[ktspsrch] adddba dba:0x0100636b curl1:0x01006280 curl2:0x01002601
ktspfsrch: Returns: BlockDBA:0x0100637b
kdt_bseg_srch_cbk: examine dba=4.0x0100637b
kdt_bseg_srch_cbk: failed dba=4.0x0100637b avs=837 afs=0 tosp=837 full=1
ktspfupdst: state change tsn 4, objd 96400, objn 71401, blksz 8192
ktspfupdst: oldstate 2, newstate 1, delta bytes 1065
ktspfupdst: fdba: 0x01006280 offset: 251 nblks: 1 newstate: 1
kdt_bseg_srch_cbk:      mark full >the blocks is marked as full
ktspfsrch:Cbk didnot like 0x010063 

6.)  If above is not the case then it could be an issue with space search cache and same can be determined by examining the stack trace taken in step 4.

Short stack dump:
ksedsts()+461<-ksdxfstk()+32<-ksdxcb()+1876<-sspuser()+112<-__sighandler()<-__poll()+47<-ssskgxp_poll()+40<-sskgxp_select()+263<-skgxpiwait()+3901<-skgxpwaiti()+1832<-skgxpwait()+162<-ossnet_wait_all()+241<-ossnet_wait()+146<-oss_wait()+991<-kfk_wait_on_oss_io()+406<-kfk_submit_oss_io_wtevt()+285<-kfk_submit_oss_io()+415<-kfk_submit_io()+105<-kfk_io1()+1068<-kfkRequest()+14<-kfk_transitIO()+1696<-kfioSubmitIO()+4777<-kfioRequestPriv()+199<-kfioRequest()+706<-ksfd_kfioRequest()+649<-ksfd_osmio()+1114<-ksfd_io()+2761<-ksfdread()+581<-kcfrbd1()+609<-kcbzib()+1888<-kcbgcur()+8274<-ktbgcur()+123<-ktspfsrch()+2569<-ktspscan_bmb()+898<-ktspgsp_main()+1817<-kdtgsp()+1576<-kdtgsph()+107<-kdtgrs()+1013<-kdtSimpleInsRow()+320<-qerltcSimpleSingleInsRowCBK()+46<-qerltcSingleRowLoad()+279<-qerltcFetch()+379<-insexe()+682<-opiexe()+5622<-kpoal8()+2076<-opiodr()+916<-ttcpip()+2210<-opitsk()+1717<-opiino()+966<-opiodr()+916<-opidrv()+570<-sou2o()+103<-opimai_real()+133<-ssthrdmain()+252<-main()+201<-__libc_start_main()+244<-_start()+36

If you see above waits for space search cache, then try disabling space search cache and see if performance improves. This action should be done only after consultation with oracle support.
alter system set event="10262 trace name context forever, level 1";

Hope you find this article useful. Pls let me know for any questions or suggestions.

References: https://support.oracle.com

Thursday, October 30, 2014

Migrate and upgrade oracle database using RMAN backupset

You can use various methods to perform database move and upgrade depending on the size of database and other constraints. If there are downtime constraints then you may probably like to consider Dataguard Rolling upgrade methods where physical standby can be created in advance and database can be upgraded using transient logical standby method. I have explained same here in my blog:
http://azharkoracle.blogspot.com/2014/08/dataguard-rolling-upgrade-using.html

For very large databases, you may also consider using Business Continuity Volumes (snapshot backups) or split mirror techniques for one time instantiation and then configure golden gate or oracle streams for replication. Refer my blog for configuring oracle streams replication:
http://azharkoracle.blogspot.com/2014/09/configuring-oracle-streams-replication.html

One other method for move and upgrade which DBA mostly considers for medium sized databases is cold backup and then upgrade the database on destination server after starting the same in upgrade mode. This time I have tried using RMAN backupsets rather than relying on cold backup for performing move and upgrade which worked absolutely fine. I moved RMAN backups sets of 11.2.0.1 for database TSTU and cloned it to TST_CFG database in 11.2.0.3 version.

Here are the high level steps for upgrade and cloning the database from one oracle version to another –
1.)    Move the backup pieces of source database to desired location (/oracle/data/ora01/TST_CFG/exports) on destination server.
2.)    Create instance with same name as that of source name (TSTU in our case) and start it in nomount mode.
3.)    Restore the control file from the backup piece. Provide exact location of backup piece where control file is lying for restoring same.
4.)    Mount the database.
5.)    Rename the redo files using alter database rename file commands.
E.g. alter database rename file '/oracle/data/ora01/TSTU/ora_log_01_01.rdo' to '/oracle/data/redo01/TST_CFG/ora_log_01_01.rdo';
6.)    Catalog the backup pieces using catalog start with <backup_location>.
7.)    Restore and recover it after setting the filename to new location –
run {
  allocate channel t1  type  disk;
  allocate channel t2  type  disk;
set newname for tempfile 1 to '/oracle/data/ora02/TST_CFG/temp_t01_01.dbf';
set newname for tempfile 2 to '/oracle/data/ora02/TST_CFG/temporary_data_t01_01.dbf';
set newname for datafile 1 to '/oracle/data/ora02/TST_CFG/system_01.dbf';
set newname for datafile 2 to '/oracle/data/ora02/TST_CFG/sysaux_01.dbf';
set newname for datafile 3 to '/oracle/data/ora02/TST_CFG/undo_t01_01.dbf';
set newname for datafile 4 to '/oracle/data/ora02/TST_CFG/tools_t01_01.dbf';
set newname for datafile 5 to '/oracle/data/ora02/TST_CFG/users_t01_01.dbf';
set newname for datafile 6 to '/oracle/data/ora02/TST_CFG/USER_DATA_01.dbf';
set newname for datafile 7 to '/oracle/data/ora02/TST_CFG/USER_DATA_02.dbf';
set newname for datafile 8 to '/oracle/data/ora02/TST_CFG/INDEX_DATA_01.dbf';
set newname for datafile 9 to '/oracle/data/ora02/TST_CFG/USER_DATA_03.dbf';
set newname for datafile 10 to '/oracle/data/ora02/TST_CFG/lob_data_t01_01.dbf';
restore database;
switch datafile all;
recover database;
release channel t1;
release channel t2;
}
8.)Open the database using reset log upgrade mode. Rename and Recreate tempfiles if not accessible.
9.) Run catupgrd.sql
10.) Run utlu112s.sql
11.) Run catuppst.sql
12.) Shut down the database and change database name using DBNID utility. Follow post steps and voila, you have just finished cloning from RMAN baseline backup of 11.2.0.3 database (TSTU) to 11.2.0.4  database (TST_CFG).

Thursday, September 11, 2014

Configuring Oracle streams replication

 Oracle streams can be considered as an alternative to GoldenGate for configuring data replication. This can save you considerable amount of downtime for huge database migrations.You can create a new database in advance and configure replication using oracle streams even for cross platform migrations. Once both databases are in sync, downtime can be coordinated with business for re-pointing the applications. Here are the high level steps for configuring oracle streams:

1.) Setup STREAMS user in both source and target database with required permissions. In our case source database is STRM_SRC and target database is STRM_TRG.

create table STREAMTST.STREAM_TST as select 1 dummy from dual;

create tablespace STREAMS_TBS
datafile '/oracle/data/ora04/STRM_TRG/streams_tbs_01.dbf' size 500M
LOGGING
ONLINE
EXTENT MANAGEMENT LOCAL AUTOALLOCATE
BLOCKSIZE 8K
SEGMENT SPACE MANAGEMENT AUTO
FLASHBACK ON;

CREATE USER STRMADMIN IDENTIFIED BY Summerend2014
DEFAULT TABLESPACE STREAMS_TBS TEMPORARY TABLESPACE TEMP_T01;

alter user STRMADMIN quota unlimited on STREAMS_TBS;

alter user STRMADMIN default role all;

grant appl_connect to STRMADMIN;

grant dba to STRMADMIN;

EXEC DBMS_STREAMS_AUTH.GRANT_ADMIN_PRIVILEGE('STRMADMIN');

CREATE DIRECTORY STRMDIR AS '/exports/pmxexpdp';

BEGIN
DBMS_STREAMS_AUTH.GRANT_ADMIN_PRIVILEGE(
GRANTEE => 'STRMADMIN',
FILE_NAME =>'streams_admin_privs.sql',
DIRECTORY_NAME => 'STRMDIR');
END;
/

2.) Setup database link and queue table in source database STRM_SRC after login as STRADMIN user -

CREATE DATABASE LINK STRM_TRG CONNECT TO STRMADMIN IDENTIFIED BY Summerend2014  USING 'STRM_TRG';

BEGIN
DBMS_STREAMS_ADM.SET_UP_QUEUE(QUEUE_NAME=>'STREAMS_QUEUE',
QUEUE_TABLE=>'STREAMS_QUEUE_TABLE',
QUEUE_USER=>'STRMADMIN',
STORAGE_CLAUSE=>'TABLESPACE STREAMS_TBS');
END;
/


3.) Setup rules to capture transaction from redo log for particular schema -

BEGIN
DBMS_STREAMS_ADM.ADD_SCHEMA_RULES (
SCHEMA_NAME =>'STREAMTST',
STREAMS_TYPE =>'CAPTURE',
STREAMS_NAME=>'STRMADMIN_CAPTURE',
QUEUE_NAME=>'STRMADMIN.STREAMS_QUEUE',
INCLUDE_DML=>TRUE,
INCLUDE_DDL=>TRUE);
END;
/


4.) Add propagation rules using the Oracle-provided PL/SQL procedure
DBMS_STREAMS_ADM.ADD_SCHEMA_PROPAGATION_RULES:

BEGIN
DBMS_STREAMS_ADM.ADD_SCHEMA_PROPAGATION_RULES (
SCHEMA_NAME=>'STREAMTST',
STREAMS_NAME=>'STRMADMIN_PROPAGATE',
SOURCE_QUEUE_NAME=>'STRMADMIN.STREAMS_QUEUE',
DESTINATION_QUEUE_NAME=> 'STRMADMIN.STREAMS_QUEUE@STRM_TRG',
SOURCE_DATABASE=>'STRM_SRC',
INCLUDE_DML=>TRUE,
INCLUDE_DDL=>TRUE,
QUEUE_TO_QUEUE=>TRUE);
END;



5.) Login to target database STRM_TRG as the Streams administrator, create the Streams capture queue:

BEGIN
DBMS_STREAMS_ADM.SET_UP_QUEUE(QUEUE_NAME=>'STREAMS_QUEUE',
QUEUE_TABLE=>'STREAMS_QUEUE_TABLE',
QUEUE_USER=>'STRMADMIN',
STORAGE_CLAUSE=>'TABLESPACE STREAMS_TBS');
END;
/

6.) Connect as the Streams administrator, and add the schema apply
rules:

BEGIN
DBMS_STREAMS_ADM.ADD_SCHEMA_RULES (
SCHEMA_NAME =>'STREAMTST',
STREAMS_TYPE =>'APPLY',
STREAMS_NAME=>'STRMADMIN_APPLY',
QUEUE_NAME=>'STRMADMIN.STREAMS_QUEUE',
INCLUDE_DML=>TRUE,
INCLUDE_DDL=>TRUE);
END;
/

7.) Determine and obtain the current SCN from the source database.
To ensure that no data is lost, this step is to be performed before
the target copy is made. This SCN will be used as a starting point
for the capture process to begin the streaming activity.

set numwidth 18
SELECT TABLE_OWNER, TABLE_NAME, SCN from DBA_CAPTURE_PREPARED_TABLES;

8. After the data has been propagated, it needs to be applied to the
appropriate schema. Identify the user in the target database that will
apply the changes
EXEC DBMS_APPLY_ADM.ALTER_APPLY(apply_name=>'STRMADMIN_APPLY',apply_user=>'STRMADMIN');

9.) Set the SCN on the target database to allow the apply process to start the schema and table level instantiation at this SCN.
BEGIN
DBMS_APPLY_ADM.SET_SCHEMA_INSTANTIATION_SCN (
source_schema_name=> 'STREAMTST',
source_database_name=>'STRM_SRC',
instantiation_scn=> 12115252424484);
END;
/

BEGIN DBMS_APPLY_ADM.SET_TABLE_INSTANTIATION_SCN(
source_object_name=> 'STREAMTST.STREAM_TST',
source_database_name=>'STRM_SRC',
instantiation_SCN=>12115252424484);
END;

10.) Start the apply process, using the following PL/SQL package:
 EXEC DBMS_APPLY_ADM.START_APPLY( apply_name => 'STRMADMIN_APPLY');

11.) Enable propagation schedule at the source database. Connect as the Streams administrator and execute the following PL/SQL
procedure:
 EXEC DBMS_PROPAGATION_ADM.START_PROPAGATION('STRMADMIN_PROPAGATE') ;

12.) The following procedure starts the capture process, mines redologs, and enqueues the mined redo information into the associated
queue:

 EXEC DBMS_CAPTURE_ADM.START_CAPTURE(capture_name=>'STRMADMIN_CAPTURE');


Now test if the changes are being replicated or not. First perform transactions in source database:

STRM_SRC> truncate table STREAMTST.stream_tst;

Table truncated.

STRM_SRC>
STRM_SRC> create table STREAMTST.stream_tst01 as select 1 dummy from dual;

Table created.


STRM_SRC> insert into STREAMTST.stream_tst (select * from STREAMTST.stream_tst);

0 rows created.

STRM_SRC> insert into STREAMTST.stream_tst01 (select * from STREAMTST.stream_tst01);

1 row created.

STRM_SRC> /

2 rows created.

STRM_SRC> /

4 rows created.

STRM_SRC> /

8 rows created.

STRM_SRC> /

16 rows created.

STRM_SRC> /

32 rows created.

STRM_SRC> commit;

Commit complete.

STRM_SRC> insert into STREAMTST.stream_tst01 (select * from STREAMTST.stream_tst01);

64 rows created.

STRM_SRC> /

128 rows created.

STRM_SRC> /

256 rows created.

STRM_SRC> /

512 rows created.

STRM_SRC> commit;

Commit complete.

STRM_SRC> select count(1) from STREAMTST.stream_tst01;

  COUNT(1)
----------
      1024

STRM_SRC> select count(1) from STREAMTST.stream_tst;

  COUNT(1)
----------
         0


Now check in target database :
SQL> select count(1) from STREAMTST.STREAM_TST;

  COUNT(1)
----------
      8192

SQL> /

  COUNT(1)
----------
         0

SQL> /

  COUNT(1)
----------
         0

SQL> select count(1) from STREAMTST.STREAM_TST01;

  COUNT(1)
----------
         1

SQL>
SQL> /

  COUNT(1)
----------
        64

SQL> /

  COUNT(1)
----------
      1024

SQL>

Here are some useful monitoring scripts which can be used for day today streams troubleshooting:
If you want to check on the source database which rules are implemented for Oracle Streams replication you can check the following dictionary data views:

select * from dba_streams_rules
select * from  dba_streams_schema_rules
select * from dba_streams_table_rules

--Check the state of the apply process and make sure it is enabled:
SELECT apply_name, apply_captured, status, rule_set_name
FROM DBA_APPLY;


--Verify that the apply process has not dequeued any events:
SELECT  APPLY_NAME, STATE,  TOTAL_MESSAGES_DEQUEUED
FROM V$STREAMS_APPLY_READER;


--Verify the destination queue is correct
SELECT PROPAGATION_NAME, DESTINATION_QUEUE_OWNER ||'.'||
DESTINATION_QUEUE_NAME ||'@'||  DESTINATION_DBLINK "Destination Queue" ,ERROR_MESSAGE,ERROR_DATE
FROM DBA_PROPAGATION AAA;


--Check the propagation schedule time and check for failures
SELECT TO_CHAR(s.START_DATE, 'HH24:MI:SS MM/DD/YY') START_DATE,
s.PROPAGATION_WINDOW, s.NEXT_TIME,  s.LATENCY,
DECODE(s.SCHEDULE_DISABLED,'Y','Disabled','N','Enabled') SCHEDULE_DISABLED,
s.PROCESS_NAME, s.FAILURES
FROM DBA_QUEUE_SCHEDULES s, DBA_PROPAGATION p
WHERE p.DESTINATION_DBLINK = s.DESTINATION
AND s.SCHEMA = p.SOURCE_QUEUE_OWNER
AND s.QNAME = p.SOURCE_QUEUE_NAME;


--Query the total number of events propagated:
SELECT SCHEMA, QNAME, DESTINATION, TOTAL_NUMBER
FROM DBA_QUEUE_SCHEDULES;

 --Check the proper rules are created for global, schema and table and at each level (capture, propagation, apply):
SELECT rule_name,rule_set_rule_enabled
FROM dba_rule_set_rules
WHERE rule_set_name = ''
and rule_name = '';

SELECT rule_name,rule_condition
FROM dba_rules
WHERE rule_name = '';

-- Verify that the apply process has not dequeued any events:
SELECT  APPLY_NAME, STATE,  TOTAL_MESSAGES_DEQUEUED
FROM V$STREAMS_APPLY_READER;

--Query the total number of events propagated:
SELECT SCHEMA, QNAME, DESTINATION, TOTAL_NUMBER
FROM DBA_QUEUE_SCHEDULES;

--Check the apply process latency:
SELECT (hwm_time-hwm_message_create_time)* 86400 "Latency in Seconds",
hwm_message_create_time "Event Creation",
hwm_time "Apply Time",
hwm_message_number "Applied Message #"
FROM v$STREAMS_APPLY_COORDINATOR;

--Check the Error Queue:
SELECT apply_name, source_database,local_transaction_id, error_message
FROM DBA_APPLY_ERROR;

--Check for any defined conflict resolution:
COLUMN object_owner      FORMAT a15
COLUMN object_name       FORMAT a15
COLUMN method_name       FORMAT a20
COLUMN column_name       FORMAT a15
COLUMN resolution_column FORMAT a60
SELECT object_owner,object_name,method_name,resolution_column,column_name
FROM dba_apply_conflict_columns;

--Check object instantiated SCN:
SELECT * FROM dba_apply_instantiated_objects;

EXEC DBMS_APPLY_ADM.SET_KEY_COLUMNS('<owner>.<table_name>','<col1>,<col2>,....');
EXEC DBMS_APPLY_ADM.EXECUTE_ALL_ERRORS;
EXEC DBMS_APPLY_ADM.START_APPLY('<apply name>');


--- stream capture status
select
capture_name,
capture_message_number,
enqueue_message_number,
state,
total_prefilter_discarded,
total_prefilter_kept,
total_messages_captured,
total_messages_enqueued
from gv$streams_capture;

set pagesize 1000
col first_scn format 999999999999999999
col next_scn format 999999999999999999
alter session set nls_date_format='dd-mon-yyyy hh24:mi:ss';
select source_database,thread#,sequence#,name,modified_time,first_scn,next_scn,dictionary_begin,dictionary_end
 from dba_registered_archived_log where &scn between first_scn and next_scn;

--stream apply status - corelated with the above query
select apply_name,state,
total_messages_dequeued,
total_messages_spilled,
dequeued_message_number
from gv$streams_apply_reader@orclstream;

------- *************** streams monitor script ***********************
COLUMN CAPTURE_NAME      FORMAT a15
COLUMN QUEUE_NAME       FORMAT a15
COLUMN PROPAGATION_NAME       FORMAT a15
COLUMN STATUS       FORMAT a15
COLUMN apply_name       FORMAT a15
COLUMN error_message FORMAT a35

prompt Redo Log Scanning Latency for Each Capture Process
SELECT CAPTURE_NAME,
((SYSDATE - CAPTURE_MESSAGE_CREATE_TIME)*86400) LATENCY_SECONDS,
((SYSDATE - CAPTURE_TIME)*86400) LAST_STATUS,
TO_CHAR(CAPTURE_TIME, 'HH24:MI:SS MM/DD/YY') CAPTURE_TIME,
TO_CHAR(CAPTURE_MESSAGE_CREATE_TIME, 'HH24:MI:SS MM/DD/YY') CREATE_TIME
FROM V$STREAMS_CAPTURE;

prompt "Capture process that are disabled"
SELECT CAPTURE_NAME, QUEUE_NAME, RULE_SET_NAME, NEGATIVE_RULE_SET_NAME, STATUS
FROM DBA_CAPTURE where capture_name='' and status<>'ENABLED';
prompt "Stream capture process that are not runing normally (state is not ok)"
select state from V$STREAMS_CAPTURE where
 state in ('SHUTTING DOWN','INITIALIZING','ABORTING','PAUSED FOR FLOW
CONTROL','DICTIONARY INITIALIZATION');

prompt "if a subscriber has a high count
for total_spilled_msg, then that subscriber is not dequeuing messages
fast enough from the queue buffer. Spilling messages to disk has a
negative impact on the performance of your Streams environment."

--prompt "check scn"
 --select * from DBA_APPLY_INSTANTIATED_OBJECTS@ORCLSTREAM;

prompt "Errors in apply? Is the apply process running? Remember to re-execute pending errors before restarting the apply"
--First check sequence
select apply_name,queue_name,status,error_number,error_message
from dba_apply@orclstream
where status<>'ENABLED' OR error_number is not null ;
-- if there are errors in the above query, you should check below query for details
--select * from dba_apply_error@orclstream ;
--execute DBMS_APPLY_ADM.EXECUTE_All_ERRORS;
--select * from dba_apply_error
--execute DBMS_APPLY_ADM.START_APPLY(apply_name => 'APPLY_MTS');
--select * from DBA_APPLY_PROGRESS

prompt "errors in capture process or disabled capture processes"
select CAPTURE_NAME,QUEUE_NAME,STATUS,CAPTURED_SCN,APPLIED_SCN,ERROR_NUMBER,ERROR_MESSAGE
FROM DBA_CAPTURE
where status<>'ENABLED' OR CAPTURED_SCN-APPLIED_SCN<>0 OR ERROR_NUMBER IS NOT NULL;

---- setting trace on for the capture process:
 -----execute dbms_capture_adm.set_parameter('&CAPTURE_NAME','trace_level','2');
prompt "Displaying Information About Propagations that Send Buffered Messages"
SELECT p.PROPAGATION_NAME,
s.QUEUE_SCHEMA,
s.QUEUE_NAME,
s.DBLINK,
s.SCHEDULE_STATUS,
P.STATUS,
P.ERROR_MESSAGE,
P.ERROR_DATE
FROM DBA_PROPAGATION p, V$PROPAGATION_SENDER s
WHERE p.DESTINATION_DBLINK = s.DBLINK AND
p.SOURCE_QUEUE_OWNER = s.QUEUE_SCHEMA AND
p.SOURCE_QUEUE_NAME  = s.QUEUE_NAME
and (p.status<>'ENABLED' OR
s.schedule_status<>'SCHEDULE ENABLED' OR ERROR_MESSAGE IS NOT NULL);
--
prompt "Displaying the Schedule for a Propagation Job"
SELECT DISTINCT
p.PROPAGATION_NAME,
TO_CHAR(s.START_DATE, 'HH24:MI:SS MM/DD/YY') START_DATE,
s.PROPAGATION_WINDOW,
s.NEXT_TIME,
s.LATENCY,
DECODE(s.SCHEDULE_DISABLED,
'Y', 'Disabled',
'N', 'Enabled') SCHEDULE_DISABLED,
s.PROCESS_NAME,
s.FAILURES
FROM DBA_QUEUE_SCHEDULES s, DBA_PROPAGATION p
WHERE
p.DESTINATION_DBLINK = s.DESTINATION
AND s.SCHEMA = p.SOURCE_QUEUE_OWNER
AND s.QNAME = p.SOURCE_QUEUE_NAME
and (s.SCHEDULE_DISABLED='Y'
or S.FAILURES>0);

prompt "Apply process errors:"
SELECT s.SUBSCRIBER_NAME,
q.QUEUE_SCHEMA,
q.QUEUE_NAME,
s.LAST_DEQUEUED_SEQ,
s.NUM_MSGS,
s.TOTAL_SPILLED_MSG ,
a.error_number,
a.error_message ,
a.status
FROM V$BUFFERED_QUEUES@orclstream q, V$BUFFERED_SUBSCRIBERS@orclstream s, DBA_APPLY@orclstream a
WHERE q.QUEUE_ID = s.QUEUE_ID AND
s.SUBSCRIBER_ADDRESS IS NULL AND
s.SUBSCRIBER_NAME = a.APPLY_NAME
and (s.total_spilled_msg>0 or A.status<>'ENABLED' OR A.ERROR_NUMBER IS NOT NULL);

prompt "Propagations Dequeuing Messages from Each Buffered Queue"
SELECT p.PROPAGATION_NAME,
s.SUBSCRIBER_ADDRESS,
s.CURRENT_ENQ_SEQ,
s.LAST_BROWSED_SEQ,
s.LAST_DEQUEUED_SEQ,
s.NUM_MSGS,
s.TOTAL_SPILLED_MSG
FROM DBA_PROPAGATION p, V$BUFFERED_SUBSCRIBERS s, V$BUFFERED_QUEUES q
WHERE q.QUEUE_ID = s.QUEUE_ID AND
p.SOURCE_QUEUE_OWNER = q.QUEUE_SCHEMA AND
p.SOURCE_QUEUE_NAME = q.QUEUE_NAME AND
p.DESTINATION_DBLINK = s.SUBSCRIBER_ADDRESS
and  s.TOTAL_SPILLED_MSG>0;

prompt "The next 2 queries should return the same number of enqueue_message and dequeued_message (respectively)"
select
capture_name, state,
capture_message_number,
enqueue_message_number,
total_prefilter_discarded,
total_prefilter_kept,
total_messages_captured,
total_messages_enqueued
from gv$streams_capture;

--stream apply status - corelated with the above query
select apply_name,state,
total_messages_dequeued,
total_messages_spilled,
dequeued_message_number
from gv$streams_apply_reader@orclstream;

------- *************** end streams monitor script ***********************

SELECT * FROM DBA_LOGMNR_PURGED_LOG;
SELECT * FROM DBA_CAPTURE_PARAMETERS;
SELECT * FROM DBA_CAPTURE_EXTRA_ATTRIBUTES
SELECT * FROM DBA_QUEUE_TABLES order by owner, queue_table
SELECT * FROM DBA_QUEUES order by owner
SELECT * FROM DBA_APPLY_DML_HANDLERS
select * from DBA_QUEUE_SCHEDULES
select * from DBA_STREAMS_COLUMNS;
select * from DBA_STREAMS_ADMINISTRATOR;
select * from DBA_STREAMS_RULES;
select * from DBA_STREAMS_TABLE_RULES;
select * from SYS.DBA_STREAMS_UNSUPPORTED;

-- join between all info on capture source database
SELECT * FROM DBA_STREAMS_RULES dsr
inner join dba_propagation dp on dp.rule_set_name=dsr.rule_set_name


SELECT distinct
destination_queue_name,replace(object_name,'PRS_DEPENDECY','PRS_DEPENDENCY')
 object_name,replace(streams_name,'PROPAGATION','APPLY') STREAM_NAME
FROM DBA_STREAMS_RULES dsr
inner join dba_propagation dp on dp.rule_set_name=dsr.rule_set_name
INNER JOIN DBA_CAPTURE_PREPARED_TABLES DCPT ON dcpt.table_name=replace(object_name,'PRS_DEPENDECY','PRS_DEPENDENCY')

begin
for rc in (
select propagation_name from dba_propagation)
loop
DBMS_PROPAGATION_ADM.START_PROPAGATION(rc.propagation_name);
end loop;
end;

/************Deleting invalid oracle streams****************/
DBMS_STREAMS_ADM.REMOVE_RULE(
   rule_name         IN  VARCHAR2,
   streams_type      IN  VARCHAR2,
   streams_name      IN  VARCHAR2,
   drop_unused_rule  IN  BOOLEAN  DEFAULT TRUE,
   inclusion_rule    IN  BOOLEAN  DEFAULT TRUE);





begin
dbms_streams_adm.remove_rule(rulename);
end;

/********************************************************/

Other useful scripts:

View Message Notification:
COLUMN STREAMS_NAME HEADING 'Messaging|Client' FORMAT A10
COLUMN QUEUE_OWNER HEADING 'Queue|Owner' FORMAT A5
COLUMN QUEUE_NAME HEADING 'Queue Name' FORMAT A20
COLUMN NOTIFICATION_TYPE HEADING 'Notification|Type' FORMAT A15
COLUMN NOTIFICATION_ACTION HEADING 'Notification|Action' FORMAT A25

SELECT STREAMS_NAME,
       QUEUE_OWNER,
       QUEUE_NAME,
       NOTIFICATION_TYPE,
       NOTIFICATION_ACTION
  FROM DBA_STREAMS_MESSAGE_CONSUMERS
  WHERE NOTIFICATION_TYPE IS NOT NULL;

Determine consumer of each message in persistent queue:

COLUMN MSG_ID HEADING 'Message ID' FORMAT 9999
COLUMN MSG_STATE HEADING 'Message State' FORMAT A13
COLUMN CONSUMER_NAME HEADING 'Consumer' FORMAT A30
SELECT MSG_ID, MSG_STATE, CONSUMER_NAME FROM AQ$OE_Q_TABLE_ANY;


View Contents of message in persisten queue:

SELECT qt.user_data.AccessNumber() "Numbers in Queue"
  FROM strmadmin.oe_q_table_any qt;
SELECT qt.user_data.AccessVarchar2() "Varchar2s in Queue"
   FROM strmadmin.oe_q_table_any qt;

Monitoring Buffered queues:
Buffered queues enable Oracle databases to optimize messages by storing them in the SGA instead of always storing them in a queue table.
Captured LCRs always are stored in buffered queues, but other types of messages can be stored in buffered queues or persistently in queue tables.
Messages in a buffered queue can spill from memory if they have been staged in the buffered queue for a period of time without being dequeued, or
if there is not enough space in memory to hold all of the messages. Messages that spill from memory are stored in the appropriate queue table.

Determine number of messages in buffered queue:
COLUMN QUEUE_SCHEMA HEADING 'Queue Owner' FORMAT A15
COLUMN QUEUE_NAME HEADING 'Queue Name' FORMAT A15
COLUMN MEM_MSG HEADING 'Messages|in Memory' FORMAT 99999999
COLUMN SPILL_MSGS HEADING 'Messages|Spilled' FORMAT 99999999
COLUMN NUM_MSGS HEADING 'Total Messages|in Buffered Queue' FORMAT 99999999

SELECT QUEUE_SCHEMA,
       QUEUE_NAME,
       (NUM_MSGS - SPILL_MSGS) MEM_MSG,
       SPILL_MSGS,
       NUM_MSGS
  FROM V$BUFFERED_QUEUES;

View captured process for LCRS in each buffered queue:
COLUMN SENDER_NAME HEADING 'Capture|Process' FORMAT A10
COLUMN SENDER_ADDRESS HEADING 'Sender Queue' FORMAT A15
COLUMN QUEUE_NAME HEADING 'Queue Name' FORMAT A10
COLUMN CNUM_MSGS HEADING 'Number|of LCRs|Enqueued' FORMAT 99999999
COLUMN LAST_ENQUEUED_MSG HEADING 'Last|Enqueued|LCR' FORMAT 9999999999
COLUMN MEMORY_USAGE HEADING 'Percent|Streams|Pool|Used' FORMAT 999
COLUMN PUBLISHER_STATE HEADING 'Publisher|State' FORMAT A10

SELECT SENDER_NAME,
       SENDER_ADDRESS,
       QUEUE_NAME,      
       CNUM_MSGS,
       LAST_ENQUEUED_MSG,
       MEMORY_USAGE,
       PUBLISHER_STATE
  FROM V$BUFFERED_PUBLISHERS;

Display information about propagations which send buffered messaged:
COLUMN PROPAGATION_NAME HEADING 'Propagation' FORMAT A15
COLUMN QUEUE_SCHEMA HEADING 'Queue|Owner' FORMAT A10
COLUMN QUEUE_NAME HEADING 'Queue|Name' FORMAT A15
COLUMN DBLINK HEADING 'Database|Link' FORMAT A10
COLUMN SCHEDULE_STATUS HEADING 'Schedule Status' FORMAT A20

SELECT p.PROPAGATION_NAME,
       s.QUEUE_SCHEMA,
       s.QUEUE_NAME,
       s.DBLINK,
       s.SCHEDULE_STATUS
  FROM DBA_PROPAGATION p, V$PROPAGATION_SENDER s
  WHERE p.SOURCE_QUEUE_OWNER      = s.QUEUE_SCHEMA AND
        p.SOURCE_QUEUE_NAME       = s.QUEUE_NAME AND
        p.DESTINATION_QUEUE_OWNER = s.DST_QUEUE_SCHEMA AND
        p.DESTINATION_QUEUE_NAME  = s.DST_QUEUE_NAME;

COLUMN PROPAGATION_NAME HEADING 'Propagation' FORMAT A15
COLUMN QUEUE_NAME HEADING 'Queue|Name' FORMAT A15
COLUMN DBLINK HEADING 'Database|Link' FORMAT A20
COLUMN TOTAL_MSGS HEADING 'Total|Messages' FORMAT 99999999
COLUMN TOTAL_BYTES HEADING 'Total|Bytes' FORMAT 999999999999

SELECT p.PROPAGATION_NAME,
       s.QUEUE_NAME,
       s.DBLINK,
       s.TOTAL_MSGS,
       s.TOTAL_BYTES
  FROM DBA_PROPAGATION p, V$PROPAGATION_SENDER s
  WHERE p.SOURCE_QUEUE_OWNER      = s.QUEUE_SCHEMA AND
        p.SOURCE_QUEUE_NAME       = s.QUEUE_NAME AND
        p.DESTINATION_QUEUE_OWNER = s.DST_QUEUE_SCHEMA AND
        p.DESTINATION_QUEUE_NAME  = s.DST_QUEUE_NAME;

Displaying Performance Statistics for Propagations that Send Buffered Messages:

COLUMN PROPAGATION_NAME HEADING 'Propagation' FORMAT A15
COLUMN QUEUE_NAME HEADING 'Queue|Name' FORMAT A13
COLUMN DBLINK HEADING 'Database|Link' FORMAT A9
COLUMN ELAPSED_DEQUEUE_TIME HEADING 'Dequeue|Time' FORMAT 99999999.99
COLUMN ELAPSED_PICKLE_TIME HEADING 'Pickle|Time' FORMAT 99999999.99
COLUMN ELAPSED_PROPAGATION_TIME HEADING 'Propagation|Time' FORMAT 99999999.99

SELECT p.PROPAGATION_NAME,
       s.QUEUE_NAME,
       s.DBLINK,
       (s.ELAPSED_DEQUEUE_TIME / 100) ELAPSED_DEQUEUE_TIME,
       (s.ELAPSED_PICKLE_TIME / 100) ELAPSED_PICKLE_TIME,
       (s.ELAPSED_PROPAGATION_TIME / 100) ELAPSED_PROPAGATION_TIME
  FROM DBA_PROPAGATION p, V$PROPAGATION_SENDER s
  WHERE p.SOURCE_QUEUE_OWNER      = s.QUEUE_SCHEMA AND
        p.SOURCE_QUEUE_NAME       = s.QUEUE_NAME AND
        p.DESTINATION_QUEUE_OWNER = s.DST_QUEUE_SCHEMA AND
        p.DESTINATION_QUEUE_NAME  = s.DST_QUEUE_NAME;

Viewing the Propagations Dequeuing Messages from Each Buffered Queue:

COLUMN PROPAGATION_NAME HEADING 'Propagation' FORMAT A11
COLUMN QUEUE_SCHEMA HEADING 'Queue|Owner' FORMAT A5
COLUMN QUEUE_NAME HEADING 'Queue|Name' FORMAT A5
COLUMN SUBSCRIBER_ADDRESS HEADING 'Subscriber|Address' FORMAT A15
COLUMN STARTUP_TIME HEADING 'Startup|Time' FORMAT A9
COLUMN CNUM_MSGS HEADING 'Cumulative|Messages' FORMAT 99999999
COLUMN TOTAL_DEQUEUED_MSG HEADING 'Total|Messages' FORMAT 99999999
COLUMN LAST_DEQUEUED_NUM HEADING 'Last|Dequeued|Message|Number' FORMAT 99999999

SELECT p.PROPAGATION_NAME,
       s.QUEUE_SCHEMA,
       s.QUEUE_NAME,
       s.SUBSCRIBER_ADDRESS,
       s.STARTUP_TIME,
       s.CNUM_MSGS,        
       s.TOTAL_DEQUEUED_MSG,
       s.LAST_DEQUEUED_NUM
FROM DBA_PROPAGATION p, V$BUFFERED_SUBSCRIBERS s
WHERE p.SOURCE_QUEUE_OWNER = s.QUEUE_SCHEMA AND
      p.SOURCE_QUEUE_NAME  = s.QUEUE_NAME AND
      p.PROPAGATION_NAME   = s.SUBSCRIBER_NAME AND
      s.SUBSCRIBER_ADDRESS LIKE '%' || p.DESTINATION_DBLINK;

Displaying Performance Statistics for Propagations That Receive Buffered Messages:

COLUMN SRC_QUEUE_NAME HEADING 'Source|Queue|Name' FORMAT A20
COLUMN SRC_DBNAME HEADING 'Source|Database' FORMAT A20
COLUMN ELAPSED_UNPICKLE_TIME HEADING 'Unpickle|Time' FORMAT 99999999.99
COLUMN ELAPSED_RULE_TIME HEADING 'Rule|Evaluation|Time' FORMAT 99999999.99
COLUMN ELAPSED_ENQUEUE_TIME HEADING 'Enqueue|Time' FORMAT 99999999.99

SELECT SRC_QUEUE_NAME,
       SRC_DBNAME,
       (ELAPSED_UNPICKLE_TIME / 100) ELAPSED_UNPICKLE_TIME,
       (ELAPSED_RULE_TIME / 100) ELAPSED_RULE_TIME,
       (ELAPSED_ENQUEUE_TIME / 100) ELAPSED_ENQUEUE_TIME
  FROM V$PROPAGATION_RECEIVER;

Viewing the Apply Processes Dequeuing Messages from Each Buffered Queue:
COLUMN SUBSCRIBER_NAME HEADING 'Apply Process' FORMAT A16
COLUMN QUEUE_SCHEMA HEADING 'Queue|Owner' FORMAT A5
COLUMN QUEUE_NAME HEADING 'Queue|Name' FORMAT A5
COLUMN STARTUP_TIME HEADING 'Startup|Time' FORMAT A9
COLUMN CNUM_MSGS HEADING 'Cumulative|Messages' FORMAT 99999999
COLUMN TOTAL_DEQUEUED_MSG HEADING 'Number of|Dequeued|Messages'
  FORMAT 99999999
COLUMN LAST_DEQUEUED_NUM HEADING 'Last|Dequeued|Message|Number' FORMAT 99999999

SELECT s.SUBSCRIBER_NAME,
       q.QUEUE_SCHEMA,
       q.QUEUE_NAME,
       s.STARTUP_TIME,
       s.CNUM_MSGS,        
       s.TOTAL_DEQUEUED_MSG,
       s.LAST_DEQUEUED_NUM
FROM V$BUFFERED_QUEUES q, V$BUFFERED_SUBSCRIBERS s, DBA_APPLY a
WHERE q.QUEUE_ID = s.QUEUE_ID AND
      s.SUBSCRIBER_ADDRESS IS NULL AND
      s.SUBSCRIBER_NAME = a.APPLY_NAME;



Friday, August 29, 2014

Dataguard Rolling Upgrade using transient logical standby

I was successfully able to complete rolling upgrade using Dataguard for one of our environment and application downtime was reduced to just 2 minutes from 4 hours. Below are the high level steps performed and case study for this rolling database upgrade.

Current Dataguard configuration looks like as below:
Primary database: rec2p455 running on server dgtest02.
Physical standby database: rec3p455 running on server dgtest03.


1. Review prerequisites and best practices from Dataguard Rolling upgrade white paper.
a. Enable flashback and created a guaranteed restore point on each databases for flashaback purpose
b. Install upgraded ORACLE_HOME 11.2.0.4 on each servers dgtest02 and dgtest03 servers.
c. Make sure that your physical standby database is configured correctly to support switchover operations. Below parameters should be configured correctly on these databases -

On primary rec2p455 -
alter system set db_unique_name='rec2p455' scope=spfile sid='*';
alter system set log_archive_config = 'DG_CONFIG=(rec2p455,rec3p455)' scope=both sid='*';
alter system set standby_file_management=AUTO scope=both sid='*';
alter system set db_file_name_convert='/oracle/data/ora01/rec2p455/','/oracle/data/ora01/rec3p455/','/oracle/data/ora01/rec3p455/','/oracle/data/ora01/rec2p455/','/oracle/data/ora02/rec2p455/','/oracle/data/ora02/rec3p455/','/oracle/data/ora02/rec3p455/','/oracle/data/ora02/rec2p455/','/oracle/data/ora03/rec2p455/','/oracle/data/ora03/rec3p455/','/oracle/data/ora03/rec3p455/','/oracle/data/ora03/rec2p455/','/oracle/data/ora04/rec2p455/','/oracle/data/ora04/rec3p455/','/oracle/data/ora04/rec3p455/','/oracle/data/ora04/rec2p455/' scope=spfile sid='*';
alter system set log_file_name_convert='/oracle/data/redo01/rec2p455/','/oracle/data/ora01/rec3p455/','/oracle/data/ora01/rec3p455/','/oracle/data/redo01/rec2p455/','/oracle/data/redo02/rec2p455/','/oracle/data/ora02/rec3p455/','/oracle/data/ora02/rec3p455/','/oracle/data/redo02/rec2p455/' scope=spfile sid='*';
alter system set log_archive_dest_2='SERVICE=rec3p455 LGWR ASYNC VALID_FOR=(ONLINE_LOGFILES,PRIMARY_ROLE) DB_UNIQUE_NAME=rec3p455' scope=spfile sid='*';
alter system set log_archive_dest_state_2=ENABLE scope=spfile;
alter system set fal_server='rec3p455' scope=spfile sid='*';
alter system set fal_client='rec2p455' scope=spfile sid='*';

On standby rec3p455:
alter system set db_unique_name='rec3p455' scope=spfile sid='*';
alter system set log_archive_config = 'DG_CONFIG=(rec2p455,rec3p455)' scope=both sid='*';
alter system set standby_file_management=AUTO scope=both sid='*';
alter system set db_file_name_convert='/oracle/data/ora01/rec2p455/','/oracle/data/ora01/rec3p455/','/oracle/data/ora01/rec3p455/','/oracle/data/ora01/rec2p455/','/oracle/data/ora02/rec2p455/','/oracle/data/ora02/rec3p455/','/oracle/data/ora02/rec3p455/','/oracle/data/ora02/rec2p455/','/oracle/data/ora03/rec2p455/','/oracle/data/ora03/rec3p455/','/oracle/data/ora03/rec3p455/','/oracle/data/ora03/rec2p455/','/oracle/data/ora04/rec2p455/','/oracle/data/ora04/rec3p455/','/oracle/data/ora04/rec3p455/','/oracle/data/ora04/rec2p455/' scope=spfile sid='*';
alter system set log_file_name_convert='/oracle/data/redo01/rec2p455/','/oracle/data/ora01/rec3p455/','/oracle/data/ora01/rec3p455/','/oracle/data/redo01/rec2p455/','/oracle/data/redo02/rec2p455/','/oracle/data/ora02/rec3p455/','/oracle/data/ora02/rec3p455/','/oracle/data/redo02/rec2p455/' scope=spfile sid='*';
alter system set log_archive_dest_2='SERVICE=rec2p455 LGWR ASYNC VALID_FOR=(ONLINE_LOGFILES,PRIMARY_ROLE) DB_UNIQUE_NAME=re2p455' scope=spfile sid='*';
alter system set log_archive_dest_state_2=ENABLE scope=spfile;
alter system set fal_server='rec2p455' scope=spfile sid='*';

alter system set fal_client='rec3p455' scope=spfile sid='*';

2. Convert the existing Physical Standby rec3p455 to a Logical Standby using the keep identity feature which doesn't require database name to be changed. While logical standby build is going on, Applications are still conected to primary database rec2p455 on
icmfg74pharma server and performing usual transactions as below -
SQL> conn userid@rec2p455_dg
Enter password:
Connected.
SQL>
create table tmp as select 1 dummy from dual;
table created.

insert into tmp as select * from tmp;
1 row inserted;
/
2 rows inserted;
/
4 rows inserted;
/
8 rows inserted;
/
16 rows inserted;
/
32 rows inserted;
/
128 rows inserted;
commit;
256 rows inserted.
commit;

SQL> select count(1) from tmp;

  COUNT(1)
----------
       256

On Primary rec2p455 -
---------------------------------------------------------------
CREATE RESTORE POINT PRE_UPGRADE_1 GUARANTEE FLASHBACK DATABASE;
EXECUTE DBMS_LOGSTDBY.BUILD;

On standby database rec3p455 for converting it to logical from physical -

CREATE RESTORE POINT PRE_UPGRADE_2 GUARANTEE FLASHBACK DATABASE; -

ALTER DATABASE RECOVER TO LOGICAL STANDBY KEEP IDENTITY;

ALTER DATABASE START LOGICAL STANDBY APPLY IMMEDIATE;

EXECUTE DBMS_LOGSTDBY.APPLY_SET('LOG_AUTO_DELETE', 'FALSE');

CREATE RESTORE POINT PRE_UPGRADE_3 GUARANTEE FLASHBACK DATABASE;

3. Perform the upgrade on the logical standby rec3p455 following the upgrade process as per oracle documentation upgrade guide as you do in any normal database and start the apply process so that logical standby is completely synchronized with the primary database rec2p455. While upgrade is going on, primary database rec2p455 is still available and applications can query and perform transactions.

Database mounted.
Database opened.
> @$ORACLE_HOME/rdbms/admin/utlu112s.sql
.
Oracle Database 11.2 Post-Upgrade Status Tool           08-29-2014 17:17:34
.
Component                               Current      Version     Elapsed Time
Name                                    Status       Number      HH:MM:SS
.
Oracle Server
.                                         VALID      11.2.0.4.0  00:09:44
Final Actions
.                                                                00:00:10
Total Upgrade Time: 00:09:55

PL/SQL procedure successfully completed.

SQL> select * from dba_registry_history;

ACTION_TIME                                                                 ACTION                      NAMESPACE                         VERSION                                ID BUNDLE_SERIES           COMMENTS
--------------------------------------------------------------------------- ------------------------------ ------------------------------ ------------------------------ ---------- ------------------------------ -----------------------------
22-SEP-10 03.42.04.503870 PM                                                APPLY                       SERVER                    11.1.0.7                                3 CPU                            CPUOct2009
22-SEP-10 03.42.45.067417 PM                                                CPU                                                                                     6452863                                view recompilation
20-MAR-13 08.50.32.513178 PM                                                APPLY                       SERVER                    11.1.0.7                               13 PSU                            PSU 11.1.0.7.13
19-JUL-13 05.20.52.017167 PM                                                VIEW INVALIDATE                                                                         8289601                                view invalidation
19-JUL-13 05.21.00.848183 PM                                                UPGRADE                     SERVER                    11.2.0.3.0                                                               Upgraded from 11.1.0.7.0
19-JUL-13 05.26.58.688228 PM                                                APPLY                       SERVER                    11.2.0.3                                4 PSU                            PSU 11.2.0.3.4
20-NOV-13 05.46.43.353854 PM                                                APPLY                       SERVER                    11.2.0.3                                7 PSU                            PSU 11.2.0.3.7
29-AUG-14 05.16.13.596405 PM                                                VIEW INVALIDATE                                                                         8289601                                view invalidation
29-AUG-14 05.16.23.809173 PM                                                UPGRADE                     SERVER                    11.2.0.4.0                                                               Upgraded from 11.2.0.3.0
29-AUG-14 05.18.10.822624 PM                                                APPLY



4. Switchover the database role so that existing logical standby database rec3p455 becomes primary while rec2p455 becomes logical standby database. At this point downtime for application is required as switchover to logical standby database doesn't require database to be started so application connections will not failover even if you are using service to support transparent application failover. In my case switchover was done in less than a minute and sometimes it can be more than a minute as it takes additional time in applying the redo gap if any to database being converted to primary. Downtime can be arranged with application team for rolling upgrade accordingly. I would say 5-10 minutes would be sufficient.

On Primary rec3p455 -

SELECT SWITCHOVER_STATUS FROM V$DATABASE;

ALTER DATABASE COMMIT TO SWITCHOVER TO LOGICAL STANDBY;

On Standby  rec2p455
SELECT SWITCHOVER_STATUS FROM V$DATABASE;

ALTER DATABASE COMMIT TO SWITCHOVER TO LOGICAL PRIMARY;


5. Flashback the original primary rec2p455 (now a logical standby) to the guaranteed restore
point from #1
SHUTDOWN IMMEDIATE;
STARTUP MOUNT;
FLASHBACK DATABASE TO RESTORE POINT PRE_UPGRADE_1;

6. Remount the original primary (now a logical standby) under the new Oracle Home.
shut down database;
a.) change oracle home in oratab and listener.ora.
b.) reload listener.ora
c.) create symbolic links to spfile and pwfile to $ORACLE_HOME/dbs as applicable if you are storing these files in another location otherwise simply copy the files from older home to this location
d.) startup mount;

7. Convert the logical standby rec2p455 back to a physical standby and resynchronize with the
new primary, automatically completing the upgrade process.
ALTER DATABASE CONVERT TO PHYSICAL STANDBY;
shut immediate;
startup mount;
RECOVER MANAGED STANDBY DATABASE USING CURRENT LOGFILE DISCONNECT; ( Before starting the managed recovery, make sure that standby destination is enabled on new primary database rec3p455).


8. Wait for physical standby to be upgraded via the redo stream. In alert log you can see that new incarnation for the the original primary(now standby) will be registered -

RFS[6]: Selected log 7 for thread 1 sequence 21 dbid 829525374 branch 856888795
Archived Log entry 4334 added for thread 1 sequence 20 rlc 856888795 ID 0x32798a25 dest 2:
Archived Log entry 4335 added for thread 1 sequence 19 rlc 856888795 ID 0x32798a25 dest 2:
Fri Aug 29 17:57:49 2014
 started logmerger process
Fri Aug 29 17:57:49 2014
Managed Standby Recovery not using Real Time Apply
Parallel Media Recovery started with 4 slaves
Media Recovery start incarnation depth : 1, target inc# : 3, irscn : 12111287417698
Waiting for all non-current ORLs to be archived...
All non-current ORLs have been archived.
Clearing online redo logfile 1 /oracle/data/redo01/rec2p455/ora_log_01_01.rdo
Clearing online log 1 of thread 1 sequence number 3924
RFS[5]: Opened log for thread 1 sequence 3913 dbid 829525374 branch 838060608
Fri Aug 29 17:57:49 2014
Archived Log entry 4336 added for thread 1 sequence 21 ID 0x32798a25 dest 1:
Archived Log entry 4337 added for thread 1 sequence 3913 rlc 838060608 ID 0x32784362 dest 2:
Fri Aug 29 17:57:50 2014
Completed: alter database recover managed standby database disconnect from session
Clearing online redo logfile 1 complete
Clearing online redo logfile 2 /oracle/data/redo01/rec2p455/ora_log_02_01.rdo
Clearing online log 2 of thread 1 sequence number 3925
Clearing online redo logfile 2 complete
Clearing online redo logfile 3 /oracle/data/redo01/rec2p455/ora_log_03_01.rdo
Clearing online log 3 of thread 1 sequence number 3923
Clearing online redo logfile 3 complete
Media Recovery Log /oracle/data/arch02/rec2p455/rec2p455_1_3911_838060608.arc
Media Recovery Log /oracle/data/arch02/rec2p455/rec2p455_1_3912_838060608.arc
Media Recovery Log /oracle/data/arch02/rec2p455/rec2p455_1_3913_838060608.arc
Media Recovery Log /oracle/data/arch02/rec2p455/rec2p455_1_1_856888795.arc
Media Recovery Log /oracle/data/arch02/rec2p455/rec2p455_1_2_856888795.arc
Fri Aug 29 17:58:00 2014
Media Recovery Log /oracle/data/arch02/rec2p455/rec2p455_1_3_856888795.arc
Fri Aug 29 17:58:12 2014
Media Recovery Log /oracle/data/arch02/rec2p455/rec2p455_1_4_856888795.arc
Media Recovery Log /oracle/data/arch02/rec2p455/rec2p455_1_5_856888795.arc
Media Recovery Log /oracle/data/arch02/rec2p455/rec2p455_1_6_856888795.arc
Media Recovery Log /oracle/data/arch02/rec2p455/rec2p455_1_7_856888795.arc
Fri Aug 29 17:58:26 2014
Media Recovery Log /oracle/data/arch02/rec2p455/rec2p455_1_8_856888795.arc
Media Recovery Log /oracle/data/arch02/rec2p455/rec2p455_1_9_856888795.arc
Media Recovery Log /oracle/data/arch02/rec2p455/rec2p455_1_10_856888795.arc
Fri Aug 29 17:58:38 2014
Media Recovery Log /oracle/data/arch02/rec2p455/rec2p455_1_11_856888795.arc
Media Recovery Log /oracle/data/arch02/rec2p455/rec2p455_1_12_856888795.arc
Media Recovery Log /oracle/data/arch02/rec2p455/rec2p455_1_13_856888795.arc
Fri Aug 29 17:58:48 2014
Media Recovery Log /oracle/data/arch02/rec2p455/rec2p455_1_14_856888795.arc
Media Recovery Log /oracle/data/arch02/rec2p455/rec2p455_1_15_856888795.arc
Media Recovery Log /oracle/data/arch02/rec2p455/rec2p455_1_16_856888795.arc
Media Recovery Log /oracle/data/arch02/rec2p455/rec2p455_1_17_856888795.arc
Media Recovery Log /oracle/data/arch02/rec2p455/rec2p455_1_18_856888795.arc
Media Recovery Log /oracle/data/arch02/rec2p455/rec2p455_1_19_856888795.arc
Media Recovery Log /oracle/data/arch02/rec2p455/rec2p455_1_20_856888795.arc
Media Recovery Log /oracle/data/arch02/rec2p455/rec2p455_1_21_856888795.arc
Media Recovery Waiting for thread 1 sequence 22 (in transit)
Fri Aug 29 18:00:53 2014

9. Switch back to the original configuration (optional). This can be done later whenever applications are ready for another downtime of 2 minutes. Since this time switchover is required between primary and physical standby databases which required database remount, so application connections will also failover automatically to new primary database if they are connecting to database service supporting transparent application failover. All select queries will continue to execute but any transactions executed during the time when switchover is happening, needs to be re-executed.

On current primary rec3p455 -
SELECT SWITCHOVER_STATUS FROM V$DATABASE;

ALTER DATABASE COMMIT TO SWITCHOVER TO STANDBY WITH SESSION SHUTDOWN;

On  standby rec2p455 -
SELECT SWITCHOVER_STATUS FROM V$DATABASE;
ALTER DATABASE COMMIT TO SWITCHOVER TO PRIMARY;
alter database open;

On new standby rec3p455 after switchover -
SHUTDOWN IMMEDIATE;
STARTUP MOUNT;
RECOVER MANAGED STANDBY DATABASE USING CURRENT LOGFILE DISCONNECT;

10. Raise the COMPATIBLE settings

You can see that select queries (reporting sessions) are still alive and returning the correct results and connecting to primary database -
SQL> /

  COUNT(1)
----------
       256

SQL> select database_role, name from v$database;

DATABASE_ROLE    NAME
---------------- ---------
PRIMARY          REC2P455

SQL>  select * from dba_registry_history;

ACTION_TIME                                                                 ACTION                      NAMESPACE                         VERSION                                ID BUNDLE_SERIES           COMMENTS
--------------------------------------------------------------------------- ------------------------------ ------------------------------ ------------------------------ ---------- ------------------------------ -------------------------------
22-SEP-10 03.42.04.503870 PM                                                APPLY                       SERVER                    11.1.0.7                                3 CPU                            CPUOct2009
22-SEP-10 03.42.45.067417 PM                                                CPU                                                                                     6452863                                view recompilation
20-MAR-13 08.50.32.513178 PM                                                APPLY                       SERVER                    11.1.0.7                               13 PSU                            PSU 11.1.0.7.13
19-JUL-13 05.20.52.017167 PM                                                VIEW INVALIDATE                                                                         8289601                                view invalidation
19-JUL-13 05.21.00.848183 PM                                                UPGRADE                     SERVER                    11.2.0.3.0                                                               Upgraded from 11.1.0.7.0
19-JUL-13 05.26.58.688228 PM                                                APPLY                       SERVER                    11.2.0.3                                4 PSU                            PSU 11.2.0.3.4
20-NOV-13 05.46.43.353854 PM                                                APPLY                       SERVER                    11.2.0.3                                7 PSU                            PSU 11.2.0.3.7
29-AUG-14 05.16.13.596405 PM                                                VIEW INVALIDATE                                                                         8289601                                view invalidation
29-AUG-14 05.16.23.809173 PM                                                UPGRADE                     SERVER                    11.2.0.4.0                                                               Upgraded from 11.2.0.3.0
29-AUG-14 05.18.10.822624 PM                                                APPLY                       SERVER                    11.2.0.4                                2 PSU                            PSU 11.2.0.4.2

10 rows selected.

SQL> sho parameter db_unique

NAME                                 TYPE                             VALUE
------------------------------------ -------------------------------- ------------------------------
db_unique_name                       string                           rec2p455
SQL>

For reporting services, below service was created along with triggers to support transparent application connections failover -

exec DBMS_SERVICE.CREATE_SERVICE -
(service_name => 'rec2p455_dg', -
network_name => 'rec2p455_dg', -
aq_ha_notifications => true, -
failover_type => 'SELECT', -
failover_retries => 180, -
failover_delay => 1);


create or replace trigger start_database_service after startup on database
declare
role varchar2(50);
omode varchar2(50);
begin
select database_role into role from v$database;
  if role='PRIMARY' then
    dbms_service.start_service('rec2p455_dg');
  end if;
end;
/

CREATE OR REPLACE TRIGGER start_service_test_role_change AFTER
DB_ROLE_CHANGE ON DATABASE
DECLARE
role VARCHAR(30);
BEGIN
SELECT DATABASE_ROLE INTO role FROM V$DATABASE;
IF role = 'PRIMARY' THEN
DBMS_SERVICE.START_SERVICE(''rec2p455_dg')');
END IF;
END;

I hope you find this post useful. Pls let me know for any suggestions and questions you have.

Tuesday, August 19, 2014

Dynamic Remastering versus Dynamic Reconfiguration in Oracle RAC

Often DBAs get themselves confused with two most distinct and significant concepts of RAC -Dynamic Remastering and Dynamic Reconfiguration. In this article I would try to explain these two concepts and will also share useful scripts that can be used in day today administration for finding master of a block or to forcefully enable the desired master for a particular block.

Dynamic Remastering: Before starting discussion on dynamic remastering, you need to be familiar with Global Resource Directory.  GRD basically contains useful information such as data block address, mode (null, shared or exclusive), role(local or global), SCN, past image and current image. Each instance maintains the portion of GRD in its own SGA. In other words, any node which is master of particular object or a block maintains the GRD portion for those blocks in its SGA. GCS in conjunction with GES is actually responsible for updating GRD for a resource in instance's SGA. The objective of dynamic remastering is to master buffer cache resource on the instance where it's mostly accessed.

Mastering of block is decided based on the demand for a block, so basically when demand for particular object or block is high on one node, that instance will become master for that object or block. And this is where dynamic mastering comes into play. For Example, instance A is currently master of object (Table EMP) because there were huge number of read/write requests made by instance A on table EMP. After sometime, other instance B started making such requests very frequently for EMP such that demand for EMP table increased on instance B. In such situations, instance A has to either ship the blocks to instance B or forward the request to other instance which is holding most current copy of the requested blocks. To avoid unnecessary interactions and network traffic, GCS remasters the blocks to instance B.

Dynamic Reconfiguration: Dynamic Reconfiguration is the process of reconfiguring resources of failed nodes among surviving nodes. Dynamic Reconfiguration comes into play when an instance crashes unexpectedly. Below are some of the high level steps due to instance crash when node failure is detected by CSSD daemon.

  1. The enqueue part of GRD is reconfigured among surviving nodes by global enqueue service. This operation occurs relatively faster.
  2. The cache part of GRD is reconfigured and SMON reads the redo log of the failed instance to identify the data blocks that it needs to recover.  Since blocks are shipped over the interconnect, SMON may have to merge the redo logs of the failed instance to overcome holes in the redo.
  3. SMON issues GRD requests to obtain all the data blocks that need recovery. Redo threads of failed instances are merged and applied to the most current versions or past image of the blocks from other instance cache. If past images are not available, then redo logs will be applied after reading the blocks from disk. All blocks become accessible now.
  4. Undo blocks are applied to the database in back ground. You can tune this rollback recovery by setting parameter fast_start_rollback_recovery parameter appropriately. 

Dynamic remastering comes into play here as well as cache resources have to be mastered among surviving nodes the only difference here that remastering was forced due to instance crash but not due to demand for resources.

Bases on the above discussion remastering of an object or block can be triggered mainly due to below three reasons:

  • Manual remastering
  • Resource affinity
  • Instance crash


Lets discuss these criteria for dynamic remastering one by one in detail. 

The scripts and demo used in this article are referred from oracle metalink support and very nice article from Anju Garg in blog (oracleinaction.com).

I have used similar demo in one of my in-house environment with some additional tweaks with the help of oracle metalink support and sharing same in this article. The environment is RAC cluster of 3 nodes (RAC1, RAC2, RAC3) and the experiments were performed on oracle provided SCOTT schema.


First of all, retrieve data_object_id of table EMP using below script:
          
    select owner, data_object_id, object_name 
           from dba_objects 
           where owner = 'SCOTT' 
             and object_name = 'EMP';

OWNER   DATA_OBJECT_ID OBJECT_NAME
———- ————– —————————————
SCOTT                32614                    EMP

 Get File_id and block_id of emp table:

SQL>select empno, dbms_rowid.rowid_relative_fno(rowid), 
                  dbms_rowid.rowid_block_number(rowid) 
          from scott.emp 
           where empno in (1, 2);

     EMPNO DBMS_ROWID.ROWID_RELATIVE_FNO(ROWID) DBMS_ROWID.ROWID_BLOCK_NUMBER(ROWID)
———- ———————————— ————————————
      1                                   6                                  120
      2                                   6                                  120

 MANUAL REMASTERING

You can manually remaster an object with oradebug command :
oradebug lkdebug -m pkey <data_object_id>

Step I. shutdown the database and restart it from RAC1

[oracle@rac1 ~]$ srvctl stop database -d rac 
                  srvctl start database -d rac
                  srvctl status database -d rac
Step II. Issue a select on the object from NODE2

SCOTT@NODE2> select * from  emp;

Step III. Find the GCS resource name to be used in  the query
   x$kjbl.kjblname = resource name in hexadecimal format([id1],[id2],[type]
   x$kjbl.kjblname2 = resource name in decimal format
   Hexname will be used to query resource in v$gc_element and v$dlm_rss views.

SYS@rac2>col hexname for a25 
             col resource_name for a15 
             select b.kjblname hexname, b.kjblname2 resource_name, 
                     b.kjblgrant, b.kjblrole, b.kjblrequest  
           from x$le a, x$kjbl b 
             where a.le_kjbl=b.kjbllockp 
              and a.le_addr = (select le_addr 
                                from x$bh 
                               where dbablk = 120 
                                and obj    = 32614 
                               and class  = 1 
                                and state   <> 3);

HEXNAME           RESOURCE_NAME   KJBLGRANT   KJBLROLE KJBLREQUE
————————- ————— ——— ———- —————— ———- ———
[0x97][0x4],[BL]          120,6,BL                 KJUSERPR            0            KJUSERNL

Step IV. Check the current master of the block –

SYS>select o.object_name, m.CURRENT_MASTER, 
                   m.PREVIOUS_MASTER, m.REMASTER_CNT 
          from   dba_objects o, v$gcspfmaster_info m
           where o.data_object_id=32614
           and m.data_object_id = 32614 ;

OBJECT CURRENT_MASTER PREVIOUS_MASTER REMASTER_CNT
—— ————– ——————— ————— ————— —
EMP                 0                                      32767                                   1

Note that current master of scott.emp is node RAC1 (numbering starts from 0). Value of previous master (32767) indicates that the previous master was not known and current the master is 0 which is instance RAC1. REMASTER_CNT = 1 indicates the object has been remastered only once.


Step V. Use following SQL to show master and owner of the block.
 This SQL joins   x$kjbl with x$le to retrieve resource name.
– Note that current master is node1(KJBLMASTER=0) and current owner of the block is
node2(KJBLOWNER = 1)

SYS@NODE2> select kj.kjblname, kj.kjblname2, kj.kjblowner, 
                       kj.kjblmaster
            from (select kjblname, kjblname2, kjblowner, 
                         kjblmaster, kjbllockp         
                  from x$kjbl
                   where kjblname = '[0x97][0x4],[BL]'
                  ) kj, x$le le
            where le.le_kjbl = kj.kjbllockp
            order by le.le_addr;

KJBLNAME              KJBLNAME2    KJBLOWNER  KJBLMASTER
—————————— —————————————-
[0x97][0x4],[BL]               120,6,BL                       1             0

Step VI. Manually master the EMP table to node RAC2 using oradebug.

SYS@rac1>oradebug lkdebug -m pkey 32614

SYS>select o.object_name, m.CURRENT_MASTER, 
                   m.PREVIOUS_MASTER, m.REMASTER_CNT 
          from   dba_objects o, v$gcspfmaster_info m 
           where o.data_object_id=32614
            and m.data_object_id = 32614 ;

OBJECT CURRENT_MASTER PREVIOUS_MASTER REMASTER_CNT
—— ————– ————— —————— ————– ————— ————
EMP                     1                                             0                        2

As you can clearly see that current_master has changed from node 1 to node 2 and remaster count has also increased to 2. It also show that previously master for the object was node 1 (PREVIOUS_MASTER = 0).

You can also determine the master at block level using below query.
SYS> select kj.kjblname, kj.kjblname2, kj.kjblowner, 
             kj.kjblmaster 
           from (select kjblname, kjblname2, kjblowner, 
                         kjblmaster, kjbllockp 
                 from x$kjbl
                  where kjblname = '[0x97][0x4],[BL]') kj, x$le le 
           where le.le_kjbl = kj.kjbllockp   
           order by le.le_addr;
KJBLNAME                       KJBLNAME2        KJBLOWNER KJBLMASTER
—————————— —————————— ———-  ———-
[0x97][0x4],[BL]               120,6,BL                                1                  1

Note that current owner of the block is Node2 (KJBLOWNER=1)from where query was issued)
Current master of the block has been changed to node2 (KJBLMASTER=1).

REMASTERING DUE TO RESOURCE AFFINITY


Resource affinity allows frequently used resource to be mastered on its local node; it used dynamic resource mastering to move location of the resource masters. There are number of parameters that can be used to dynamically remaster an object.

There may be situations when you would like to tweak or disable dynamic resource mastering for an object or for the whole RAC database. Frequent remastering of any block or object can cause significant traffic in RAC interconnect and cause delays in serving resource requests as GCS has to quiesce the resource for performing DRM. Frequent DRM can result in database spending time mostly on waits such as GCS buffer busy (acquire/release) and other DRM freeze/releases as well as remaster events. These waits can be easily visible in AWR/ASH/AMM or cluster coherency section under OEM . There are ways you can tune this by setting resource affinity attributes. DRM attributes are intentionally undocumented and normally these attributes should not be changed without discussing with Oracle Support.

Following parameters affect dynamic remastering due to resource affinity :
Pls note that these parameters are renamed from 11G onwards as earlier they used to be _gc_affinity_ but 11g onwards they are changed to _gc_policy_

_gc_policy_limit : If an instance opens 50 more open BL requests on an object than the other instance (controlled by _gc_policy_limit parameter), then that object is a candidate for remastering. That object is queued and LMD0 reads the queue and initiates GRD freeze. LMON in conjunction with LMS is responsible for remastering of resources. All these are visible in LMD0/LMON trace files. These statistics are maintained in X$object_affinity_statistics.

_gc_policy_time : Intervals in minutes in which object statistics are evaluated and queue is checked to determine if dynamic remastering must be triggered.

_gc_policy_minimum: The minimum number of global cache operations (OPEN/CONVERT/CLOSE) per minute to qualify an object for affinity (object remastering).

_gc_read_mostly_locking: Setting this parameter to false disable read mostly related DRM. Default is TRUE.

_gc_undo_affinity: This parameter enables dynamic undo remastering. Mastering of undo segments differ from that of non-undo segments. Non-undo segments remastering uses hash techniques and remastering occurs on the basis of number of open BL lock requests while undo segment remastering enabled for a instance that actually activates undo segment which makes sense.

_lm_drm_max_banned_objs: The parameter plays an important role if you want to skip dynamic remastering at object level. Set this parameter to number of objects you want to disable remastering. Maximume value is 235

_lm_drm_banned_objs: Set this parameter to list of object ids separated by commas to skip dynamic remastering for the objects.

Lets consider a scenario where you would like to minimize dynamic remastering for an object. Lets say for an object name EMP, there have been frequent requests coming from nodes RAC1 and RAC2. If number of opens gets beyond default value of 50 for an instance, you would not just want that object to be remastered among nodes just because of that.

So if its normal for multiple instances to issue these many requests, you would prefer to avoid dynamic remastering happening frequently. You can increase the value of _gc_policy_limit to higher than average requests of object per instance as well as you can consider setting higher value of _gc_policy_minimum to control dynamic remastering.

Pls also note that setting higher values or setting above parameters to zero completely disable dynamic remastering. Setting the above parameters also require database restart.

You can use queries described earlier under section "MANUAL REMASTERING" as well as check for number of opens in X$object_policy_statistics to verify if you should consider tuning the parameters discussed above. Sum of sopens, xopens and xfers decides whether the object will be considered for DRM .

SQL> select object, node, sopens, xopens, xfers  from x$object_policy_statistics  where object=
    ( select object_id pe from dba_objects where object_name='EMP' and object_type='TABLE' and owner='SCOTT');
    OBJECT   NODE  SOPENS  XOPENS  XFERS
---------- ---------- ---------- ---------- ---------- ---------- ----------

     32614        2               0            3817         1195

There are other useful views that DBA should always keep checking for tuning the dynamic remastering latency. gv$policy_history which displays DRM history at object level (included from 11g onwards) as well as gv$dynamic_remaster_stats which displays statistical information about dynamic remastering process.

select * from gv$policy_history;
   INST_ID POLICY_EVENT  DATA_OBJECT_ID TARGET_INSTANCE_NUMBER EVENT_DATE
---------- -------------------- -------------- ---------------------- -------------------- --------------------
     1        initiate_affinity         32614                      1                            09/14/2014 13:11:14

     1         push_affinity           32614                      2                            09/14/2014 13:15:54

When DRM triggers, it can easily be visible from LMON and LMD trace files. Alternatively, you can also explicitly turn on the tracing using below:

alter system set events '10430 trace name context forever, level 8';

Diable tracing:
Alter system set events '10430 trace name context off';

 REMASTERING DUE TO INSTANCE CRASH


Presently node RAC2 is the master of SCOTT.EMP. Lets crash this node by shutting it down abnormally and see what's happening:

$srvct stop instance -d rac -i rac2 -o abort

You can clearly see in the output of below query that current_master has changed to 1 i.e. node 2 from previous_master = 2 (node 3) and remaster count has also increased to 5.

SYS@RAC2>select o.object_name, m.CURRENT_MASTER, 
                          m.PREVIOUS_MASTER, m.REMASTER_CNT
                   from   dba_objects o, v$gcspfmaster_info m 
                   where o.data_object_id=32614
                   and m.data_object_id = 32614;

OBJECT_NAME     CURRENT_MASTER PREVIOUS_MASTER REMASTER_CNT
————— ————– ————— ————————— ————– ————
EMP                                  1                              2                                5

I hope you find this article useful for you. Pls let me know for any questions or suggestions to make it more understandable for you.

References:
http://oracleinaction.com/dynamic-remastering/
https://support.oracle.com