Translate

jueves, 28 de agosto de 2014

Ejemplo Mejorado Proceso Paralelo Modulo de Funciones

En la siguiente entrada se explica como con un cambio de enfoque podemos hacer mucho más eficiente el parallel processing o proceso paralelo con modulo de funciones para así tener más control del mismo y un uso eficiente de la memoria del servidor.

A continuación mostraré un breve ejemplo:

Acá se seleccionan el número de paquetes en el cual se debe distribuir la carga en tu función paralela:
SELECTION-SCREEN BEGIN OF BLOCK b03 WITH FRAME TITLE text-010.
SELECTION-SCREEN SKIP.
SELECT-OPTIONSso_packs for CPETASKLOG-CPEINSTANCEID no INTERVALS.
SELECTION-SCREEN SKIP.
SELECTION-SCREEN END OF BLOCK b03.
Se seleccionan los datos a procesar se hace la llamada al modulo de funciones que se va a ejecutar en paralelo:
START-OF-SELECTION.
PERFORM GET_DATA CHANGING  tab_datos.
Se inicia el proceso previo al proceso paralelo:
PERFORM F_PROCESS_PP USING TAB_DATOS
                                                             GT_REPROCESS.
Toda la información que se almacena en gt_reprocess son registros que no fueron procesados por diferentes razones.
En la próxima entrada indicaremos como proceder a su reprocesamiento
PERFORM SHOW_LOGS USING GT_LOG.

“------------------------------------------------------------------------------------
FORM F_PROCESS_PP  USING tab_reg_datos  like tab_datos
                                         CHANGING  P_REPROCESS LIKE gt_reprocess.
  DATA: lineas TYPE i,
        ii TYPE string,
        lv_msg TYPE string,
        zit_trab  TYPE TABLE OF zbl02_st_datos_trabajador.
****************************************************************
  data : rng_nro TYPE i,
         lv_LINE_from type I,
         lv_LINE_to   type I,
         e_cnt_packages TYPE BANK_DTE_PP_CNT_PACKAGES,
         tabix TYPE sytabix.
  DATA: lv_range type i.
*****************************************************************
  DESCRIBE TABLE tab_reg_datos LINES gv_lineas.
Se asignan el número de paquetes en el cual debemos subdividir el trabajo paralelo.
e_cnt_packages = so_packs-low.
  DO e_cnt_packages  TIMES.
    add 1 to rng_nro .
“Esta function  nos dice el rango de que linea a que linea va cada paquete
    CALL FUNCTION 'ZMF_FILE_PACK_RANGE'
      EXPORTING
        I_RANGE_NO         = rng_nro
        I_CNT_RANGES_TOTAL = e_cnt_packages
        I_START_NUM        = 1
        I_LAST_NUM         = gv_lineas
      IMPORTING
        E_LINE_FROM        = lv_LINE_from
        E_LINE_TO          = lv_LINE_to
      EXCEPTIONS
        RANGE_INVALID      = 1
        OTHERS             = 2.
    IF SY-SUBRC <> 0.
* Implement suitable error handling here
    ENDIF.
·         Agregamos el numero de lineas a una nueva table para su procesamiento paralelo
    APPEND LINES OF tab_reg_trab FROM lv_line_from TO lv_line_to TO TAB_DATOS.
*
Aca llamamos a nuestra función que se desarrollo en la entrada anterior con nuevos parámetros
    PERFORM CALL_PARALLEL USING TAB_DATOS
                                                                  rng_nro
                                           CHANGING  P_REPROCESS.
    free TAB_DATOS[].
  ENDDO.
* Se hace esperar a la tarea para que se ejecute correctamente.
  WAIT UNTIL gv_rcv_jobs >= gv_snd_jobs.
ENDFORM.                    " F_PROCESS_PP

FORM f_procesar_reg USING    tab_datos LIKE tab_datos
                                                       task TYPE i
                                      CHANGING  tab_reprocess LIKE gt_reprocess.
  DATA: lv_msg TYPE string,
        modul TYPE i,
        wa_reprocess TYPE ty_tasks.
  "Fecha para mostrar el log
  CONCATENATE sy-datum+6(2)
              sy-datum+4(2)
              sy-datum(4)
         INTO gv_fecha
  SEPARATED BY gc_punto.
*
  CLEAR gs_reg_trab.
  gv_system = text-003.
* Se obtiene el índice para generar el nombre de la tarea.
  gv_index = task.
  CONDENSE gv_index.
* Se genera el nombre de la tarea, único por cada registro a procesar.
  CONCATENATE text-006
              gv_index
         INTO gv_taskname.
