Showing posts with label 18c. Show all posts
Showing posts with label 18c. Show all posts

Sunday, January 28, 2024

ASMFD - Troubleshooting ASMCMD-9513

 I was converting my Oracle ASM disk management from udev rules to ASM Filter Driver (ASMFD) and encountered the below errors which might happen during the conversion operation and this post might help you fix the issue as quickly as possible. 


You can read more on Oracle ASMFD, configuration and migrating to ASMFD using this 19c documentation

1. Wrong/incorrect afd_diskstring value

[root@ol819-rac1 ~]#
[root@ol819-rac1 ~]# $ORACLE_HOME/bin/asmcmd afd_label asmdisk1 /dev/sdb1 --migrate
No devices to be labeled.
ASMCMD-9513: ASM disk label set operation failed.
Since my disks are already pointing to /dev/oracleasm due to the udev rules, ASMFD config file picked up the afd_diskstring parameter picked up this value by default and hence the issue. Edit the /etc/oracleafd.conf config file and retry the operation

[root@ol819-rac1 ~]# cat /etc/oracleafd.conf
afd_diskstring='/dev/oracleasm/*'
[root@ol819-rac1 ~]# vi /etc/oracleafd.conf
[root@ol819-rac1 ~]# cat /etc/oracleafd.conf
afd_diskstring='/dev/sd*'
[root@ol819-rac1 ~]# 
[root@ol819-rac1 ~]# $ORACLE_HOME/bin/asmcmd afd_label asm-disk1 /dev/sdb1 --migrate
Invalid label string : asm-disk1
ASMCMD-9513: ASM disk label set operation failed.

[root@ol819-rac1 ~]# $ORACLE_HOME/bin/asmcmd afd_label asmdisk1 /dev/sdb1 --migrate
[root@ol819-rac1 ~]#
[root@ol819-rac1 ~]# $ORACLE_HOME/bin/asmcmd afd_lsdsk
--------------------------------------------------------------------------------
Label                     Filtering   Path
================================================================================
ASMDISK1                    ENABLED   /dev/sdb1
[root@ol819-rac1 ~]# ls -al /dev/oracleafd/disks
total 4
drwxrwxr-x. 2 oracle dba      60 Jan 18 10:28 .
drwxrwx---. 3 oracle dba      80 Jan 18 09:45 ..
-rw-rw-r--. 1 oracle oinstall 10 Jan 18 10:28 ASMDISK1
[root@ol819-rac1 ~]# 
Note: You can see we get the Invalid label string when provided the disk name as asm-disk1. hyphen (-) is not allowed in the disk name. If needed we can use underscore (_). I left it without any special characters. 

2. Retry label after wrong path

I used a wrong path value during label operation by providing the pointer instead of actual disk path and tried to relabel again causing ASM-9513 
We can use the kfod command to check the label details of the ASM disk. 

[root@ol819-rac1 ~]# asmcmd afd_label asmdisk1 /dev/sdb1 --migrate
label name ASMDISK1 is already in use
ASMCMD-9513: ASM disk label set operation failed.

[root@ol819-rac1 ~]# kfod OP=DISKS status=TRUE disks=ASM label=TRUE dscvgroup=TRUE
--------------------------------------------------------------------------------
 Disk          Size Header    Path                                    Disk Group         Site      Failgroup           Disk-Label User     Group
================================================================================
   1:      25599 MB MEMBER    /dev/oracleasm/asm-disk1                 DATA                 #              #             ASMDISK1 oracle   dba
[root@ol819-rac1 ~]# 
In the above you can see the path is /dev/oacleasm/asm-disk1 with the Disk-label set to ASMDISK1. This might work now as I still have udev rules in place so the pointer path would work. Once I remove the udev rules, this path will not exist and would cause issues in identifying disks and my cluster won't come up as the disk contains the voting disk as well
[root@ol819-rac1 ~]# asmcmd afd_unlabel asmdisk1
disk AFD:ASMDISK1 is already provisioned for ASM
No devices to be unlabeled.
ASMCMD-9514: ASM disk label clear operation failed.

[root@ol819-rac1 ~]# 
[root@ol819-rac1 ~]# 
[root@ol819-rac1 ~]# afdtool -delete '/dev/oracleasm/asm-disk1'
disk /dev/oracleasm/asm-disk1 is already provisioned for ASM
No devices to be unlabeled.
[root@ol819-rac1 ~]# afdtool -delete -f '/dev/oracleasm/asm-disk1'
Unlabeled device ASMDISK1
[root@ol819-rac1 ~]# 
[root@ol819-rac1 ~]# kfod OP=DISKS status=TRUE disks=ASM label=TRUE dscvgroup=TRUE
--------------------------------------------------------------------------------
 Disk          Size Header    Path                                    Disk Group         Site      Failgroup           Disk-Label User     Group
================================================================================
   1:      25599 MB MEMBER    /dev/oracleasm/asm-disk1                 DATA                 #              #                    # oracle   dba
[root@ol819-rac1 ~]# 
afd_unlabel fails with ASMCMD-9514 stating disk already provisioned for ASM. This means afd_label command not only labels the disk but also includes it in the list of disks managed by AFD. So AFD does not allow altering the label for a disk that is currently being managed by AFD.

We can use afdtool command to delete the label in this case. 
After unlabeling the disk, we can see the kfod command has null value for Disk-label for the disk. 

We can then use afd_label command to label the disk using the proper path as shown in this post above.

References:
 
ASMCMD AFD_LABEL --RENAME FAILS (Doc ID 2884565.1)

Friday, April 7, 2023

Oracle Transparent Application Continuity (TAC) - Part 2

I have started a transaction in my bank account and money got deducted but the screen doesn't get me the confirmation of the transaction and ended with a time out error. There could be multiple reason and one of the reason being one of the node on which the session was working crashed.

How many of you encountered this situation?

Now, if the bank/application once recovered and connected to the database (on a surviving node) and there is no details of the previous transaction available to the new session just got connected. What happens to the data? Would it get committed?

If the application tries to run the same previous transaction, money might be deducted twice. If the application doesn't retry, your transaction might not be carried out and you might miss some important last date of bill payment

So, how does Oracle handles this? 

In my last post, I explained about how Transparent Application Failover works and how it would be a great advantage for applications that mostly reads the database. 

As a continuation of that post, we will see about the Transparent Application Continuity on what is it and how does it benefit applications utilizing this feature of Oracle. 

Okay! We should know about Transaction Guard and Application Continuity before we dive into Transparent Application Continuity. Let's see them in brief to understand what they are.. 

Transaction Guard:

Transaction Guard provides a generic tool for applications to use for at-most-once execution in case of planned and unplanned outages. Applications use the logical transaction ID to determine the outcome of the last transaction open in a database session following an outage.
If you take the bank example, if the commit already happened and if application tries to replay the execution without knowing the status of the last transaction, this might lead to logical corruption of the data by committing duplicate transaction.

Benefits of Transaction Guard includes preserving the commit outcome, ensuring known outcome of every transaction and it can be used to provide at-most-once transaction execution

Let's see the illustration below



DB node crashed when: 
Session 1 committed the transaction but acknowledgement is not yet returned to application. Application now don't know status of the previous transaction
Session 2 is idle, hence no impact to the session even this session goes off
Session 3 issues the commit but before the commit is received in the database

So without transaction guard, session 1 and 3 won't be able to proceed further since they don't know the status of the previous transaction. Client is left unknown whether transaction is committed. Logical corruption is possible if application try to rerun the transaction and missing data is possible if the transaction is not retried. This is why it is difficult to failover DML when a failure occurs on a specific node as the application fails to recognize that the last submission has committed, or shall commit sometime soon, or has not run to completion. 

How do we resolve this mystery? What if we store the commit outcome in the database itself before letting the application/client know? This is what was done by introducing LTXID in Oracle database version 12.1.0.1. 

Durability of commit is saved as a Logical Transaction Identifier (LTXID) at commit for all supported transaction types and is unique for each transaction. Applications can use this LTXID to determine the commit status. LTXID is persisted on commit and reused following a rollback. The transaction history table maintains the mapping of LTXIDs to database transaction. LTXID_TRANS table is created to store the LTXIDs and is not accessible by non-DBA user. 
Hope it is clear what exactly is transaction guard provides us. 

Transaction Guard is enabled using proper database service configuration and the requirements are as follows

1. Use a non-default service
2. Set the property COMMIT_OUTCOME=TRUE in the service to determine COMMIT is accessible and to get the commit outcome of the last transaction. RETENTION_TIMEOUT can be increased if necessary (default is 24 hrs)
3. Ensure that FAN is configured with ONS to talk with 12c database clients
 
In depth understanding of Transaction Guard is provided in this whitepaper

If I had to modify the orabliss_taf service that we created in our previous post to include transaction guard, we need to modify the service as below
srvctl modify service -db cdbrac -service orabliss_taf -commit_outcome TRUE
[oracle@ol819-rac2 bin]$ srvctl modify service -db cdbrac -service orabliss_taf -commit_outcome TRUE
[oracle@ol819-rac2 bin]$ srvctl config service -db cdbrac -service orabliss_taf |egrep "Commit|Retention"
Commit Outcome: true
Retention: 86400 seconds
[oracle@ol819-rac2 bin]$

