Optimización de Procesos Críticos en SAP ERP: Estrategias Avanzadas de Performance Testing con LoadRunner

Aprende a optimizar transacciones SAP complejas (SD, MM, FI) mediante performance testing con LoadRunner. Incluye scripts en C funcionales, análisis de cuellos de botella en ST03N y SM50, y configuración de monitoreo.

Optimización de Procesos Críticos en SAP ERP: Estrategias Avanzadas de Performance Testing con LoadRunner

En mis más de 13 años como analista de rendimiento, trabajando con gigantes como Bank of America y Toyota, he aprendido una verdad incómoda: SAP ERP no se lanza, se despliega, y luego se reza para que no se caiga. Pero, en el entorno empresarial moderno, la oración no es una estrategia de TI válida.

Hace unos años, en un proyecto para una aerolínea importante (no diré nombres, pero sus aviones son azules y rojos), el proceso de “Cierre de Mes” (Month-End Closing) tardaba 18 horas. Los reportes financieros se retrasaban, afectando la toma de decisiones en la bolsa. El problema no era el hardware (servidores HANA de última generación), sino cómo las transacciones customizadas de ABAP interactuaban con la base de datos bajo carga.

Hoy, vamos a diseccionar cómo abordamos esta optimización. No hablaremos de teoría académica. Vamos a ver cómo atacar los cuellos de botella en SAP ERP utilizando LoadRunner, con scripts reales, métricas específicas de SAP y estrategias de troubleshooting que salvaron mi carrera más de una vez.

El Desafío Técnico: Por qué SAP es Diferente

A diferencia de una aplicación web típica (stateless), SAP es una bestia compleja que mantiene el estado y utiliza una arquitectura propia de servidores de aplicación y presentación. Cuando hablamos de rendimiento en SAP, no solo medimos “tiempo de respuesta”; medimos:

  1. Dialog Response Time: Tiempo total para que un paso de diálogo complete.
  2. GUI Time: Tiempo que el frontend (SAP GUI o Fiori) tarda en renderizar.
  3. Roll-in / Roll-out Time: Tiempo en crear y destruir el contexto de usuario en los work processes.
  4. DB Request Time: Tiempo real de consulta a la base de datos.

El objetivo del Performance Testing en SAP no es solo “romper” el servidor, sino identificar el equilibrio entre la lógica de aplicación (ABAP) y el acceso a datos.

Selección de Herramientas: ¿Por qué LoadRunner?

Si bien uso k6 y Locust para APIs modernas, cuando se trata de SAP ERP, LoadRunner Professional sigue siendo el estándar de oro por una razón: sus protocolos propietarios.

Existen principalmente dos enfoques:

  1. SAP GUI/Web Protocol: Ideal para automatizar transacciones complejas que interactúan con la interfaz gráfica (VA01, MIRO, F-02). Captura el tráfico a nivel de SAP GUI o WebGUI (HTTP/Dieter).
  2. SAP - Specific Protocol (SAPGUI): Más antiguo, interactúa directamente con la DLL del SAP GUI. Útil para escenarios muy específicos de scripting, pero menos estable en alta concurrencia.

Para este artículo, nos centraremos en SAP Web (HTTP/HTML) protocol, ya que es la forma más robusta de probar escenarios híbridos (Fiori + SAP GUI for HTML) y se integra mejor con pipelines de CI/CD modernos que el protocolo GUI tradicional.

Arquitectura de la Solución de Prueba

Antes de escribir código, definimos el escenario de carga basado en un caso real de retail:

  • Transacción: VA01 (Creación de Pedidos de Venta).
  • Usuarios Peak: 500 usuarios simultáneos.
  • Distribución: 70% Venta, 20% Facturación, 10% Consulta de Stock.
  • KPI Objetivo: Tiempo de respuesta < 4 segundos para VA01 bajo carga pico.

1. Configuración del Entorno

Asumimos que tienes LoadRunner Professional instalado. La clave aquí es la Correlación. SAP utiliza cookies de sesión y tokens dinámicos (como sap-contextid o sap-wd-cltwndid) que fallarán el script si no se manejan correctamente.

2. Scripting Funcional: La Creación del Pedido (VA01)

Este es un script en C (estándar de LoadRunner) para el protocolo SAP Web - HTTP/HTML. He eliminado las partes de grabación estándar para centrarme en la lógica de negocio y la correlación dinámica.

