ORA-12154 when selecting via db-link

Inhalt

Problem

Bei DML ueber einen Database Link tritt der Fehler ORA-12154 auf.

ORA-12154: TNS:could not resolve the connect identifier specified

Analyse

Die sqlnet Dateien liegen per Default im $ORACLE_HOME/network/admin. Sind die Files nicht in diesem Verzeichnis verfuegbar, dann kann der Server Prozess diese nur finden, wenn TNS_ADMIN gesetzt ist.

Wird die Verbindung ueber einen Listener aufgebaut, dann bekommt der neu erzeugte Server Prozess das Environment der DB Instanz. Nicht das vom Listener Prozess!

Unter Linux werden neue Prozesse per fork()/exec() erstellt. D.h. es wird zuerst ein Kopie des laufenden Prozesses erzeugt, die dann das gewuenschte Executeable startet. Das Enviroment erbt der neue Prozess jedoch nicht vom Parent Prozess, sondern es wird beim Start explizit gesetzt.

Mit einem strace auf den Listener Prozess ist zu erkennen, was bei einem Verbindungsaufbau konkret geschieht.

 
# strace  -ff -tt  -o /tmp/connect.trc -p $(ps -ef|grep tnsl|grep -v grep|awk '{print $2}')
 
11:30:17.537566 set_robust_list(0x7f695bf08880, 0x18) = 0
11:30:17.537607 close(41)               = 0
11:30:17.537623 close(42)               = 0
11:30:17.537685 setsid()                = 13778
11:30:17.537704 geteuid()               = 1001
11:30:17.538149 setsid()                = -1 EPERM (Operation not permitted)
11:30:17.538185 execve("/u00/app/oracle/product/12.1.0/db_1_0/bin/oracle", ["oracleORCL1", "(LOCAL=NO)"], [/* 40 vars */]) = 0
11:30:17.538458 brk(0)                  = 0xf8c5000
...

Um zu sehen welche Environment Variable uebergeben werden, muss strace mit -v gestartet werden.

strace  -v -ff -tt  -o /tmp/connect.trc -p $(ps -ef|grep tnsl|grep -v grep|awk '{print $2}')

Mit -v werden werden im Trace die einzelnen Umgebungsvariablen aufgefuehrt, so laesst sich leicht erkennen, ob TNS_ADMIN gesetzt wird oder nicht.

Woher weiss der Listener, welche Environment Variablen fuer den neuen Prozess gesetzt werden muessen?

In Oracle12c informiert der LREG Prozess den Listener.

Welches Environement der Listener fuer welchen Service verwendet kann man sehen, wenn man DISPLAYMODE=verbose setzt und dann die Services anzeigt.

lsnrctl 
set displaymode verbose
services
quit
! rm -v /u00/app/oracle/product/12.1.0/db_1_0/network/admin/tnsnames.ora
! ln -v -s /u00/app/oracle/network/admin/tnsnames.ora /u00/app/oracle/product/12.1.0/db_1_0/network/admin/tnsnames.ora
disconnect
connect scott/tiger
drop database link remote_db;
create database link remote_db connect to scott identified by tiger using 'REMOTE_DB';
select * from test@remote_db;
!grep SERVICE_NAME /u00/app/oracle/product/12.1.0/db_1_0/network/admin/tnsnames.ora
--
rollback;
alter session close database link remote_db;
create database link remote_db 
 connect to scott identified by tiger 
 using 
  '(DESCRIPTION =
    (ADDRESS_LIST =
      (ADDRESS = (PROTOCOL = TCP)(HOST = oel21.dammeyer.local)(PORT = 1521))
    )
    (CONNECT_DATA =
      (SERVICE_NAME = ORCL1)
    )
  )'
;

Wird die Verbindung zur DB Instanz ueber den Listener aufgebaut, so bekommt der Server Prozess das Environment, das beim Starten der Instanz gueltig war.

Der LREG Prozess registriert die Datenbank Instanz beim Listener, neben dem Executeable wird dabei dem Listener auch das Environment mitgeteilt.

Der Serverprozesss wird per clone/execv erstellt, wobei das Environment gesetzt wird. War beim Starten der Instanz kein TNS_ADMIN gesetzt und befindet sich im $ORACLE_HOME/network/admin keine tnsnames.ora, gibt es Probleme beim Aufloesen von TNS Aliasen, wie sie zum Beispiel in DB Links verwendet werden.

Die Namensaufloesung findet beim Oeffnen des DB-Links statt. Damit Aenderungen in der tnsnames.ora wirksam werden, muss also der DB Link geschlossen und neu geoeffnet werden. Der DB Link kann innerhalb einer Session geschlossen werden, dazu muss die TX abgeschlossen werden (rollback,commit).

ALTER SESSION CLOSE DATABASE LINK remote_db;

Die Ursache ist, dass die tnsnames.ora nicht im default pfad verfuegbar ist und TNS_ADMIN waehrend des Starts der Instance nicht gesetzt war.

Aenderungen in der tnsnames.ora sind erst nach einem Neustart der session (disconnect/connect) sichtbar. Bzw.

Loesung

Will man Probleme mit der Namensaufloesung und Database Links vermeiden gibt es mehrere Ansaetze.

  • Beim Start der Instanz TNS_ADMIN setzen
  • Beim Create Database Link die vollstaendige Connect Beschreibung angeben
  • Einen Symbolic Link auf die gueltige tnsnames.ora im $ORACLE_HOME/network/admin

Um sicher zu gehen, dass TNS Aliase immer aufgeloest werden koennen, muss im $ORACLE_HOME/network/admin ein Symbolic Link auf die zentrale tnsnames.ora vorhanden sein. Idealerweise wird per Node eine tnsnames.ora gepflegt.

Fazit

Referenzen