Connection Pools and Application Continuity: 

Application Continuity was first introduced for JDBC thin applications in Oracle 12.1 which uses an Oracle Connection pool such as Universal Connection Pool (UCP) for JDBC clients and the support for OCI was enhanced in Oracle 12.2. Connection pools can also be configured for third party applications as well. 

A connection pool is a cache of database connection objects. The objects represent physical database connections that can be used by an application to connect to a database. At run time, the application requests a connection from the pool which reuses the connection objects and reduces new connection object creation. The pool also leverages many high availability and performance features available through an Oracle RAC database. These features include Fast Connection Failover (FCF), Run-time connection Load Balancing (RLB), and Connection Affinity.

An LTXID is associated with an application request at check-out from the connection pool, and is disassociated from the application request at check-in back to the pool. Between check-out and check-in, the LTXID on the session is exclusively held by that application request meaning the LTXID is associated with the next connection session. This allows to detect duplication and failover and also basic replay by third party containers replaying the last request after a non recoverable outage. 

Application Continuity makes use of Transaction Guard (LTXID) and replays database requests exactly as they were originally submitted, to ensure a result consistent with the request. To do this, it first restores and validates the session on surviving instance and replays as necessary. 
Mutables such as NEXTVAL of sequences and calls to sysdate are taken care by using the same values when they are replayed. This is accomplished by calls to Transaction Guard. 

So KEEP privileges for mutables are required to be provided to users.

To configure Application Continuity the following parameters are needed in the service configuration. 


So for Application Continuity to be enabled in the Oracle RAC, I have to modify my service as below. 
srvctl modify service -db cdbrac -service orabliss_taf -failover_restore LEVEL1 -failoverdelay 30 -failoverretry 5 -failovertype TRANSACTION -replay_init_time 1800 -notification TRUE -drain_timeout 300 -stopoption IMMEDIATE
As a Database Administrator, Application Continuity has a major blocker for implementation since there needs to be code changes in the application to make use of both connection pools and get LTXID return codes and program as necessary. In this post, we are not going to test how Application Continuity works as that requires client coding. 

With Oracle 18c, Oracle introduced Transparent Application Continuity which eliminates the use of connection pools (still is recommended) and in Oracle 19c, it is further enhanced and extends support to applications that do not use connection pools. 

Transparent Application Continuity (TAC):

Transparent Application Continuity is a mode of Application Continuity that transparently tracks and records session and transactional state so that a DB session can be recovered following an outage. There are no code changes involved in the application and DBA doesn't even needs to know of application code to implement TAC. TAC uses LTXID and a state tracking infrastructure 

To use TAC, the following configuration/steps can be followed

  1. Use a supported client. SQLPLUS 19c or later is supported and we will be using this in our demo. Few other clients are Oracle JDBC Replay Driver 18c or later, UCP 18c or later, OCI Session Pool 19c or later, etc. 
  2. If using connection pool, connections should be returned to the connection pool
  3. Set FAILOVER_RESTORE to AUTO
  4. Enable mutables use in application by providing KEEP grants to Sequences and date time to users. Oracle 19c automatically KEEPs the mutables for sql. 
  5. SIDE EFFECTS (like an extenal action such as sending emails, transfer files, etc) are disabled.
With this change, if I need to enable TAC in my database I have to modify my service with below parameters (compare this with Application Continuity)



You may notice failover_restore and failovertype both defined as AUTO to enable TAC. My service modification would be as below. 
srvctl modify service -db cdbrac -service orabliss_taf -failover_restore AUTO -failoverdelay 30 -failoverretry 5 -failovertype AUTO -replay_init_time 1800 -notification TRUE -drain_timeout 300 -stopoption IMMEDIATE

Demo of Transparent Application Continuity (TAC): 

Let's check how TAC gives a seemless application failover with DML statements by doing the following. 
  1. Connect to database using SQLPLUS with oracle_taf without Transaction Guard enabled (without -commit_outcome=TRUE)
  2. Perform a DML and check ltxid_trans table from a different SYS session
  3. Shut down instance running DML and observe error
  4. Modify oracle_taf to enable Transaction Guard and connect to database using the service
  5. Perform DML and check ltxid_trans table from a different session
  6. Modify oracle_taf to enable Transparent Application Continuity 
  7. Connect to database using the TAC enabled service
  8. Perform DML and shut down instance running DML and observe the results
[oracle@ol819-rac1 ~]$ sqlplus dummy/dummy@orabliss_taf

<<< Removed Banner >>> 

dummy@node1> select count(*) from test_table;

  COUNT(*)
----------
   8199536

dummy@node1> delete from test_table where object_id=57732;

112 rows deleted.

dummy@node1> commit;

Commit complete.

dummy@node1>

Check ltxid_trans table
[oracle@ol819-rac1 ~]$ sql / as sysdba

<<< Removed Banner >>>

SQL> @/home/oracle/ccon
Enter value for container: orabliss
old:alter session set container=&container
new:alter session set container=orabliss

Session altered.

SQL>
SQL> select * from ltxid_trans;

no rows selected

SQL> 
I'll now trigger a delete on the table and while the delete is running midway, I'll shut the OS (node 2) abruptly to create a crash scenario. 
sys@node1> @/home/oracle/sessions.sql

   INST_ID    SID    SERIAL    SQL_ID    Username    Seconds in Wait    Command                   Machine    OS User      Status                                        Module
__________ ______ _________ _________ ___________ __________________ __________ _________________________ __________ ___________ _____________________________________________
         2    424      1281           DUMMY                                     ol819-rac1.selvapc.com    oracle     inactive    SQL*Plus

sys@node1> 

dummy@node1> select sys_context('userenv','sid') from dual;

SYS_CONTEXT('USERENV','SID')
--------------------------------------------------------------------------------
424

dummy@node1> delete from test_table where rownum<500000; 

499999 rows deleted.

dummy@node1>

<<< Shut down node 2 abruptly to create crash scenario >>>

dummy@node1> commit;
commit
     *
ERROR at line 1:
ORA-25405: transaction status unknown


dummy@node1> select program,sid,failover_type,failover_method,failed_over from v$session where sid=sys_context('userenv', 'sid');

PROGRAM                                                 SID FAILOVER_TYPE FAILOVER_M FAI
------------------------------------------------ ---------- ------------- ---------- ---
sqlplus@ol819-rac1.selvapc.com (TNS V1-V3)              460 SELECT        BASIC      YES

dummy@node1> select count(*) from test_table;

  COUNT(*)
----------
   8199424

dummy@node1>

We can see the transaction didn't commit even though the session got failed over to the other instance. 
What if there is a select involved with the DML statement? Let's check that out..
dummy@node1> select sys_context('userenv','sid') from dual;

SYS_CONTEXT('USERENV','SID')
--------------------------------------------------------------------------------
430

dummy@node1> insert into test_table select * from test_table;

 
Session 430 from node 1 got connected to node 2 and started an insert statement. While the statement is under process, I shutdown the node 2 abruptly. 
sys@node1> @/home/oracle/sessions.sql

   INST_ID    SID    SERIAL    SQL_ID    Username    Seconds in Wait    Command                   Machine    OS User      Status      Module
__________ ______ _________ _________ ___________ __________________ __________ _________________________ __________ ___________ ___________
         2    430     10274           DUMMY                                     ol819-rac1.selvapc.com    oracle     inactive    SQL*Plus

<<< Shut down node 2 here... >>>

sys@node1> @/home/oracle/sessions.sql

no rows selected

 
After sometime, when I cancelled the session I got the below error
dummy@node1> insert into test_table select * from test_table;

^Cinsert into test_table select * from test_table
*
ERROR at line 1:
ORA-25408: can not safely replay call


dummy@node1> 
This is because the fact that Transparent Application Failover (TAF) cannot handle transactions that include data manipulation language (DML). In this case, we need to log out of application, reconnect and rerun the transaction. We can see the session got failedover to instance 1

dummy@node1> select program,sid,failover_type,failover_method,failed_over from v$session where sid=sys_context('userenv', 'sid');

PROGRAM                                                 SID FAILOVER_TYPE FAILOVER_M FAI
------------------------------------------------ ---------- ------------- ---------- ---
sqlplus@ol819-rac1.selvapc.com (TNS V1-V3)              175 SELECT        BASIC      YES

dummy@node1>

We will now modify the service to enable Transaction guard and check the ltxid_trans table to check commit data details. Note: We are not going to write code to get the commit_outcome and test but will just see the contents of the ltxid_trans table. We will proceed with TAC once this check is done.
[oracle@ol819-rac1 ~]$ srvctl modify service -db cdbrac -service orabliss_taf -commit_outcome TRUE
[oracle@ol819-rac1 ~]$ srvctl config service -db cdbrac -service orabliss_taf |egrep "Commit|Retention"
Commit Outcome: true
Retention: 86400 seconds
[oracle@ol819-rac1 ~]$ sqlplus dummy/dummy@orabliss_taf

