• Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other Pie Elite all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Jeanne Boyarsky
  • Ron McLeod
  • Paul Clapham
  • Liutauras Vilda
Sheriffs:
  • paul wheaton
  • Rob Spoor
  • Devaka Cooray
Saloon Keepers:
  • Stephan van Hulst
  • Tim Holloway
  • Carey Brown
  • Frits Walraven
  • Tim Moores
Bartenders:
  • Mikalai Zaikin

B&S Locking Strategy RFC

 
Greenhorn
Posts: 24
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Hi, I'm looking for some comments or suggestions on my locking strategy. Here is a high-level overview.

DBMain A = new Data();
DBMain B = new Data();

Assume two clients, A, and B, and assume a database with record 0.

A.update(0, record); // client A updates record, because it's unlocked.
B.lock(0); // client B locks record

// client A wants to update, but waits(), because B currently has the lock.
A.update(0, record);

// client B unlocks 0, so the A.update above continues.
B.unlock(0);

Is this behavior okay for locking? Basically, if no one has locked it, anyone can do whatever they want. If someone has locked it, then other clients must wait on that record until the locker unlocks it for update and delete options. Anyone can read, regardless of whether the record is locked or unlocked.
 
Ranch Hand
Posts: 52
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Originally posted by Titus Barik:
Basically, if no one has locked it, anyone can do whatever they want. If someone has locked it, then other clients must wait on that record until the locker unlocks it for update and delete options. Anyone can read, regardless of whether the record is locked or unlocked.



my idea of locking was, that any modification must be protected by obtaining a lock first (waiting in here if records is locked already), modifying and releasing the lock.
Additionally may be used to protect against data corruption while reading.

the strategy given above has some drawbacks from my POV:
1. records can be modified concurrently with unpredictable results (if i understood you correctly) if noone locks the record before.
2. reading a record while updating may lead to broken/half-changed/corrupt records in the frontend
3. update seems to work different if a record was locked before by the same thread (how do you recognize it anyway ?). too much magic powder ...

my advice: hide the locking at a certain layer and do something like


[ May 22, 2005: Message edited by: Uwe Sch�fer ]
 
Titus Barik
Greenhorn
Posts: 24
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Since the methods are synchronized on a static raf, you'll never get a dirty read. What I meant above is that you never have to acquire a lock to do a read, since it does not change any data. Also, there is no 'data corruption' because of this.

The locking system is orthogonal to the database. You can use the database without locking, but if you choose the use locking on records, the database will respect that in the manner given above.

Basically, each client gets its own Data object. I synchronize on 'this' in the Data class, which is how I know who is accessing it.
[ May 22, 2005: Message edited by: Titus Barik ]
 
Ranch Hand
Posts: 357
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Originally posted by Titus Barik:
Is this behavior okay for locking? Basically, if no one has locked it, anyone can do whatever they want. If someone has locked it, then other clients must wait on that record until the locker unlocks it for update and delete options. Anyone can read, regardless of whether the record is locked or unlocked.



Hi Titus,

This seems to be not the way it is intended. The mere prensence of the lock method seems to me to suggest that you need to lock any record that must be updated or deleted.
OTOH, the instructions don't seem to contradict your locking behavior, but I would not gamble on outsmarting the exam developers.

Frans.
 
Ranch Hand
Posts: 118
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
You are asking 2 separate questions. First, your code sample points out that the code that is using the DBMain interface could choose not to use lock() and unlock(), which in your code sample happens the first time you call A.update(). If this happened, it would be bad... what if A.update() takes a long time, and is not done by the time B.lock() is called? Obviously, this could cause data corruption, especially if B starts changing or even deleting the very same record.

I think that you can have your Client interface be higher level, and hide the details of the locking. Then your RMI interface, can implement the client interface and *use* the Data class. That way, you can guarantee that the client can't shoot himself in the foot by writing bad code (for example, code that calls lock() but does not call unlock()).

