mardi 5 octobre 2010

Problème de connexion

Soit un client PRO*C qui a épisodiquement des temps de connexion à une instance Oracle de l'ordre de quelques centaines de ms, voire plusieurs secondes.

La première étape consiste à isoler le problème. Dans le cas présent, la latence est observée au moment de l'exécution de l'instruction CONNECT.

Pour améliorer les temps de connexion, deux paramètres peuvent être intéressants à modifier:
- SDU ( http://www.sun.com/blueprints/1002/817-0370-10.pdf );
- tcp.nodelay ( désactivation de l'algorithme de Nagle lié à la couche TCP ).

Pour aller plus loin, consulter le guide d'administration sur la couche sqlnet.

Si le problème n'est pas résolu, on peut alors poser  les traces sqlnet sur le client et ensuite éventuellement sur le listener, voire le dispatcher en cas d'utilisation du mode shared server. Pour les traces sqlnet sur le client, on peut utiliser une configuration spécifique ( sqlnet.ora + tnsnames.ora ) via la variable d'environnement TNS_ADMIN. Le paramétrage des traces du listener s'effectue dans le fichier listener.ora. Puis, on lance le listener pour activer le mode trace ( exemple: set trc_level support ). L'étude du dispatcher se fait via l'event 10248.

Dans le cas présent, aucune erreur ( nserror ) n'a été détectée.  Une analyse réseau ( tcpdump sur le client et un noeud du RAC, utilisation de sondes ) a ensuite été menée, ce qui a permis de localiser le problème au niveau de la machine cliente: des paquets de type ACK retardé ou donnée ne sont pas émis immédiatement. Il est à noter que les adresses virtuelles des noeuds du RAC sont résolues directement dans le fichier host. 


En changeant de client Oracle ( 9i -> 10g ), le problème a persisté, d'où une mise en cause de la couche système, en particulier au niveau de son paramétrage TCP. Une analyse via l'outil truss n'a pas permis d'obtenir de résultats probants. L'outil Dtrace ne peut pas être utilisé, la machine cliente étant en Solaris 8.

Quelques modifications ont été effectuées au niveau du paramétrage TCP du système, sans grand succès ( exemple: tcp_time_wait_interval ). Pour dédouaner complètement le client Oracle, un client/serveur de type écho à base de socket TCP a été développé ( serveur sur un noeud du RAC ) en se basant sur les sources disponibles à cette adresse : http://cs.baylor.edu/~donahoo/practical/CSockets/textcode.html. Une fois ce dernier installé, on a pu observer que la durée de traitement moyenne de la commande recv du client était de l'ordre de quelques dizaines de micros pour un échange de 8 ko. Cependant, de temps en temps, elle peut excéder la dizaine de ms.

Suite à cette constatation, le problème est étudié par l'équipe système, qui ne trouve pas de solution.  

Lors de ces tests, la charge transactionnelle était faible ( 4 transactions par seconde ), d'où une solution possible. Si la charge augmente ou pour éviter ce type de problème sur une machine mal configurée, on peut mettre en place un pool de sockets dédiées aux connexions Oracle. Le principe est le suivant:
- Lors du démarrage du serveur d'application, on crée les sockets entre le client Oracle et l'instance, puis on met en mémoire les pointeurs de connexion dans un segment de mémoire partagée, ce dernier étant protégé par un sémaphore;
- Lors du traitement d'une transaction, le serveur crée un processus fils qui va rechercher dans le segment de mémoire partagée une connexion disponible, si le sémaphore le permet ( unicité d'accès à la ressource ). Une fois une connexion sélectionnée ( temps d'exécution inférieur à 1 ms ), la requête est exécutée et les données transmises au client. La déconnexion, quant à elle, se limite à rendre de nouveau disponible la connexion utilisée.
- Lors de l'arrêt du serveur, les sockets sont closes et les ressources système libérées.

De cette manière, la discontinuité de performance se réduit à l'échange des données entre le client et l'instance après l'exécution d'une requête. Les temps de connexion/déconnexion, quant à eux, sont considérés comme négligeables.