dummy@node1> select sys_context('userenv','sid') from dual;

SYS_CONTEXT('USERENV','SID')
--------------------------------------------------------------------------------
423

dummy@node1> delete from test_table where rownum<500000;

499999 rows deleted.

dummy@node1> commit;

Commit complete.

dummy@node1>
SYS@node2> select * from ltxid_trans;

   MAJ_VERSION    MIN_VERSION    INST_ID         DB_ID                        SESSION_GUID    TXN_UID    COMMIT_NO                                START_DATE    SERVICE_ID    STATE    FLAGS    REQ_FLAGS    ERROR_CODE
______________ ______________ __________ _____________ ___________________________________ __________ ____________ _________________________________________ _____________ ________ ________ ____________ _____________
             1              2          2    2960387100 F8B741BDAFB62A60E053CA38A8C0014E           111            0 07-APR-23 08.33.32.185503000 AM +05:30                1        1        0            0             0

SYS@node2>
Now, as we have enabled transaction guard once the transaction is committed, we can see the ltxid_trans table is updated with the session and transaction information. This information will be used for replaying the transaction during failover

As we have enabled only Transaction Guard as of now and if the node crashed before committing the data, we would be getting the below error message if we try to commit the data after the session failover to the surviving instance. 
dummy@node1> insert into test_table select * from test_table;

7199426 rows created.

<<< Shutdown OS abruptly >>>

dummy@node1> commit;
commit
     *
ERROR at line 1:
ORA-25402: transaction must roll back

dummy@node1> 
Now, as the final phase of our testing we will enhance the orabliss_taf to enable Transparent Application Continuity (TAC) and observe the results 

[oracle@ol819-rac1 ~]$ srvctl modify service -db cdbrac -service orabliss_taf -failover_restore AUTO -failovertype AUTO -failoverdelay 30 -failoverretry 5 -replay_init_time 1800 -notification TRUE -drain_timeout 300 -stopoption IMMEDIATE
[oracle@ol819-rac1 ~]$
[oracle@ol819-rac1 ~]$ sqlplus dummy/dummy@orabliss_taf

...
...

dummy@node1> select sys_context('userenv','sid') from dual;

SYS_CONTEXT('USERENV','SID')
--------------------------------------------------------------------------------
232

dummy@node1> insert into test_table select * from test_table;

<<< Shut down node 2 while insert is running >>>

14898851 rows created.

dummy@node1> dummy@node1> commit;

Commit complete.

dummy@node1> select sys_context('userenv','sid') from dual;

SYS_CONTEXT('USERENV','SID')
--------------------------------------------------------------------------------
42

dummy@node1> select program,sid,failover_type,failover_method,failed_over from v$session where sid=sys_context('userenv', 'sid');

PROGRAM                                                 SID FAILOVER_TYPE FAILOVER_M FAI
------------------------------------------------ ---------- ------------- ---------- ---
sqlplus@ol819-rac1.selvapc.com (TNS V1-V3)               42 AUTO          BASIC      YES

dummy@node1>
Though I have introduced the crash in between the DML statement, the session failed over to instance 1 and completed the insert statement without any issues. 
Session state from node 1 was observed as below.
SYS@node1> @/home/oracle/sessions.sql

   INST_ID    SID    SERIAL           SQL_ID    Username    Seconds in Wait    Command                   Machine    OS User      Status      Module
__________ ______ _________ ________________ ___________ __________________ __________ _________________________ __________ ___________ ___________
         1     24      7850                  DUMMY                                     ol819-rac1.selvapc.com    oracle     inactive    SQL*Plus
         2    232     62564 19uk9dbxy0zbr    DUMMY                        3 INSERT     ol819-rac1.selvapc.com    oracle     active      SQL*Plus

SYS@node1> @/home/oracle/sessions.sql

   INST_ID    SID    SERIAL           SQL_ID    Username    Seconds in Wait    Command                   Machine    OS User      Status      Module
__________ ______ _________ ________________ ___________ __________________ __________ _________________________ __________ ___________ ___________
         1     24      7850                  DUMMY                                     ol819-rac1.selvapc.com    oracle     inactive    SQL*Plus
         1     42     53960 19uk9dbxy0zbr    DUMMY                        2 INSERT     ol819-rac1.selvapc.com    oracle     active      SQL*Plus

SYS@node1>
We can see the session 232 was running the INSERT statement against INST_ID 2 and then failed over to INST_ID 1 with SID 42. 

Conclusion: 

So the answer to my initial question "Whether DMLs will be failed over to surviving instance if one of the instance in a Oracle RAC is crashed?" is now clear. 
Oracle introduced Transaction Guard which is effectively enhanced to be utilized by Transparent Application Continuity (TAC) that can be implemented in the database without any code changes to application to have a downtime free database experience.. !

References: 



Happy TACing...!!!



Tuesday, March 21, 2023

Oracle Transparent Application Continuity (TAC) - Part 1 (TAF)

 I was asked a question a few days ago whether DMLs will be failed over to surviving instance if one of the instance in a Oracle RAC is crashed. 

I took sometime to get a proper answer to this question as I was not sure how DMLs were handled in RAC. All I know until that time was "Select" queries will be failed over to another instance. This mechanism is called Transparent Application Failover or TAF which is in Oracle from a long time since Oracle Parallel Server (OPS) introduced in Oracle 8i. 

Transparent Application Failover

When an instance fails, TAF creates a new session on the surviving node or when the SELECT mode is configured, the sessions are replayed on the surviving node. For example, let us assume we are querying a table with a million records and when the query is in midway and node fails, the session will be created on the other surviving node to the state where the query is started and the SELECT is replayed in the other node to provide output to the end user/application. This new session creation on the other node is transparent to the end user/application.

FAN and ONS

Fast Application Notification (FAN) is a critical component in all of the High Availability solutions where continuous service and continuous connections built into RAC are extended to applications and application servers. When a state of database service changes such as down or unresponsive, the new status is posted to interested subscribers via FAN events. More on FAN can be found here

Oracle Notification Services (ONS) is installed as part of installation of Oracle Grid Infrastructure or Oracle dataguard installation. ONS is responsible for propagating FAN events to all other ONS daemons it is registered with. There is one ONS daemon started per cluster node. ONS facilitates FAN and also the load balancing feature of Oracle RAC for workload management. 

Session Failover without TAF

Let's see an example of a session's behavior when we connect via the default service. This service is basic service created to connect to the database. 

My TNS entry is as below

orabliss =
    (DESCRIPTION =
        (ADDRESS = (PROTOCOL = TCP)(HOST = ol819-scan)(PORT = 1521))
        (CONNECT_DATA =
            (SERVER = DEDICATED)
            (SERVICE_NAME = orabliss.selvapc.com)
        )
    )
I have created a table TEST_TABLE under DUMMY user in the database so I'll perform the full table select and simulate the crash by turning off the node where the session is connected. 

The select statement takes ~ 50 seconds to complete and I will be terminating the node while the query is running. 

Session details: 


You can see I have connected to INST_ID 2 (ol819-rac2) from Machine ol819-rac1

First normal query execution:
SQL> select * from test_table;

=== Suppressed query output ===

440,813 rows selected.

Elapsed: 00:00:50.744
 
Now I'll be introducing a crash by turning off node 2 after firing the select query again and monitor the existing session from node 1:
...
...
...
OWNER
--------------------------------------------------------------------------------------------------------------------------------
OBJECT_NAME
--------------------------------------------------------------------------------------------------------------------------------
SUBOBJECT_NAME                                                                                                                    OBJECT_ID DATA_OBJECT_ID OBJECT_TYPE             CREATED   LAST_DDL_
-------------------------------------------------------------------------------------------------------------------------------- ---------- -------------- ----------------------- --------- ---------
TIMESTAMP           STATUS  T G S  NAMESPACE EDITION_NAME                                                                                                                     SHARING            E O A
------------------- ------- - - - ---------- -------------------------------------------------------------------------------------------------------------------------------- ------------------ - - -
DEFAULT_COLLATION                                                                                    D S CREATED_APPID CREATED_VSNID MODIFIED_APPID MODIFIED_VSNID
---------------------------------------------------------------------------------------------------- - - ------------- ------------- -------------- --------------
2019-04-17:01:42:19 VALID   N N N          1                                                                                                                                  METADATA LINK        Y N
                                                                                                     N N

ERROR:
ORA-03135: connection lost contact
Process ID: 55738
Session ID: 599 Serial number: 54452



29370 rows selected.

SQL>
Now, once the node is down the session running the select query encountered an error ORA-03135. Note the query fetched only ~29k  records of total  ~ 440k records before failure. 

Configuring TAF

TAF is configured by creating a service on RAC and assigning failover parameters as shown below. Do not use the default service that is created while creating a container database or PDB as this service will be running on all the instances and running always on a RAC system. You should not alter or modify this service. This default service cannot be disabled, relocated or restricted so it doesn't provide high availability. Instead create a specific service with unique identifier to connect to the intended database with the intended properties. 

