vsobotka
vsobotka

Reputation: 163

Hash a temp-table row

Backstory

I work on the backend for Android clients. There are a lot of data to be displayed and some offline functionality needed, so I have to solve synchronization. Said data consists of temp-tables.

Our solution

Right now, to let the client know about possible changes and not to enforce complete client db wipe and creation (takes a lot of time), I make a hash of each of the temp-table rows. Said hash is created by taking the temp-table row, creating a JSON for it and then making a hash out of that JSON. It is working.

My problems with this solution

I think it is not the ideal solution. All those operations look demanding and from my testing, it shows:

What I want

I am interested if we are doing something wrong. Is there a better solution for this problem? Is there less demanding way for creating hash out of each individual temp-table record?

We are currently using OpenEdge 10.2B.

Procedure responsible for hash creation, create_hash create widget-pool.

define input parameter inp_hBuffer as handle no-undo.
define output parameter out_cHash as c no-undo.

define var ttDynamic as handle no-undo.
define var hBufferTT as handle no-undo.

define var lResult as l no-undo.
define var cDataTT as longchar no-undo.

define var itime as i.

do on error undo,return error:
   if not(valid-handle(inp_hBuffer) and inp_hBuffer:type = 'BUFFER':u) then
      return error substitute('Neplatny typ vstupniho parametru predaneho procedure "&1".','m_ghashb':u).

   create temp-table ttDynamic.
   ttDynamic:add-fields-from(inp_hBuffer).
   ttDynamic:temp-table-prepare(inp_hBuffer:table).

   hBufferTT = ttDynamic:default-buffer-handle.

   hBufferTT:buffer-copy(inp_hBuffer).
   ttDynamic:write-json('longchar':u,cDataTT).

   out_cHash = hex-encode(md5-digest(cDataTT)).
end.

return.

And its usage

for first lbUsrtab where
    lbUsrtab.ucje = GetUcje('m_usrtab':U) and
    lbUsrtab.login-name = LoginName no-lock,
each lbWcesta where
    lbWcesta.ucje = GetUcje('wcesta':U) and
    lbWcesta.kodu = lbUsrtab.kodu no-lock,
each lbWciorg where
    lbWciorg.ucje = GetUcje('wciorg':U) and
    lbWciorg.cest = lbWcesta.cest no-lock,
each lbKontaktr where
    lbKontaktr.ucje = GetUcje('kontaktr':U) and
    lbKontaktr.corg = lbWciorg.corg no-lock
    by lbKontaktr.zako descending
    on error undo, return error return-value:

    create ttKontaktr.
    buffer-copy lbKontaktr to ttKontaktr.
    run create_hash(input buffer ttKontaktr:handle, output ttKontaktr.hash).
end.

What I want

Upvotes: 2

Views: 933

Answers (2)

halfer
halfer

Reputation: 20469

(Posted on behalf of the OP).

Solution

I implemented a solution suggested by Mike. For our structure, I am not able to move helper content outside of procedure as suggested. After implementation to my real world problem and measuring the time of the entire process, with 2450 invocations I get 2054ms for prior solution as opposed to Mike's solutions which takes only 240ms. Which means I am not able in the real world implementation to get the full benefit, but even 8.5 times faster works very well for me. I will keep all that in mind and come back to it to optimize further when I have the time.

Here is how my procedure looks like

DEFINE INPUT  PARAMETER inp_hBuffer AS HANDLE    NO-UNDO .
DEFINE OUTPUT PARAMETER out_cHash   AS CHARACTER NO-UNDO .

DEFINE VARIABLE hRawField AS HANDLE NO-UNDO.
DEFINE TEMP-TABLE ttWithRaw NO-UNDO
    FIELD raw_field AS RAW .

ASSIGN hRawField = BUFFER ttWithRaw:BUFFER-FIELD("raw_field") .
CREATE ttWithRaw .

inp_hBuffer:RAW-TRANSFER (TRUE, hRawField) .
out_cHash = HEX-ENCODE (MD5-DIGEST (ttWithRaw.raw_field)).

Problems we encountered

We have finally got the time to further optimize it according to Mike's suggestion (temp-table definition and creation invoked only once) and it is now even faster. I do not really get why the temp-table definition, but mostly creation of a record, is that "slow".

Also keep in mind that raw-transfer method can't work with fields bigger than 32kB.

Upvotes: 0

Mike Fechner
Mike Fechner

Reputation: 7192

This is an almost 20 times faster solution to your hash (for 1000 invokations it's 320 msec vs 17 msec):

PROCEDURE hash2:
    DEFINE INPUT  PARAMETER inp_hBuffer AS HANDLE    NO-UNDO .
    DEFINE OUTPUT PARAMETER out_cHash   AS CHARACTER NO-UNDO .

    inp_hBuffer:RAW-TRANSFER (TRUE, hRawField) .

    out_cHash = HEX-ENCODE (MD5-DIGEST (ttWithRaw.raw_field)).
END PROCEDURE .

You'll need a helper temp-table with a RAW field and a single record, as the buffer object handle's RAW-TRANSFER method only works on a buffer field object handle, so have something like this outside the hash2 procedure, you'll need to run it only once:

DEFINE VARIABLE hRawField AS HANDLE NO-UNDO.

DEFINE TEMP-TABLE ttWithRaw NO-UNDO
    FIELD raw_field AS RAW .

ASSIGN hRawField = BUFFER ttWithRaw:BUFFER-FIELD("raw_field") .

CREATE ttWithRaw .

I haven't tried yet to optimize your code.

I am interested if we are doing something wrong. Is there a better solution for this problem? Is there less demanding way for creating hash out of each individual temp-table record?

A good portion of the runtime will be from creating the dynamic temp-table for each record - and I doubt that you'll need to create that temp-table for every record over and over again.

Maybe, by moving this part

   create temp-table ttDynamic.
   ttDynamic:add-fields-from(inp_hBuffer).
   ttDynamic:temp-table-prepare(inp_hBuffer:table).

out of the hash procedure, your code will improve as well. I doubt however, that it will be faster than the version based on the RAW-TRANSFER method.

Upvotes: 5

Related Questions