Friday, 18 March 2022

Prefill UI2 Cache for better performance @ initial call of FLP

Case Description

I noticed that when Fiori Launchpad (FLP) is starting, it’s calling /UI2/PAGE_BUILDER_PERS OData service via the URL like below:

/sap/opu/odata/UI2/PAGE_BUILDER_PERS/PageSets(‘%2FUI2%2FFiori2LaunchpadHome’)?$expand=Pages/PageChipInstances/Chip/ChipBags/ChipProperties,Pages/PageChipInstances/RemoteCatalog,Pages/PageChipInstances/ChipInstanceBags/ChipInstanceProperties,AssignedPages,DefaultPage&sap-cache-id=6C0B84AA82291EDCA8F56801507C3C1B

This call is needed to get the needed information for your personal FLP configuration, including tiles, groups etc.

The result of this call is cached in your Internet Browser, however during the first login into FLP such call may take a very long time. It depends on the complexity of your FLP configuration.

But the performance of this call is also dependent on UI2 Cache (table /UI2/CACHE). You can disable cache in your Internet Browser and check the performance.

Performance Measurements

First, we delete entries in /UI2/CACHE for my user using the report /UI2/DELETE_CACHE.

After that we execute the sample URL in teh GW Client (/IWFND/GW_CLIENT).

SAP ABAP Exam Prep, SAP ABAP Career, SAP ABAP Skills, SAP ABAP Preparation, SAP ABAP Jobs, SAP ABAP Materials
GW Client – First Execution – 153 seconds

One can see it took around 153 seconds.

After that we can see that 332 entries have been created in the table /UI2/CACHE.

SAP ABAP Exam Prep, SAP ABAP Career, SAP ABAP Skills, SAP ABAP Preparation, SAP ABAP Jobs, SAP ABAP Materials
332 Cache Entries Added

After that we execute the ULR again and observe the performance is just about 15 seconds, or 10 times better than at initial call.

SAP ABAP Exam Prep, SAP ABAP Career, SAP ABAP Skills, SAP ABAP Preparation, SAP ABAP Jobs, SAP ABAP Materials
GW Client – Second Call – 15 seconds

Idea Realization


When a big number of new users or Big-Bang Go-Live is expected, it would be beneficial for end users and also from system capacity perspective to have the needed data pre-calculated and stored in UI2 Cache.

To do this, I have developed a report, which calls the needed URLs for the selected users.

SAP ABAP Exam Prep, SAP ABAP Career, SAP ABAP Skills, SAP ABAP Preparation, SAP ABAP Jobs, SAP ABAP Materials
Fill UI2 Cache Report

It will schedule a background job under the selected user’s name. The job will call the above URL, where the parameter sap-cache-id will be generated.

SAP ABAP Exam Prep, SAP ABAP Career, SAP ABAP Skills, SAP ABAP Preparation, SAP ABAP Jobs, SAP ABAP Materials
SM37 – Job Monitor

When job is finished the UI2 cache is filled, and the consequent executions of such query will take normal time.

At first login into Fiori Launchpad the data will be read from UI2 cache and will be cached on the client side.

SAP ABAP Exam Prep, SAP ABAP Career, SAP ABAP Skills, SAP ABAP Preparation, SAP ABAP Jobs, SAP ABAP Materials
HTTP Trace – First Call – 12 seconds

Afterwards, the data will be read from the browser cache.

SAP ABAP Exam Prep, SAP ABAP Career, SAP ABAP Skills, SAP ABAP Preparation, SAP ABAP Jobs, SAP ABAP Materials
HTTP Trace – Second Call – 0.5 Seconds

Coding


Below you can find the coding of the report.

Please do not treat it as SAP standard coding. It’s just a template you can freely use for your needs and on your own responsibility. Obviously, no support is provided.

*&---------------------------------------------------------------------*
*& Report ZDMSH_UI2_CACHE_FILL
*&---------------------------------------------------------------------*
*&
*&---------------------------------------------------------------------*
report zdmsh_ui2_cache_fill.

tables: usr02.