From version 11.2 onwards we can create services using srvctl command. 

srvctl add service -db cdbrac -pdb orabliss -service orabliss_taf -tafpolicy BASIC -failovertype SELECT -clbgoal LONG -preferred cdbrac1,cdbrac2

[oracle@ol819-rac1 ~]$ srvctl add service -db cdbrac -pdb orabliss -service orabliss_taf -tafpolicy BASIC -failovertype SELECT -clbgoal LONG -preferred cdbrac1,cdbrac2
[oracle@ol819-rac1 ~]$ srvctl start service -db cdbrac -service orabliss_taf
[oracle@ol819-rac1 ~]$ srvctl status service -db cdbrac -service orabliss_taf
Service orabliss_taf is running on instance(s) cdbrac1,cdbrac2
[oracle@ol819-rac1 ~]$

We now see the details of the services from the database as below


-tafpolicy accepts only BASIC from version 11.2 meaning the failover occurs only at the time of failure and there is no pre-connect option available. 

-failovertype of SELECT enables the read on other surviving node once there is a crash on the session's existing node. Other option for TAF is SESSION where upon node failure the session is connected on the surviving instance but does not continue the SELECT.

Now, my TNS entry is as below using the newly created service

orabliss_taf =
    (DESCRIPTION =
        (ADDRESS = (PROTOCOL = TCP)(HOST = ol819-scan)(PORT = 1521))
        (CONNECT_DATA =
            (SERVER = DEDICATED)
            (SERVICE_NAME = orabliss_taf.selvapc.com)
        )
    )
I'm connecting to the DB using the orabliss_taf service and simulate the crash as before. 
SQL> select program,sid,failover_type,failover_method,failed_over from v$session where sid=sys_context('userenv', 'sid');

PROGRAM                                                 SID FAILOVER_TYPE FAILOVER_M FAI
------------------------------------------------ ---------- ------------- ---------- ---
sqlplus@ol819-rac1.selvapc.com (TNS V1-V3)              606 SELECT        BASIC      NO

Elapsed: 00:00:00.00
SQL> select * from test_table;

=== Suppressed query output ===

=== Introduced the crash while query is running midway ===

440813 rows selected.

Elapsed: 00:08:01.75
SQL>
SQL> select program,sid,failover_type,failover_method,failed_over from v$session where sid=sys_context('userenv', 'sid');

PROGRAM                                                 SID FAILOVER_TYPE FAILOVER_M FAI
------------------------------------------------ ---------- ------------- ---------- ---
sqlplus@ol819-rac1.selvapc.com (TNS V1-V3)               26 SELECT        BASIC      YES

Elapsed: 00:00:00.06
SQL>
While the original session was created with SID=606, the session failed over to node 1 and have a new SID=26. We can see the FAILED_OVER column also changed to YES. 
The query also took a significantly longer time of ~ 8 minutes against ~ 50 seconds as the failover happens to the other node and then the select has to start again from the beginning after the instance recovery is completed. So this 8 minute is a combined time of all the failover activities along with the query execution time where the session waits for the instance recovery as well.

We can see the alert log will register the below details indicating the instance recovery due to node 2 crash. This is just to check whether instance recovery is completed properly
Reconfiguration started (old inc 4, new inc 6)
List of instances (total 1) :
 1
Dead instances (total 1) :
 2
My inst 1
publish big name space -  dead or down/up instance detected, invalidate domain 0
 Global Resource Directory frozen
* dead instance detected - domain 0 invalid = TRUE
* dead instance detected - domain 2 invalid = TRUE, need cdb-level instance recovery
* dead instance detected - domain 3 invalid = TRUE, need cdb-level instance recovery
 Communication channels reestablished
 Master broadcasted resource hash value bitmaps
...
...
 Post SMON to start 1st pass IR
2023-03-18T10:11:45.794262+05:30
CDB IR excluding pdb 2 which was cleanly closed.
2023-03-18T10:11:45.794383+05:30
Instance recovery: looking for dead threads
2023-03-18T10:11:45.812848+05:30
Beginning instance recovery of 1 threads
...
...
...
Started redo application at
 Thread 2: logseq 83, block 157204, offset 0
...
...
...
2023-03-18T10:11:46.025646+05:30
Redo thread 2 internally disabled at seq 84 (SMON)
CDB instance recovery complete: pdb 2 valid 1 (flags x10, pdb flags x40080)
CDB instance recovery complete: pdb 3 valid 1 (flags x10, pdb flags x40080)
CDB instance recovery complete: pdb 0 valid 1 (flags x10, pdb flags x80)
...
...
So, the Transparent Appliation Failover (TAF) option is great for applications which reads a lot as they don't have to worry about DB getting down as the services will get the work done transparent to the application. 

Now, what if a batch job runs in the database performing millions of DML and then the node is crashed? This could happen and mostly it's not by software error but sometimes due to patching window where applications are not appraised of the schedule, issues with underlying OS or storage causing DB node to crash, etc,. What would be the condition of the database sessions? Will the DMLs continue to run on the other surviving node? Do we have any automated mechanism to take care of this situation for us? 

Let's see about that in the next post. 

References: 


Happy TAFing...!!!

Monday, January 30, 2023

Oracle CMU - Service Principal Name with multiple servers

 I have written regarding setting up Oracle Centrally Managed Users (CMU) and the various troubleshooting steps in my previous posts. You can take a look at those by clicking below links.

Once we complete the setup, one of the most anticipated questions one will have is how many Service Principal Accounts (oserv - in our lab) do we need to create if we have databases on multiple servers. For example, if we have 10 servers, do we need 1 Service Principal Account (SPN) or 10 SPNs individual to each server. 

I have raised this question to Oracle support on multiple SRs and the answers differ from each SR. One engineer says all we need is 1 SPN and another engineer says we need 10 SPNs if we have 10 servers. This is confusing. So, now we will test it ourselves. I'll be configuring Kerberos Authentication on another server (linux75.selvapc.com) running Oracle database 12.2.0.1 

For this test, we are not going to change any of the steps from the PART A and PART B of the CMU configuration since that will complete the Kerberos authentication. My sqlnet.ora and krb5.conf will not have any changes from the other server. 

[oracle@linux75 admin]$ more sqlnet.ora
# sqlnet.ora Network Configuration File: /u01/db/122/network/admin/sqlnet.ora
# Generated by Oracle configuration tools.

NAMES.DIRECTORY_PATH= (TNSNAMES, EZCONNECT)
#Kerberos Parameters
SQLNET.AUTHENTICATION_SERVICES=(beq,kerberos5)
SQLNET.FALLBACK_AUTHENTICATION=TRUE
SQLNET.AUTHENTICATION_KERBEROS5_SERVICE=oracle
SQLNET.KERBEROS5_CONF=/u01/db/122/network/admin/krb5.conf
SQLNET.KERBEROS5_CLOCKSKEW=6000
SQLNET.KERBEROS5_CONF_MIT=TRUE
#Following parameter is server-side only
SQLNET.KERBEROS5_KEYTAB=/u01/db/122/network/admin/linux75-database.keytab
[oracle@linux75 admin]$
[oracle@linux75 admin]$ more krb5.conf
[libdefaults]
    default_realm = ORABLISS.COM
    clockskew = 6000
    passwd_check_s_address = false
    noaddresses = true
    forwardable = yes
[realms]
    ORABLISS.COM = {
        kdc = 192.168.56.99:88
    }
[domain_realm]
    ORABLISS.COM = ORABLISS.COM
    .ORABLISS.COM = ORABLISS.COM
    orabliss.com = ORABLISS.COM
    .orabliss.com = ORABLISS.COM
[oracle@linux75 admin]$
You can notice, the only difference will be the keytab file specific to my linux75 server. 

The keytab file is generated as below 
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.

PS C:\Users\Administrator> ktpass -princ oracle/linux75.selvapc.com@ORABLISS.com -pass Qwerty123# -mapuser oserv@orabliss.com -crypto ALL -ptype KRB5_NT_PRINCIPAL -out Z:\linux75-database.keytab
Targeting domain controller: DS2019.orabliss.com
Successfully mapped oracle/linux75.selvapc.com to oserv.
Password successfully set!
Key created.
Key created.
Key created.
Key created.
Key created.
Output keytab to Z:\linux75-database.keytab:
Keytab version: 0x502
keysize 66 oracle/linux75.selvapc.com@ORABLISS.com ptype 1 (KRB5_NT_PRINCIPAL) vno 11 etype 0x1 (DES-CBC-CRC) keylength 8 (0xfb5e5b384c467904)
keysize 66 oracle/linux75.selvapc.com@ORABLISS.com ptype 1 (KRB5_NT_PRINCIPAL) vno 11 etype 0x3 (DES-CBC-MD5) keylength 8 (0xfb5e5b384c467904)
keysize 74 oracle/linux75.selvapc.com@ORABLISS.com ptype 1 (KRB5_NT_PRINCIPAL) vno 11 etype 0x17 (RC4-HMAC) keylength 16 (0xc5c6bc2b1d924244af78c13df85a5eee)
keysize 90 oracle/linux75.selvapc.com@ORABLISS.com ptype 1 (KRB5_NT_PRINCIPAL) vno 11 etype 0x12 (AES256-SHA1) keylength 32 (0xc9e1bf8b841bd336c3ab4fca0c67f0037b21d5719b4c661606a0745cd823e7f7)
keysize 74 oracle/linux75.selvapc.com@ORABLISS.com ptype 1 (KRB5_NT_PRINCIPAL) vno 11 etype 0x11 (AES128-SHA1) keylength 16 (0x4e84b8fae0598b793cd76a09f5658f29)
PS C:\Users\Administrator>
You can now notice in the above command, I'm still using the -mapuser as oserv@orabliss.com which is our SPN. Now the account will look like below.