Nota: Este script simula el login, la navegación a la transacción VA01 y el guardado del pedido.

#include "web_api.h"

Action()
{
    int status;
    char *timestamp = lr_eval_string("{timestamp}");
    
    // --- 1. LOGIN Y CORRELACIÓN INICIAL ---
    // Guardamos el contexto de sesión dinámico que SAP genera en cada login
    web_reg_save_param_ex(
        "ParamName=sap_contextid",
        "LB=sap-contextid=",
        "RB=;",
        SEARCH_FILTERS,
        "Scope=Headers",
        LAST
    );

    web_add_header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36");

    lr_start_transaction("SAP_Login");
    
    web_url("sap", 
        "URL=https://erp-frontend.company.com:443/sap/bc/webdynpro_abap/sap/yy_appl_start?sap-client=100", 
        "TargetFrame=", 
        "Resource=0", 
        "RecContentType=text/html", 
        "Referer=", 
        "Snapshot=t1.inf", 
        "Mode=HTML", 
        LAST);
    
    web_submit_data("login_form", 
        "Action=https://erp-frontend.company.com/sap/bc/webdynpro_abap/sap/yy_appl_start", 
        "Method=POST", 
        "RecContentType=text/html", 
        "Referer=https://erp-frontend.company.com/sap/bc/webdynpro_abap/sap/yy_appl_start", 
        "Snapshot=t2.inf", 
        "Mode=HTML", 
        ITEMDATA, 
        "Name=sap-user", "Value={User}", ENDITEM, 
        "Name=sap-password", "Value={Password}", ENDITEM, 
        "Name=sap-client", "Value=100", ENDITEM, 
        "Name=sap-language", "Value=EN", ENDITEM, 
        // Este parámetro suele ser dinámico y requiere correlación
        "Name=sap-contextid", "Value={sap_contextid}", ENDITEM, 
        LAST);

    lr_end_transaction("SAP_Login", LR_AUTO);

    // --- 2. NAVEGACIÓN A VA01 ---
    lr_start_transaction("SAP_Navigate_VA01");
    
    web_url("Navigate_to_VA01", 
        "URL=https://erp-frontend.company.com/sap/bc/gui/sap/its/webgui?~transaction=VA01", 
        "TargetFrame=", 
        "Resource=0", 
        "RecContentType=text/html", 
        "Referer=", 
        "Mode=HTML", 
        LAST);
    
    lr_end_transaction("SAP_Navigate_VA01", LR_AUTO);

    // --- 3. LLENADO DEL PEDIDO (CORRELACIÓN DE CAMPOS DINÁMICOS) ---
    // En SAP web, los nombres de los campos de entrada (ID) pueden ser dinámicos.
    // A menudo necesitamos capturar el ID del campo antes de enviar datos.
    
    // Supongamos que necesitamos capturar un token CSRF o ID de ventana único
    web_reg_save_param_ex(
        "ParamName=window_id",
        "LB=windowId="",
        "RB="",
        "Scope=Body",
        LAST
    );

    lr_start_transaction("SAP_Create_Order_VA01");

    // Enviar datos del pedido (Sales Area, Material, Cantidad)
    web_submit_data("VA01_Submit", 
        "Action=https://erp-frontend.company.com/sap/bc/gui/sap/its/webgui", 
        "Method=POST", 
        "RecContentType=text/html", 
        "Referer=https://erp-frontend.company.com/sap/bc/gui/sap/its/webgui?~transaction=VA01", 
        "Mode=HTML", 
        ITEMDATA, 
        "Name=~~OkCode", "Value=/00", ENDITEM, // Enter key simulation
        "Name=VA01-ORDER_TYPE", "Value=OR", ENDITEM, // Standard Order
        "Name=VA01-SALES_ORG", "Value=1000", ENDITEM, 
        "Name=VA01-DIST_CHAN", "Value=10", ENDITEM, 
        "Name=VA01-DIVISION", "Value=00", ENDITEM, 
        "Name=VA01-REQ_DATE_H", "Value={Date_Today}", ENDITEM, 
        "Name=VA01-PO_NUMBER", "Value=PO_{timestamp}_{UserID}", ENDITEM, // Unique PO Number
        "Name=~~WINDOW_ID", "Value={window_id}", ENDITEM, // Dynamic parameter usage
        LAST);

    lr_end_transaction("SAP_Create_Order_VA01", LR_AUTO);

    // --- 4. VERIFICACIÓN DE ÉXITO ---
    // Validación de que el pedido se creó buscando un patrón de éxito en la respuesta HTML
    web_reg_find("Text=Standard Order created", LAST);
    
    // --- 5. LOGOUT ---
    lr_start_transaction("SAP_Logout");
    web_url("Logout", 
        "URL=https://erp-frontend.company.com/sap/bc/gui/sap/its/webgui?~command=/n", 
        "TargetFrame=", 
        "Resource=0", 
        "RecContentType=text/html", 
        "Referer=", 
        "Mode=HTML", 
        LAST);
    lr_end_transaction("SAP_Logout", LR_AUTO);

    return 0;
}

