• 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

Database Locks

 
Ranch Hand
Posts: 70
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
As per requirements from the assignment, if you pass -1 to Lock method, whole Database gets locked.
One thing is sure, DB lock to be applied when you add or delete a record. This addition & deletion of record over server should not be allowed to remote client. It must be allowed in administrator mode. In my case I will be allowing administrator(Since administrator for DB is single) mode in Standalone startup of server(i.e. Non Networked mode). Other meaning is, when you start your server in standalone mode, you are not allowing remote clients.
So if administrator only going to access DB in non networked mode, then why we need to lock the whole Database ?
So I dont understand whats the need of Locking all the Database, Adding / Deleting Record, then Unlocking DB !
So whats the need of Locking whole Database ?
 
author
Posts: 3252
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Originally posted by Shivaji Bhosale:
So if administrator only going to access DB in non networked mode, then why we need to lock the whole Database ?

Two things: first, you are thinking way too much Fly By Night, remember that this database is to be re-used; second, this might tell you that your assumption that maintenance is only done in local mode is incorrect. The point is pretty academic anyway - you don't have to code any of the maintenance part, the instructions simply ask you to pave the way for it.
- Peter
 
Greenhorn
Posts: 20
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Peter:
I have designed my databaseLock in the first draft, since I have
never practically done a lock before, though I read a lot of books.
Could you pls tell me if this is acceptable?
1) Inside LockManger there is a hashmap to maintain the row lock.
2) Modify the getLock(int rec, string sessionID) and unlock(int rec, String sessionID) signature.
3) DatabaseLock may block other databaseLock and rowLock, rowLock may only block lock on this row and other databaseLock. But even if the rowLock is locked by the same user, the consecutively databaseLock by the same user will be blocked too.
4) DatabaseLock synchronized based on lockManager, while rowLock
synchronized on the hashMap itself.
5) When doing unlock, always notify the databaseLock first, later
notify the rowLock.
6) I put a lot of thoughts into the race condition and dead lock.
the lock hierchary is Sychronized(this) first, then Synchronized(hashMap) so there will not be any dead lock. Also based on effective Java, always include the wait() in a while loop to eliminate the race condition.
I know I may ask too much for you to review my code, it seems too detailed, but I only has too less experience to know if this is actually a valid design without any apparent flaw.
What confused me most is every wait() must check two conditions
Thx
Pat
-----------------------------------------------------
package com.hachi.server.lock;
public class LockManager {
int outStandingDatabaseLock = 0;
int waitForDatabaseLock = 0;
int outStandingRowLock = 0;
String curDatabaseLock = null;
HashMap locks;
String ANY_SESSION_LOCK = "ANY_SESSION_DATABASE_LOCK";
public LockManager() {
locks = new HashMap(); // key record number, value client sessionID
}

void synchronized getDatabaseLock(String sessionID) {
waitForDatabaseLock++;
synchronized (lock) {
if (locks.isEmpty() && (outStandingDatabaseLock ==0)) {
outStandingDatabaseLock++;
waitForDatabaseLock--;
curDatabaseLock = sessionID;
return;
}
}
wait(); // may caused either by other database lock or by any row lock
//databaseLock always get notified before row lock
//eliminate the race condition that a row lock will sneak between the notify()
//since the row lock will be blocked by ANY_SESSION_LOCK
while ( curDatabaseLock != null && !curDatabaseLock.equals(ANY_SESSION_LOCK) ) {
wait();
}
outStandingDatabaseLock++;
waitForDatabaseLock--;
curDatabaseLock = sessionID;
}
void getLock(int recNum, String sessionID) {
Integer rec = new Integer(recNum);
if (recNum == DATABASE_LOCK_RECORD) {
getDatabaseLock(sessionID);
return;
}
synchronized (this) {
synchronized (locks)
if (curDatabaseLock ==null && (!locks.contain(rec))) {
locks.add(rec, sessionID);
return;
}
else if (curDatabaseLock !=null)
locks.add(rec, curDatabaseLock);
}
synchronized (locks) {
while ( locks.contain(rec)) {
locks.wait(); //database lock has priority over row lock, so there must be no database lock.
if (curDatabaseLock !=null)
locks.add(rec, curDatabaseLock); // prevent the race condition that a database lock is acquired during the wake up
}
locks.add(rec, sessionID);
}
}
}
void synchronized unLock(int recNum, String sessionID) {
if (curDatabaseLock !=null ) {//only database lock held, no row locked, only need to release database lock
if (recNum != DATABASE_LOCK_RECORD) | | (!curDatabaseLock.equals(sessionID))
return;
outStandingDatabaseLock--;
synchronized(locks) {
locks.clear();
}
if (waitingForDatabaseLock > 0) {
curDatabaseLock = ANY_SESSION_LOCK; // ANY_SESSION_LOCK is used to block the row lock.
notifyAll();
return;
}
else {
synchronized(locks) {
locks.notifyAll();
return;
}
}
}
// release row lock; curDatabaseLock is null
if (recNum == DATABASE_LOCK_RECORD) return;
Integer rec = new Integer(recNum);
synchronized (locks) {
String lockSession = locks.get(rec);
if (lockSession.equals(sessionID)) {
locks.remove(rec);
if (locks.isEmpty() && waitForDatabaseLock > 0)
curDatabaseLock = ANY_SESSION_LOCK;
notifyAll();
else
locks.notifyAll();
}
}
}
}

 
Peter den Haan
author
Posts: 3252
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Patrick, I'm not the arbiter of what is acceptable as long as you can defend the particular design choices you made - like: it is OK for a database lock to block when the client has a record lock outstanding. One thing though: why two levels of synchronization?
- Peter
 
