Is there a reliable alternative to current-changed in an opt

Posted by cverbiest on 06-Jul-2017 09:10

We found that current-changed is not reliable.

Any find that fetches the record after it has been changed, anywhere in the session,  will cause the current-changed to be false.

In an event driven UI there is no way of telling what might have been activated by the user between the original find-display-enable and the find-current+test-current-changed.

I'm looking for an alternative.

I'm thinking of creating a checksum of the orignal record to compare instead of using current-changed but I wonder if there are other approaches

sample to demonstrate issue

  • create sports database
  • start server
  • start 2 sessions with -rereadnolock, or change the lock in external_find.p to share-lock
  • run find_current.p in the first session, leave it at the pause.
  • run upd_customer.p in the second session.
  • return to the session with find_current and press space
  • this session now show current changed as false.

In this sample it's prettyn obvious external_find causes it to fail, in an event driven UI with optimistic locking it's impossible to guarantee the user will not start something that causes a find.

/* external_find.p */
&scoped lock no-lock
define buffer lb-customer for customer.
def input param ifirst as log.
if ifirst
then find first lb-customer {&lock}.
else find last lb-customer {&lock}.

/* find_current.p */
find _myconnection.
disp _myconnection._MyConn-Pid.
find first customer no-lock.


pause.
run external_find.p (yes).

find current customer exclusive-lock.
message current-changed customer.

/* upd_customer.p */
find _myconnection.
disp _myconnection._MyConn-Pid.
find first customer.
Customer.Comments = substitute("updated by &1 at &2", _MyConn-Pid, now).

Posted by cverbiest on 06-Jul-2017 10:30

I've come up with this so far, the checksum check does show changes have been made.

def var lRaw as raw.
def var lCurrentHash as raw.


find _myconnection.
disp _myconnection._MyConn-Pid.
find first customer no-lock.

raw-transfer customer to lRaw.
lCurrentHash = SHA1-DIGEST (lRaw).
pause.
run external_find.p (yes).

find current customer no-lock .
raw-transfer customer to lRaw.

message "current-changed" current-changed customer "hash changed" lCurrentHash ne SHA1-DIGEST (lRaw).

All Replies

Posted by Tim Kuehn on 06-Jul-2017 09:30

What happens if you leave out  -rereadnolock?

Posted by cverbiest on 06-Jul-2017 09:49

Without -rereadnolock you need a share-lock in external_find.p to run into problems.

Posted by Tim Kuehn on 06-Jul-2017 10:04

-rereadnolock's effect on current-changed would seem to be 'as designed' because the record's being refreshed, ergo current-changed is false.

For what you're looking for I'd suggest using prodatasets and implementing optimistic locking that way.

Posted by cverbiest on 06-Jul-2017 10:30

I've come up with this so far, the checksum check does show changes have been made.

def var lRaw as raw.
def var lCurrentHash as raw.


find _myconnection.
disp _myconnection._MyConn-Pid.
find first customer no-lock.

raw-transfer customer to lRaw.
lCurrentHash = SHA1-DIGEST (lRaw).
pause.
run external_find.p (yes).

find current customer no-lock .
raw-transfer customer to lRaw.

message "current-changed" current-changed customer "hash changed" lCurrentHash ne SHA1-DIGEST (lRaw).

Posted by Laura Stern on 06-Jul-2017 11:21

I don't believe there is any problem here.  The purpose of CURRENT-CHANGED is to tell you if the value that you have in the buffer has been modified by another user since you read it.  If you have yourself re-read the record such that you are now seeing the updated value, then CURRENT-CHANGED should be false. When I run this, I do in fact have the updated value in the buffer once you do the FIND in external_find.p.  It was not only in the local buffer inside external_find.p.  It is also now in the original customer buffer.  We do not keep different versions of the same record in different buffers.   If you display the updated field in the MESSAGE statement where you do CURRENT-CHANGED, you will see the new value.

Posted by smat-consulting on 06-Jul-2017 12:39

I have an integer field in every table, whose value is upped by 1 whenever a change is saved into he record using a write-trigger. That way there's no danger of overwriting an intermittent change, no matter if I in another window, or somebody else changed the record since it was last read for update.

For webspeed this seems the easiest way to do optimistic locking...

Posted by cverbiest on 07-Jul-2017 02:42

[mention:04fbfb2e92784123a464ff2aade602b1:e9ed411860ed4f2ba0265705b8793d05] , current-changed probably works as expected when you know that buffers aren't really separate when they point to the same record. Most developers don't read the manual up to the notes :-(  

The problem is that the procedure find_current.p has no knowledge, and should not have any knowledge, of what the procedures it runs do, they are black boxes.

In find_current.p I need a reliable way to test if the customer record is still the same compared to the previous find in the find_current.p procedure.

In a multi-window event driven gui application a user will open a customer update window , the values are displayed.

She/he will then opens an order entry window, creates an order, the code of that window could fetches the customer record displayed on the first window.

The user then returns to the customer update window, makes some changes and saves. The customer update window has no way of telling that the order entry window fetched the customer record.

The integer field is a good idea but it requires schema changes to our database and it will fail if there are sql updates.

I have added the digest check to our framework.

Posted by martinz on 07-Jul-2017 05:36

The integer field should be updated automatically by the database engine, when a record is updated. That way SQL updates should not be a problem. I submitted an idea for this. You may want to vote here.

Posted by cverbiest on 07-Jul-2017 07:45

[mention:f3395557ba9b4dd281855af1d38a7c39:e9ed411860ed4f2ba0265705b8793d05] You already had my vote ;-)

Posted by Laura Stern on 07-Jul-2017 07:56

Ah.  So it's really a UI problem.  I.e., the screen is not refreshed with the most current data that you have in your local buffer.  Maybe you could subscribe to the ACTIVATE (or would it be ENTRY?) event for the window and make sure the data is refreshed on the screen when that happens.

Or the opposite approach, which is to have a 2nd buffer where you save the currently selected record on LEAVE of the window.  Then you could to a BUFFER-COMPARE to the real buffer as soon as the user tries to type into a field.

Something like that!  You'd have to figure out the best approach.

Posted by marian.edu on 07-Jul-2017 08:10

Indeed Carl, how come you still have UI widgets bound directly to the database buffers? Ah, you probably take OERA for what it is… just a reference :)


Do have to agree that what Laura said it’s expected behaviour though.

Marian Edu

Acorn IT 
+40 740 036 212

Posted by smat-consulting on 07-Jul-2017 08:17

martinz I went to your idea, wanting to vote, but didn't see any obvious way of adding my vote... :|

I totally agree with you, that this should be part of the DB engine, not the client.

This thread is closed