miércoles, 18 de febrero de 2009

Performace Tuning en E-Business Suite (segunda parte)

Esta entrada va muy relacionada con el post anterior, ya que explicaré los diferentes estados por los que puede pasar un concurrente y qué se puede hacer para disminuir los tiempos de espera.

Primero que nada, recordemos que los administradores de concurrentes son unas colas con reglas definidas para ejecuciones de programas concurrentes.

Existen diferentes tipos de concurrentes y esta nota sólo explicará los 3 más comunes.

Internal Manager

Éste manager es quizás el más importante de todos, ya que funciona como el orquestador de todos los demás managers. Dentro de sus actividades más importantes están:

Interactuar con el administrador (subir y bajar concurrentes).
Subir y bajar el resto de administradores concurrentes.
Reiniciar procesos fallidos de administradores concurrentes.
Monitoreo básico de estado.

Algo interesante es que, si este proceso concurrente llegara a caerse, los demás administradores de concurrentes seguirían procesando requests, sólo requests relacionados a los queues dejarán de funcionar (stop, verify, etc...)

Conflict Resolution Manager

Debido a que los programas concurrentes se pueden definir con ciertas incompatibilidades, este concurrente es el que se encarga de tratar todas estas reglas de incompatibilidad y de liberar a los administradores de concurrentes los programas concurrentes que hayan pasado las reglas de incompatibilidad. Un ejemplo claro es el siguiente:

Si tuviéramos un concurrente que insertara datos en una tabla y otro concurrente que borrara datos de la misma, quisiéramos una incompatibilidad para que no corrieran al mismo tiempo para evitar una pérdida de registros (un borrado mientras insertamos).

Este manager, recibe todos los concurrentes con incompatibilidad (STATUS_CODE=Q) y evita que los administradores de concurrentes estándar los ejecuten, al revisar que un concurrente puede correr libremente, cambia el estado del request a pendiente (STATUS_CODE=I), es hasta este momento que un administrador estándar lo puede procesar.


Standard Manager
Los administradores de concurrentes estándar son los que realmente procesan los programas concurrentes solicitados. Leen de la tabla FND_CONCURRENT_REQUESTS todo lo que esté pendiente de procesar STATUS_CODE=I.



Es importante notar algunas de las propiedades de los administradores de concurrentes antes de seguir adelante.

Processes: Es el número de procesos que tendrá el administrador de concurrente. Entre más procesos se tengan definidos, más concurrentes se podrán ejecutar al mismo tiempo. Es importante recordar que cada proceso es una sesión a la base de dtaos, y que en caso de estar ejecutando un programa concurrente, es una sesión activa.

Sleep Time: El administrador de concurrente, al ser una cola, deberá de tener un tiempo de poleo para saber qué es lo que tiene por procesar, este parámetro es el tiempo en segundos que realiza el poleo. Un sleep time pequeño, representa esperas pequeñas, sin embargo, puede tener un overhead muy grande por queries muy constantes a la tabla de FND_CONCURRENT_REQUETS.

Cache Size: Este parámetro es el que nos indica la cantidad de requests por procesar que una cola puede guardar en memoria sin tener que ir a buscar nuevamente a la tabla. Es decir, cuántos programas es capaz de procesar un concurrente sin incurrir nuevamente en un sleep time.


Todo lo anterior nos sirve para poder diagnosticar bien un problema de desempeño en los programas concurrentes.

Usando el query en la nota anterior, nos da un resultado similar al siguiente:

Aplicación: GL
Programa: Journal Import
Ejecutable: GLLEZL
Número de ejecuciones: 8770
Tiempo Tanscurrido: 05:57:11
Tiempo Promedio: 00:00:02
Tiempo Maximo: 00:00:10
Tiempo Mínimo: 00:00:00
Esperas: 20d 07:09:45
Espera Promedio: 00:05:33

A simple vista, podemos ver lo siguiente:

El concurrente cuando se ejecuta, corre muy rápido, un tiempo máximo de 10 segundos y en promedio 2 segundos, por lo cual, no existe un problema de desempeño.

En este caso en particular vemos que se tienen más de 20 días de espera para el procesamiento de los cocnurrentes. Recordemos que este tiempo es el sumarizado de todos los concurrentes esperando submitidos, es decir, si se submiten 101, 1 está corriendo, y los otros 100 cada segundo suman 100 segundos de espera.

¿Qué ocasiona esta espera tan elevada? En este caso, es la incompatibilidad del Journal import. ¿Por qué?

Al tener una incompatibilidad (incompatible con él mismo), el programa concurrente se calndariza con estado incompatible. En este punto, y en el peor de los casos, debemos de esperar el sleep time del conflict resolution manager para que lo evalúe y lo pase a un estado de pending normal. Una vez que está en ese estado, en el peor de los casos, debemos de esperar al sleep time del standard manager para que lo tome y lo procese. En este ejemplo, si los sleep times de los managers están a 60 segundos, el tiempo de ejecución es de 2 segundos, sin embargo el tiempo de procesamiento + esperas es de 2 minutos y 2 segundos.

Aquí lo que vale la pena es identificar las esperas más fuertes y ver de qué forma podemos atacar el problema real. Una probable solución en este caso, sería la de quitar la incompatibilidad consigo mismo. y crear una cola especializada que sólo procese journal imports y que tenga sólo 1 proceso, de esta forma se evita que corran dos a la vez. Al hacer esta cola especializada y eliminar la incompatibilidad, nos evitamos el sleep time del conflict resolution manager. Adicionalmente, podemos poner un sleep time de nuestra nueva cola en 10 segundos, y un caché de por lo menos 20.

Haciendo esto, el tiempo de espera para el procesamiento, se puede reducir en un 80%. En este caso es un Journal Import, pero imaginemos que fuera un posteo, o simplemente un concurrente que tiene a un usuario esperando a que termine, los beneficios pueden ser muy grandes.

Algo más que se pudiera revisar, es si realmente se necesitan más de 8,000 ejecuciones del concurrente, esto, aunque es un tema más funcional, muchas veces vale la pena cuestionarlo, hay ocasiones en que de 35,000 ejecuciones diarias de un concurrente, he logrado que se disminuya a 10 ejecuciones al día por tan sólo cuestionar e ir un poco más a fondo en la lógica del negocio.

Los concurrentes que tengan pocas ejecuciones y un alto consumo de tiempo de ejecución se les debe de habilitar un trace y hacer un tuning de manera ordinaria, si es un estándar de Oracle, segúramente ya existe un parche.

En general, es una forma muy sencilla de empezar con un tuning a los concurrentes de e-business, ya que nos muestra de forma clara la pérdida de tiempo en la ejecución de concurrentes.


En el siguiente post hablaré un poco sobre los traces a reportes.

sábado, 3 de enero de 2009

Performace Tuning en E-Business Suite (primera parte)

Un comentario en Perfiles de Desempeños preguntaba sobre el performance Tuning de E-Business Suite y cómo se puede realizar ya que es una aplicación muy cerrada; así que decidí escribir esta entrada como un primer intento en acercarnos a la mejora del desempeño de un E-business suite.

Me ha tocado trabajar con versiones de Oracle Apps desde 10.4, pasando por 10.7, 11.0.3, 11i y recientemente con R12. Y lo que me ha tocado observar es que Oracle siempre nos ha facilitado la vida en cada nuevo release.

Primero que nada hay que empezar por entender la arquitectura, la cual (en su mínimo) está formada por:

Servidor de Base de datos
Servidor de Formas
Servidor de Reportes
Servidor Apache

Hay que tener en cuenta que, en cada uno de estos servicios o capas, pudiéramos encontrar una posibilidad de mejora, pero generalmente, el punto más crítico es la relación de Reportes - Base de Datos o Formas - Base de datos.