Patrick Wang
Greenhorn
Posts: 20
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Because there are two levels of lock, databaseLock and rowLock.
F.g. a database lock may either locked by another database lock, or by a row lock.
Database Lock is checked through the curDatabaseLock variable value, so need to synchronize access to it using this lock.
Row lock check is checked through the locks hashmap, so need to synchronize on it.
Another reason is row lock is waited against locks hashmap object, while database lock is waited against this object.
Thx
Pat
 
Patrick Wang
Greenhorn
Posts: 20
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Thx Peter,
I found the two level synchronized is not necessary,
just use synchronize(this) to synchronize both the primitive
member and locks object. In that way the design will be
much cleaner.
Pat
 
Peter den Haan
author
Posts: 3252
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Thanks Patrick, you just saved me an answer but I cannot resist one remark. You mentioned "[with one lock level] the design will be much cleaner".
In fact, the observation that there are actually two wildly different types of lock, and the idea to see if the lock manager can be split in to layers, are both excellent. The reason that it doesn't work is that the logic relating to the two types of lock is all tangled up - at least I couldn't find a way to cleanly separate out two logical steps. As a consequence you end up with two layers (maybe two classes) that are so tightly coupled that they are to all intents and purposes one entity.
Other problems may well become cleaner by adding layering rather than removing it.
Some call this "factoring". The problem cannot be factored into loosely coupled sub-problems. It's an analogy with algebra -- x^2 + 3x + 2 can be nicely factored as (x+1)*(x+2), but x^2 - 3x + 2 cannot (I can hear someone shouting "complex numbers!" now).
If you want to know more about factoring, I can recommend Fowler's Refactoring. It's great to see how he chisels away at the code, shaping it beneath his hands - you cannot read this book without having your design skills sharpened as much as your refactoring skills.
- Peter

[This message has been edited by Peter den Haan (edited October 24, 2001).]
 