Notice the User logon name has changed from ORACLE/linux-8.selvapc.com to oracle/linux75.selvapc.com 

Note: Please see the reason for small case usage for "oracle" in the troubleshooting post. 

Now, we are ready for testing. Transfer the keytab file generated to database server linux75.selvapc.com and create the externally authenticated user as usual. Once all is done, we are ready to connect to the database. 
SQL> create user cmuuser identified externally as 'snagulan@ORABLISS.COM';
grant create session to cmuuser;
grant select on v_$database to cmuuser;
User created.

SQL>
Grant succeeded.

SQL>

Grant succeeded.

SQL>
SQL> exit
Disconnected from Oracle Database 12c Enterprise Edition Release 12.2.0.1.0 - 64bit Production
[oracle@linux75 admin]$ okinit snagulan

Kerberos Utilities for Linux: Version 12.2.0.1.0 - Production on 29-JAN-2023 12:53:43

Copyright (c) 1996, 2016 Oracle.  All rights reserved.

Configuration file : /u01/db/122/network/admin/krb5.conf.
Password for snagulan@ORABLISS.COM:
[oracle@linux75 admin]$
[oracle@linux75 admin]$ oklist

Kerberos Utilities for Linux: Version 12.2.0.1.0 - Production on 29-JAN-2023 12:53:50

Copyright (c) 1996, 2016 Oracle.  All rights reserved.

Configuration file : /u01/db/122/network/admin/krb5.conf.
Ticket cache: FILE:/tmp/krb5cc_54321
Default principal: snagulan@ORABLISS.COM

Valid starting     Expires            Service principal
01/29/23 12:53:48  01/29/23 22:53:48  krbtgt/ORABLISS.COM@ORABLISS.COM
        renew until 01/30/23 12:53:44
[oracle@linux75 admin]$ sqlplus /@orclpdb

SQL*Plus: Release 12.2.0.1.0 Production on Sun Jan 29 12:53:58 2023

Copyright (c) 1982, 2016, Oracle.  All rights reserved.


Connected to:
Oracle Database 12c Enterprise Edition Release 12.2.0.1.0 - 64bit Production

SQL> set heading off
select 'DB_NAME (from v$database) : '||name,
       'CONTAINER_NAME            : '||sys_context('USERENV','CON_NAME'),
       'SESSION_USER              : '||sys_context('USERENV','SESSION_USER'),
       'AUTHENTICATED_IDENTITY    : '||sys_context('USERENV','AUTHENTICATED_IDENTITY'),
       'AUTHENTICATION_METHOD     : '||sys_context('USERENV','AUTHENTICATION_METHOD'),
SQL>        'IDENTIFICATION_TYPE       : '||sys_context('USERENV','IDENTIFICATION_TYPE'),
  2    3    4    5    6    7         --'LDAP_SERVER_TYPE          : '||sys_context('USERENV','LDAP_SERVER_TYPE'),
       'ENTERPRISE_IDENTITY       : '||sys_context('USERENV','ENTERPRISE_IDENTITY')
from v$database;  8    9

DB_NAME (from v$database) : ORCL
CONTAINER_NAME            : ORCLPDB
SESSION_USER              : CMUUSER
AUTHENTICATED_IDENTITY    : snagulan@ORABLISS.COM
AUTHENTICATION_METHOD     : KERBEROS
IDENTIFICATION_TYPE       : EXTERNAL
ENTERPRISE_IDENTITY       : snagulan@ORABLISS.COM


SQL> !hostname
linux75.selvapc.com

SQL>
 
Ok, we have successfully tested the kerberos connection from the newly configured database server. What happens to the previously configured database server since we have now change the SPN User Logon name? Does the CMU connection to the server linux-8.selvapc.com still work or does it break after new server configuration? 

Let's test it. 
[oracle@linux-8 ~]$ okinit snagulan

Kerberos Utilities for Linux: Version 19.0.0.0.0 - Production on 29-JAN-2023 23:31:46

Copyright (c) 1996, 2019 Oracle.  All rights reserved.

Configuration file : /oracle/db/db19/network/admin/krb5.conf.
Password for snagulan@ORABLISS.COM:
[oracle@linux-8 ~]$ oklist

Kerberos Utilities for Linux: Version 19.0.0.0.0 - Production on 29-JAN-2023 23:31:53

Copyright (c) 1996, 2019 Oracle.  All rights reserved.

Configuration file : /oracle/db/db19/network/admin/krb5.conf.
Ticket cache: FILE:/tmp/krb5cc_1000
Default principal: snagulan@ORABLISS.COM

Valid starting     Expires            Service principal
01/29/23 23:31:50  01/30/23 09:31:50  krbtgt/ORABLISS.COM@ORABLISS.COM
        renew until 01/30/23 23:31:47
[oracle@linux-8 ~]$ sqlplus /@odb1

SQL*Plus: Release 19.0.0.0.0 - Production on Sun Jan 29 23:32:12 2023
Version 19.10.0.0.0

Copyright (c) 1982, 2019, Oracle.  All rights reserved.

Last Successful login time: Sat Jan 28 2023 23:10:46 +05:30

Connected to:
Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
Version 19.10.0.0.0

SQL> set heading off
select 'DB_NAME (from v$database) : '||name,
           'CONTAINER_NAME                        : '||sys_context('USERENV','CON_NAME'),
       'SESSION_USER              : '||sys_context('USERENV','SESSION_USER'),
       'AUTHENTICATED_IDENTITY    : '||sys_context('USERENV','AUTHENTICATED_IDENTITY'),
       'AUTHENTICATION_METHOD     : '||sys_context('USERENV','AUTHENTICATION_METHOD'),
       'IDENTIFICATION_TYPE       : '||sys_context('USERENV','IDENTIFICATION_TYPE'),
       'LDAP_SERVER_TYPE          : '||sys_context('USERENV','LDAP_SERVER_TYPE'),
       'ENTERPRISE_IDENTITY       : '||sys_context('USERENV','ENTERPRISE_IDENTITY')
from v$database;SQL>   2    3    4    5    6    7    8    9

DB_NAME (from v$database) : CDB19
CONTAINER_NAME                    : ODB1
SESSION_USER              : SELVA
AUTHENTICATED_IDENTITY    : snagulan@ORABLISS.COM
AUTHENTICATION_METHOD     : KERBEROS_GLOBAL
IDENTIFICATION_TYPE       : GLOBAL EXCLUSIVE
LDAP_SERVER_TYPE          : AD
ENTERPRISE_IDENTITY       : cn=Selvakumar Nagulan,cn=Users,dc=orabliss,dc=com


SQL>
SQL> !hostname
linux-8.selvapc.com

SQL>
 
We can see the CMU connection still works without any issues. This concludes we just need 1 SPN (in our case, oserv) to configure kerberos or CMU connection for multiple oracle database servers. 

Happy CMUing...!!!

Thursday, December 29, 2022

Troubleshooting Kerberos and Oracle CMU configuration

 In one of my previous posts, I have shared complete steps to configure Oracle Centrally Managed Users (CMU) which was completely done in my personal lab environment. Though I have encountered few hiccups, I was able to set it up without big issues. 

Now I'm trying to perform a proof of concept of the same set up in my work environment where I was stuck with several different issues and crossing them one after the other. As I was working with Oracle support and trying to iron out all issues, I learnt a few things related to AD, LDAP, etc.,.


In this post, I'll share the troubleshooting steps for the issues encountered so that it could help fellow DBAs to set up in their environment. 

The following support note will be of much help but its very limited in identifying the exact issue. We can use this note as starting point to dig deeper. 

Kerberos Troubleshooting Guide (Doc ID 185897.1)

Ok, let's start with the different troubleshooting steps. This is not step by step approach and any step can be used any time as per need basis

1. Tracing:

We might just get random errors like ORA-12638, ORA-12631, etc., but it lead you to nowhere. So need to trace the session in order to work with Oracle support and provide them with what they are looking for. So how do you trace the session? 

We need to set the below parameters in sqlnet.ora in both database server (if server trace is needed) and client machine (usually this will be mandatory to have). 

