Servicenamen und Data Guard Dr. Martin Wunderli Trivadis Zürich-Glattbrugg Problemstellung Die Netzwerkkonfiguration im Data Guard Umfeld enthält eine entscheidende Herausforderung: Den Client dazu zu bringen, beim Verbindungsversuch auf die Standby Site dies zu erkennen und auf die Primary Site zu wechseln. Ein Knackpunkt dabei ist der Listener auf der Standby Site. Einerseits braucht man ihn, damit die archivierten Redo Log Dateien auf die Standby Site übertragen werden können. Auf der anderen Seite muss ein Client mit TAF-Netzwerkkonfiguration einen Netzwerk-Fehler beim Connect auf einen Standby Host/Listener bekommen, damit er einen Failover auf die (richtige) Primary Site durchführt. Eine Möglichkeit hierzu ist ein fehlender Listener. Ein Widerspruch? Eine schnelle Lösung wäre die mit 2 Listenern auf unterschiedlichen Ports, einen für die Redo Übertragung, der immer gestartet sein darf, und einen für die Client Connects, der nur auf der Primary Site gestartet sein darf. Leider erfordert diese Lösung manuelle Eingriffe bei Switchover, Failover etc (der Client Listener muss jeweils korrekt – auf der Primary – gestartet werden). Eine andere Lösung wäre, einen zusätzlichen Servicenamen (Datenbankparameter service_names) für die Datenbank zu definieren, falls sie für Client Connects bereit ist. Dieser Servicename wird dann in den TNSNAMES Einträgen auf den Clients benutzt. Ein Client Connect auf einen Listener ergibt einen Netzwerkfehler, falls der Listener diesen Servicenamen nicht kennt und er kennt ihn nur, wenn der PMON der Datenbank ihn aufgrund des Parameters service_names beim Listener registriert hat. Doch auch diese Lösung erfordert eine manuelle Interaktion (Modifikation von service_names) bei Rollenwechseln, oder nicht? Die Lösung Die Lösung heisst Database Trigger, genauer: Startup Trigger. Er feuert, nachdem die Datenbank geöffnet wurde und kann als SYS erzeugt werden. Der nachfolgend abgebildete Trigger Code prüft, ob die Datenbank den korrekten open_mode (Read-Write) hat und hängt im Erfolgsfall einen zusätzlichen Servicenamen an den bestehenden Datenbankparameter service_names an. Wir wählen hier db_name_RW.trivadistraining.com (könnte man noch generischer durch Auslesen von db_domain aus v$parameter machen...). Damit wir bei Shutdown etc. diesen zusätzlichen Servicenamen nicht löschen müssen, erfolgt die Modifikation nur im Memory. create or replace trigger service_trigger after startup on database declare name varchar(9); open_mode varchar(10); service_names varchar(4000); begin select name,open_mode into name,open_mode from v$database; select value into service_names from v$parameter where name = 'service_names'; if open_mode = 'READ WRITE' then service_names := service_names || ', ' || name || '_RW.trivadistraining.com'; end if; execute immediate 'alter system set service_names = ''' || service_names || ''' scope=memory'; end; Der passende TNSNAMES Eintrag für Clients sieht dann wie folgt aus: MIS.TRIVADISTRAINING.COM = (DESCRIPTION = (FAILOVER=ON) (LOAD_BALANCE=OFF) (ADDRESS_LIST = (ADDRESS = (PROTOCOL = TCP)(HOST = bagheera)(PORT = 1521)) (ADDRESS = (PROTOCOL = TCP)(HOST = hathi)(PORT = 1521)) ) (CONNECT_DATA = (SERVICE_NAME = MIS_RW.TRIVADISTRAINING.COM) ) ) Die Triggererweiterung Die vorgestellte Lösung ist schon ganz nett. Was aber, wenn eine physiche Standby Datenbank Read-Only geöffnet wird? Und was ist mit einer logischen Standby Datenbank? Diese ist Read-Write geöffnet, auf sie darf aber nur lesend zugegriffen werden! Durch einen weiteren Servicename mit der Endung _RO, auf den ein zusätzlicher TNSNAMES Eintrag zugreifen kann, erfüllt der Trigger auch diese Anforderung. create or replace trigger service_trigger after startup on database declare name varchar(4000); open_mode varchar(10); database_role varchar(16); service_names varchar(4000); begin select name,open_mode,database_role into name,open_mode,database_role from v$database; select value into service_names from v$parameter where name = 'service_names'; if open_mode = 'READ WRITE' and database_role = 'PRIMARY' then service_names := service_names || ', ' || name || '_RW.trivadistraining.com'; else service_names := service_names || ', ' || name || '_RO.trivadistraining.com'; end if; execute immediate 'alter system set service_names = ''' || service_names || ''' scope= memory'; end; / In diesem Fall sieht der passende TNSNAMES Eintrag für Clients dann wie folgt aus: MIS_RO.TRIVADISTRAINING.COM = (DESCRIPTION = (FAILOVER=ON) (LOAD_BALANCE=OFF) (ADDRESS_LIST = (ADDRESS = (PROTOCOL = TCP)(HOST = bagheera)(PORT = 1521)) (ADDRESS = (PROTOCOL = TCP)(HOST = hathi)(PORT = 1521)) ) (CONNECT_DATA = (SERVICE_NAME = MIS_RO.TRIVADISTRAINING.COM) ) ) Bei Switchover Operationen meldet sich die Primary sauber beim Listener ab, der Servicename verschwindet. Bei Failover Operationen muss man einen eventuell noch laufenden Listener restarten oder reloaden, damit er den Servicenamen verliert. Wechselt eine physische Standby Datenbank von Read-Only zurück in den Log-Apply Modus, ist es am einfachsten, diese zu restarten. Damit ist der _RO Servicename weg und der Bounce stört ja nicht besonders. Kritische Würdigung Der abgebildete Datenbank Trigger ist sehr einfach gehalten und nutzt die Möglichkeiten von Oracle 10g nicht (Oracle Notification Service, das Package dbms_services etc. Vgl. [1]). Er hilft daher nicht, ein immer noch verbleibendes Problem des Failovers im DataGuard Umfeld zu lösen: Das der TCP Timeouts im Falle eines totalen Serverausfalls (IP des Servers nicht mehr erreichbar): Denn ein TAF-enableter TNSNAMES Eintrag, der den abgeschalteten Server als ersten in der Adressliste enthält, wird längere Zeit (abhängig vom Betriebssystem) warten, bis er eine Connection zu dem nächsten Server in der ADDRESS_LIST versucht. Hier kann nur eine Benachrichtigung durch die neue Primary Datenbank an (registrierte) Clients oder ein Konzept mit virtuellen IPs (welche failovern) Abilfe schaffen. Fazit Die beschriebene Lösung aktiviert zuverlässig Services auf Primary Datenbanken, auf physichen Standby Datenbanken, die Read-Only geöffnet sind, und auf logischen Standby Datenbanken. Zudem ist sie lauffähig mit den Oracle Releases 9.x und 10.x (eigentlich auch 8.1.x ☺), einem standardisierten Einsatz auch in gemischten Umfeldern steht also nichts im Wege. Bleibt eigentlich nur die Frage, warum Oracle nicht selbst auf diese Lösung gekommen ist... Sie sehen also, dass die Herausforderungen für eine optimale Infrastruktur oftmals im Detail liegen. Trivadis achtet auf diese Details, wovon sie zum Beispiel in unseren Oracle High Availability Kursen O-RAC (Real Application Clusters) und O-DG (Data Guard) profitieren können. Kontaktadresse: Trivadis AG Dr. Martin Wunderli Europastrasse 5 CH-8152 Glattbrugg [email protected] http://www.trivadis.com Literatur [1] Fast-Start Failover – Oracle Data Guard 10g Release 2. Oracle White Paper September 2005