Ranch Hand
Posts: 196
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Gentlemen, there has been a lot of talk about locks .
Take a look at this:
if (rec != -1) {
dbLock.ReadLock();
recLocks[rec].Writelock();
} else {
dbLock.WriteLock();
}
this is two-level lock implemented according to ReadWriteLock pattern. DB lock is not aware of record locking, record locks are not aware of DB lock. No coupling. No logic intermixing. 1 class is used to lock DB and records. am I wrong? what am i missing?
On the other hand, I just dont see how you achive db&record locking with single-layered lock. Can you please explain that?
Thanks

[This message has been edited by Gennady Shapiro (edited October 24, 2001).]
 
Ranch Hand
Posts: 223
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Patrick Wang,
Has the lock signature changed in the new assignments now. The signature used to be lock(int row) when I did it a year ago. If that be the case you will not be in compliance of the instuctions.
Sanjay
 
Peter den Haan
author
Posts: 3252
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Originally posted by Gennady Shapiro:
this is two-level lock implemented according to ReadWriteLock pattern.

Wow. Such a completely natural and straightforward factoring, once you've seen it you cannot imagine how you could've missed it You're not missing a thing as far as I can see.

On the other hand, I just dont see how you achive db&record locking with single-layered lock. Can you please explain that?

You cannot dispense with two different types of lock, but most implement the logic in one single monolithic block of code. The reason for this lack of separation is that, if you don't keep track of a database read lock, then you need to trawl through the record locks to see if a database lock can be granted. The degree of coupling between the two lock types is pretty high that way.
With the code you presented, you can layer them perfectly. BTW: as it stands you record lock array grows with the number of records in the database, not the best scaling behaviour. Which may not be that important - the database doesn't scale very well anyway.
- Peter
 
Patrick Wang
Greenhorn
Posts: 20
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Here is the source for the single layered lock.
This is what I hacked through, getDatabaseLock need to get the
curDatabaseLock object, while getLock synchronized based on
this. I use "this" object to guard the access to the hashMap locks and the rest variables.
It is not as nice as the two level locks presented above.
Thx.
B.T.W, I changed the lock signature on the server side, where
on client, it is still the old lock signature.
-----------------------------------------------------
package com.hachi.server.lock;
import com.hachi.server.connect.*;
import java.util.*;
public class LockManager {
public static int DATABASE_LOCK_RECORD = -1;
int waitForDatabaseLock = 0;
RemoteConnection curDatabaseLock = null;
HashMap locks;
ArrayList waitingForDatabaseLock = new ArrayList();
public LockManager() {
locks = new HashMap(); // key record number, value client sessionID
}

void getDatabaseLock(RemoteConnection sessionID) throws InterruptedException {
synchronized (this) {
waitForDatabaseLock++;
if (locks.isEmpty() && (curDatabaseLock ==null)) {
waitForDatabaseLock--;
curDatabaseLock = sessionID;
return;
}
waitingForDatabaseLock.add((RemoteConnectionImpl) sessionID);
}
synchronized (curDatabaseLock) {
while ( !sessionID.equals(curDatabaseLock)) {
wait();
}
}
synchronized (this) {
waitForDatabaseLock--;
int i = waitingForDatabaseLock.indexOf(sessionID);
waitingForDatabaseLock.remove(i);
}
}
void getLock(int recNum, RemoteConnection sessionID) throws InterruptedException {
Integer rec = new Integer(recNum);
if (recNum == DATABASE_LOCK_RECORD) {
getDatabaseLock(sessionID);
return;
}
synchronized (this) {
if ((curDatabaseLock ==null) && (locks.get(rec)==null)) {
locks.put(rec, (RemoteConnectionImpl) sessionID);
return;
}
while ( curDatabaseLock !=null | | locks.get(rec)!=null) {
wait(); //database lock has priority over row lock, so there must be no database lock.
}
locks.put(rec, (RemoteConnectionImpl) sessionID);
}
}
synchronized public void unLock(int recNum, RemoteConnection sessionID) {
// release DatabaseLock
if (curDatabaseLock !=null ) {//only database lock held, no row locked, only need to release database lock
if ((recNum != DATABASE_LOCK_RECORD) | | (!curDatabaseLock.equals(sessionID)) )
return;
if (waitForDatabaseLock > 0) {
curDatabaseLock = (RemoteConnection) waitingForDatabaseLock.get(0); // ANY_SESSION_LOCK is used to block the row lock.
synchronized (curDatabaseLock) {
// notify database lock
curDatabaseLock.notifyAll();
}
return;
}
else {
curDatabaseLock = null;
// notify row lock()
notifyAll();
}
}
// release row lock;
if (recNum == DATABASE_LOCK_RECORD) return;
Integer rec = new Integer(recNum);
RemoteConnection lockSession = (RemoteConnection) locks.get(rec);
if (lockSession.equals(sessionID)) {
locks.remove(rec);
if (locks.isEmpty() && waitForDatabaseLock > 0) {
RemoteConnection databaseSession = (RemoteConnection) waitingForDatabaseLock.get(0);
curDatabaseLock = databaseSession;
synchronized (curDatabaseLock) {
curDatabaseLock.notifyAll();
}
}
else {
curDatabaseLock = null;
notifyAll();
}
}
}

}
 