TRACE_UNIQUE_SERVER = ON
TRACE_LEVEL_SERVER = 16
TRACE_DIRECTORY_SERVER = /oracle/ABC/19.0.0/network/log
TRACE_FILE_SERVER = Server_Trace
TRACE_TIMESTAMP_SERVER = ON
DIAG_ADR_ENABLED=OFF
TRACE_UNIQUE_CLIENT = ON
TRACE_LEVEL_CLIENT = 16
TRACE_DIRECTORY_CLIENT = /oracle/ABC/19.0.0/network/log
TRACE_FILE_CLIENT = Client_Trace
TRACE_TIMESTAMP_CLIENT = ON
DIAG_ADR_ENABLED=OFF
Once tracing is completed, we can switch the parameter TRACE_LEVEL_SERVER or TRACE_LEVEL_CLIENT to OFF which will stop tracing of sessions. Make sure you just turn on and turn off quickly once you test your connection as this is instance wide and all the sessions will be traced filling up your log location/disk quickly. 

You can identify the trace file by looking into the sqlnet.log file, just giving a sample log content from the sqlnet.log
***********************************************************************
Fatal NI connect error 12631, connecting to:
 (DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=xxxxx.com)(PORT=1521))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=ABC)(CID=(PROGRAM=sqlplus)(HOST=xxxxx)(USER=oracle))))

  VERSION INFORMATION:
        TNS for Linux: Version 19.0.0.0.0 - Production
        TCP/IP NT Protocol Adapter for Linux: Version 19.0.0.0.0 - Production
  Version 19.16.0.0.0
  Time: 23-DEC-2022 19:34:37
  Tracing to file: /oracle/ABC/19.0.0/network/log/client_trace_3655062.trc
  Tns error struct:
    ns main err code: 12631
    TNS-12631: Username retrieval failed
    ns secondary err code: 0
	
***********************************************************************
The logfile is indicated for the error that we have received. We can provide this log file to Oracle support for further review

2. Generating krb5 trace:

Below commands would help generate krb5 trace where we can trace actions related to okinit 

a) Set krb5_trace parameter

export KRB5_TRACE=<path>/krb5trace.trc
echo $KRB5_TRACE

b) On the database server run

$ okdstry
$ okinit <xxx>
$ oklist
$ sqlplus /@<connect_identifier>

This will generate krb5trace.trc file which can be uploaded to support for their analysis

3. Use of same case for principal name:

While creating SPN for database and generating keytab file, make sure you are using small case for creation and configuration 

I have used 
ktpass -princ ORACLE/linux-8.selvapc.com@ORABLISS.com -pass Qwerty123# -mapuser oserv@orabliss.com -crypto ALL -ptype KRB5_NT_PRINCIPAL -out Z:\linux-8-database.keytab

This resulted in error in my PoC environment. 

Instead use small case for database SPN
ktpass -princ oracle/linux-8.selvapc.com@ORABLISS.com -pass Qwerty123# -mapuser oserv@orabliss.com -crypto ALL -ptype KRB5_NT_PRINCIPAL -out Z:\linux-8-database.keytab  

sqlnet.ora should also have principal name in small case. 

SQLNET.AUTHENTICATION_KERBEROS5_SERVICE=oracle

4. ldapbind:

The ldapbind command-line tool enables you to see whether you can authenticate a client to a server. When we set up CMU and if we are getting error, we can check whether the client machine is getting authenticated with the server by using the command as below.

ldapbind -h <AD Host> -p 389 -D <value of ORACLE.SECURITY.DN> -w <value of ORACLE.SECURITY.PASSWORD>
ldapbind -h <AD Host> -p 389 -D <value of ORACLE.SECURITY.USERNAME> -w <value of ORACLE.SECURITY.PASSWORD>
ldapbind -h <AD Host> -p 636 -U 2 -W "file:<wallet location>" -P <wallet password> -D <value of ORACLE.SECURITY.DN> -w <value of ORACLE.SECURITY.PASSWORD>
ldapbind -h <AD Host> -p 636 -U 2 -W "file:<wallet location>" -P <wallet password> -D <value of ORACLE.SECURITY.USERNAME> -w <value of ORACLE.SECURITY.PASSWORD>

The first 2 test will test authentication with TCP and the next 2 will test authentication with SSL which we would have already set up in CMU. 

Example given below: 
$ ldapbind -h xxx.xxx.example.com -p 389 -D "CN=SV-oraservice,OU=Service,OU=Accounts,DC=xxx,DC=xxx,DC=example,DC=com" -w '%*9&nnh!!GFulh!G'
bind successful
$ ldapbind -h xxx.xxx.example.com -p 389 -D SV-oraservice -w '%*9&nnh!!GFulh!G'
bind successful
$ ldapbind -h xxx.xxx.example.com -p 636 -U 2 -W "file:/oracle/grid/19.0.0/network/admin/cmu_wallet" -P W9&nnhYT# -D "CN=SV-oraservice,OU=Service,OU=Accounts,DC=xxx,DC=xxx,DC=example,DC=com" -w '%*9&nnh!!GFulh!G'
bind successful
$ ldapbind -h xxx.xxx.example.com -p 636 -U 2 -W "file:/oracle/grid/19.0.0/network/admin/cmu_wallet" -P W9&nnhYT# -D SV-oraservice -w '%*9&nnh!!GFulh!G'
bind successful
$
If there is any issue with the connectivity, we will be getting a different error. For eg, let me try with a wrong password for the service account..
$ ldapbind -h xxx.xxx.example.com -p 389 -D "CN=SV-oraservice,OU=Service,OU=Accounts,DC=xxx,DC=xxx,DC=example,DC=com" -w 'wrongpass'
ldap_bind: Invalid credentials
ldap_bind: additional info: 80090308: LdapErr: DSID-0C090439, comment: AcceptSecurityContext error, data 52e, v4563
$
You can see when I provided a wrong password, I got the Invalid credentials error as expected. 

5. LDAP force sync

When there is change in the AD side such as password change for the service account, Oracle sometimes won't sync up with the changes and will throw error. We need to force sync Oracle with the LDAP services using the below parameters. 
Note: This is needed only when you doubt that Oracle is not synced up with AD/LDAP 
SQL> alter system set "_ldap_config_force_sync_up"=TRUE;

System altered.

SQL> alter system set "_ldap_config_force_sync_up"=FALSE;

System altered.

SQL> exit

$ sqlplus /@<connect string> 
 
We just need to set _ldap_config_force_sync_up TRUE and FALSE and retry the connection to the database. 

6. Viewing available certificates in AD server

Usually certificates are provided by issuing authority or if it's internal as in this case, AD team will provide you with the certificates. In my case, if I need to check what are the certificates I might need from AD team for the configuration to work, we can check using the openssl -showcert command. 

The command is as follows 
openssl s_client -connect <AD host>:636 -showcerts
$ orapki wallet display -wallet .
Oracle PKI Tool Release 19.0.0.0.0 - Production
Version 19.4.0.0.0
Copyright (c) 2004, 2021, Oracle and/or its affiliates. All rights reserved.

Requested Certificates:
User Certificates:
Oracle Secret Store entries:
ORACLE.SECURITY.DN
ORACLE.SECURITY.PASSWORD
ORACLE.SECURITY.USERNAME
Trusted Certificates:
Subject: CN=EXAMPLE MSPKI XXX Root CA
$
$ openssl s_client -connect abcdef12.xxx.xxx.example.com:636 -showcerts
CONNECTED(00000003)
depth=1 DC = com, DC = example, DC = xxx, DC = xxx, CN = qasmime95
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 CN = ABCDEF12.xxx.xxx.example.com
verify return:1
---
Certificate chain
 0 s:CN = ABCDEF12.xxx.xxx.example.com
   i:DC = com, DC = example, DC = xxx, DC = xxx, CN = qasmime95
-----BEGIN CERTIFICATE-----
MIIFuDCCBKCgAwIBAgITbwAABIUbCx91qS38VQAAAAAEhTANBgkqhkiG9w0BAQsF
ADB2MRMwEQYKCZImiZPyLGQBGRYDY29tMRkwFwYKCZImiZPyLGQBGRYJaG9uZXl3
...
...
...
ZA8ptBPzUXSgSNI+5wfh3F58Tfoi8Ig07ryyR4HD3WKGDE2DDSmMdnZ8oB2cBnuJ
T1RQK8+16GKI1bQFoNRfbwZZXmOjlIJiESZvUg==
-----END CERTIFICATE-----
 1 s:DC = com, DC = example, DC = xxx, DC = xxx, CN = qasmime95
   i:CN = EXAMPLE QMSPKI XXX Root CA
-----BEGIN CERTIFICATE-----
MIIGJDCCBAygAwIBAgITfQAAAAvG9D//4L986AAAAAAACzANBgkqhkiG9w0BAQsF
ADAsMSowKAYDVQQDEyFIb25leXdlbGwgUU1TUEtJIEludGVybmFsIFJvb3QgQ0Ew
...
...
...
ZNaJmBGIBZsLM6IrUw/WGAdNdPQUbcXMBdlSGY5DaGYMGNjnoOqPHQ==
-----END CERTIFICATE-----
---
Server certificate
subject=CN = ABCDEF12.xxx.xxx.example.com

