• 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
  • Tim Cooke
  • paul wheaton
  • Liutauras Vilda
  • Ron McLeod
Sheriffs:
  • Jeanne Boyarsky
  • Devaka Cooray
  • Paul Clapham
Saloon Keepers:
  • Scott Selikoff
  • Tim Holloway
  • Piet Souris
  • Mikalai Zaikin
  • Frits Walraven
Bartenders:
  • Stephan van Hulst
  • Carey Brown

No need for a locking manager?

 
Ranch Hand
Posts: 44
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Fly by Night
In many posts i have read how people make use of a LockManager class to provide the required behaviour for connections performing lock operations.
A request to unlock() a record owned by a connection other than the locker, returns with no effect.
In my mind, the mechanism inwhich connections may obtain and release locks is just an addition to the constraints built on top of Data's locking mechanism. Surely, if a connection knows what it has locked then it can ensure by itself that it does not unlock a record that it has not locked.
i.e
Data data;
void lock(int record){
data.lock(); // blocks until lock is obtained
addOwnedLocks(record);
}
void unlock(int record){
if (ownsLock(lock)){
data.unlock();
removeLock(record);
}
}
Does this seem reasonable?
What use if any is there in making use of a seperate class such as LockManager for this purpose? Are there any other worthy features which a connection could not do by itself?
 
author and jackaroo
Posts: 12200
280
Mac IntelliJ IDE Firefox Browser Oracle C++ Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Hi Patrick,

Surely, if a connection knows what it has locked then it can ensure by itself that it does not unlock a record that it has not locked.


I agree. I had a Connection Factory on the server, and it was each instance of the server side connection that ensured that it did not unlock any locks it did not own.
I also used Unreferenced in my server side, so having a list of owned locks made it easy to clear outstanding locks if the client called close() or if the client died.
There are issues though if you do not have a Connection Factory, or if you try and have the client side of the connection checking ownership.
Regards, Andrew
 