3. Puesta a Punto Avanzada (Runtime Settings)

En SAP, configurar el “Pacing” (ritmo) incorrecto es el error #1. Si lanzas 500 usuarios sin pensar, saturarás los Dialog Work Processes (DWP) en el servidor de aplicación SAP.

  • Think Time: Habilitado. Los usuarios reales no escriben datos de ventas en 0.5 segundos. Utiliza un think time de 5-10 segundos entre pantallas.
  • Pacing: Configura un “Random Pacing” del 60% al 100%. Esto simula el comportamiento humano real.
  • Number of Connections: Limita a 2 conexiones por usuario. Más conexiones consumen demasiados recursos en el SAP Web Dispatcher.
  • Browser Emulation: Usa Mozilla/Chrome para emular el peso real del renderizado del lado del cliente si estás probando SAP GUI for HTML.

Análisis de Resultados y Monitoreo (El “Performance Engineering”)

Aquí es donde separas a un novato de un senior. LoadRunner te dará gráficos bonitos, pero no te dirá qué está mal dentro de SAP. Necesitas cruzar los datos.

Métricas Clave de SAP a Monitorear

Durante la prueba, debes tener abiertas las transacciones ST03N (Workload Monitor) y SM50 (Process Overview) en tu sistema de desarrollo/quality.

  1. Ratio de Wait en Work Processes:

    • Si en SM50 ves muchos procesos en estado “Wait” (esperando por GUI o por Roll-In), tu problema es de red o de configuración de dispatcher, no de base de datos.
    • Si ves “CPU"" alto, tienes lógica ABAP ineficiente (bucles infinitos, cálculos pesados).
    • Si ves “DB Request"" alto, tienes consultas SQL mal escritas o índices faltantes.
  2. Expensive Statements (ST05):

    • Ejecuta un SQL Trace (ST05) durante la carga. Si ves que una consulta SELECT * FROM VBAK se ejecuta 500 veces por segundo, has encontrado el problema.

Caso de Troubleshooting Real

El Problema: En el script anterior, el tiempo de respuesta para SAP_Create_Order_VA01 era de 12 segundos.

Análisis en LoadRunner: El “Network Time” era bajo (100ms), pero el “Server Time” era altísimo.

Análisis en SAP (ST03N): Vimos que el tiempo de “Processing” era de 1 segundo, pero el “Roll Wait Time” era de 10 segundos.

Diagnóstico: Los Dialog Work Processes estaban todos ocupados procesando otras transacciones pesadas (reportes de contabilidad). La cola de espera crecía.

Solución: No optimizamos el código de VA01 (era estándar de SAP). En su lugar:

  1. Aumentamos el número de Work Processes en el perfil del instance (RZ10).
  2. Implementamos RFC Server Groups (RZ12) para separar la carga de usuarios online de los trabajos batch que se ejecutaban en paralelo durante el día.

Resultado: El tiempo bajó de 12s a 3.5s.

Optimización de Scripts: Manejo de Datos masivos

Cuando optimizamos SAP, a menudo necesitamos pre-llenar datos o crearlos en tiempo real. Usar el mismo Pedido de Venta (PO Number) en un script de carga causará errores de “Duplicate Entry”.

Usemos una función en C para generar datos aleatorios realistas para materiales y clientes.

// Función auxiliar para generar un número de material aleatorio basado en rangos
char* get_random_material() {
    // Supongamos que los materiales van de 1000000 a 1999999
    int min = 1000000;
    int max = 1999999;
    int random_num = min + (rand() % (max - min + 1));
    
    // Convertir a string y retornar (LoadRunner maneja esto en memoria)
    static char material_str[20];
    sprintf(material_str, "%d", random_num);
    return material_str;
}