data: gv_url         type string.

data: gr_dockingleft type ref to cl_gui_docking_container,
      gr_text_editor type ref to cl_gui_textedit,
      gv_repid       type syrepid,
      gt_textlines   type table of tline-tdline.

select-options: so_usr for usr02-bname.
parameters:     pa_usr type usr02-bname no-display.
parameters:     pa_url type string no-display.
parameters:     pa_btc type flag no-display default abap_false.

at selection-screen output.

  gv_repid = sy-repid.

  if sy-batch = abap_false and gr_dockingleft is not bound.

    create object gr_dockingleft
      exporting
        repid     = gv_repid
        dynnr     = sy-dynnr
        side      = gr_dockingleft->dock_at_left
        extension = 1070.

    create object gr_text_editor
      exporting
        parent = gr_dockingleft.

    " Prepopulate with the longest request...
    gt_textlines = value #( ( '/sap/opu/odata/UI2/PAGE_BUILDER_PERS/PageSets(''%2FUI2%2FFiori2LaunchpadHome'')?'                    )
                            ( '$expand=Pages/PageChipInstances/Chip/ChipBags/ChipProperties,Pages/PageChipInstances/RemoteCatalog,' )
                            ( 'Pages/PageChipInstances/ChipInstanceBags/ChipInstanceProperties,AssignedPages,DefaultPage'           )
                           ).

    call method gr_text_editor->set_text_as_r3table
      exporting
        table  = gt_textlines
      exceptions
        others = 1.
  endif.

start-of-selection.

  perform check_params.

  perform get_base_url.

  if pa_btc is initial.
    perform start_process.
  else.
    perform call_url.
  endif.

**********************************************************
* Get Base URL
**********************************************************
form check_params.
  " Batch Mode...
  if pa_btc is not initial.
    if pa_url is initial.
      message 'URL is not valid' type 'E'.
    endif.
    if pa_usr is initial.
      message 'User ID is not specified' type 'E'.
    endif.
  endif.
endform.

************************************************************
* Get Base URL
************************************************************
form get_base_url.

  if pa_url is not initial and pa_btc = abap_true.
    gv_url = pa_url.
  else.
    if sy-batch = abap_false.
      call method gr_text_editor->get_text_as_r3table
        importing
          table  = gt_textlines
        exceptions
          others = 1.

      clear: gv_url.
      loop at gt_textlines into data(ls_text_line).
        concatenate gv_url ls_text_line into gv_url.
      endloop.
      condense gv_url no-gaps.

      " Remove SAP-CACHE-ID parameter...
      split gv_url at '&' into table data(lt_url).
      clear gv_url.
      loop at lt_url into data(lv_url).
        if not to_lower( lv_url ) cs 'sap-cache-id'.
          concatenate gv_url lv_url into gv_url separated by '&'.
        endif.
      endloop.
      shift gv_url left by 1 places in character mode.
    endif.
  endif.

  if gv_url is initial.
    message 'URL is not valid' type 'E'.
  endif.
endform.

**********************************************************
* Call URL {gv_url}
**********************************************************
form call_url.

  data: lo_client_proxy      type ref to /iwfnd/cl_sutil_client_proxy.
  data: lt_request_header    type /iwfnd/sutil_property_t.

  check: pa_btc is not initial.

  lo_client_proxy = /iwfnd/cl_sutil_client_proxy=>get_instance( ).

  " Set HTTP Method...
  append value #( name  = if_http_header_fields_sap=>request_method
                  value = 'GET' )
            to lt_request_header[].

  " Set Request URI...
  append value #( name  = if_http_header_fields_sap=>request_uri
                  value = gv_url )
            to lt_request_header[].

  message |Calling URL: { gv_url } | type 'S'.

  call method lo_client_proxy->web_request(
    exporting
      it_request_header  = lt_request_header
    importing
      ev_status_code     = data(lv_status_code)
      ev_status_text     = data(lv_status_text)
      ev_error_text      = data(lv_error_text)
      ev_error_timestamp = data(lv_error_timestamp)
      ev_duration        = data(lv_duration) ).

  message |HTTP Code: { lv_status_code } | type 'S'.
  message |Code Text: { lv_status_text } | type 'S'.
  if lv_error_text is not initial.
    message |Error: { lv_error_text } | type 'S'.
    message |Error Timestamp: { lv_error_timestamp } | type 'S'.
  endif.
  message |Duration: { lv_duration } | type 'S'.