issuer=DC = com, DC = example, DC = xxx, DC = xxx, CN = qasmime95
...
...
 
$
This check is required if you suspect that the provider team has given a prod certificate in place of non prod certificate or vice versa. You can see from above that the wallet has MSPKI XXX Root CA whereas the AD server has QMSPKI XXX Root CA. So we have an incorrect certificate in the wallet and hence it needs to be removed and proper certificate needs to be imported. 
Note: O/p of the command has been trimmed to include only needed content. 

7. Check your account availability

I know this can be an odd situation as to check our account availability but its better to check whether the account that you are trying to access is created/available in the AD and is binded to the service account. We can use ldapsearch command to check for the account existence with all the account details 

Command is as below 
ldapsearch -R -h <AD host> -p 389 -b "DC=xxxx,DC=xx,DC=com" -D <ad super user> -w "<ad user password>" "sAMAccountName=<AD user>"
$ ldapsearch -R -h abcdef12.xxxxx.xx.example.com -p 389 -b "DC=xxxxx,DC=xx,DC=example,DC=com" -D SV-oraservice -w 'password' "sAMAccountName=h368225"
CN=Selvakumar Nagulan,OU=Accounts,OU=Tier1,OU=Admin,DC=xxxxx,DC=xx,DC=example,DC=com
objectClass=top
objectClass=person
objectClass=organizationalPerson
objectClass=user
cn=Selvakumar Nagulan
...
...
 
$
Note that we use our service account as super user to check whether our DB user is properly bind to super user or not. You will get a blank response if the user is not bind or not configured. 



So that's all for now as I was able to successfully configure CMU after working with Oracle support with 2 different support tickets and internal AD team spanning over a month. You might connect with me on any issues regarding setting up CMU in your organization and I would be happy to help with my experiences troubleshooting the same. 

References: 

Oracle Centrally Managed Users (CMU) configuration

Happy Troubleshooting!!! 
 
 
 
 
 
 
 
 

Tuesday, November 22, 2022

Oracle Database and Anti Virus Softwares

 If you do a search for antivirus related issues in Oracle support, you will hit many notes talking about AVs causing issues with the Oracle database and this is not specific to any AV but in general to all the AV software. 

Taking a look at Oracle support note How To Configure Anti-Virus On Oracle Database Server (Doc ID 782354.1), Oracle mentions the below

we recommend the following files to be excluded from online anti-virus scanning.

  • Oracle datafiles
  • Control files
  • Redo-log files
  • Archived redo-log files if database is in archive log mode
  • Files with extension '.ora'
  • Password file
  • File in ADR directory
  • Oracle Software directories (including Oracle_HOME and Oracle base)
  • /dev/shm on Linux platform

So, is that it? Are we good if we just exclude all the files mentioned above? 

I'll discuss regarding the 2 scenarios which caused outages to the database w.r.to AV software (I'm not mentioning the names of the AV software here)

Scenario 1: 

We had installed one of the leading AV software on all our DB servers. During the testing phase everything goes smooth. Few files were asked to be excluded as suggested by Oracle and finally agreed to deploy them on production servers after rigorous testing in the lower environments. 

Soon after the production deployment, we started having issues with ASM disk writes as the ASM disks were invisible to Oracle. The following error pops up frequently on busy and high load databases. 

ORA-00202: control file: '+DATA/ABC/cntrlabc.dbf'~ORA-15081: failed to submit an I/O operation to a disk
ORA-00312: online log 5 thread 1: '+LOG/ABC/ONLINELOG/group_5.261.981173379'~ORA-15081: failed to submit an I/O operation to a disk
So what's really happening here? 

We are using udev rules to define the ASM disks on all our environments (old school but still works effectively). Our assumption is the AV software locks or prevents other processes from reading the udev rules files or the ASM disk header exactly during Oracle tries read/write udev rules file or the ASM disk header. The exact cause is not known till now as the incident is not reproducible at will.

To get out of this situation, we need to reload the udev rules and things will get back to normal. This is no joke as the busy database might lose data or the operations will get stalled when the control file/data file is missing from where it has to be. 
 
We worked with Oracle support who couldn't figure out anything specific from the logs provided and also we worked closely with the AV software vendor close to 8 months to trap the error/incident by trying different tracing and testing same workload during same previous issue time, etc., but as I mentioned we couldn't trap the issue and the tracing can't be directly enabled on the production database server for long times due to enormous logs the tracing produces, performance impact, etc. 

In the mean time, multiple version updates with minor to major file handling updates w.r.to Oracle database were introduced to the AV software but none of them help prevent the issue from occurring infrequently all of a sudden which made the business people unhappy.  
Finally, business agreed to move to another AV vendor 😜

The horror story continues... 

Scenario 2: 

During the 8 months time where all the R&D was happening with the previous vendor, there comes another AV software vendor with all the super cool features that one system can have to get rid of all the virus (just like the complete dose of COVID vaccine and on top of that a booster dose too). 
So just like the previous one, the testing went with flying colors. No outages like disk not visible, etc. was seen and no performance slowness reported in the lower environments and also a pilot was done on one of low risk production database server as well. 

Finally, it was decided to take the AV software to production along with other changes we usually do such as OS and DB patching every quarter. The deployment went fine for all the production DB servers with no issues reported during the maintenance window and post maintenance testing window. All are happy, all went to celebrate and sleep peacefully. 

Next day, business began as usual.. Time passed slowly.. Application team noticed slight delay in batch processing for one or other jobs.. They still were monitoring thinking it's an one off case and waited for sometime.. Performance is now getting a huge hit on one of the very critical OLTP database and the entire database was slowing down.. we had multiple tickets bombarding and eventually a high priority ticket was assigned and members from different groups (Application team, OS admins, Infra admins, DBAs) jumped on to the bridge to check from their end.

Initial checks with DB showed almost all the queries were running slow. This is not an ideal situation where all the queries would get hit. So the issue seems to be something out of database. Checks on OS shows that run queue for the CPU is too high and the time spent running the kernel is taking high percentage of the CPU. This is definitely unusual. 


You can see the load average is near 400 and this system is a 72 core system which translates to a load of ~ 5.5 which is too high. Interestingly, unlike all other days the kernel processing is taking > 50% (ideally should be < 10%) is a hint saying something messed up at the OS/kernel. 

So the first thing we did is to check for the AV activity and stop that right there as it's the only alien on the system. OS admin disabled the AV software. Still the CPU usage didn't come down. So we guessed it to be some other thing that's causing the issue. Remember we did OS and Database patching as well. So the plan now is to rollback both the patches one after another to figure out the cause. 

Meanwhile, support from vendors such as Oracle, Redhat and SAP (since the application is SAP) were also involved via high priority tickets to each of them. Oracle and SAP clearly mention that the cause might be from OS as the kernel processing is unusually high. So first we rolled back the kernel to previous version and rebooted the entire system. The issue was still persistent. Database patch was then rolled back and still the CPU usage didn't reduce to normal levels. 

We then installed perf tool which is not a standard package that comes along with Linux as Redhat needs some finer details to pin point the cause. Take a look here to learn much about perf
With the OS team providing the required details to Redhat support (I have limited knowledge on what traces were provided but guess perf stat trace), Redhat support got back to us with this module event_collector_2_3_xxx which is a third party module got loaded to the kernel and causing the kernel to suck up all the CPU. This module is from the AV software. We have to uninstall completely of the AV software and take a clean reboot to get rid of the module from kernel. 

One thing to understand is that though we disabled the AV software as first step of our troubleshooting, the same AV software was identified as culprit causing the slowness issue after very long investigation. This seriously would have been avoided if we know how the AV would behave if we just disable it.

So, what's the solution for this? 

Oracle doesn't certify any AV software to work properly with Oracle databases. We can't leave our production databases/servers unprotected. So we can't totally get rid of anti virus softwares at all. In the same time, we can't risk unexpected production downtime as we saw in the above 2 cases of the multiple other probabilities that might occur. 

What we can try is other alternatives than relying upon AVs such as below
  1. DB server should be restricted only for DB admin login, specifically only a senior DBA who does admin work (such as startup and shutdown using a sys account) and not the management work like adding/extending tablespaces kind of tasks. These other activities can be done remote using system or customized DBA account.  This makes sure very limited logins are available on the server directly unlike we have 1 common account (in most cases oracle) where all the members in team use to login. This poses a very serious security risk
  2. Place all DB servers under a subnet with tight firewalls such as auto log off in x seconds/minutes, trace entire activity and audit frequently on what commands been run, etc. Only SA and DBA should have direct login accounts. 
  3. OS login account should be coupled with AD and should use MFA to authenticate. The configuration seems complicated but this is one time configuration that needs the initial efforts to set up and we are done. 
  4. To limit direct access to DB, use kerberos/radius authentication or Centrally Managed Users (available from version 18c) which implements coupling enterprise users with AD accounts and groups which would allow users with their enterprise logins. Again this provides double protection as once the member leaves the organization, his/her account is totally wiped out making the server/DB secure 
  5. Use TCP.VALIDNODE_CHECKING = YES , TCP.EXCLUDED_NODES, TCP.INVITED_NODES sqlnet parameters to accept connections only from specific ip address or subnet making the application access to database super strong. 
