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.