Ranch Hand
Posts: 2937
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
if (ownsLock(lock)){
You may not realize it, but this is your implementation of the LockManager, only in a different form. In your case each connection mainains its own list of locked records. The other alternative is, of course, to have a single instance of LockManager (per table), which would maintain the mapping between the clients and the locked records. Both solutions seem fine to me, although the LockManager gives you a little more encapsulation and control.
 
Patrick Cobbett
Ranch Hand
Posts: 44
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Hi Patrick,
quote:
--------------------------------------------------------------------------------
Surely, if a connection knows what it has locked then it can ensure by itself that it does not unlock a record that it has not locked.
--------------------------------------------------------------------------------
I agree. I had a Connection Factory on the server, and it was each instance of the server side connection that ensured that it did not unlock any locks it did not own.
I also used Unreferenced in my server side, so having a list of owned locks made it easy to clear outstanding locks if the client called close() or if the client died.
Hi Andrew,

There are issues though if you do not have a Connection Factory, or if you try and have the client side of the connection checking ownership.


Absolutely, there needs to be a connection factory, this can allow each connection(which is associated with each client) to maintain references to locks it solely owns. I agree that objects should take on as much responsibility as they are capable of. It's nice to have reassuring advice about my implementation. What do you mean by Unferenced in your comments? Does the design explained allow for recovery of locks if the client crashes? I did think that having a LockManager would provide a location for maintaining which locks were owned by which clients. Is this what you dicuss when you mention having a list of records? Where does this list of records reside?
Hi Eugene,

The other alternative is, of course, to have a single instance of LockManager (per table), which would maintain the mapping between the clients and the locked records. Both solutions seem fine to me, although the LockManager gives you a little more encapsulation and control.


So is it your opinion that the connection should just behave as an adapter which speaks directly to the Data class? Conceptually, i see that having a LockManager would just be a place to localise the mapping of client connections to locks. Is this necessary to recover from client crashes? Can connections themselves know how to recover locks from such an occurance? I have a couple ideas here but would like to hear yours first.
In your implementation does your connection delegate requests to lock() and unlock() to the LockManager with references to itself? For example, would the lock operation defined in LockManager be:
public void lock(int record, Connection conn)
I found that if I used a LockManager there was quite tight coupling between the Connection instances and the LockManager class. Meaning that they passed quite alot of arguments to each other. If they exhange or share quite of lot of information then surely their responsibilites are quite tightly coupled? .. tight enough to reside in a single class?
There does seem to be a fine line when it comes to deciding whether a LockManger class is necessary. You are quite right to say that either approach is feasible. Are there any opinions, agreed or disagreed with what i have said?
 
town drunk
( and author)
Posts: 4118
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I've not felt that a Lockmanager was the best choice for this assignment, though I do think it works, and it is a popular design choice on the ranch.
If you're interested in alternatives, consider a static hashmap that's a member of Data. It's fully sufficent to deal with the needs of the assignment, yet light weight enough to avoid undue complexity.
All best,
M
 
Andrew Monkhouse
author and jackaroo
Posts: 12200
280
Mac IntelliJ IDE Firefox Browser Oracle C++ Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Hi Patrick

What do you mean by Unferenced in your comments? Does the design explained allow for recovery of locks if the client crashes?


Take a look at java.rmi.server.Unreferenced. If your connection code implements this interface, then after your client disconnects (possibly because of a crash) then your unreferenced() method will be called. When that happens, you can unlock any records that are still locked.

Where does this list of records reside?


In the solution you proposed, you called a method addOwnedLocks(record). Presumably that method adds the record number to a set or something. I had that set in the connection code.
Eugene suggests taking it one step further and creating a lock manager. If you did this, then you would probably use a map within the lock manager instead of a set, as you would want to track ownership as well as record numbers.

What use if any is there in making use of a seperate class such as LockManager for this purpose?


The problem with each connection tracking it's own locks is that it does not know about any other locks. So it can never determine if a deadlock might occur.
A LockManager does know who owns what locks and so it could perform deadlock detection as well.
Also, as Eugene said, the LockManager gives you a little more encapsulation and control.

Conceptually, i see that having a LockManager would just be a place to localise the mapping of client connections to locks. Is this necessary to recover from client crashes? Can connections themselves know how to recover locks from such an occurance?


I didn't have a separate LockManager. I just maintained a set within the connection containing the numbers of the records that I had locked. Then when unreferenced was called, I could unlock them.

I have a couple ideas here but would like to hear yours first.


Hmmmm. I think I would prefer to hear your thoughts first, then I can agree or I can suggest problems / alternatives.
Regards, Andrew
 
Patrick Cobbett
Ranch Hand
Posts: 44
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Hi Max, i've read you discuss having a WeakHashMap in the Data class in other posts to map client connections to records..
..Firstly, do you think that the Data class is really where it should reside? Are you implying that the Data class would be dependant on the Connection class? On a higher level it would mean that the database package would depend on the network package, which would already necessarily depend on the database package. Violation if ADP?
Your discussion of a WeakHashMap has inspired me, but perhaps it should reside else where. Either in a LockManager or a static member of Connection?
By the way, incase you missed my last reply to another post i enjoyed your book.
Hi Andrew, thanks for advising me on the Unreferenced interface. It makes sense from what you say, will look into it immediately.
You seem to agree that it is feasible for connections to maintain references to locks it owns and also discuss having a LockManager to maintain references between Connections and the LockManager. Are these different suggestions or are you saying that connections should have references to locks it owns aswell as LockManager having references to it. If the mapping between connection and LockManger reside in a single place, (which would have to be in LockManager), then Connection would have to delegate all lock requests to LockManager using the code provided before and below:
public void lock(int record, Connection conn)

A LockManager does know who owns what locks and so it could perform deadlock detection as well


If deadlock actually occured then what could LockManager do about it? I thought that preventing deadlock would be the responsibility of the client?
 
John Smith
Ranch Hand
Posts: 2937
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
In your implementation does your connection delegate requests to lock() and unlock() to the LockManager with references to itself? For example, would the lock operation defined in LockManager be:
public void lock(int record, Connection conn)

Yes, you got it, although the signature may as well be "void lock(int recNum, Object clientId)", since the only reason that you pass the connection object to this method is to test for identity. With this approach, the LockManager is completely decoupled from Connection (and Data, for that matter).
Whatever the approach you take, let the high cohesion and low coupling be your guide. That is, if the purpose of the Data class is to describe data and the functionality to access/modify that data, avoid adding the methods such as "getNextPrime()" and member variables such as "List guiDependencies" to that class.
 
Andrew Monkhouse
author and jackaroo
Posts: 12200
280
Mac IntelliJ IDE Firefox Browser Oracle C++ Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Hi Patrick

You seem to agree that it is feasible for connections to maintain references to locks it owns and also discuss having a LockManager to maintain references between Connections and the LockManager. Are these different suggestions


These are different suggestions. Sorry for any confusion.
I don't want to influence you with one way over another, so I will happily discuss different options. Perhaps I should do two separate posts though

If deadlock actually occured then what could LockManager do about it? I thought that preventing deadlock would be the responsibility of the client?


The client can only do a limited amount of deadlock prevention. It does not have a clue what other clients are out in the world, or what records they have locked.
For example, consider the following scenario:
  • Client A locks record 1.
  • Client B locks record 2.
  • Client A attempts to lock record 2.
  • Client B attempts to lock record 1
    It is only the server that could detect this scenario and act on it.
    By the way - this is in part just a theoretical discussion answering your question on what the benefits of having a lock manager are. I am not sure that deadlock prevention is required by the assignment.
    Regards, Andrew
  •  
    Bartender
    Posts: 1872
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Hi Patrick,
    You wrote :

    I found that if I used a LockManager there was quite tight coupling between the Connection instances and the LockManager class. Meaning that they passed quite alot of arguments to each other. If they exhange or share quite of lot of information then surely their responsibilites are quite tightly coupled? .. tight enough to reside in a single class?


    I use a LockManager class and there is no coupling at all between the Connection instances and the LockManager. The only coupling I have is between the Data class and the LockManager class. And it's very lightweight : Data delegates locking to LockManager, while LockManager even doesn't know that Data exists. It acts as a quite generic locking management service, simply serving Data instances in this project.
    Don't forget that if our project currently uses only one table, a database system must be multi-table by design. If we want to support that so little future improvement consisting in adding one more table in the system, your locking scheme must integrate the notion of "locking scope" (record numbers scope is the table, while record-locking must span multiple tables).
    That's my main argument to justify a LockManager class.
    Further, as Andrew noticed :

    A LockManager does know who owns what locks and so it could perform deadlock detection as well.


    I thought that preventing deadlock would be the responsibility of the client?


    Unfortunately, when a deadlock arises, the only thing the client can do is suffering .
    With no global view, what else could it do ?

    If deadlock actually occured then what could LockManager do about it?


    Just break it ! :-)

    Andrew: By the way - this is in part just a theoretical discussion answering your question on what the benefits of having a lock manager are. I am not sure that deadlock prevention is required by the assignment.


    That's the only point I don't agree with you Andrew. OK, deadlock prevention is not required explicitly but I think it's an implicit must.
    Just a comparison to make that clear :
    While building a multithread application, you'll be very careful about synchronizing to avoid any deadlock : by avoiding nested synchronize or - if you really need it - you'll make sure you do it in a well-known consistent order. If, despite your efforts, a deadlock arises sometimes, you'll conclude : there is a bug somewhere and I must fix it.
    What's the difference in the database part of the application ? I don't think that if two client applications hang from time to time, telling the customer "OK, it's normal, it's just a database deadlock" may form a good excuse. From the customer's point of view, you'd simply have failed into building the safe multi-users application they expected.
    All that IMHO, of course.
    Regards,
    Phil.
    [ July 13, 2003: Message edited by: Philippe Maquet ]
     
    Andrew Monkhouse
    author and jackaroo
    Posts: 12200
    280
    Mac IntelliJ IDE Firefox Browser Oracle C++ Java
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Hi Philippe
    I was thinking about your point about deadlocks, and I was about to agree with you, when for some reason I decided to look up what Kathy Sierra & Bert Bates said in their book.

  • Is there any possibility of a deadlock? Where two or more clients are waiting for each other's locks?
    <Exam Watch!> Check for this more than you check for anything else.


  • Ooops I guess I really have to start agreeing with you now.
    Regards, Andrew
     
    Philippe Maquet
    Bartender
    Posts: 1872
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Hi Andrew,
    I read Kathy Sierra & Bert Bates's book too. Great as far as SCJP is concerned, a little "lightweight" in comparison with Max's one for SCJD. So I forgot their advice which is common sense IMO.
    Ooops I guess you just convinced me to start agreeing with myself now.
    OK, I seem kidding here - and I do - but just a little bit in fact : I read here so much people claiming that going further than the explicit requirements may lead you to fail, that I feel myself sceptic about any "good-idea-at-first-sight" I may have regarding this assignment.
    Anyway, in the URLyBird assignment, locking weights 20% of the total points, twice as much as the whole Data class or the whole GUI client (!) :


    General Considerations (100 points)
    Documentation (70 points)
    Object-orietned design (30 points)
    User Interface (40 points)
    Locking (80 points)
    Data class (40 points)
    Server (40 points)


    So I do think it's worth while to make a (little) effort in that area.
    BTW, I don't forget that without you my LockManager class would (maybe) still be bugged ... (see this thread.
    Thanks again,
    Phil.
     
    Patrick Cobbett
    Ranch Hand
    Posts: 44
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Ok, perhaps i've missed a point that's been made. What i am saying is that essentially there are two ways that dead-lock could occur. Either the client crashes before releasing it's locks, or the clients explicitly cause dead-lock amongst themselves.
    Andrew, you quite rightly explained the first of these. Making the RemoteConnection implement Unreferenced, and clean up afterward.
    What you discuss is not dead-lock prevention, it's dead-lock recovery. Apart from client crashes, which client code code can't account for, in this case client's can and should be written so that deadlock can not occur.


    Is there any possibility of a deadlock? Where two or more clients are waiting for each other's locks?


    This is posed as a question. For any locking mechanism it is possible for clients to cause deadlock amongst themselves.
    Could i suggest that the question is saying that IF there is a possibilty of deadlock as a result of the way inwhich the clients behave, then dead-lock recovery would be necessary.
    It is always possible to envisage hostile clients causing deadlock. But why is the advice given in your reference material not a statement, but rather a question? Why does it not say that all locking mechanism should implement a dead-lock recovery scheme?
    The way i have implemented the client there is no way for deadlock to occur.. so it this really necessary?
     
    Patrick Cobbett
    Ranch Hand
    Posts: 44
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Andrew, one furthur comment. If your Data class 'has' a LockManager, then does your lock() method take the connection as an argument?
    public void lock(int record, Connection)
    I am taking FBNS, and the Data class allows any connection to lock and unlock any record, although locking blocks until that record is released. It is the connection layer itself which adds the constraint of only allowing connections which has locked records to unlock them.
    If i used a LockManager then it would have to be 'used' by Connection, which would mean the that Connection would be passing references to itself in method calls to lock requests by LockManager. I have no real problem with this but you requiring clarification.
    With respect to the previous comments, admittedly although deadlock can be prevented it does seem to cross responsibility boundaries. I do not disagree necesarrily that deadlock recovery should be allowed, just that the reference used is not justification enough to do it.
    Perhaps you could explain a little more, either concretely or theoretically for your use of dead-lock recovery.
     
    Philippe Maquet
    Bartender
    Posts: 1872
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Hi Patrick,
    Sorry to answer in place of Andrew.

    But why is the advice given in your reference material not a statement, but rather a question?


    Because in that chapter, they list the questions you should ask to yourself before uploading your work. It's just a - but useful - checklist.

    The way i have implemented the client there is no way for deadlock to occur.. so it this really necessary?


    I have a quite different approach regarding the assignment. In fact, I don't see one assignment, but four distinct ones to develop, each of them having their own specs :
  • a database system (including record locking)
  • a GUI client program
  • a network tier
  • and finally, the application as a whole, making sure that all parts of it fit well together and fulfill the global expectations ("Application Overview" is the main chapter here IMO)


  • In URLyBird 1.2.1 (I suppose it's comparable to your assignment as far as locking is concerned), the database system consists in a Data class which exposes (among others) a lock(...) method. Nothing in its contract (the DBAccess interface in URLyBird, but any public method is part of a conceptual interface anyway) limits the locking scheme to allow only one lock per client at any time. Taking this into consideration, there is - from the Data class point of view - a risk of deadlock among clients requests. So, IMO, it's Data class's responsability to make sure to the outside world that such an awful situation won't arise.

    It is always possible to envisage hostile clients causing deadlock.


    They may even be gentle. Any reservation process is transactional : you want to book two rooms or none of them. Even if your application doesn't need it today, the need for multiple-records-locking may (will ?) come tomorrow. Anyway, such consideration are not Data's business : it has a contract, it claims it through its interface, point.
    Of course, if you have only one table in the game, a smart business tier (not the client) may avoid deadlock by sorting the recNos before claiming the locks.
    But how to justify it in a choices.txt file ? With a sentence like : "My application as a whole works fine. I needed a smart business tier because my Data implementation is poor and doesn't fulfill its contract." ?
    It's not the design choice I would dare to document.
    Regards,
    Phil.
     
    Philippe Maquet
    Bartender
    Posts: 1872
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Hi Patrick,
    In my LockManager implementation, the lock's owner is Thread.currentThread(). No need for a Connection parameter. Makes That sense ?
    Cheers,
    Phil.
     
    Max Habibi
    town drunk
    ( and author)
    Posts: 4118
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Originally posted by Patrick Cobbett:
    Hi Max, i've read you discuss having a WeakHashMap in the Data class in other posts to map client connections to records..
    ..Firstly, do you think that the Data class is really where it should reside? Are you implying that the Data class would be dependant on the Connection class? On a higher level it would mean that the database package would depend on the network package, which would already necessarily depend on the database package. Violation if ADP?
    Your discussion of a WeakHashMap has inspired me, but perhaps it should reside else where. Either in a LockManager or a static member of Connection?
    By the way, incase you missed my last reply to another post i enjoyed your book.


    Hi Patrick,
    I did miss that you had my book: I'm glad it's proving itself useful, and thanks for the kind words: heck, post'em on Amazon, and maybe I can retire to help out here full time . But seriously, Terry, Jeremy and I really tried hard to cover all the bases, research all the topics we could think of, read the relevant specs, argue(and argue) about various best approaches, etc. I don't we came up with the only valid answers, of course, but it really means a lot to me that it's proving itself useful to people.
    To your question: do I think I that Data class is really where the locking should reside? This is a really important question, so I'm going to sidestep for a second. Bear with me, I'll get to this at the end.
    Regardless of where it should be from an aesthetic level, my feeling is that Sun intended for the locking control to reside inside Data, because they put the locking method signature inside the Data class. Thus, I think you're coloring outside the lines a bit if you don't do that. This is just my opinion, of course. But I think Data is really supposed to be your low level DB API.
    Further, I don't think that Data class should be dependent on the connection class at all: As a matter of fact, I don't think that Data should know anything at all about the Connection class. nadda, zip, zilch. IMO, Data should simply be a private member variable of what you're referring as the Connector class(I might refer to it as a Mediator or Adapter). Thus, Connector has a method called, say, lockRec. internally, the lockRec method uses the private member variable Data(one per Connector) to lock the record.
    In this scenario, you don't need a lock manager at all, because the Data object handles it's own internal problems and conflicts, like a good object is supposed to. The way is does this is through a static Hashmap. The role of Data here is to make the Connector(Adaptor/Mediator, whatever), feel like the world is simply, sun shiny place to be, and that complexity is just a bad dream. This way, Connector can just be a happy, go lucky, simple remote object.
    Conceptually, this is pretty similar to how most APIs work. For example, When you use the FileChannel API, you don't know, or care, how FileChannels deal with internal complexity, or how they enforce trans process file locking: it just works. You may have to call it, but thats it. I think the goal here is to provide a similar process.
    Ok, this brings us back to the question of, do I think that this is best way to organize a project? Well, probably not, if my goal is to write a working application. I'd use a real DB, or even a free one: MySQL comes to mind. However, if the goal is to teach talented and hungry programmers how to deal with complex and intimidating topics, then yeah. I have to say, I think Sun's done a pretty good job of designing this project.
    As an aside, I'm sure you know that our book has a sample project in it. As we were building that, we really started to appreciate the depth and subtlety of Sun's assignment. It's not perfect by any stretch of the imagination, but it's an impressive effort. Like most well engineering things, it makes you take for granted it's best features.
    All best,
    M
     
    Philippe Maquet
    Bartender
    Posts: 1872
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Hi Max,

    But I think Data is really supposed to be your low level DB API.


    I love that API notion.

    In this scenario, you don't need a lock manager at all, because the Data object handles it's own internal problems and conflicts, like a good object is supposed to.


    I don't agree with you, here. By letting Data delegating the whole locking mechanism to a separate class - independant from Data itself - , you get a few pros "for free" :
  • easiness in testing (BTW I could test LockManager before the first line of Data was ever written)
  • reusability (locks are served and managed to any caller class, Data here but any other class if needed)
  • code clarity (LockManager just handles locks, but it's enough complexity to manage it in its own "box")


  • From the outside world, it doesn't change anything : LockManager is a package-level class, so Data is the only visible db class.
    Any comment of you will welcome : I am a Java newbie while you (I know it not from reputation but because I read your book) are an expert.
    Cheers,
    Phil.
     
    John Smith
    Ranch Hand
    Posts: 2937
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    In my LockManager implementation, the lock's owner is Thread.currentThread().
    This is a bad idea. RMI makes no guarantees with respect to mapping remote object invocations to threads. That means that client A may use thread T1 to lock a record, and then use thread T2 to unlock the same record.
     
    Philippe Maquet
    Bartender
    Posts: 1872
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Hi Eugene,

    This is a bad idea. RMI makes no guarantees with respect to mapping remote object invocations to threads. That means that client A may use thread T1 to lock a record, and then use thread T2 to unlock the same record.


    Good point, but i knew it, and I'll document as well.
    As I chose to implement sockets, I'am not a RMI specialist. But I've read over multiple threads here, that if RMI specs "makes no guarantees with respect to mapping remote object invocations to threads", nobody knows any implementation which doesn't.
    Anyway, as I chose to implement sockets, I could be not concerned. But as I intend (my network layer is not written yet), to serve connections through a pool of thredas, I cannot garantee myself that a given connection will execute all its commands within a given thread.
    OK, that's just life constraint :-).
    What does it mean in practice ? : I just must care that my locking scope doesn't span multiple command calls. Example :

    As it's just what we expect from the system, it's seems to be OK, isn't it ?
    Do you imagine how an application would work if it let the user :
  • acquire one ore more database lock(s)
  • go away and drink a cup of coffee
  • come back and use its locks

  • ???
    Am I right ?
    Cheers,
    Phil.
     
    John Smith
    Ranch Hand
    Posts: 2937
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    But I've read over multiple threads here, that if RMI specs "makes no guarantees with respect to mapping remote object invocations to threads", nobody knows any implementation which doesn't.
    Err, I know one implementation that makes no guarantees with respect to mapping remote object invocations to threads, -- it's Sun RMI implementation. Just run a few tests and you will see it for yourself.
     
    Max Habibi
    town drunk
    ( and author)
    Posts: 4118
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Eugene is right, RMI behaves exactly as he's describing it. I was also under the impression that this was a theoretical point, but I tested it, and it's not.
    Actually, the testing is worthwhile to do, especially since you're so interested in the details of how things work under the hood. The trick is to create, say, 15 remote objects, then let 5 of them go, and then create 3 more remote objects. Initially, RMI creates 15 threads to manage your 15 remote objects, one per object. However, when you release the initial set of 5objects, RMI pools those threads, in case you ask for more connections. Thus, when you create your next set of 3 objects, they don't get separate threads, they get 3 pooled threads.
    There are other ways and times when the thread recycling occurs, but this is probably the most easily visible.
    BTW, 15 is just a number that works on my system, of course: it might be 25 or some such on yours.
    All best,
    M
     
    Ranch Hand
    Posts: 108
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Originally posted by Andrew Monkhouse:
    Hi Patrick

    For example, consider the following scenario:

  • Client A locks record 1.
  • Client B locks record 2.
  • Client A attempts to lock record 2.
  • Client B attempts to lock record 1
    It is only the server that could detect this scenario and act on it.

  • Regards, Andrew


    It's why I so dislike the idea to allow client to lock records remotely.
    My clients can lock and records remotely, but they should do it one by one or all at once or provision should be made that they declare locking multiple record so lock() method accepts array of records number. With help of Comparator, for ex., the array can be sorted and locs are aquired and released in predefined order.
    On other hand if you have method call that encapsulates lock-unlock reasonable clients won't cause deadlocks:
    compare(DataInfo di1, DataInfo di2) {
    lock(1);
    lock(2);
    COMPARE();...
    unlock(2);
    unlock(1);
    }
    all others are welcome to wait.
    If you do this on server side that has compare() in its interface and does everything in proper order, you are fine. It's why I like to have functions implemented on server-side with as thin client as possible.
    :roll:
     
    Max Habibi
    town drunk
    ( and author)
    Posts: 4118
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Originally posted by Philippe Maquet:
    Hi Max,

    I don't agree with you, here. By letting Data delegating the whole locking mechanism to a separate class - independant from Data itself - , you get a few pros "for free" :

  • easiness in testing (BTW I could test LockManager before the first line of Data was ever written)
  • reusability (locks are served and managed to any caller class, Data here but any other class if needed)
  • code clarity (LockManager just handles locks, but it's enough complexity to manage it in its own "box")


  • From the outside world, it doesn't change anything : LockManager is a package-level class, so Data is the only visible db class.
    Any comment of you will welcome : I am a Java newbie while you (I know it not from reputation but because I read your book) are an expert.
    Cheers,
    Phil.


    Hi Phil,
    I think you have an completely reasonable argument here. There's nothing that you're missing, AFIK. My original point was not that Lockmanager shouldn't ever be used, but that it's not necessary, per the thread subject. As an aside, I would argue you can test the locking mechanism in Data before you actually start doing file IO, just as you could with a LockManager.
    So the real issue reduces to, do you want to write your own class that's a static member of Data(the lockmanager), or do you want to use an existing class that's a static member of Data(a HashMap/WeakHashmap). The WeakHashmap impl can be particularly interesting, but that's a different thread.
    I don't quite give the same amount of weight to the use of a reusable locking mechanism, because I don't see where it would be reused in this project, and I have a prejudice against theoretical code. My personal software architecture principle has always been to write the absolute simplest thing that will work. But OTOH, you're not really adding a lot of complexity here. Either way, it sounds like you have a good grasp of the situation, are making reasonable choices, and articulating your arguments well. I'm looking forward to congratulating you on your high score
    All best,
    M
    [ July 13, 2003: Message edited by: Max Habibi ]
     
    Andrew Monkhouse
    author and jackaroo
    Posts: 12200
    280
    Mac IntelliJ IDE Firefox Browser Oracle C++ Java
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Hi everyone,

    Philippe: I read Kathy Sierra & Bert Bates's book too. Great as far as SCJP is concerned, a little "lightweight" in comparison with Max's one for SCJD.


    I have not yet been able to see Max's book so I cant compare them. But I have seen Kathy's comments on Max's book (her review on Amazon was amazing - but it appears that her name has been changed in the review) and it sounds like she believes that Max's book is the book to get if you only get one book. However what everyone seems to agree on is that her book gives greater insight into what the examiners are looking for (not surprising when you consider the source).

    Patrick: It is always possible to envisage hostile clients causing deadlock. But why is the advice given in your reference material not a statement, but rather a question? Why does it not say that all locking mechanism should implement a dead-lock recovery scheme?


    Kathy Sierra and Bert Bates wrote nearly all the SCJD reference material as a set of questions - they deliberately avoided definitive statements on what the examiners would be looking for. The fact that this particular queston is immediately followed by a comment saying that this is something you should spend some time on puts this particular question into an almost definitive statement category for me.
    Sorry for apparently putting to much weight on the reference without explaining it: Kathy Sierra works for Sun-ed, and from memory (I cant find references at present) she was roped into working on the new assignments /assesments at Sun-ed even though her main expertise is SCJP. So she has more knowledge than most people of what is really in the assesments.
    One thing I should say at this point is that Kathy Sierra & Bert Bates wrote their book for the SCJP 1.4 exam and the new SCJD assignments (contractors / hotels). This is very obvious when they were discussing deliverables. Their comments on what the examiners would be looking for may not apply to FBNS.

    Andrew, one furthur comment. If your Data class 'has' a LockManager, then does your lock() method take the connection as an argument?


    After all this talk where I am recommending deadlock prevention, I am embarassed to say that I did not do deadlock checking and I did not have a lock manager.

    Patrick: If i used a LockManager then it would have to be 'used' by Connection, which would mean the that Connection would be passing references to itself in method calls to lock requests by LockManager. I have no real problem with this but you requiring clarification.


    Yes, if I were developing a lock manager, then I would do it the way you are suggesting.

    Patrick: Could i suggest that the question is saying that IF there is a possibilty of deadlock as a result of the way inwhich the clients behave, then dead-lock recovery would be necessary.


    You could interpret it this way. I did for my assignment. I am just having second thoughts now, especially for people doing the new assignment.

    Patrick: With respect to the previous comments, admittedly although deadlock can be prevented it does seem to cross responsibility boundaries. I do not disagree necesarrily that deadlock recovery should be allowed, just that the reference used is not justification enough to do it.


    I am not sure that it does cross responsibility boundaries. It is a bit like the whole "a client can only unlock records it locked" issue - a good client would never try to unlock a record that it has not locked, so why do we have this requirement? Because we cannot guarantee that only good clients will connect.
    Likewise the argument against deadlock prevention is that we dont need it at present since our clients are only locking and modifying one record at a time. Unfortunately as Philippe pointed out, this is not a normal circumstance. With flight bookings people usually book a return trip, and sometimes book quite involved ittineraries.

    Philippe: In my LockManager implementation, the lock's owner is Thread.currentThread()
    Eugene: This is a bad idea. RMI makes no guarantees with respect to mapping remote object invocations to threads. That means that client A may use thread T1 to lock a record, and then use thread T2 to unlock the same record.
    Philippe: But I've read over multiple threads here, that if RMI specs "makes no guarantees with respect to mapping remote object invocations to threads", nobody knows any implementation which doesn't.


    I think what you are thinking about Philippe was a thread that was discussing whether an implementation of RMI used one and only one thread for all it's work. This was something that might be allowed by the specification but is fairly unlikely in real life.
    However the other two implications: that one client may use different threads on subsequent calls, and that two clients may happen to use the same thread (though not at the same time) does happen with the Sun RMI VM.
    Max described one test you could do to see that threads are reused. So the thread that client 'A' uses now may be the same thread that client 'B' uses later.
    Another quick test you can do is have one client call two methods via RMI (or even the same method twice) with a pause of 25 seconds between them (I think that the timer in Sun's RMI VM is set to 20 seconds, but 25 seconds should be safe). You will normally see that a different thread is used for subsequent calls, even though the client is still connected.

    Svetlana: It's why I so dislike the idea to allow client to lock records remotely.


    Unfortunately for FBNS we had to allow the client code to call lock() via RMI - it is part of the instructions.
    Regards, Andrew
     
    Patrick Cobbett
    Ranch Hand
    Posts: 44
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Thanks to all who have submitted posts regarding my initial query: No need for a LockManager?
    Max, you have said that the Data class should know who owns which locks. You suggest that the mapping should reside in a static HashMap of Data.


    Max wrote:
    Regardless of where it should be from an aesthetic level, my feeling is that Sun intended for the locking control to reside inside Data, because they put the locking method signature inside the Data class. Thus, I think you're coloring outside the lines a bit if you don't do that. This is just my opinion, of course. But I think Data is really supposed to be your low level DB API.


    The Data class provides methods signatures to lock/unlock records.
    public void lock(int record)
    However, the signature as provided does not allow for the Data class to know who has locked what. Are you suggesting overloading lock(int) to lock(int, Object src)?
    Or are you accessing the static Map in Data directly? At the moment, my view is that record-locking or indeed lock-recovery should not reside in Data. Data provides primitive record-locking services. Data does not care who is performing lock and unlock operations. This is in accordance with the Data class's provided lock signatures. When this is born in mind, there is no such thing as dead-lock because Data doesn't know what clients have what locks and which locks they are trying lock. The locking mechanism as seen from the connection layer is built on top of Data's primitive locking mechanism. Therefore it is the responsibility of the network layer to provide the dead-lock recovery which the networking layer itself would need due to concurrent connections.
    what is your view on this?
     
    Max Habibi
    town drunk
    ( and author)
    Posts: 4118
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Originally posted by Patrick Cobbett:
    Or are you accessing the static Map in Data directly?


    This is what I'm suggesting, yes. Of course, you shouldn't implement this solution if it doesn't seem reasonable to you: it's your assignment, not mine


    At the moment, my view is that record-locking or indeed lock-recovery should not reside in Data. Data provides primitive record-locking services. Data does not care who is performing lock and unlock operations. This is in accordance with the Data class's provided lock signatures.


    I'm a little confused here. How can data lock a record, and in ifact, unlock that record for the owner if it doesn't know who the owner is? As I recall, the unlock record explicitly requires that Data know about the owner: otherwise, it's supposed to refuse to unlock. To me, this implies that Data does, in fact, need to track the owner.


    When this is born in mind, there is no such thing as dead-lock because Data doesn't know what clients have what locks and which locks they are trying lock. The locking mechanism as seen from the connection layer is built on top of Data's primitive locking mechanism. Therefore it is the responsibility of the network layer to provide the dead-lock recovery which the networking layer itself would need due to concurrent connections.
    what is your view on this?


    I think this is a reasonable argument, though it's not the most convincing one to me. In my scenario( assuming we're talking about the older assignment), Data has a map, and that map is static. Thus, inside of the record of a given Data object, you have

    That's all.
    Again, this is not the only reasonable solution, IMO. My strongest possible advocacy is that you find a solution that feels correct to you, and implement that. This solution vs. that other isn't really so important and you making the choices your own.
    All best,
    M
     
    Patrick Cobbett
    Ranch Hand
    Posts: 44
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Thanks Max. In my assignment FBNS, the Data class declares a method:
    lock(int record)
    and not
    lock(int record, Object src);
    Therefore is it reasonable to assume that as i must implement lock(int) then Data is assumed not to know who locks what?
    Why is so much reference used to a Map? I may be missing a big point here. I am using an ArrayList to map Connections to RecordLocks. A list is used for an ordered collection referenced by index. If, as in my assignment suggests, we need to lock according to an int index, then doesn't it make sense to use a List? How is the map being implemented? What is being bound to what? I may be missing something here.
    thanks in advance
     
    Max Habibi
    town drunk
    ( and author)
    Posts: 4118
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Originally posted by Patrick Cobbett:
    Thanks Max. In my assignment FBNS, the Data class declares a method:
    lock(int record)
    and not
    lock(int record, Object src);
    Therefore is it reasonable to assume that as i must implement lock(int) then Data is assumed not to know who locks what?
    Why is so much reference used to a Map? I may be missing a big point here. I am using an ArrayList to map Connections to RecordLocks. A list is used for an ordered collection referenced by index. If, as in my assignment suggests, we need to lock according to an int index, then doesn't it make sense to use a List? How is the map being implemented? What is being bound to what? I may be missing something here.
    thanks in advance


    Hi Pat, hope you have a good weekend. Could you post the javadoc comments for your unlock method?
    Thanks,
    M
     
    Svetlana Koshkina
    Ranch Hand
    Posts: 108
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Hi guy!
    Is it alright if i barge in?

    My ass. said that client-side implementation
    "should include a class that implements the same public methods as the suncerify.db.Data class".
    Here is no indication that we need USE these method from the client-side.
    For example, my intermediate client implements ALL these methods but actually I did not want to use them on client side. My server that is working with this client (i.e. that constitutes connection roughly speaking) doing all reservations on server side: call lock and unlock methods on the database interface. So that calling unlock in finally clause is actually enough for recovery from unexcpected client's crash. I thought if they wanted to use those lock-unlock remotely they are welcome on their own risk, I can stipulate it in docs, my client CAN do this but did not chose it. Lock-unlock are too fine grained for remote operation anyway and too dangerous. in real world you'll want to encapsulate it in a database like in Oracle for ex. They say to you that you won't have dirty reads and you are fine believing.
    Please correct me if i am wrong because i am kind of worried too.
    Also i have a LockManager. It's looking and feels good to have it in design but of course it's a matter of taste.
     
    Patrick Cobbett
    Ranch Hand
    Posts: 44
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    This is what my requirements say alot locking..


    public void lock(int record)
    public void unlock(int record)
    Record locking must be implemented using the methods public void lock(int) and public void unlock(int). These methods are required to allow concurrent use of the database when booking flights from multiple clients. Note that the locking required is effectively a "write" lock only. If updates occur to a record that is being displayed on another client, then no change occurs at the other client. However, if two clients attempt to perform the sequence lock, read, modify, write, unlock concurrently, then both modification attempts will be handled correctly. The aim is to ensure that if two customers attempt to book a seat on the same flight concurrently, then the number of available seats is definitely reduced by two, unless there was only one seat, in which case, one customer will be advised at booking time that no more seats are available.
    The lock method should block until the requested lock can be applied. The integer argument indicates the record to be locked. If the method is called with an argument of -1, the entire database should be locked.
    The unlock method simply removes the lock from the specified record. If an attempt is made to unlock a record that has not been locked by this connection, then no action is be taken.


    from this last bit, unlock(int) removes the lock from the specified record, irrespective of the invoker? It is the connection layer that ensures that only clients which own a lock may release it.
    what do you think?
     
    Max Habibi
    town drunk
    ( and author)
    Posts: 4118
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Originally posted by Patrick Cobbett:
    This is what my requirements say alot locking..
    [qb]
    The unlock method simply removes the lock from the specified record. If an attempt is made to unlock a record that has not been locked by this connection, then no action is be taken.

    from this last bit, unlock(int) removes the lock from the specified record, irrespective of the invoker? It is the connection layer that ensures that only clients which own a lock may release it.
    what do you think?[/QB]


    I read this a little differently. IMO, it's highly respective of the user, because the Data.unlock )(emphasis on [d]Data[/b]) method ensure that if a record has not been locked by this connection, then no action is be taken.
    The networking solution you're suggesting seems to differ this responsibility to another class: namely, the networking layer. That fine, I know that others have passed( and done well) with a similar interpretation. However, it's not the strictest reading on the requirements, IMO.
    M
     
    Andrew Monkhouse
    author and jackaroo
    Posts: 12200
    280
    Mac IntelliJ IDE Firefox Browser Oracle C++ Java
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Hi Svetlana
    There is nothing in the assignment that stops you from adding methods (including business methods) to the RMI interface, and having your client call those business methods. Some people have likened this to calling a stored procedure in a database.
    However, we do have to make networked versions of all the public methods in the Data class, and we have to get them working correctly.
    Now - if you have lock(), read(), modify() and unlock() all working correctly over the network, then why wouldn't you use them? If you are going to have a bookFlights() method (or whatever you are going to call it) then why not have it client side?
    As I see it, there are two advantages to having it server side:
  • Less network traffic: there is only one call to a remote procedure, with one set of data, and only one response back. Contrast with client side bookFlights() where there has to be at least four remote procedure calls with their appropriate responses.
  • Simpler coding client side.


  • However performance is not an issue for this project (apart from the search algorithm in criteriaFind()), so I am not concerned with the first point.
    And while the code is simpler client side, it is not simpler by very much: if you have implemented lock() etcetera correctly (which you have to do) then the bookFlights() method should be identical whether it is client side or server side.
    So what are the benefits of having it client side:
  • I think this is what Sun wanted. Why would they bother telling us that we have to create remote methods of all the public methods in Data if they did not want us to use them?
  • The database is more generic. The Data class does not contain anything that ties it to a flight reservations system - you could use the same class for any database. If you do not create any remote business methods then the same applies for the network solution - you have a ready made networked solution for any database, not just flights.
  • Many changes to the client requirements can be handled on the client side - no need to modify the server at all. Say a later requirement is to book two flights simultaneously. If you have your bookFlight() method server side then you have to change the server in order to get this new functionality to work. Speaking from experience, changes to server code are rarely welcome, and the cutover is usually viewed with great distrust.


  • Lock-unlock are too fine grained for remote operation anyway and too dangerous. in real world you'll want to encapsulate it in a database like in Oracle for ex.


    Even in Oracle you can still lock individual records remotely.
    Don't dismiss remote lock and unlock just because they are fine grained. Yes, they are, and it makes the assignment a bit more complex to handle that. But I suspect that Sun would like us to have complex problems to solve in order to make the certification more valuable.
    Regards, Andrew
     
    Andrew Monkhouse
    author and jackaroo
    Posts: 12200
    280
    Mac IntelliJ IDE Firefox Browser Oracle C++ Java
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Hi Patrick

    Instructions: If an attempt is made to unlock a record that has not been locked by this connection, then no action is be taken.
    Patrick: from this last bit, unlock(int) removes the lock from the specified record, irrespective of the invoker? It is the connection layer that ensures that only clients which own a lock may release it.
    Max: IMO, it's highly respective of the user, because the Data.unlock )(emphasis on [d]Data[/b]) method ensure that if a record has not been locked by this connection, then no action is be taken.


    Hmm, I read that sentence the same way Patrick did. That sentence is talking about a connection attempting to unlock a record, not the unlock method itself.
    The Data class, as provided, has no knowledge of connections. And I think this is a good thing - it allows for a clear separation of responsibilities: the Data class is only responsible for accessing the database. Then you write network code that handles connections. And in this networked code you handle ownership of locks issues.
    Regards, Andrew
     
    Max Habibi
    town drunk
    ( and author)
    Posts: 4118
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Originally posted by Andrew Monkhouse:
    Hi Patrick

    Hmm, I read that sentence the same way Patrick did. That sentence is talking about a connection attempting to unlock a record, not the unlock method itself.
    The Data class, as provided, has no knowledge of connections.


    This is open to some interpretation. My reading is that because this snippet is in the Data class, and is actually on a method of the Data class, Sun meant for it to be implemented in the Data class. Remember, the documentation didn't say 'a connection'. It said 'this connection'. Think of it this way: the smart folks at Sun wouldn't very well write this sort of a comment for a method that wasn't supposed to work . The comment explains, in detail, what the Data.unlock method is supposed to do. It's a bit of a reach to assume it's documentation for how some other method in some other object is supposed to work, IMO.


    And I think this is a good thing - it allows for a clear separation of responsibilities: the Data class is only responsible for accessing the database. Then you write network code that handles connections.


    I'm with you so far


    And in this networked code you handle ownership of locks issues.
    Regards, Andrew


    This doesn't seem fully justified in light of the requirements, nor does it follow from the previous statements. That is, there is no indication that the networked code is supposed to handle locking issues: that indication is being read in. There is, however, a fairly strong indication, via the method comment of the Data.unlock method, that the Data class should do so.
    The above, IMO, is the most intuitive approach, and the most natural, from an OO perspective. Data represents a Database: it should handle it's own locking control, at the request of the client. By forcing a tightly coupled relationship between the networking code and the Database, you're sort of mixing two unnatural things. The whole point of RMI is to make remote objects appear as if they were local: A Data that handles it's own locking achieves this. It seems unclean, IMO, to violate that by tightly coupling the networking connectors with the Database locks.
    M
     
    Andrew Monkhouse
    author and jackaroo
    Posts: 12200
    280
    Mac IntelliJ IDE Firefox Browser Oracle C++ Java
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Hi Max,
    As you say - this is open to interpretation. I don't really disagree with anything you have said, but just to explain my thoughts a bit further ....
    We are talking about FBNS here. This is the one where Sun provided the Data class. So we have some limitations. The lock method should be implemented in the Data class or in a class that extends Data. I don't believe we should be making one instance of Data class per connection. And the signature for the lock method does not allow any way of indicating the connection. So I don't see how the Data class itself can meet that requirement.
    If you have a LockManager as a front end to the Data class (so <client-code> --> <network> --> <LockManager> --> <Data> then it becomes easy. But this does not meet your interpretation that the Data class has to know who owns the lock.
    Likewise if you have the connection code itself track which locks it has been granted (one step down from a LockManager) then it is also easy. But again this does not meet your interpretation that the Data class has to know who owns the lock.
    Just so we are clear here: when I am talking about connection code, I am talking about the code on the server. I am not talking about code on the client side.
    Regards, Andrew
     
    Max Habibi
    town drunk
    ( and author)
    Posts: 4118
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Hi Andrew,
    I'm enjoying this discussion. I really just want to talk about the aesthetic of the various designs, because I tend to just be a geek that way


    Hi Max,
    As you say - this is open to interpretation. I don't really disagree with anything you have said, but just to explain my thoughts a bit further ....
    We are talking about FBNS here. This is the one where Sun provided the Data class. So we have some limitations. The lock method should be implemented in the Data class or in a class that extends Data.


    So far, no disagreement


    I don't believe we should be making one instance of Data class per connection.


    Ok, that's fine. However, there's no reason, based on the requirements, to constrain ourselves design-wise at this point. That is, this is a design constraint you're placing on yourself, not one that being placed on you by the SCJD.
    However, you do, I think, believe that it's ok to create a remote connection per client, correct? So it's not so much per client remote objects that you object to, it's per client remote Data objects.


    And the signature for the lock method does not allow any way of indicating the connection. So I don't see how the Data class itself can meet that requirement.


    We'll come back to this.


    If you have a LockManager as a front end to the Data class (so <client-code> --> <network> --> <LockManager> --> <Data> then it becomes easy. But this does not meet your interpretation that the Data class has to know who owns the lock.


    Well, it doesn't strictly meet Sun's requirement that the Data.unlock method know who owns the lock: that's not open to interpretation, since the javadoc comment on the data.unlock explicitly requires this. However, that's not really the part that concerns me.
    What bothers me, from a aesthetic design perspective, is that with a single Data instance, the LockManager itself becomes superfluous. That is, a method in the connection that locks, reads, modifies, and unlocks is perfectly acceptable, so long as it synchronizes on Data. Further, your implementing makes the actual locking/ unlocking vestigial. That is, with a single instance of data being shared by all of the clients, why lock anything at all? Just synchronize access to the Data instance and be done with it. Thus,

    since your design sidesteps the multiple Data instance issue, this is a perfectly acceptable follow through. If you're not going to implement the data.lock/Data.unlock methods, why not just do this?


    Likewise if you have the connection code itself track which locks it has been granted (one step down from a LockManager) then it is also easy. But again this does not meet your interpretation that the Data class has to know who owns the lock.


    Again, this isn't my requirement, by the one imposed by Sun. That is, if your Grader failed you on the spot because you were supposed to implement the Data.lock method, and you just didn't, there would be nothing you could legitimacy say


    Just so we are clear here: when I am talking about connection code, I am talking about the code on the server. I am not talking about code on the client side.


    The connection code is a Remote Object, correct? Thus, it's being remotely accessed by the client, if I understand you correctly.


    And the signature for the lock method does not allow any way of indicating the connection. So I don't see how the Data class itself can meet that requirement.


    The requirements clearly state that data.unlock needs to know that this connection should be the only one that locked the record. It seems natural to me, assuming that data has a static member variable called staticMap, that the lock could should corresponding lock this connection. thus


    This clearly meets the requirements, and seems like the most natural thing in the world, from an OO perspective.
    All best,
    M
    [ July 22, 2003: Message edited by: Max Habibi ]
     
    Patrick Cobbett
    Ranch Hand
    Posts: 44
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Thanks to both Max and Andrew who each uniquely share the two threads which are causing me mental war-fare.
    Andrew, it was you who persuaded me that the connection should be responsible for ensuring that it does not lock anything it has not locked(perhaps via a LockManager).
    I totally agree with this because the requirements specifies that lock operations should be called only with respect to the record number. To me Data provides a basic locking mechansim where any client can invoke lock and unlock operations. It is the responisbility of the connection layer to handle multiple connections which it itself introduces.
    Max, I do not deny that Data is where i'd be happiest putting the mapping of connections to record locks. But our requirements does not allow it unless we change the signature of the supplied lock operations.
    You say that from an OOD perspective, it makes most sense for the Map to exist as a static member of Data. I feel that the use and referencing of static member variables is not very OOD compliant. What if we have multiple instances of Data operating on different databases? Surely the record locking mechanisms would get in each other's way?
    Also, assuming that this static Map member is at the very most package-private, how could client java classes written outside the package handle locking correctly?
    This would mean that the way Data operates is inconsistant with the ways you can operate with it(it's contract).
    The inconsistency in my mind is that Data is suppossed to be fully synchronized and yet does not support locking on a multi-user level. Then again, Data would have to be synchronized with one client anyway (perhaps local) incase there were multiple threads.
    The easiest thing to do would be to overload lock, allowing the second argument to indicate the owner.
    lock(int){ lock(int, null);
    lock(int, Object src){ // do locking; }
    Is this allowed?
    Additionally, i've found that a LockManager is only good for detecting dead-lock. RemoteConnections themselves can ensure that they only unlock what they have locked. I delegated all requests to lock to the LockManager, however with refactoring LockManager may be eliminated. The only advantage for LockManager (except for seperating responsibilities) is to have a localised mapping of connections and locks. This is really only useful for dead-lock recovery. Who is actually implementing this?
    Anyone have any example code for detecting a dead-lock recovery?
    Also Andrew, are there inconsistencies with the contracts for unlock()?
    Data declares unlock(int) as unlocking the record irrespective of the invoker..
    My RemoteConnectionImpl class calls:
    lock(int, Object src) on LockManager
    i.e:
    void lock(int i){
    lockManager(i, this);
    }
    .. What is the contract for this method declared in RemoteConnectionImpl?
    When do we introduce the constraint that only connections which called the lock() can successfuly unlock it? Should it be declared in Connection? This would mean that the contract wasn't quite the same as that of the Data class?
    I think that this is feasible to change. If as the requirements specifies a connection can not unlock something it has not locked, then this is the appropriate place to declare that contract.
     
    Philippe Maquet
    Bartender
    Posts: 1872
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Hi Andrew and Max,

    Andrew
    If you have a LockManager as a front end to the Data class (so <client-code> --> <network> --> <LockManager> --> <Data> ) then it becomes easy.

    <client-code> --> <network> --> <Data> --> <LockManager> is even easier, with just this little constraint that the lock() .. unlock() calls must reside within single methods, server-side (because of my use of currentThread() as the lock owner and because of the way RMI (and maybe a given sockets implementation) allocate threads to connections.
    But I recall the main pros of the LockManager solution :
  • automatic deadlock-by-concurrency prevention (I mean without any care of any other class about this issue, including ordering recNos (and tables))
  • automatic deadlock-by-crash prevention (every second, any lock owned by any "dead" thread is automatically unlocked)
  • automatic unlock of locks which were granted for longer than a given timeout


  • Max
    Again, this isn't my requirement, by the one imposed by Sun. That is, if your Grader failed you on the spot because you were supposed to implement the Data.lock method, and you just didn't, there would be nothing you could legitimacy say

    If the line is now <client-code> --> <network> --> <Data> --> <LockManager>, Data simply delegates its locking job to some smarter class to achieve it, but IMO, by doing so, it's still implementing lock/unlock. Right ?
    BTW, my Data class also uses MetaData, Field, Index, Cache and a few other helper classes which have no other purpose than to help me to present a Data code wich is easier-to-read and easier-to-maintain.
    Max
    //data.lock
    //synch on staticMap
    //insert this and recono unto static method (if it's not already there: otherwise wait)
    //exit synch block

    Nice !
    But I suppose that your unlock method would be written :

    If it's right, just two (little) remarks :
  • Each time a record is unlocked, all (if any) threads waiting for the lock compete to get it. It goes against performance and order (I like order )

  • I love metaphors too : it's like if in a MacDonald, each time a Big Mac is served, all people in the line had to fight with others to get a chance to be the next happy customer. A lot of energy lost, and ... what a mess !
    In comparison, for each recNo in a HashMap, the value is a LinkedList of pending lock objects. As each waiting thread is waiting on its own Lock object, in unlock() I just need to notify the next Lock in the list.
  • The instructions say that "Any attempt to lock a resource that is already locked should cause the current thread to give up the CPU, consuming no CPU cycles until the desired resource becomes available.". Well with notifyAll(), you'll consume a few CPU cycles just to compete for the lock, which is a (slight ) deviation from the requirements.



  • Best,
    Phil.
    [ July 22, 2003: Message edited by: Philippe Maquet ]
     
    Max Habibi
    town drunk
    ( and author)
    Posts: 4118
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    /*Hi Patrick,
    Just to further clarify my thoughts on this, and answer a few questions you raised.


    Max, I do not deny that Data is where I'd be happiest putting the mapping of connections to record locks. But our requirements does not allow it unless we change the signature of the supplied lock operations.


    I don't believe that this is correct. If Data is am member variable of the Connection, then simply locking on Data.this is appropriate and sufficient. Again, this is the way that things really work.
    For example, you don't write your JDBC code one way if you're accessing it on a network, and another way if you're accessing a local database. You defer locking to the database layer, because that's what it's there for. The bank doesn't trust clients to honestly interact with each other's accounts: it ID's the users.


    You say that from an OOD perspective, it makes most sense for the Map to exist as a static member of Data. I feel that the use and referencing of static member variables is not very OOD compliant.


    Static has come to be a dirty word in the Java world: How or why, I never really understood. But there's nothing in OO design principles that advocates that static not be used. As a matter of fact, all OO languages have them, and for good reason. They have place, if you want all of the member objects of a class to know something about their collective state at the same time. Heck, that's exactly what they exist
    However, if you're not comfortable with static, the same result could be achieved by having the factory that creates Data objects give them all a reference to the same instance of the HashMap.


    What if we have multiple instances of Data operating on different databases? Surely the record locking mechanisms would get in each other's way?


    Not at all: as a matter of fact, the solution would be congruent with how the LockManager implementation would solve this issue: That is, a lockmanger solution would have to instantiate a whole new data object, one per DB. The Data solution would have a Map, one per DB.


    Also, assuming that this static Map member is at the very most package-private, how could client Java classes written outside the package handle locking correctly?


    Well, the point here is that they wouldn't have to: the Data class would handle the locking. Conceptually, this is already manifest in the jdk 1.4 architecture. For example, FileChannels handle their own internal locking mechanism, thus making certain that only a given Thread(and indeed, even different processes) interacts with the File at any given time for reading/writing. The pattern they use is really the same pattern that the Data class takes. That is, the instance variables track internal state information about each other's activities, and don't step on each other's toes

    However, all of this is just banter . It sounds like you've thought through your decision carefully, and have arrived at a working solution. That's all that can reasonably be expected. Go with it
    Good luck,
    M
     
    Politics n. Poly "many" + ticks "blood sucking insects". Tiny ad:
    Gift giving made easy with the permaculture playing cards
    https://coderanch.com/t/777758/Gift-giving-easy-permaculture-playing
    reply
      Bookmark Topic Watch Topic
    • New Topic