Gennady Shapiro
Ranch Hand
Posts: 196
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Patric, my comment on your implementation would be ... yet again
1. effectively you are doing the same thing I am which is two level-loocking (sych on db then synch on record --2 levels). The difference is that you are synchronizing on the DB internally hence DB/Rec coupling.
I guess this has become a matter of terminology, I kept thinking of HOW MY GOD HOW can I do 1 level locking for this?? The answer is no way, you have to lock DB AND Recs and synchronize on both.
2. I see you use (RemoteConnection sessionID) as lock id , I am not sure how you generate this but I let my locks id my users by their Thread IDs, so I dont even have identify them in any matter. If you believe there is a downside to this please let me know.
Thanks
 
Patrick Wang
Greenhorn
Posts: 20
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Gennady:
Remote Connection is distinct for each client, as long as thread
is distinct for each client which is true, I think by using thread is another approach. Thx for giving the two level lcoks
design, it is beautiful.
Thx
Pat
 
Peter den Haan
author
Posts: 3252
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Originally posted by Gennady Shapiro:
I kept thinking of HOW MY GOD HOW can I do 1 level locking for this?? The answer is no way, you have to lock DB AND Recs and synchronize on both.

You cannot get rid of treating the database lock differently from the record locks, but you can roll it all into a single lock manager class and synchronize just once, no problem.
- Peter
 
Ranch Hand
Posts: 413
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Gennadiy, I could see one problem with threadID - if you use RMI, there is no garantee, that the same client will always use the same thread. (Or only one thread).
For example, I was using unreferenced, to catch dead clients and unlock his record - but there was a case, when unreference method was called, when client were still waiting for lock - so it was uttemting to unlock records, and than it locked it again .
What I used, is RemoteDB instance per each client - at this case you dont even need to have client ID - Remote DB knows what it locked.
thanks, Yuriy
 
Gennady Shapiro
Ranch Hand
Posts: 196
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Yuriy,
while it's true that your thread IDs become invalide if your client loses connection and unreferenced() get called. But I do believe this is the whole point of using unreferenced() to clean up after dead sessions. This is how most commercial system work, HTTPSession is good example - you time out your session info is gone and you have to relogin (try explaining to Netegrity why your session info should be preserved after you time out ).
Also, I think RMI unreferenced timeout is 10 min or so (I could be wrong about this), if your client has to wait more than a few seconds for a lock there's has to be a bigger problem with the system.
Also, with increasing number of users you instantiate increasing number of objects and consume more and more system resources. That might not be a big deal for Fly By Night (I always thought it was funny how they named the project -- sounds like air carrier's operations system used to launch strikes agains Kabul), it could become a problem when number of user and complexity of objects grow.
Anyway, its a matter of choice.
 
Consider Paul's rocket mass heater.
reply
    Bookmark Topic Watch Topic
  • New Topic