These are the few things I can think of to secure the database/server from exploitation which would eliminate or minimize the requirement for an anti virus software. 

Let me know in comments of various other implementations that you come across to secure the database/servers in your organization so that we can help each other learn and secure our databases. 

References:

How To Configure Anti-Virus On Oracle Database Server (Doc ID 782354.1)What is TCP_VALIDNODE_CHECKING and How to Use It (Doc ID 462933.1)

Happy Anti Virusing...!!!

Tuesday, August 23, 2022

Oracle Centrally Managed Users (CMU) configuration

Centrally managed users (CMU) provides a simpler integration with Microsoft Active Directory to allow centralized authentication and authorization of users. 

Quote from official documentation
"In order for the Oracle Database CMU with Active Directory integration to work, the Oracle database must be able to login to a service account specifically created for the database in Active Directory. The database uses this service account to query Active Directory for user and group information when a user logs into the database. This Active Directory service account must have all the privileges required to query the user and group information as well as being able to write updates related to the password policies in Active Directory (for example, failed login attempts, clear failed login attempts). Users can authenticate using passwords, Kerberos, or PKI and either be assigned to an exclusive schema or a shared schema. Mapping of an Active Directory user to a shared schema is determined by the association of the user to an Active Directory group that is mapped to the shared schema. Active Directory groups can also be mapped to database global roles. An Active Directory security administrator can assign a user to groups that are mapped to shared database global users (schemas) and/or database global roles, and hence update privileges and roles that are assigned to the Active Directory user in a database."

In this post, I'll be explaining how to configure Oracle Centrally Managed Users using Windows server 2019 Active Directory and Oracle database version 19c. 


Since the configuration is a bit long with multiple screen shots I have made, I have divided the steps into 4 major parts which are as below

  • Part A: AD Configuration
  • Part B: Database Server Configuration
  • Part C: Enhancing to CMU
  • Part D: Enhancing CMU with Shared users and roles

All the steps are explained in the downloadable document. Click below link to download the file.

>>> Centrally Managed Users - orabliss.com <<<

Please make use of the document and share the feedback or any clarification if you have, I'll try to help solve them. 

I had to set up my own AD on virtual box for which I have not covered the steps in the document but the first 2 reference links will help you set up one if you need to.. 

References: 


Happy CMing Users!!! :)

Thursday, May 26, 2022

Script to gather details while using Datapump for DB migration

 Here is a script that can be used to gather source database details when we are attempting to upgrade from one version of DB to another or migrate Oracle DB from one server to another. 

The purpose is to gather as much details so that the target DB can be prepared in such a way we don't have much work post import of the required schemas. 

/* -----------------------------------------------------------------------------------------
Script name 	: export_details.sql
Usage			: sqlplus / as sysdba @export_details.sql

Instructions	: 
Replace all SCHEMA1 and SCHEMA2 with the schemas you are intended to export 
and save the file as export_details.sql, use as many users needed

Important Note	: 
Once details gathered, export NLS_LANG as specified below to set env variable
and start export by using the options mentioned in the end of this file

export NLS_LANG=AMERICAN_AMERICA.AL32UTF8 - Check what is the NLS_LANG needed as per app

This script will create 3 o/p files, 1 html and 2 sql files.
------------------------------------------------------------------------------------------ */

set echo on pages 999 long 90000
col OWNER for a30
col USERNAME for a30
col PROFILE for a30
col GRANTED_ROLE for a30
col GRANTEE for a30
col PROPERTY_NAME for a40
col PROPERTY_VALUE for a40
col DISPLAY_VALUE for a50
col DEFAULT_VALUE for a50
set markup html on spool on pre off
spool source_details.html

Prompt Parameter details:
------ ------------------
select name, DISPLAY_VALUE from v$parameter where name in ('service_names', 'compatible','sga_max_size','sga_target','log_buffer','db_cache_size','pga_aggregate_target', 'pga_aggregate_limit','cpu_count','session_cached_cursors','open_cursors','processes') order by name;

Prompt User details:
------ -------------
select count(*) "Total Schemas" from dba_users;
select sum(bytes)/1024/1024/1024 Total_DB_used_Size_In_GB from dba_segments;
select sum(bytes)/1024/1024/1024 Total_DB_allocated_Size_In_GB from dba_data_files;
SELECT owner,sum(bytes)/1024/1024/1024 Size_In_GB from dba_segments WHERE owner IN ('SCHEMA1','SCHEMA2') group by owner order by owner;

Prompt Info about Schemas:
------ -------------------
select USERNAME,ACCOUNT_STATUS,LOCK_DATE,EXPIRY_DATE,DEFAULT_TABLESPACE,TEMPORARY_TABLESPACE,PROFILE from dba_users where USERNAME in ('SCHEMA1','SCHEMA2') order by 1;

set markup html on spool on pre on
Prompt Profile details for user:
------ -------------------------
SELECT dbms_metadata.get_ddl('PROFILE', p.profile) || ';' from dba_profiles p where profile not in ('DEFAULT') and resource_name='COMPOSITE_LIMIT';

set markup html on spool on pre off
Prompt Tablespace Size for particular schema:
------ --------------------------------------
select owner, TABLESPACE_NAME, sum(BYTES)/1024/1024/1024 Size_In_GB from dba_segments where OWNER in ('SCHEMA1','SCHEMA2') group by owner,TABLESPACE_NAME order by owner,TABLESPACE_NAME;

set markup html on spool on pre on
Prompt DDL for Schema, Non default Roles and Tablespace:
------ -------------------------------------------------
SELECT dbms_metadata.get_ddl('USER',u.username) || ';' as USERS from dba_users u where username in ('SCHEMA1','SCHEMA2');
select dbms_metadata.get_ddl('TABLESPACE',tb.tablespace_name) || ';' as Tablespaces from dba_tablespaces tb where tablespace_name not in ('SYSTEM','SYSAUX','UNDOTBS','TEMP');
select dbms_metadata.get_ddl('ROLE', r.role) || ';' as Roles from dba_roles r where ORACLE_MAINTAINED<>'Y';

set markup html on spool on pre off
Prompt Temp tablespace size:
------ ---------------------
select tablespace_name, sum(BYTES)/1024/1024/1024 from dba_temp_files group by tablespace_name;

Prompt Objects counts:
------ ---------------
select owner, object_type, status, count(*) from dba_objects where owner in ('SCHEMA1','SCHEMA2') group by object_type,owner, status order by 1,2,3;

Prompt Roles:
------ ------
select GRANTEE, GRANTED_ROLE, ADMIN_OPTION from dba_role_privs where grantee in ('SCHEMA1','SCHEMA2') order by 1,2;
select GRANTEE, PRIVILEGE, ADMIN_OPTION from dba_sys_privs where grantee in ('SCHEMA1','SCHEMA2') order by 1,2;


Prompt System privileges having a property value = 1
Prompt Use specific DBMS packages such as DBMS_RESOURCE_MANAGER_PRIVS to provide grants to these objects. 
------ --------------------------------------------------------------------------------------------------
select p.grantee, m.name from system_privilege_map m, dba_sys_privs p where m.name=p.privilege and m.property=1 and p.grantee in ('SCHEMA1','SCHEMA2') order by p.grantee, m.name;

Prompt DB Time Zone Details:
------ ---------------------
select sysdate,systimestamp,current_timestamp,sessiontimezone,dbtimezone from dual;

Prompt DB Characterset:
------ ----------------
select PROPERTY_NAME,PROPERTY_VALUE from database_properties where PROPERTY_NAME in ('NLS_CHARACTERSET','NLS_NCHAR_CHARACTERSET');

spool off

Prompt Generating grants script:
------ -------------------------
set markup html off
set lines 200 pages 0 head off feed off
spool source_all_privs.sql

select 'grant ' || privilege ||' on '|| owner || '.' || table_name ||' to '||grantee||';' from dba_tab_privs where grantee in ('SCHEMA1','SCHEMA2') and table_name NOT like 'BIN%';
spool off

spool source_all_synonyms.sql

SELECT 'CREATE OR REPLACE PUBLIC SYNONYM '||SYNONYM_NAME||' FOR '||TABLE_OWNER||'.'||TABLE_NAME||';' FROM ALL_SYNONYMS WHERE OWNER='PUBLIC' AND TABLE_OWNER IN ('SCHEMA1','SCHEMA2');

spool off

exit
Once the details are gathered, we can use the generated html file to prepare the target DB like creating required tablespaces, roles, etc. 
Once the import is completed in the target DB, both the sql scripts can be run against target DB to make sure all the grants are provided and public synonyms are created. 

Let me know in comments on whether we can further add any details which might be needed for collecting details on source DB. 

Also check, Datapump & system privileges with property value = 1 for details on this part. 

Happy Datapumping...!!!