* Se llama el RFC
  CALL FUNCTION 'ZBL02_FM_PROC_DAT_TRAB' STARTING NEW TASK gv_taskname
    DESTINATION IN GROUP gv_system
    PERFORMING f_finalizar_funcion ON END OF TASK
    EXPORTING
      it_str_datos_trab     = tab_datos
    EXCEPTIONS
      system_failure        = 1  MESSAGE mess
      communication_failure = 2  MESSAGE mess
      resource_failure      = 3.      "Tipo de relación
  CASE sy-subrc.
    WHEN 0. "Si no hay error se incrementa contador de éxito.
      gv_snd_jobs = gv_snd_jobs + 1.
    WHEN 1 or 2.
      MESSAGE mess TYPE 'I'.
    WHEN 3. "Si hubo error se hace esperar a la tarea.
      IF  gv_snd_jobs >= 1 AND gv_exc_flag = 0.
        gv_exc_flag = 1.
        WAIT UNTIL gv_rcv_jobs >= gv_snd_jobs
        UP TO 5 SECONDS.
      ENDIF.
      IF sy-subrc = 0.
        gv_exc_flag = 0.
      ELSE.
        MESSAGE 'Resource failure' TYPE 'I'.
      ENDIF.
      wa_reprocess-taskname = gv_taskname.
      wa_reprocess-tasknro  = task.
      APPEND wa_reprocess to tab_reprocess
“Aca guardamos solo la tarea que fallo para su porterior reprocesamiento
      free: lv_msg,wa_reprocess.
    WHEN OTHERS.
      MESSAGE 'Other Error' TYPE 'I'.
  ENDCASE.
*
ENDFORM.                    
Una vez finalizado todos los llamados a la función paralela el programa llama al perform donde recibimos el resultado de su ejecución y guardamos los logs correspondientes.
FORM f_finalizar_funcion USING gv_taskname.
  DATA: mess  TYPE c LENGTH 80.
  task_wa-name = gv_taskname.
  gv_rcv_jobs = gv_rcv_jobs + 1.
* Se reciben los resultados del RFC para loguear los mensajes.
  RECEIVE RESULTS FROM FUNCTION ' Z_FM_PARALLELS '
   TABLES
    e_tab_return = gt_log
“Es importante colocar las exceptions en caso de algún error de comunicación o el paralelo tenga “algún problema
  EXCEPTIONS
      system_failure        = 1 MESSAGE mess
      communication_failure = 2 MESSAGE mess .
  IF sy-subrc eq 0.
    task_wa-dest = info-rfcdest.
  ELSE.
    task_wa-dest = mess.
  ENDIF.
  APPEND task_wa TO task_list.
*
* Se loguean todos los mensajes
  CLEAR gs_log.
  LOOP AT gt_log INTO gs_log.
    CLEAR gv_tam_msj.
    gv_tam_msj = strlen( gs_log-message ).
    IF gv_tam_msj > 50.
      gs_message-msg_text_2 = gs_log-message+50.
      IF gv_tam_msj > 100.
        gs_message-msg_text_3 = gs_log-message+100.
      ENDIF.
    ENDIF.
    gs_message-msgty      = gs_log-type.
    gs_message-msg_text_1 = gs_log-message.
    APPEND gs_message TO gt_message.
    CLEAR: gs_message.
*           gt_message[].
    CLEAR gs_log.
*
  ENDLOOP.
  FREE: gt_log.
*
ENDFORM.


·         Los puntos relevantes del código se han puesto en negrita para facilitar su importancia

miércoles, 27 de agosto de 2014

Ejemplo Básico Parallel Processing con Modulo de Funciones

Iniciemos explicando cómo podemos generar un proceso paralelo usando modulo de funciones:
Básicamente son dos fragmentos de código los cuales se deben desarrollar:
Después de seleccionar los datos a procesar se hace la llamada al modulo de funciones que se va a ejecutar en paralelo:
START-OF-SELECTION.
PERFORM GET_DATA CHANGING  tab_datos.
“Nos enfocaremos solo en el perform call parallel
PERFORM CALL_PARALLEL USING TAB_DATOS.
PERFORM SHOW_LOGS USING GT_LOG.

FORM call_parallel  using tab_datos  type ztt_datos.
Field-Symbols: <fs_datos> type zst_datos.
Loop at tab_datos assigning <fs_datos> .
  CALL FUNCTION 'Z_FM_PARALLELS' STARTING NEW TASK gv_taskname
    DESTINATION IN GROUP gv_system
    PERFORMING f_finalizar_funcion ON END OF TASK
    EXPORTING
      it_datos    = <fs_datos>
“ Es muy importante agregar las exceptions ya que nos permitirán informarnos en qué momento “falla la llamada al paralelo y saber la razón”
    EXCEPTIONS                
      system_failure        = 1  MESSAGE mess
      communication_failure = 2  MESSAGE mess
      resource_failure      = 3.      "Tipo de relación
  CASE sy-subrc.
    WHEN 0. "Si no hay error se incrementa contador de éxito.
      gv_snd_jobs = gv_snd_jobs + 1.
    WHEN 1 or 2.
      MESSAGE mess TYPE 'I'.
    WHEN 3. "Si hubo error se hace esperar a la tarea.
      IF  gv_snd_jobs >= 1 AND gv_exc_flag = 0.
        gv_exc_flag = 1.
        WAIT UNTIL gv_rcv_jobs >= gv_snd_jobs
        UP TO 5 SECONDS.
      ENDIF.
      IF sy-subrc = 0.
        gv_exc_flag = 0.
      ELSE.
        MESSAGE 'Resource failure' TYPE 'I'.
      ENDIF.
    WHEN OTHERS.
      MESSAGE 'Other Error' TYPE 'I'.
