Additional Blogs by Members
cancel
Showing results for 
Search instead for 
Did you mean: 
Former Member

The concept of a function module based virtual cube is well known for its flexibility and ease of use. It can be used to handle complex reporting requirements which can't be covered by a standard BEx functionality. However, the query performance is often an issue when performing complex read and lookup operations. This issue can be addressed by using another approach of storing and accessing data - ABAP shared objects.

In today’s blog I’d like to guide you through a custom solution for a specific business requirement, where the use of virtual infoprovider combined with shared memory resulted in high performance reporting.

Business Requirement

In the process of product innovation of a final product, individual materials are getting obsolete and replaced by new materials. The requirement is to track down the material replacements and show the chain of materials in a BW report.

The data is stored in a DSO as pairs of materials:

successorpredecessorrelationship...
81238119A...
81238119B...
81198117A...
8117...

Besides the material pairs, the type of the relationship and a whole lot of other information describing the 2 materials is stored in a single record.

Two types of reports are required – the chain report and the hierarchy report.  In the chain report, a material chain is displayed as a single row:

mat1mat2mat3mat4
812381198117

In the hierarchy report, the chain is displayed level by level successively, with all the additional information related to the last pair of materials:

mat1mat2mat3mat4 relatonship...          
81238119A...
81238119B...
812381198117#A...
812381198117...

The amount of materials in a single chain differs from chain to chain, there are chains with 30 or more materials. Both reports have a mandatory input variable for a single material number. The reports must display all chains which contain the provided material (independent on which position).

Solution Overview

More solution variants have been evaluated. In the implemented solution (see Figure 1 Solution architecture), the raw data is first loaded into the central DSO from more sources (1). After that, new data is processed through a loop transformation which performs the complex business logic (2). In the last step of the process chain, the data is loaded from the DSO into the shared memory area (3). The data in the shared memory is stored in a class member attribute as a sorted internal table with two fields – successor and predecessor. The chains of materials are created on the fly in the virtual infoprovider for a specified input parameter (4).

Figure 1: Solution architecture

Solution Details

Creating Shared Memory Area

A shared memory area can be created using the SHMA transaction. A good how-to guide can be found here: http://scn.sap.com/docs/DOC-10461.

After creating the shared memory area, two ABAP classes are automatically generated (see Figure 2):

Figure 2: SM-Area definition

The ABAP class /BMW/PRO_SNR_VSNR is a technical class, which contains all of the services methods for attaching, reading and writing into the shared memory area.

The ABAP class /BMW/PRO_A001_CL_SNRVSNR is the user defined class which stores the actual data. Additionally it can provide some freely defined methods with logic to read and manipulate the data. The only requirement for the class is to implement the interface IF_SHM_BUILD_INSTANCE with a single method IF_SHM_BUILD_INSTANCE~BUILD, which is invoked from the framework at the moment of creating the SM area instance.

An instance of the class /BMW/PRO_SNR_VSNR, which resides in the shared memory area, holds a member attribute ROOT, which is a reference to an instance of the class /BMW/PRO_A001_CL_SNRVSNR – see Figure 3.

Figure 3: ROOT attribute of the technical class

Following an example coding of the IF_SHM_BUILD_INSTANCE~BUILD method:

method IF_SHM_BUILD_INSTANCE~BUILD.
 
data: l_sh_area type ref to /bmw/pro_snr_vsnr,
        l_snr_vsnr
type ref to /bmw/pro_a001_cl_snrvsnr,
        lx_exception
type ref to cx_root.

 
try.
    l_sh_area
= /bmw/pro_snr_vsnr=>attach_for_write( ).
     
catch cx_shm_error into lx_exception.
       
raise exception type cx_shm_build_failed
         
exporting previous = lx_exception.
 
endtry.

 
try.

   
create object l_snr_vsnr area handle l_sh_area.
    l_sh_area
->set_root( l_snr_vsnr ).
    l_sh_area
->detach_commit( ).

    catch cx_shm_error into lx_exception.
     
raise exception type cx_shm_build_failed
       
exporting previous = lx_exception.
 
endtry.

 
if invocation_mode = cl_shm_area=>invocation_mode_auto_build.
   
call function 'DB_COMMIT'.
 
endif.
endmethod.

In the method, the instance of the class /BMW/PRO_A001_CL_SNRVSNR is created. The constructor of the class calls the method REFRESH(), which encapsulates the select logic for the internal table:

method CONSTRUCTOR.

  refresh( ).

endmethod.

