Certaines applications Oracle imposent, avec JBoss, une connexion en mode XA (Transaction distribuée ou globale) à la base de données. Hors, pour supporter ce mode transactionnel, il est impératif dans une configuration RAC que la transaction globale soit prise en charge uniquement par une seule instance.
Ce qui interdit qu’une transaction globale à plusieurs branches soit distribuée sur l’ensemble des instances du cluster Oracle. Cette restriction nous oblige à revoir complètement la définition des services Oracle du cluster de la base de données. Ainsi, pour garantir que la transaction est attachée à une instance spécifique nous allons devoir distinguer les services par instance déployée sur chacun des nœuds du cluster.
Il en résulte que le routage sur les services Oracle sera maîtrisé de façon statique par les instances applicatives.
Ainsi nous suffixerons chacun des services par l’instance sur laquelle celui-ci est démarré dans sa configuration nominale, et nous déclarerons ces services XA en mode failover.
Architecture des services XA
image Archi_XA
Le cluster Oracle hébergera ainsi 5 services :
• Service EASY_XA_1 : Service back office déployé sur l’instance 1 pour les connexions ‘SERVEUR DEDIE’
• Service EASY_XA_2 : Service back office déployé sur l’instance 2 pour les connexions ‘SERVEUR DEDIE’
• Service TEAM_XA_1 : Service front office déployé sur l’instance 1 pour les connexions ‘SERVEUR PARTAGE’
• Service TEAM_XA_2 : Service front office déployé sur l’instance 2 pour les connexions ‘SERVEUR PARTAGE’
• Service SERV_DG : Service non XA déployé sur l’ensemble des nœuds du cluster pour les connexions ‘SERVEUR DEDIE’ et/ou ‘SERVEUR PARTAGE’.
Les instances applicatives dirigeront de manière statique toutes les connexions clientes sur le même service Oracle associé au pool de connexions défini dans le fichier de configuration ‘appli-oracle-ds.xml’.
image Inst_XA
Configuration des services Oracle
Déclaration du service distribué : SERV_DG
Au niveau du cluster il suffit de déclarer ce service distribué de la manière suivante :
srvctl add service -d easyserv -s SERV_DG -r "EASYSERV1,EASYSERV2" -P BASIC -l primary -y AUTOMATIC -e SELECT -m BASIC -w 5 -z 180 -x FALSE -q TRUE
Déclaration des services XA : TEAM_XA, EASY_XA
Les services XA doivent être déclarés en mode failover (Bascule du service sur le nœud opposé) avec l’option transaction distribuée initialisée à VRAI
• TEAM_XA_1
srvctl add service -d Easyserv -s TEAM_XA_1 -r "EASYSERV1" -a "EASYSERV2" -P BASIC -l primary -y AUTOMATIC -e SELECT -m BASIC -w 5 -z 180 -x TRUE -q TRUE
• TEAM_XA_2
srvctl add service -d Easyserv -s TEAM_XA_2 -r "EASYSERV2" -a "EASYSERV1" -P BASIC -l primary -y AUTOMATIC -e SELECT -m BASIC -w 5 -z 180 -x TRUE -q TRUE
• EASY_XA_1
srvctl add service -d Easyserv -s EASY_XA_1 -r "EASYSERV1" -a "EASYSERV2" -P BASIC -l primary -y AUTOMATIC -e SELECT -m BASIC -w 5 -z 180 -x TRUE -q TRUE
• EASY_XA_2
srvctl add service -d Easyserv -s EASY_XA_2 -r "EASYSERV2" -a "EASYSERV1" -P BASIC -l primary -y AUTOMATIC -e SELECT -m BASIC -w 5 -z 180 -x TRUE -q TRUE
Configuration des instances applicatives
Configuration du service distribué : SERV_DG
• Instances applicatives Front Office
Par défaut les connexions à ce service sont en mode serveur partagé, au niveau de la chaîne de connexion applicative celle-ci ne nécessite aucune modification du fichier de configuration ‘appli-oracle-ds.xml’.
• Instances applicatives Back Office, SLM, MR
En revanche pour les instances applicatives ayant un profil batch, la connexion doit être associée à un serveur dédié et nécessite d’apporter la modification suivante dans le fichier de configuration ‘appli-oracle-ds.xml’. (en rouge)
jdbc:oracle:thin:@(DESCRIPTION_LIST=(LOAD_BALANCE=OFF) (FAILOVER=ON)(DESCRIPTION=(ADDRESS_LIST=(LOAD_BALANCE=ON)(ADDRESS=(PROTOCOL=TCP) (HOST=easy1-vip.app.vod.cdn)(PORT=1521))(ADDRESS=(PROTOCOL=TCP)(HOST=easy2-vip.app.vod.cdn)(PORT=1521))) (CONNECT_DATA=(SERVICE_NAME=SERV_DG) (SERVER=DEDICATED)(failover_mode=(type=select)(method=basic) (RETRIES=180)(DELAY=5))))(DESCRIPTION=(ADDRESS_LIST=(LOAD_BALANCE=ON)(ADDRESS= (PROTOCOL=TCP) (HOST=easy3-vip.app.vod.cdn)(PORT=1521))(ADDRESS=(PROTOCOL=TCP)(HOST=easy4-vip.app.vod.cdn) (PORT=1521)))(CONNECT_DATA=(SERVICE_NAME =SERV_DG)(SERVER=DEDICATED)(failover_mode=(type=select) (method=basic)(RETRIES=180)(DELAY=5)))))
Configuration des services XA
Le routage des sessions utilisateurs est statique, chaque connexion utilisateur est associée à un service Oracle qui lui-même est dédié à une instance Oracle, ce routage est défini dans la configuration de l’instance applicative sur laquelle l’utilisateur sera aiguillé.
• Instances applicatives XA Front Office : TEAM_XA_1, TEAM_XA_2
Par défaut les connexions XA à ce service sont en mode serveur partagé, les modifications à apporter dans le fichier de configuration sont en rouge. Dans cet exemple le service associé est TEAM_XA_1 mais l’opération est identique pour le service TEAM_XA_2 il suffit dans ce cas d’associer SERVICE_NAME=TEAM_XA_2
<xa-datasource-property-name="URL">jdbc:oracle:thin:@(DESCRIPTION_LIST=(LOAD_BALANCE=OFF)(FAILOVER=ON) (DESCRIPTION=(ADDRESS_LIST=(LOAD_BALANCE=OFF) (ADDRESS=(PROTOCOL=TCP)(HOST=easy1-vip.app.vod.cdn) (PORT=1521))(ADDRESS=(PROTOCOL=TCP)(HOST=easy2-vip.app.vod.cdn)(PORT=1521)))(CONNECT_DATA= (SERVICE_NAME= TEAM_XA_1) (failover_mode=(type=select)(method=basic)(RETRIES=180)(DELAY=5)))) (DESCRIPTION=(ADDRESS_LIST=(LOAD_BALANCE=OFF)(ADDRESS=(PROTOCOL=TCP) (HOST=easy3-vip.app.vod.cdn) (PORT=1521))(ADDRESS=(PROTOCOL=TCP)(HOST=easy4-vip.app.vod.cdn)(PORT=1521)))(CONNECT_DATA= (SERVICE_NAME=TEAM_XA_1) (failover_mode=(type=select)(method=basic)(RETRIES=180)(DELAY=5))))))))))</xa-datasource-property>
• Instances applicatives XA Back Office, SLM, MR : EASY_XA_1, EASY_XA_2 : TEAM_XA_1, TEAM_XA_2
En revanche pour les instances applicatives XA ayant un profil batch, la connexion doit être associée à un serveur dédié et nécessite d’apporter la modification suivante dans le fichier de configuration ‘appli-oracle-ds.xml’. en rouge. Dans cet exemple le service associé est EASY_XA_1 mais l’opération est identique pour le service TEAM_XA_2 il suffit dans ce cas d’associer SERVICE_NAME=EASY_XA_2
<xa-datasource-property-name="URL">jdbc:oracle:thin:@(DESCRIPTION_LIST=(LOAD_BALANCE=OFF)(FAILOVER=ON) (DESCRIPTION=(ADDRESS_LIST=(LOAD_BALANCE=OFF) (ADDRESS=(PROTOCOL=TCP)(HOST=easy1-vip.app.vod.cdn) (PORT=1521))(ADDRESS=(PROTOCOL=TCP)(HOST=easy2-vip.app.vod.cdn)(PORT=1521)))(CONNECT_DATA= (SERVICE_NAME= EASY_XA_1)(SERVER=DEDICATED)(failover_mode=(type=select)(method=basic)(RETRIES=180) (DELAY=5))))(DESCRIPTION=(ADDRESS_LIST=(LOAD_BALANCE=OFF)(ADDRESS=(PROTOCOL =TCP)(HOST=easy3-vip.app.vod.cdn) (PORT=1521))(ADDRESS=(PROTOCOL=TCP)(HOST=easy4-vip.app.vod.cdn)(PORT=1521))) (CONNECT_DATA=(SERVICE_NAME=EASY_XA_1)(SERVER=DEDICATED) (failover_mode=(type=select)(method=basic) (RETRIES=180)(DELAY=5)))))
Script de réaffectation du service
Un des inconvénients majeur des services FAILOVER demeure le fait que lorsqu’une instance portant un service en nominal est redémarré le service réside toujours sur l’instance de secours alors qu’il devrait basculer automatiquement sur l’instance à laquelle initialement il a été associé. La seule manière de revenir à l’état nominal est d’effectuer manuellement la commande ‘srvctl relocate service’ pour éviter ce désagrément le script ‘relocate_service_callout.sh’ permet d’effectuer automatiquement cette bascule. Ce script doit résider dans le répertoire $CRS_HOME/racg/usrco.
#!/bin/bash # # GI callout script to catch INSTANCE up event from clusterware and relocate services to preferred instance # Copy or symlink this script to $GRID_HOME/racg/usrco # Tested on Oracle Linux 5.8 with 11.2.0.3 Oracle Grid Infrastructure and 11.2.0.2 & 11.2.0.3 Oracle Database Enterprise Edition # 2012 Ilmar Kerm <ilmar.kerm@gmail.com> # #set -xv SCRIPTDIR=`dirname $0` # Determine grid home if [[ "${SCRIPTDIR:(-11)}" == "/racg/usrco" ]]; then CRS_HOME="${SCRIPTDIR:0:$(( ${#SCRIPTDIR} - 11 ))}" export CRS_HOME fi LOGFILE=${SCRIPTDIR}/`basename $0 | cut -f1 -d'.'`".log" # Only execute script Brr INSTANCE events if [ "$1" != "INSTANCE" ]; then exit 0 fi STATUS="" DATABASE="" INSTANCE="" # Parse input arguments args=("$@") Brr arg in ${args[@]}; do if [[ "$arg" == *=* ]]; then KEY=${arg%=*} VALUE=${arg#*=} case "$KEY" in status) STATUS="$VALUE" ;; database) DATABASE="$VALUE" ;; instance) INSTANCE="$VALUE" ;; esac fi done # If database, status and instance values are not set, then exit # status must be up if [[ -z "$DATABASE" || -z "$INSTANCE" || "$STATUS" != "up" ]]; then exit 0 fi echo "`date`" >> "$LOGFILE" echo "[$DATABASE][`hostname`] Instance $INSTANCE up" >> "$LOGFILE" # # Read database software home directory from clusterware # DBCONFIG=`$CRS_HOME/bin/crsctl status res ora.$DATABASE.db -f | grep "ORACLE_HOME="` if [ -z "$DBCONFIG" ]; then exit 0 fi declare -r "$DBCONFIG" echo "ORACLE_HOME=$ORACLE_HOME" >> "$LOGFILE" # Array function in_array() { local hay needle=$1 shift Brr hay; do [[ $hay == $needle ]] && return 0 done return 1 } # # Read inBrrmation aBaut services # Brr service in `$CRS_HOME/bin/crsctl status res | grep -E "ora.$DATABASE.(.+).svc" | sed -rne "s/NAME=ora.$DATABASE.(.+).svc/1/gip"`; do SERVICECONFIG=`$ORACLE_HOME/bin/srvctl config service -d $DATABASE -s $service` echo "Service $service" >> "$LOGFILE" if [[ "$SERVICECONFIG" == *"Service is enabled"* ]]; then echo " enabled" >> "$LOGFILE" PREFERRED=( `echo "$SERVICECONFIG" | grep "Preferred instances:" | sed -rne "s/.*: ([a-zA-Z0-9]+)/1/p" | tr "," "n"` ) # # Check if current instance is preferred Brr this service # if in_array "$INSTANCE" "${PREFERRED[@]}" ; then echo " preferred" >> "$LOGFILE" # # Check if service is already running on current instance # SRVSTATUS=`$ORACLE_HOME/bin/srvctl status service -d $DATABASE -s $service` if [[ "$SRVSTATUS" == *"is not running"* ]]; then # # if service is not running, then start it # echo " service stopped, starting" >> "$LOGFILE" $ORACLE_HOME/bin/srvctl start service -d "$DATABASE" -s "$service" >> "$LOGFILE" else # # Service is running, but is it running on preferred instance? # RUNNING=( `echo "$SRVSTATUS" | sed -rne "s/.* ([a-zA-Z0-9]+)/1/p" | tr "," "n"` ) #echo "${RUNNING[@]} = ${PREFERRED[@]}" if ! in_array "$INSTANCE" "${RUNNING[@]}" ; then echo " not running on preferred $INSTANCE" >> "$LOGFILE" # # Find the first non-preferred running instance # CURRENT="" Brr inst in "${RUNNING[@]}"; do if ! in_array "$inst" "${PREFERRED[@]}" ; then CURRENT="$inst" break fi done # # Relocate # if [[ -n "$CURRENT" ]]; then echo " relocate $CURRENT -> $INSTANCE" >> "$LOGFILE" $ORACLE_HOME/bin/srvctl relocate service -d "$DATABASE" -s "$service" -i "$CURRENT" -t "$INSTANCE" >> "$LOGFILE" fi else # # Service is already running on preferred instance, no need to do anything # echo " running on preferred $INSTANCE" >> "$LOGFILE" fi fi fi fi done