Endloop….
* Se hace esperar a la tarea para que se ejecute correctamente.
  WAIT UNTIL gv_rcv_jobs >= gv_snd_jobs.
Endform.

Una vez finalizado todos los llamados a la función paralela el programa llama al perform donde recibimos el resultado de su ejecución y guardamos los logs correspondientes.
FORM f_finalizar_funcion USING gv_taskname.
  DATA: mess  TYPE c LENGTH 80.
  task_wa-name = gv_taskname.
  gv_rcv_jobs = gv_rcv_jobs + 1.
* Se reciben los resultados del RFC para loguear los mensajes.
  RECEIVE RESULTS FROM FUNCTION ' Z_FM_PARALLELS '
   TABLES
    e_tab_return = gt_log
“Es importante colocar las exceptions en caso de algún error de comunicación o el paralelo tenga “algún problema
  EXCEPTIONS
      system_failure        = 1 MESSAGE mess
      communication_failure = 2 MESSAGE mess .
  IF sy-subrc eq 0.
    task_wa-dest = info-rfcdest.
  ELSE.
    task_wa-dest = mess.
  ENDIF.
  APPEND task_wa TO task_list.
*
* Se loguean todos los mensajes
  CLEAR gs_log.
  LOOP AT gt_log INTO gs_log.
    CLEAR gv_tam_msj.
    gv_tam_msj = strlen( gs_log-message ).
    IF gv_tam_msj > 50.
      gs_message-msg_text_2 = gs_log-message+50.
      IF gv_tam_msj > 100.
        gs_message-msg_text_3 = gs_log-message+100.
      ENDIF.
    ENDIF.
    gs_message-msgty      = gs_log-type.
    gs_message-msg_text_1 = gs_log-message.
    APPEND gs_message TO gt_message.
    CLEAR: gs_message.
*           gt_message[].
    CLEAR gs_log.
*
  ENDLOOP.
  FREE: gt_log.
*
ENDFORM.



Cabe destacar que con esta lógica solo estamos generando un proceso paralelo por cada registro de la tabla interna y lo ideal sería darle mas carga a la función paralela para que sea más eficiente.

lunes, 25 de agosto de 2014

Introducción Parallel Processing usando Modulo de funciones

Introducción


Se busca desarrollar un programa que ejecute una gran carga de registros de manera paralela de una manera eficiente y eficaz.

Problema


Realizando las pruebas y mejoras de performance a los programas que usan procesos paralelos se pudo determinar:
1.       Se ejecutaba un hilo por cada registro.
2.       Si había gran cantidad de registros se colapsa la memoria RAM.

3.       Al momento de colapsar la memoria, empezaban a fallar las llamadas a la función paralelo ocasionando perdida de registros a procesar, sin que se pudieran recuperar y mucho menos saber cuáles registros fallaron.

Desarrollo de la Solución         


Teniendo en cuenta los problemas expuestos anteriormente se fue implementando una solución al mismo:
Usado conocimientos:
1.       Procesos paralelos que usan Modulo de Funciones con la funcionalidad :
-                “STARTING NEW TASK gv_taskname ”.
                      PPF (Parallel Procesing Framework).

Antes de explicar cada uno a profundad se van a explicar algunos conceptos básicos.
Rangos: porción de registros tomados de un universo de registros mayor.
Jobs/tarea: procesos que se ejecutan un rango especifico de manera paralela, es decir se puede procesar más 2 al mismo tiempo.

Implementando la primera


Se pudo verificar que el proceso paralelo al momento de colapsar la memoria la llamada a la función paralela fallaba perdiendo registros hasta que se liberará otro proceso y dar oportunidad se ejecutar a los siguientes.

Implementando la segunda

 Implementado la lógica que usa el Parallel Procesing Framework se logro que el programa se pueda configurar lo cual explico a continuación:

1.       Cantidad de procesos paralelos finita (Jobs/tareas).
2.       La carga de registros (Rangos) se divide en entre los procesos paralelos asignados o calculados .

Mezclando ambas


Implementando la información de cada uno de estas vertientes se pudo implementar:
1.       En caso de falla en la llamada al proceso paralelo se puede saber cual rango de registros (Tarea)  fallo.
2.       Se pueden re-procesar las tareas que fallaron.

E    En esta entrada se explico de manera general los problemas a los que nos hemos enfrentado al iniciar en el mundo de los procesos paralelos abap que estoy seguro que ya les han sucedido, en mi próxima entrada iré explicando mas a fondo cada uno de estos aspectos...

   Mayor información