Oracle suele entregar un producto probado, y generalmente sin problemas fuertes de desempeño, pero esto sólo sucede en un ambiente "Fresh Vanilla", sin tener en cuenta todas las interfaces y/o desarrollos que se deben de hacer al sistema. Creo yo que en mi experiencia, el 95% de los problemas de desempeño en ambientes de E-Business suite, suelen ser causados por desarrollos, o aplicaciones que acceden al mismo servidor de base de datos.

Ahora, ya que tenemos muy customizado nuestro ambiente de E-business suite, ¿qué podemos hacer para ver qué ocasiona el problema de desempeño?

Hace aproximadamente 10 años, encontre en internet un white paper en www.oncalldba.com que me abrió la mente sobre el performance de concurrentes en e-business, desgraciadamente el white paper se convirtió después en libro, y no me ha sido posible conseguirle.

Desde entonces, tengo un query (que he modificado varias veces) que me da un punto de entrada para revisar que es lo que realmente sucede.

concurrentes.sql


SELECT --q.concurrent_queue_name cola,
f.application_short_name app ,
SUBSTR(tl.user_concurrent_program_name, 1, 40) descripcion ,
SUBSTR(p.concurrent_program_name, 1, 20) programa_concurrente,
COUNT(*) ejecuciones ,
CASE
WHEN SUM(actual_completion_date -actual_start_date) > 1
THEN TRUNC(SUM(actual_completion_date -actual_start_date))
|| 'd '
|| TO_CHAR(to_date(MOD(ROUND(SUM(actual_completion_date -actual_start_date) *24 *60 *60), 86400), 'sssss'), 'hh24:mi:ss')
ELSE TO_CHAR(to_date(ROUND(SUM(actual_completion_date -actual_start_date) *24 *60 *60), 'sssss'), 'hh24:mi:ss')
END Transcurrido ,
CASE
WHEN AVG(actual_completion_date -actual_start_date) > 1
THEN TRUNC(AVG(actual_completion_date -actual_start_date))
|| 'd '
|| TO_CHAR(to_date(MOD(ROUND(AVG(actual_completion_date -actual_start_date) *24 *60 *60),86400), 'sssss'), 'hh24:mi:ss')
ELSE TO_CHAR(to_date(ROUND(AVG(actual_completion_date -actual_start_date) *24 *60 *60), 'sssss'), 'hh24:mi:ss')
END promedio ,
CASE
WHEN MAX(actual_completion_date -actual_start_date) >1
THEN TRUNC(MAX(actual_completion_date -actual_start_date))
||'d '
|| TO_CHAR(to_date(mod(ROUND(MAX(actual_completion_date -actual_start_date) *24 *60 *60),86400), 'sssss'), 'hh24:mi:ss')
ELSE TO_CHAR(to_date(ROUND(MAX(actual_completion_date -actual_start_date) *24 *60 *60), 'sssss'), 'hh24:mi:ss')
END MAX ,
CASE
WHEN MIN(actual_completion_date -actual_start_date) >1
THEN TRUNC(MIN(actual_completion_date -actual_start_date))
||'d '
|| TO_CHAR(to_date(mod(ROUND(MIN(actual_completion_date -actual_start_date) *24 *60 *60),86400), 'sssss'), 'hh24:mi:ss')
ELSE TO_CHAR(to_date(ROUND(MIN(actual_completion_date -actual_start_date) *24 *60 *60), 'sssss'), 'hh24:mi:ss')
END MIN,
CASE
WHEN SUM(actual_start_date -requested_start_date) >1
THEN TRUNC(SUM(actual_start_date -requested_start_date))
||'d '
|| TO_CHAR(to_date(MOD(ROUND(SUM(actual_start_date -requested_start_date) *24 *60 *60), 86400), 'sssss'), 'hh24:mi:ss')
ELSE TO_CHAR(to_date(ROUND(SUM(actual_start_date -requested_start_date) *24 *60 *60), 'sssss'), 'hh24:mi:ss')
END esperas ,
CASE
WHEN AVG(actual_start_date -requested_start_date) > 1
THEN TRUNC(AVG(actual_start_date -requested_start_date))
||'d '
|| TO_CHAR(to_date(mod(ROUND(AVG(actual_start_date -requested_start_date) *24 *60 *60),86400), 'sssss'), 'hh24:mi:ss')
ELSE TO_CHAR(to_date(ROUND(AVG(actual_start_date -requested_start_date) *24 *60 *60), 'sssss'), 'hh24:mi:ss')
END prom_espera
FROM fnd_application f ,
applsys.fnd_concurrent_requests r ,
applsys.fnd_concurrent_queues q ,
applsys.fnd_concurrent_processes PROC,
apps.fnd_concurrent_programs p ,
apps.fnd_concurrent_programs_tl tl
WHERE r.program_application_id = p.application_id
AND tl.application_id = f.application_id
AND tl.concurrent_program_id = p.concurrent_program_id
AND r.concurrent_program_id = p.concurrent_program_id
AND tl.LANGUAGE = 'US'
AND r.status_code IN('C', 'G', 'E')
AND actual_completion_date BETWEEN trunc(sysdate) AND sysdate
-- AND actual_completion_date BETWEEN to_date('17-08-2008 09:00:00', 'dd-mm-yyyy hh24:mi:ss')
--AND to_date('18-08-2008 08:00:00', 'dd-mm-yyyy hh24:mi:ss')
AND p.application_id = f.application_id
AND r.program_application_id = f.application_id
AND
(
proc.concurrent_queue_id = q.concurrent_queue_id
AND queue_application_id = q.application_id
)
AND r.controlling_manager = proc.concurrent_process_id
GROUP BY
--q.concurrent_queue_name,
f.application_short_name ,
p.concurrent_program_name,
tl.user_concurrent_program_name
ORDER BY SUM(actual_completion_date -actual_start_date) DESC;