endform.

**********************************************************
* Start Process
**********************************************************
form start_process.

  type-pools: sbtch.

  data: lv_jobname            type tbtcjob-jobname.
  data: lv_jobcount           type tbtcjob-jobcount.
  data: ls_print_parameters   type pri_params.
  data: lv_url                type string.
  data: lv_max_proc           type i.

  check: pa_btc is initial.

  " Get List of Users...
  select distinct bname from usr02
    into table @data(lt_usr)
    where bname in @so_usr[].
  if sy-subrc <> 0.
    message 'No users selected' type 'E'.
  endif.

  call function 'TH_COUNT_WPS'
    importing
      btc_wps = lv_max_proc.
  if lv_max_proc = 0.
    lv_max_proc = 10.
  endif.

  loop at lt_usr into data(lv_usr).

    clear: lv_jobname,
           lv_jobcount,
           ls_print_parameters,
           lv_url.

    lv_jobname = gv_repid && '_' && lv_usr-bname.

    " Job open...
    call function 'JOB_OPEN'
      exporting
        delanfrep        = ' '
        jobgroup         = ' '
        jobname          = lv_jobname
        sdlstrtdt        = sy-datum
        sdlstrttm        = sy-uzeit
      importing
        jobcount         = lv_jobcount
      exceptions
        cant_create_job  = 01
        invalid_job_data = 02
        jobname_missing  = 03.
    if sy-subrc <> 0.
      write: /, 'Did not start for ', lv_usr-bname.
      continue.
    endif.

    " Set-up Print Parameteres...
    call function 'GET_PRINT_PARAMETERS'
      exporting
        immediately            = ' '   " leave blank so is not sent to print
        no_dialog              = 'X'
      importing
        out_parameters         = ls_print_parameters
      exceptions
        archive_info_not_found = 1
        invalid_print_params   = 2
        invalid_archive_params = 3
        others                 = 4.
    if sy-subrc <> 0.
      write: /, 'Did not start for ', lv_usr-bname.
      continue.
    endif.

    try.
        lv_url = gv_url && '&sap-cache-id=' && cl_system_uuid=>if_system_uuid_static~create_uuid_c32( ).
      catch cx_root.
        exit.
    endtry.

    " Start Report for another user...
    submit zdmsh_ui2_cache_fill to sap-spool and return
      with pa_url = lv_url
      with pa_usr = lv_usr-bname
      with pa_btc = abap_true
      user lv_usr-bname
      spool parameters ls_print_parameters
      without spool dynpro
      via job lv_jobname
      number lv_jobcount.

    " Start Job...
    call function 'JOB_CLOSE'
      exporting
        jobcount             = lv_jobcount
        jobname              = lv_jobname
        strtimmed            = abap_true
      exceptions
        cant_start_immediate = 01
        invalid_startdate    = 02
        jobname_missing      = 03
        job_close_failed     = 04
        job_nosteps          = 05
        job_notex            = 06
        lock_failed          = 07
        others               = 99.
    if sy-subrc <> 0.
      write: /, 'Did not start for ', lv_usr-bname.
      continue.
    endif.

    commit work.

    " Check Job Count...
    do 100 times.
      select count(*) into @sy-dbcnt
       from tbtcp where progname = @gv_repid
                    and sdldate  = @sy-datum
                    and status   in ( @sbtch_running, @sbtch_ready, @sbtch_scheduled, @sbtch_released ).
      if sy-dbcnt ge lv_max_proc.
        wait up to 10 seconds.
      else.
        exit.
      endif.
    enddo.

  endloop.

endform.

Source: sap.com

No comments:

Post a Comment