Skip to main content


I wondering what others do for pessimistic locking strategies.

D3's normal READU will lock a record for that PIB, and that execute level, which is good, and it is the behavior all D3 Basic programs probably use.

 My character based apps allow a user to "push" a level, so if you were in a customer maintenance screen,  for example, the customer record ABCD would be locked, and if you pushed a level in that screen  and used a menu selection to open a new customer maintenance screen and tried to edit ABCD, it would locked via the normal READU behavior. This would work the same if some other user on another PIB had  ABCD locked.
 

With  a Java Swing app using MVSP, because it's a GUI, I can have multiple windows opened at the same time, so I could have 2 (or 200)  customer maintenance screens open and clearly don't want to edit the same record on more than one screen.  If I used one D3 connection per open window, I'd pretty soon run out of user licences, so I'm only using one D3 connection for the entire Swing app (I may change that to use 2 connections to take advantage of the 2-for-1 licencing). This approach works fine because Swing is single threaded, so only one window is active at a time.  Unfortunately, this means I need to implement my own pessimistic locking scheme by writing a lock record to a file (since if I used READU, because the connection is the same PIB and same level, it will allow the record to be locked again) .

Because checking for locks needs to be synchronized and atomic, I'm using the "LOCK nn" / "UNLOCK nn" Basic statement to ensure only one process can update the lock file at a time.