Este query nos arroja una salida similar a la siguiente:


APP DESCRIPCION PROGRAMA_CONCURRENTE EJECUCIONES TRANSCURRIDO PROMEDIO MAX MIN ESPERAS PROM_ESPERA
------ ----------------------------------------- --------------------- ------------ ------------- --------- -------- --------- ------------ ------------
AR Autoinvoice Import Program RAXTRX 592 10:29:47 00:01:04 00:03:04 00:00:27 02:16:50 00:00:14
WSH Pick Selection List Generation - SRS WSHPSRS 17951 09:56:04 00:00:02 00:00:30 00:00:00 4d 15:58:23 00:00:22
BOM Copy Item Costs Across Organizations CMCCOC 81 06:54:56 00:05:07 00:06:33 00:03:00 00:32:21 00:00:24
ALR Check Event Alert ALECTC 13770 05:57:11 00:00:02 00:00:10 00:00:00 5d 07:09:45 00:00:33
ONT Order Import OEOIMP 1469 04:17:02 00:00:10 00:05:35 00:00:00 16:57:16 00:00:42
CMX Custom Pick Slip Report XXCMX_WSHRDPIK 948 02:17:22 00:00:09 00:00:21 00:00:07 00:46:31 00:00:03
FND Workflow Background Process FNDWFBG 1214 02:09:42 00:00:06 00:06:28 00:00:00 00:40:21 00:00:02
FND Purge Concurrent Request and/or Manager FNDCPPUR 5 01:31:51 00:18:22 00:27:28 00:06:57 00:12:58 00:02:36



Con esta información podemos saber qué es lo que realmente está sucediendo en cuanto programas concurrentes se refiere.

En un siguiente Post analizaré a detalle lo que cada uno de estos campos significan y la forma de atacarlos.