DBMS_SESSION: Anwender und Sessions mehrschichtiger Anwendungen in der DB identifizieren

“Wer war das”!?

In mehrschichtigen Softwarearchitekturen (also einem Großteil der Web-Anwendungen) wird die Verbindung zur Datenbank meist über wiederverwendbare Verbindungen, sog. Connection Pools, aufgebaut (siehe dazu auch meinen Artikel über Proxy-User). Wenn die Benutzerverwaltung nicht in der Datenbank sondern in der Anwendung stattfindet, verbindet sich dabei der Application Server üblicherweise mit einem einzigen, “technischen User” mit der Datenbank.

Dem Vorteil der dadurch eingesparten Zeit für einen Verbindungsaufbau bei jeder DB-Abfrage steht damit aber der Nachteil gegenüber, daß auf der DB-Seite nicht mehr nachvollzogen werden kann, welcher Anwender und welche (oder welcher Teil der) Anwendung in einer bestimmten Session läuft. Das ist zwar im Normalbetrieb nicht so wichtig, wenn es aber zu Problemen kommt, dann kann diese fehlende Information die Fehlersuche schwer beeinträchtigen. Drei häufige Szenarien seien hier genannt:

  • Der DB-Server ist plötzlich überlastet. Die Ursache kann zwar bis zu einer bestimmten Session in der Datenbank verfolgt werden, aber nun ist noch nicht klar, wer diese Session besitzt und welche Aufgabe er gerade ausführen will.
  • Ein Anwender beklagt sich über zu lange Antwortzeiten in einem bestimmten Anwendungsteil. Von den 500 offenen Sessions auf der DB kann aber nicht identifiziert werden, in welcher dieser Sessions der Anwender aktiv ist, und daher kann keine gezielte Fehlerverfolgung (z.B. mittels Tracing) durchgeführt werden.
  • Zur Erfüllung von Sicherheitsrichtlinien sollen Informationen über die Aktivitäten von Anwendern in der Datenbank gesammelt werden (Auditing). Das Oracle RDBMS kann das zwar von Haus aus, wenn aber die Verbindung nur über einen einzigen Useraccount läuft, fehlen wesentliche Informationen.

Softwareentwickler sollten daher schon im eigenen Interesse Ihre Anwendungen von Anfang an so instrumentieren, daß ein DBA bei Problemen auf der Datenbank gezielt Informationen sammeln kann.

Für Informationen über die Anwendung und den laufenden Anwendungsteil gibt es das Package DBMS_APPLICATION_INFO, das die Informationen zu Anwendung und Anwendungsabschnitt (z.B. der aktuellen Transaktion) schreibt. Details dazu finden sich in zwei guten, deutschsprachigen Artikeln von Carsten Czarski, daher soll dieses Package hier nicht tiefer erläutert werden.

Client Identifier

Das zweite Package, das bei der Verwendung von Connection Pools eingesetzt werden sollte, ist DBMS_SESSION, speziell die Prozedur SET_IDENTIFIER. Diese schreibt einen String in das Data Dictionary, der die Anwender-ID enthalten sollte. Dieser String ist über die View v$session abrufbar und wird auch verwendet, wenn Auditing in der DB aktiviert ist. Somit ist eine Zuordnung einer Session zu einer User-ID, die nicht von der DB verwaltet wird, möglich.

Beispiel: Eine Anwendung verbindet sich und hinterlegt den Benutzernamen (der Anwendung):

SQL> conn pooluser/pooluser@orcl
Connect durchgeführt.
SQL> exec DBMS_SESSION.SET_IDENTIFIER ( 'Frank/2' )

PL/SQL-Prozedur erfolgreich abgeschlossen.

Ein DBA kann nun gezielt nach der Session des Anwenders “Frank/2″ suchen:

select username, module
  from v$session
 where client_identifier = 'Frank/2';

USERNAME                       MODULE
------------------------------ ----------------------
ESDBM                          SQL*Plus