Your second question, well not exactly a question, says that, "Anyone can read, regardless of whether the record is locked or unlocked." There is a subtle danger here, and it actually is similar to the code sample above. With uncontrolled reading, what happens if you start to read a record, and the read takes a long time. While the read is taking place, another thread starts a lock()-update()-unlock() sequence. You could end up with corrupted data if half of your read happens before the write and half of your read happens after the write. So in my opinion, you have to be careful with reads as well as with writes.

I know what you are thinking of though. I was thinking of the same thing when I was originally working on a DatabaseCache class (which I am probably shelving, just because it's not required and introduces lots of complexity). If the DatabaseCache doesn't change, you can do as many reads as you want, and you don't even need synchronization. But, if some data changes, you have to at least be sure that no one is reading the data at the time that you are writing it.

If you wanted to be sophisticated, you could write a program that did Read-Locks and Write-Locks. You could let multiple threads read a record at the same time, as long as you kept track of how many were reading. Any attempt to set the Write-Lock would have to fail or at least wait until the count on the Read-Lock went down to 0. A Write-Lock would have to be exclusive, only one thread could write at a time.
[ May 23, 2005: Message edited by: Lara McCarver ]
 
Titus Barik
Greenhorn
Posts: 24
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Originally posted by Frans Janssen:

This seems to be not the way it is intended. The mere prensence of the lock method seems to me to suggest that you need to lock any record that must be updated or deleted.
OTOH, the instructions don't seem to contradict your locking behavior, but I would not gamble on outsmarting the exam developers.



Hi Frans,

Thanks for your comments. In my opinion, the lock and unlock functions solely exist for the purpose of making a set of non-atomic operations atomic. For example, let's say we wanted to refresh a record to get the latest copy before updating, we would do:

read()
update()

But we can't guarantee that the values won't change between the two calls! read is atomic, update is atomic, but the COMBINATION of read and update is not atomic. That's why the higher-level locking is there:

lock()
read()
update()
unlock()

We've turned a non-atomic set of operations into an atomic one by introducting higher level locking, thus allowing us to safely "group" operations.

Doing:

lock()
update()
unlock()

is just silly. update is ALREADY atomic. Two things can't update the same record simultaneously anyway because of the lower-level synchronization on the raf, so there's no point in doing a lock around the single atomic update instruction.
 
Uwe Schäfer
Ranch Hand
Posts: 52
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Originally posted by Titus Barik:

is just silly. update is ALREADY atomic. Two things can't update the same record simultaneously anyway because of the lower-level synchronization on the raf, so there's no point in doing a lock around the single atomic update instruction.



i think, there is. synchronization (via language) is not enforceable by the interface contract. so the atomicity of update(...) may be considered implementation detail of the lower-level data-access layer.
so explicit (higher level) locking in the business layer that is usable for simple operations as well as complex (multi-method-call) ones is not that pointless.
 
Frans Janssen
Ranch Hand
Posts: 357
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Originally posted by Titus Barik:
update is ALREADY atomic. Two things can't update the same record simultaneously anyway because of the lower-level synchronization on the raf, so there's no point in doing a lock around the single atomic update instruction.


You are completely right, of course. But as I said before, I would not gamble that the assessor will agree.

Anyway, in the real-life situation, you will always do the read+update combo. So why would you want to make your update method more complex by allowing also the sole update with implicit locking?

Frans.
 
Titus Barik
Greenhorn
Posts: 24
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Originally posted by Uwe Sch�fer:

i think, there is. synchronization (via language) is not enforceable by the interface contract. so the atomicity of update(...) may be considered implementation detail of the lower-level data-access layer.
so explicit (higher level) locking in the business layer that is usable for simple operations as well as complex (multi-method-call) ones is not that pointless.



The interface contract doesn't really say either way, but whether you want to force a lock or not, but are easily enforcable.

update() {
if (client has not locked record)
throw new ClientLock("You must lock the record before using this!");

execute update code
}

alternative:

update() {
canProceed();
+ if no one has locked it
+ if the current client has locked it
+ otherwise, wait until one of the above conditions are true

execute update code

}
 
The glass is neither half full or half empty. It is too big. But this tiny ad is just right:
a bit of art, as a gift, the permaculture playing cards
https://gardener-gift.com
reply
    Bookmark Topic Watch Topic
  • New Topic