Action()
{
    // ... código previo ...
    
    // Uso de la función en el web_submit_data
    lr_save_string(get_random_material(), "Dynamic_Material");

    web_submit_data("VA01_Add_Item", 
        "Action=https://erp-frontend.company.com/...", 
        "Method=POST", 
        ITEMDATA, 
        // Inyectamos el material dinámico
        "Name=RV45A-MATNR(01)", "Value={Dynamic_Material}", ENDITEM, 
        "Name=RV45A-KWMENG(01)", "Value=10", ENDITEM, 
        LAST);
    
    // ...
}

Integración CI/CD con GitHub Actions

En un proyecto moderno para una empresa HCM, no podemos ejecutar pruebas manualmente cada vez que un desarrollador toca un objeto ABAP.

Aquí hay un fragmento de cómo integramos LoadRunner en un pipeline de Azure DevOps (o GitHub Actions usando contenedores Docker).

Requisito: Tener la imagen de loadrunner-community o el enterprise runner configurado.

# .github/workflows/sap-performance-test.yml
name: SAP Performance Smoke Test

on:
  push:
    branches: [ main ]

jobs:
  load-test:
    runs-on: ubuntu-latest
    container:
      image: loadrunner/community:latest # Imagen oficial o custom
    steps:
    - uses: actions/checkout@v2
    
    - name: Run LoadRunner Test
      env:
        LG_HOST: ${{ secrets.LG_HOST }} # Load Generator Host
        SAP_USER: ${{ secrets.SAP_USER }}
        SAP_PASS: ${{ secrets.SAP_PASS }}
      run: |
        # Instalar dependencias si es necesario
        # Ejecutar el script desde CLI
        /opt/HP/LoadRunner/bin/lrrun \
          -usr SAP_VA01_Action.usr \
          -result results/ \
          -host $LG_HOST \
          -test SAP_Test_Scene \
          -load \
          -duration 2m 
          
    - name: Analyze Results
      if: always()
      run: |
        # Parsear el archivo .evt para buscar errores
        # Si hay más de 5 errores, fallar el pipeline
        if grep -c "Error" results/*.evt > 5; then
          echo "Performance threshold exceeded!"
          exit 1
        fi

Herramientas Alternativas y Complementarias

Aunque LoadRunner es el rey en SAP, no está demás mencionar alternativas para escenarios específicos:

  1. NeoLoad: Tiene un excelente grabador SAP y su licencia por VUser (Virtual User) a veces es más económica para nubes de prueba masivas. Su soporte para SAP S/4HANA es muy robusto.
  2. k6 (con extensiones xk6-sap): Para equipos DevOps puros que no quieren lidiar con la curva de aprendizaje de C (LoadRunner). k6 usa JavaScript/Go. Sin embargo, soporta principalmente protocolos web; para transacciones SAP GUI profundas, se queda corto.
  3. SolMan eCATT: La herramienta nativa de SAP. Es excelente para pruebas funcionales automatizadas, pero pésima para pruebas de carga estrésantes (no genera carga concurrente real, es secuencial). Úsala para validar datos, no para estresar.

Conclusión y Lecciones Aprendidas

La optimización de procesos SAP ERP mediante testing de rendimiento no es magia negra, es ingeniería de observabilidad aplicada.

Los puntos clave para llevarte de hoy son:

  1. No asumas que es la base de datos: En SAP, el 70% de los problemas que he visto están en la lógica de aplicación ABAP, bloqueos de Enqueue o falta de Work Processes.
  2. Scripting Inteligente: Usa correlación dinámica y datos parametrizados. Un script fallido distorsiona las métricas de rendimiento.
  3. Monitoreo Integrado: Nunca ejecutes LoadRunner sin mirar ST03N y SM50 al mismo tiempo. LoadRunner te dice “qué tan lento” va; SAP te dice “por qué”.

Si logras vincular el tiempo de respuesta de LoadRunner con los tiempos de DB y Roll-in de SAP, te convertirás en el héroe que evitó que el sistema se caiga el Black Friday. Y créeme, eso es lo que define a un Senior Performance Engineer.


Sobre el autor: Analista de Rendimiento Senior especializado en arquitecturas de misión crítica. Apasionado por desmenuzar logs de SAP y encontrar ese microsegundo perdido. Si te has quedado atascado en una correlación de sap-wd-cltwndid, déjame un comentario.