Die Information kann in SQL-Statements auch über die Funktion SYS_CONTEXT abgerufen werden, so daß gezielt auf bestimmte Client-Identifier reagiert werden kann:

SELECT SYS_CONTEXT( 'USERENV', 'CLIENT_IDENTIFIER' )
  FROM DUAL;

SYS_CONTEXT('USERENV','CLIENT_IDENTIFIER')
------------------------------------------
Frank/2

In der Java-Welt

Wer seine Anwendungen in Java schreibt, muß nicht unbedingt die DBMS-Packages aufrufen. Oracles JDBC-Erweiterungen stellen die Methode oracle.jdbc.OracleConnection.setEndToEndMetrics() bereit, die die besprochenen Funktionen von DBMS_APPLICATION_INFO und DBMS_SESSION vereint.

Anmerkung: Selbst in der Oracle-11g-Doku findet sich noch die Methode setClientIdentifier(), die an anderer Stelle der Doku aber als “deprecated” gekennzeichnet ist. Es sollte setEndToEndMetrics() verwendet werden.

Hier folgt ein vollständiges Beispiel, das sich der Klasse OracleDataSource bedient, die im Umfeld von Connection Pools und JNDI-Sourcen gerne zum Einsatz kommt. Natürlich geht das Beispiel auch mit dem klassischen OracleDriver; wichtig ist nur, daß das Connection-Objekt vom Typ OracleConnection ist:

import java.sql.*;         //Standard JDBC-Features
import oracle.jdbc.*;      //Oracle JDBC Treiber
import oracle.jdbc.pool.*; //Oracle JDBC Treiber: Connection Pools
import oracle.sql.*;       //Oracle SQL-Features (optional)
import java.io.IOException;
class ClientIdTest{
public static void main(String[] args) {
  try {
    OracleDataSource ods = new OracleDataSource();
    // Connection Properties
    java.util.Properties prop = new java.util.Properties();
    prop.put("user", "scott");
    prop.put("password", "tiger");
    //prop.put("internal_logon", "sysoper");
    ods.setConnectionProperties(prop);
    // URL zur Datenbank-Instanz
    String url = "jdbc:oracle:thin:@meinserver:1521:ORCL";
    ods.setURL(url);
    OracleConnection conn = (OracleConnection)ods.getConnection();
    // Jetzt die Client Info setzen //////////////////////////////////////////////
    String metrics[] = new String[OracleConnection.END_TO_END_STATE_INDEX_MAX];
    metrics[OracleConnection.END_TO_END_CLIENTID_INDEX] = "Frank/2";
    metrics[OracleConnection.END_TO_END_MODULE_INDEX] = "Flugbuchung";
    metrics[OracleConnection.END_TO_END_ACTION_INDEX] = "Suche verfügbare Flüge";
    conn.setEndToEndMetrics(metrics, (short) 0);
    // Erst mit dem nächsten Call wird die Client Info übertragen
    // Zur Demo lassen wir die Session mal einige Zeit herumgammeln:
    CallableStatement cs = conn.prepareCall ("{call DBMS_LOCK.SLEEP(?)}");
    cs.setInt(1,60);    // Platzhalter 1: 60s schlafen
    cs.executeUpdate(); // Ausführen, ohne ein Ergebnis zurückzulesen
    cs.close();
    conn.close();
  } catch(Exception e) {
      // Verbindung schließen etc.
  }
}
}

Während der Laufzeit der Session können wir jetzt als DBA z.B. ein Trace für genau diesen Client Identifier erzeugen:

exec DBMS_MONITOR.CLIENT_ID_TRACE_ENABLE(client_id => 'Frank/2', waits => TRUE, binds => FALSE);

Das Thema “Troubleshooting” gibt aber schon wieder genug für einen neuen Artikel her…

Weblinks

Statistic Gathering for Client Identifier

About these ads

Ein Gedanke zu „DBMS_SESSION: Anwender und Sessions mehrschichtiger Anwendungen in der DB identifizieren

  1. Pingback: Oracle 12c / Java7: setEndToEndMetrics() deprecated | Oraculix

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ photo

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s