Not too sure how this will scale.

 
For a web app, for which the connection to D3 is implicitly non-persistent (unless maybe if you're using web sockets) how do people handle READU style locking across requests ?

Any thoughts appreciated.



------------------------------
Bryan Buchanan
------------------------------


I wondering what others do for pessimistic locking strategies.

D3's normal READU will lock a record for that PIB, and that execute level, which is good, and it is the behavior all D3 Basic programs probably use.

 My character based apps allow a user to "push" a level, so if you were in a customer maintenance screen,  for example, the customer record ABCD would be locked, and if you pushed a level in that screen  and used a menu selection to open a new customer maintenance screen and tried to edit ABCD, it would locked via the normal READU behavior. This would work the same if some other user on another PIB had  ABCD locked.
 

With  a Java Swing app using MVSP, because it's a GUI, I can have multiple windows opened at the same time, so I could have 2 (or 200)  customer maintenance screens open and clearly don't want to edit the same record on more than one screen.  If I used one D3 connection per open window, I'd pretty soon run out of user licences, so I'm only using one D3 connection for the entire Swing app (I may change that to use 2 connections to take advantage of the 2-for-1 licencing). This approach works fine because Swing is single threaded, so only one window is active at a time.  Unfortunately, this means I need to implement my own pessimistic locking scheme by writing a lock record to a file (since if I used READU, because the connection is the same PIB and same level, it will allow the record to be locked again) .

Because checking for locks needs to be synchronized and atomic, I'm using the "LOCK nn" / "UNLOCK nn" Basic statement to ensure only one process can update the lock file at a time.

Not too sure how this will scale.

 
For a web app, for which the connection to D3 is implicitly non-persistent (unless maybe if you're using web sockets) how do people handle READU style locking across requests ?

Any thoughts appreciated.



------------------------------
Bryan Buchanan
------------------------------

Hi Brian,

The short answer is: you can't.

For all the reasons you state. Web is inherently non-persistent and thus state-less. It's a bitch; but not impossible.

Tools like designbais & visage take care of this for you; but in essence the methods involve combinations of:

  1. Recording 'state' at the backend for a given user including the 'original' version of a record for comparison.
  2. Possibly writing and updating your own item-lock-table.
  3. But most likely wrapping the WRITE command[s] with your own; which always re-reads the source, compares it to the 'original' and if different takes some action. In it's simplest form it would just abandon the transaction and ask the user to start over. BTW: This can be surprisingly effective, although at massive scale it could be a problem depending on your system design. For example if you have a single record that contains stats it would be being constantly 'hit'. More complex forms of logic could attempt to 'decide' if the changes under way overwrite the changes between 'original' and 'current' and then do a further comparison to the 'updated' version in memory and attempt to merge the changes from 'current' and then perform the write.

I'm keen to hear what others say.

Best wishes,



------------------------------
David Knight
Senior Software Engineer
H3O Business Technologies Limited
------------------------------



I wondering what others do for pessimistic locking strategies.

D3's normal READU will lock a record for that PIB, and that execute level, which is good, and it is the behavior all D3 Basic programs probably use.

 My character based apps allow a user to "push" a level, so if you were in a customer maintenance screen,  for example, the customer record ABCD would be locked, and if you pushed a level in that screen  and used a menu selection to open a new customer maintenance screen and tried to edit ABCD, it would locked via the normal READU behavior. This would work the same if some other user on another PIB had  ABCD locked.
 

With  a Java Swing app using MVSP, because it's a GUI, I can have multiple windows opened at the same time, so I could have 2 (or 200)  customer maintenance screens open and clearly don't want to edit the same record on more than one screen.  If I used one D3 connection per open window, I'd pretty soon run out of user licences, so I'm only using one D3 connection for the entire Swing app (I may change that to use 2 connections to take advantage of the 2-for-1 licencing). This approach works fine because Swing is single threaded, so only one window is active at a time.  Unfortunately, this means I need to implement my own pessimistic locking scheme by writing a lock record to a file (since if I used READU, because the connection is the same PIB and same level, it will allow the record to be locked again) .

Because checking for locks needs to be synchronized and atomic, I'm using the "LOCK nn" / "UNLOCK nn" Basic statement to ensure only one process can update the lock file at a time.

Not too sure how this will scale.

 
For a web app, for which the connection to D3 is implicitly non-persistent (unless maybe if you're using web sockets) how do people handle READU style locking across requests ?

Any thoughts appreciated.



------------------------------
Bryan Buchanan
------------------------------

Hi Brian, 

To manage a pessimist lock, we use a central subr to perform read/readu/write. 

With the record data we compile a 'digest' sha, the digest  is saved on a global 'transaction'  file: Key=account/mainfile/key data=digest, time stamp, usersessionid. They are returned with the record data as 'metadata' of record 

If the key already exist we can inform the app, there another session is working for update. 

After a defined timeout, transaction Records are deleted by a monitor.

When write is requested, metadata 'digest' is passed with the record and the subr io write compare the current record digest with the provided one. If transacRecirs does not exist or usersessionid is 'me' and If digestes are equals, ok you can update, if not, update is refused and app must re run the readu.

I hope this help. 



------------------------------
Manu Fernandes
------------------------------

Hi Brian, 

To manage a pessimist lock, we use a central subr to perform read/readu/write. 

With the record data we compile a 'digest' sha, the digest  is saved on a global 'transaction'  file: Key=account/mainfile/key data=digest, time stamp, usersessionid. They are returned with the record data as 'metadata' of record 

If the key already exist we can inform the app, there another session is working for update. 

After a defined timeout, transaction Records are deleted by a monitor.

When write is requested, metadata 'digest' is passed with the record and the subr io write compare the current record digest with the provided one. If transacRecirs does not exist or usersessionid is 'me' and If digestes are equals, ok you can update, if not, update is refused and app must re run the readu.

I hope this help. 



------------------------------
Manu Fernandes
------------------------------

Hi Manu,

Your method sounds interesting. Not sure I fully understand the full event flow.

How do you ensure serialised access to the global transaction file ?

Bryan



------------------------------
Bryan Buchanan
------------------------------

Hi Manu,

Your method sounds interesting. Not sure I fully understand the full event flow.

How do you ensure serialised access to the global transaction file ?

Bryan



------------------------------
Bryan Buchanan
------------------------------

Hi Brian

We do not 'serialize' the transacFile. We update via simple readu. 

It's not a lock table, it's a info about potentialy record in-update status.

Other words, "the record is currently edited by user".

We never perform direct update from 'client side', all are done via call to subr. 

When we need to perform a 'multi file' update like recording a invoice (insert/update invoice, update stock, update accumulators, update...)  we do it inside a subr with transaction processing (BEGIN/COMMIT). 

Globally we turn u2 server/app to a 'transaction app request server'.

Another aspect is the problem of cached data on client side ( like items list, used to be 'static data' ) 

So, static data  are cached on a collectionDataSouce in client's memory.

From a global request which set on server side into 'sessionid' metadata, the timestamp of the load for each static data. 

When a item is updated on server, all client needs to refresh the cached list.

For each file we have a 'metadata' record : 'bigger timestamp'.

On each record, a field contain a 'last update timestamp' which is indexed. 

When client perform refresh, it does it if the 'bigger timestamp' is greater than the last load. 

If a refresh is needed, we do it incremental from last refresh with the help of BSCAN. We do not delete static data, we disable it. 

All can be done  into a generic subr for  static data concept. 

The refresh can be done via a loop/request from client or a callBack listener set at begining of the session. 

I hope this help



------------------------------
Manu Fernandes
------------------------------

Hi Brian

We do not 'serialize' the transacFile. We update via simple readu. 

It's not a lock table, it's a info about potentialy record in-update status.

Other words, "the record is currently edited by user".

We never perform direct update from 'client side', all are done via call to subr. 

When we need to perform a 'multi file' update like recording a invoice (insert/update invoice, update stock, update accumulators, update...)  we do it inside a subr with transaction processing (BEGIN/COMMIT). 

Globally we turn u2 server/app to a 'transaction app request server'.

Another aspect is the problem of cached data on client side ( like items list, used to be 'static data' ) 

So, static data  are cached on a collectionDataSouce in client's memory.

From a global request which set on server side into 'sessionid' metadata, the timestamp of the load for each static data. 

When a item is updated on server, all client needs to refresh the cached list.

For each file we have a 'metadata' record : 'bigger timestamp'.

On each record, a field contain a 'last update timestamp' which is indexed. 

When client perform refresh, it does it if the 'bigger timestamp' is greater than the last load. 

If a refresh is needed, we do it incremental from last refresh with the help of BSCAN. We do not delete static data, we disable it. 

All can be done  into a generic subr for  static data concept. 

The refresh can be done via a loop/request from client or a callBack listener set at begining of the session. 

I hope this help



------------------------------
Manu Fernandes
------------------------------

Hi Manu,

Interesting. So your  "record in-update status" is a bit like an optimistic locking strategy ?

Bryan



------------------------------
Bryan Buchanan
------------------------------

Hi Manu,

Interesting. So your  "record in-update status" is a bit like an optimistic locking strategy ?

Bryan



------------------------------
Bryan Buchanan
------------------------------

Hi Bryan,

Yes optimistic, but en forces if you want. 

It's app managed not plateform.



------------------------------
Manu Fernandes
------------------------------


I wondering what others do for pessimistic locking strategies.

D3's normal READU will lock a record for that PIB, and that execute level, which is good, and it is the behavior all D3 Basic programs probably use.

 My character based apps allow a user to "push" a level, so if you were in a customer maintenance screen,  for example, the customer record ABCD would be locked, and if you pushed a level in that screen  and used a menu selection to open a new customer maintenance screen and tried to edit ABCD, it would locked via the normal READU behavior. This would work the same if some other user on another PIB had  ABCD locked.
 

With  a Java Swing app using MVSP, because it's a GUI, I can have multiple windows opened at the same time, so I could have 2 (or 200)  customer maintenance screens open and clearly don't want to edit the same record on more than one screen.  If I used one D3 connection per open window, I'd pretty soon run out of user licences, so I'm only using one D3 connection for the entire Swing app (I may change that to use 2 connections to take advantage of the 2-for-1 licencing). This approach works fine because Swing is single threaded, so only one window is active at a time.  Unfortunately, this means I need to implement my own pessimistic locking scheme by writing a lock record to a file (since if I used READU, because the connection is the same PIB and same level, it will allow the record to be locked again) .

Because checking for locks needs to be synchronized and atomic, I'm using the "LOCK nn" / "UNLOCK nn" Basic statement to ensure only one process can update the lock file at a time.

Not too sure how this will scale.

 
For a web app, for which the connection to D3 is implicitly non-persistent (unless maybe if you're using web sockets) how do people handle READU style locking across requests ?

Any thoughts appreciated.



------------------------------
Bryan Buchanan
------------------------------

I think I'd be inclined to manage the "locking problem" where it occurs, in the Swing app. A single MV session would hold an ordinary READU at the DB level but multiple instances of that record in the Swing app should be managed by the app. Inter-window messages allow you to notify the user that a modified version of the record exists elsewhere in their session. But this is what the Singleton pattern is good for.



------------------------------
Stuart Boydell
Technical Specialist
Meier Business Systems PTY LTD
Carnegie Vic AU
------------------------------

Hi Manu,

Interesting. So your  "record in-update status" is a bit like an optimistic locking strategy ?

Bryan



------------------------------
Bryan Buchanan
------------------------------

Hi brian

Yes, it's optimistic but app-managed.

Regards



------------------------------
Manu Fernandes
------------------------------

I think I'd be inclined to manage the "locking problem" where it occurs, in the Swing app. A single MV session would hold an ordinary READU at the DB level but multiple instances of that record in the Swing app should be managed by the app. Inter-window messages allow you to notify the user that a modified version of the record exists elsewhere in their session. But this is what the Singleton pattern is good for.



------------------------------
Stuart Boydell
Technical Specialist
Meier Business Systems PTY LTD
Carnegie Vic AU
------------------------------

Hi Stuart,

That's not a bad idea - hadn't occurred to me :(  Something to look into.

Thanks,

Bryan



------------------------------
Bryan Buchanan
------------------------------

I think I'd be inclined to manage the "locking problem" where it occurs, in the Swing app. A single MV session would hold an ordinary READU at the DB level but multiple instances of that record in the Swing app should be managed by the app. Inter-window messages allow you to notify the user that a modified version of the record exists elsewhere in their session. But this is what the Singleton pattern is good for.



------------------------------
Stuart Boydell
Technical Specialist
Meier Business Systems PTY LTD
Carnegie Vic AU
------------------------------

Hi Stuart,

Your idea is a brilliant idea. It actually makes locking much simpler, and means the existing character based code which uses the normal D3 READU doesn't need to be refactored, so a win all round. I've changed my code and all seems to work a treat.

Thanks again for your suggestion.

Cheers,

Bryan



------------------------------
Bryan Buchanan
------------------------------

Hi Stuart,

Your idea is a brilliant idea. It actually makes locking much simpler, and means the existing character based code which uses the normal D3 READU doesn't need to be refactored, so a win all round. I've changed my code and all seems to work a treat.

Thanks again for your suggestion.

Cheers,

Bryan



------------------------------
Bryan Buchanan
------------------------------

Thanks! and no problem :)



------------------------------
Stuart Boydell
Technical Specialist
Meier Business Systems PTY LTD
Carnegie Vic AU
------------------------------


I wondering what others do for pessimistic locking strategies.

D3's normal READU will lock a record for that PIB, and that execute level, which is good, and it is the behavior all D3 Basic programs probably use.

 My character based apps allow a user to "push" a level, so if you were in a customer maintenance screen,  for example, the customer record ABCD would be locked, and if you pushed a level in that screen  and used a menu selection to open a new customer maintenance screen and tried to edit ABCD, it would locked via the normal READU behavior. This would work the same if some other user on another PIB had  ABCD locked.
 

With  a Java Swing app using MVSP, because it's a GUI, I can have multiple windows opened at the same time, so I could have 2 (or 200)  customer maintenance screens open and clearly don't want to edit the same record on more than one screen.  If I used one D3 connection per open window, I'd pretty soon run out of user licences, so I'm only using one D3 connection for the entire Swing app (I may change that to use 2 connections to take advantage of the 2-for-1 licencing). This approach works fine because Swing is single threaded, so only one window is active at a time.  Unfortunately, this means I need to implement my own pessimistic locking scheme by writing a lock record to a file (since if I used READU, because the connection is the same PIB and same level, it will allow the record to be locked again) .

Because checking for locks needs to be synchronized and atomic, I'm using the "LOCK nn" / "UNLOCK nn" Basic statement to ensure only one process can update the lock file at a time.

Not too sure how this will scale.

 
For a web app, for which the connection to D3 is implicitly non-persistent (unless maybe if you're using web sockets) how do people handle READU style locking across requests ?

Any thoughts appreciated.



------------------------------
Bryan Buchanan
------------------------------

On all our web/vue/json applications we are writing the last save user/date/time to the record when the record is saved from the web application or the terminal.   When a user requests a edit screen we send the lock information to the user as a hidden field with that request. That hidden field is sent back on the save.  On save we check that the terminal locking isn't active and then verify that the hidden field has not changed, some other web user or terminal has updated, and if all is ok we save the record and update the lock stamp.   If either fail we send a notification back along with the updated record for the user to review and try again.



------------------------------
craig curtis
System Programmer
Sevier Valley School District
Richfield UT US
------------------------------

On all our web/vue/json applications we are writing the last save user/date/time to the record when the record is saved from the web application or the terminal.   When a user requests a edit screen we send the lock information to the user as a hidden field with that request. That hidden field is sent back on the save.  On save we check that the terminal locking isn't active and then verify that the hidden field has not changed, some other web user or terminal has updated, and if all is ok we save the record and update the lock stamp.   If either fail we send a notification back along with the updated record for the user to review and try again.



------------------------------
craig curtis
System Programmer
Sevier Valley School District
Richfield UT US
------------------------------

Thanks Craig. So you use a pretty vanilla optimistic locking strategy if I read it correctly.



------------------------------
Bryan Buchanan
------------------------------

Hi Brian,

The short answer is: you can't.

For all the reasons you state. Web is inherently non-persistent and thus state-less. It's a bitch; but not impossible.

Tools like designbais & visage take care of this for you; but in essence the methods involve combinations of:

  1. Recording 'state' at the backend for a given user including the 'original' version of a record for comparison.
  2. Possibly writing and updating your own item-lock-table.
  3. But most likely wrapping the WRITE command[s] with your own; which always re-reads the source, compares it to the 'original' and if different takes some action. In it's simplest form it would just abandon the transaction and ask the user to start over. BTW: This can be surprisingly effective, although at massive scale it could be a problem depending on your system design. For example if you have a single record that contains stats it would be being constantly 'hit'. More complex forms of logic could attempt to 'decide' if the changes under way overwrite the changes between 'original' and 'current' and then do a further comparison to the 'updated' version in memory and attempt to merge the changes from 'current' and then perform the write.

I'm keen to hear what others say.

Best wishes,



------------------------------
David Knight
Senior Software Engineer
H3O Business Technologies Limited
------------------------------

Hi David,

My use case is a desktop GUI, which does have a persistent connection. I followed Stuart's suggestion and have the client side checking if other open windows have a record locked, and use vanilla READU locking on the server.

For web apps, if you use Web Sockets, you could have a persistent connection without transferring state between HTTP requests.

Cheers,

Bryan



------------------------------
Bryan Buchanan
------------------------------