Database Link Session bleibt trotz Netzwerkausfall aktiv

Eine Applikation nutzt eine Prozeduraufruf via Database Link. Kommt es zu einem Netzwerkausfall, bleibt der Datenbank Link weiter offen und die Session, die auf das Ergebnis der Prozedur wartet, bleibt hängen.

Dieses Verhalten liegt grundsätzlich am TCP/IP Protokoll welches Stateless (also ohne laufende Verifizierung) funktioniert. Wäre dies nicht der Fall, würde das Internet durch permanente Sessionverifizierungen überlastet.

Oracle bietet dafür eine Lösung an. Wenn man die SQLNET.EXPIRE_TIME im SQLNET.ORA konfiguriert, führt dies bei einem Client Connect dazu, dass der Server in der angegebenen Anzahl von Minuten prüft ob der Client noch reagiert. Typischerweise nutzt man hier Werte im Bereich zwischen 5 und 30 (Minuten). Dies ist auch wichtig, da sonst eine zwischen dem Client und dem Datenbank Server vorhandene Firewall die Session irgendwann – meinst nach 1-2 Stunden Inaktivität – verwerfen würde.

Öffnet man jetzt eine Session mittels Database Link zwischen zwei Datenbanken, wird leider die Einstellung von SQLNET.EXPIRE_TIME nicht berücksichtigt.

Die kann man unter Linux wie folgt selbst prüfen:

ss -nop | egrep "Netid|1521" | egrep "Netid|10.130.1.1"

Wobei 1521 der Listener Port und 10.130.1.1 die IP Adresse des (Datenbank Link Ziel) Datenbank Servers darstellt. Die IP Adresse 10.130.5.2 gehört zu dem Datenbank Server von dem aus der Datenbank Link geöffnet wird. Der Befehlt wird auf dem Datenbank Server abgesetzt, von dem der Datenbank Link geöffnet wird.

Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port
...
tcp   ESTAB      0      0 10.130.5.2:39350   10.130.1.1:1521    users:(("oracle_6106_dbl",pid=6106,fd=9))
...

Als Gegebprobe eine SQLNET Verbindung mittels SQLPLUS zwischen den beiden Datenbank Servern führt zu:

Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port
...
tcp    ESTAB      0     0 10.130.5.2:1521    10.130.1.1:39312 users:(("oracle_562_db2",pid=562,fd=14)) timer:(keepalive,1min54sec,0)
...

Wie man sieht, gibt es hier die Info über den keepalive für die Session, sofern SQLNET.EXPIRE_TIME konfiguriert wurde. Der Client hat hingegen keinen Keepalive aktiv:

Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port
...
tcp   ESTAB      0      0 10.130.1.1:17405   10.130.5.2:1521   users:(("sqlplus",pid=11378,fd=9)))
...

Die Session, die den Datenbanklink öffnet, verhält sich demnach analog zum SQLPLUS wie eine Client Session.

Wie schafft man es nun, auch auf der Client Seite (sowohl für’s SQLPLUS als auch fur den DB Link) für die tcp Sessions einen Keep-Alive zu aktivieren?

Zuerst muss man im TNSNAMES.ORA den Connect-String, den der Datenbank Link nutzt, um (ENABLE=BROKEN) erweitern.

... (DESCRIPTION= (ENABLE=BROKEN ...)

Dann muss man die Einstellungen für TCP Keepalive am Betriebssystem konfigurieren. Das hier angeführte Beispiel sorgt dafür, dass ca. alle 5 Minuten ein zusätzliches Paket für jede Idle Connection verschickt wird. In einem LAN stellt das kein Problem dar – im WAN oder Internet schon.

vi /etc/sysctl.conf

net.ipv4.tcp_keepalive_time=240
net.ipv4.tcp_keepalive_probes=4
net.ipv4.tcp_keepalive_intvl=15

aktivieren mittels

sysctl -p

nicht vergessen.

Ab diesem Zeitpunkt bekommt man auch für Datenbank Link Sessions eine Keepalive Funktionalität, und es kommt nicht mehr zu hängenden Sessions, wenn es eine Netzwerkstörung gab.