method REFRESH.
  refresh _t_snr_vsnr[].
  select
    /bic/prosnr as snr
    /bic/prosnrvg1 as vsnr
    from /bic/aprodksn100 into corresponding fields of table _t_snr_vsnr
    where /bic/proflag2 = 'X'.

  refresh _t_vsnr_snr[].
  select
    /bic/prosnrvg1 as vsnr
    /bic/prosnr as snr
    from /bic/aprodksn100 into corresponding fields of table _t_vsnr_snr
    where /bic/proflag2 = 'X'
    and /bic/prosnrvg1 ne space.
endmethod.

Refreshing shared memory area after new data is loaded

In the process chain, there is a step for refreshing the SM area, after the data is loaded and processed. It can be implemented using ABAP program call:

Figure 4: Process chain step to refresh the SM area

REPORT  /BMW/PRO_SNR_PK_SHMM.

start-of-selection.

call function '/BMW/PRO_SNR_VSNR_REFRESH'
  destination /bmw/pro_a001_cl_param=>rfc_shm
  exceptions
    EXCLUSIVE_LOCK        = 1
    ATTACH_ERROR          = 2
    WRONG_SERVER_INSTANCE = 3
    OTHERS                = 99.
if sy-subrc = 0.
  message 'OK' type 'I'.
elseif sy-subrc = 1.
  message 'EXCLUSIVE_LOCK!' type 'I'.
elseif sy-subrc = 2.
  message 'ATTACH_ERROR!' type 'I'.
elseif sy-subrc = 3.
  message 'WRONG_SERVER_INSTANCE!' type 'I'.
else.
  message 'OTHERS!' type 'I'.
endif.

In the called function module, the SM area is attached for update and the method REFRESH() is called:

FUNCTION /BMW/PRO_SNR_VSNR_REFRESH.
*"----------------------------------------------------------------------
*"*"Lokale Schnittstelle:
*"  EXCEPTIONS
*"      EXCLUSIVE_LOCK
*"      ATTACH_ERROR
*"      WRONG_SERVER_INSTANCE
*"      OTHERS
*"----------------------------------------------------------------------
  data:
    sm_handle type ref to /bmw/pro_snr_vsnr,
    l_t_servers type standard table of msxxlist,
    l_s_servers type msxxlist.

* Prüfung, ob wir auf der richtigen Instanz laufen
  call function 'TH_SERVER_LIST'
    tables list = l_t_servers
    exceptions others = 99.
  if sy-subrc <> 0.
    raise OTHERS.
  endif.

  read table l_t_servers into l_s_servers with key name = /bmw/pro_a001_cl_param=>rfc_shm.
  if sy-subrc ne 0 or sy-host ne l_s_servers-host.
    raise WRONG_SERVER_INSTANCE.
  endif.

  try.
    sm_handle = /bmw/pro_snr_vsnr=>attach_for_update( ).
    sm_handle->root->refresh( ).
    sm_handle->detach_commit( ).

    catch cx_shm_no_active_version.
      sm_handle = /bmw/pro_snr_vsnr=>attach_for_write( ).
      data: sm_data type ref to /bmw/pro_a001_cl_snrvsnr.
      create object sm_data area handle sm_handle.
      sm_handle->set_root( sm_data ).
      sm_handle->detach_commit( ).
    catch cx_shm_exclusive_lock_active.
      raise EXCLUSIVE_LOCK.
    catch cx_shm_attach_error.
      raise ATTACH_ERROR.
    catch cx_root.
      raise OTHERS.
  endtry.

ENDFUNCTION.

Reading from shared memory area using virtual cube

The reporting is performed on a virtual infocube PROVSVKH based on a function module:

Figure 5: Virtual infocube - definition

In the implemented function module, another function module is called, which performs the reading from the SM area. The following diagram shows the simplified logic flow:

Figure 6: Function module for the virtual infoprovider

In the function module, first the ATTACH_FOR_READ( ) static method is called, then the custom method GET_HIERARCHIES( ) to construct the material number chains is performed:

FUNCTION /BMW/PRO_SNR_GET_HIER.
*"----------------------------------------------------------------------
*"*"Lokale Schnittstelle:
*"  IMPORTING
*"     VALUE(I_T_SNR) TYPE  /BMW/PRO_A001_SNR_RANGE_T
*"     VALUE(I_DEPTH) TYPE  INT4
*"     VALUE(I_PAPA_OPA) TYPE  RS_BOOL DEFAULT 'X'
*"     VALUE(I_NUR_ROOT) TYPE  RS_BOOL DEFAULT 'X'
*"  EXPORTING
*"     VALUE(E_T_HIER) TYPE  /BMW/PRO_SNR_HIER_T
*"  EXCEPTIONS
*"      NO_ACTIVE_VERSION
*"      EXCLUSIVE_LOCK_ACTIVE
*"      ATTACH_ERROR
*"      WRONG_SERVER_INSTANCE
*"----------------------------------------------------------------------
  data:
    sm_handle type ref to /bmw/pro_snr_vsnr,
    l_t_snr type /bmw/pro_snr_t,

    l_t_servers type standard table of msxxlist,
    l_s_servers type msxxlist.

* Prüfung, ob wir auf der richtigen Instanz laufen
  call function 'TH_SERVER_LIST'
    tables list = l_t_servers
    exceptions others = 99.
  if sy-subrc <> 0.
    raise OTHERS.
  endif.

  read table l_t_servers into l_s_servers with key name = /bmw/pro_a001_cl_param=>rfc_shm.
  if sy-subrc ne 0 or sy-host ne l_s_servers-host.
    raise WRONG_SERVER_INSTANCE.
  endif.

  select distinct /bic/prosnr as snr from /bic/pprosnr
    into table l_t_snr where /bic/prosnr in i_t_snr.
  check sy-subrc = 0.

  try.
    sm_handle = /bmw/pro_snr_vsnr=>attach_for_read( ).
    call method sm_handle->root->get_hierarchies(
      exporting
        i_t_snr           = l_t_snr
        i_depth           = i_depth
        i_papa_opa_urteil = i_papa_opa
        i_nur_root       = i_nur_root
      receiving
        r_t_hier          = e_t_hier
    ).
    sm_handle->detach( ).

    catch cx_shm_no_active_version.
      raise NO_ACTIVE_VERSION.
    catch cx_shm_exclusive_lock_active.
      raise EXCLUSIVE_LOCK.
    catch cx_shm_attach_error.
      raise ATTACH_ERROR.
  endtry.

ENDFUNCTION.

The method GET_HIERARCHIES( ) is the heart peace of the solution. It constructs the material number chains (M1 – M2 – M3 – … – MN) using a recursion algorithm on the internal table with material pairs (predecessor – successor).

Dealing with multi-instance server landscape

In the production environment there are usually more server instances over which the workload is distributed. Each server instance has its own shared memory. To be able to use the shared memory approach over more server instances, two solutions are possible:

  • keep the shared memory area on every server instance
  • choose a single server instance, where the SM area will reside and perform all the
    READ and UPDATE operations on this server instance over RFC

In our solution, the second approach was used to save some memory space. The chosen server instance is controlled dynamically through a parameter.  All function modules, which access the SM area are called with an RFC destination:

call function '/BMW/PRO_SNR_VSNR_REFRESH'
  destination /bmw/pro_a001_cl_param=>rfc_shm
  exceptions
    EXCLUSIVE_LOCK        = 1
    ATTACH_ERROR          = 2
    WRONG_SERVER_INSTANCE = 3
    OTHERS                = 99.

CALL FUNCTION '/BMW/PRO_SNR_GET_HIER'
  destination /bmw/pro_a001_cl_param=>RFC_SHM
  EXPORTING
    i_t_snr     = l_t_snr_range
    i_depth     = l_depth
    i_papa_opa  = l_papa_opa
    i_nur_root  = l_root
  IMPORTING
    e_t_hier    = l_t_hier
  exceptions
    no_active_version = 1
    exclusive_lock_active = 2
    attach_error = 3
    wrong_server_instance = 4
    system_failure = 5
    communication_failure = 6
    others = 99.

In the function module logic, additional check is performed to prove if the current server instance (sy-host) is correct:

call function 'TH_SERVER_LIST'
  tables list = l_t_servers
    exceptions others = 99.
 
if sy-subrc <> 0.
  raise OTHERS.
endif.

read table l_t_servers into l_s_servers
  with key name = /bmw/pro_a001_cl_param=>rfc_shm.
if sy-subrc ne 0 or sy-host ne l_s_servers-host.
  raise WRONG_SERVER_INSTANCE.
endif.

Summary

Using shared memory approach in a BW reporting scenario can bring a significant performance gain. Of course you cannot load the whole BW system into the shared memory. However, this approach can be considered for special business reporting requirements with complex reporting logic, where many read operations have to be performed to render a single report.