Discussions

J2EE patterns: Long-Lived Pessimistic Transactions

  1. Long-Lived Pessimistic Transactions (16 messages)

    Profil is an Entity EJB.

    Motivation
    ==========
    A user edit his profile and want to be sure that nobody try to changes it at the same time. If the user click on "submit", the changes must be recorder if the data are corrects. (Not an optimistic locking).

    While the data is edited by a user, nobody else must be able to edit the same data. If a user try, he will receive a message such as "This data is locked by xxxxx, Sorry".


    Solution
    ========
    EJB interface
    -------------

    The EJB Profile has a StateHolder (or "Detailed object", or "Value object"). The state holder is broken into 2 classes:
           ProfileStateReader
              contains only the get methods
           ProfileStateHolder extends ProfileStateReader:
              contains the get and set methods.

    ProfilStateReader getStateHolderRO(int isolationLevel)
                throws LockedException
        Get the EJB entity state holder for read only.
        If the EJB is already locked, the method may throw
        an exception or not, according to the isolation level.

    ProfilStateHolder getStateHolderRW()
                throws LockedException
        Get the EJB entity state holder and lock the object.
        If the object is already locked, the method throws an
        exception LockedException.

    setStateHolder(ProfileStateHolder profil)
        Change the EJB state and unlock the object.
        The client must have the lock ownership.


    Problem:
    --------
    What's happen if the client crash while the EJB is locked?
    The server must be able to remove the lock automatically.

    EJB Implementation 1: Store the lock
    -------------------------------------

    Add a column in the DB and store a '*' when the entity is locked.

      => What's happen if client who has the lock ownership
         crash?
    To solve this problem, you need to add another field containing the creation time of the lock and manage a timeout.

      => We want to manage long-lived locking on the server:
         so how can we define the time-out????

    A best solution is to use a Lease like in JINI or in the RMI DGC: lease the lock for a fixed duration. Then the client need to renew the Lease on a regular basis, before the expiration time.

    To manage the lease, you need to add a new remote method to the EJB: renew(). This method just reset the lock creation time. You need to write the code on the client to manage the lease, or write a manager (see Lease in JINI).


    EJB Implementation 2: use a locking service
    --------------------------------------------
    Create a RMI object to manage remote locks. The RMI server can create time-out and ping a client to check if he is still up (be ware about firewalls and proxies).

    This technical service extends the services of the J2EE plat-form. Locking service example exist in the CORBA specification.

    Important Remark: I did not try to implement this pattern.

    Any other idea?

    Tibo
    Valtech

    Threaded Messages (16)

  2. Tibo

    The pattern looks good. I have a question.
    Can you explain how you did the Lease part of RMI in greater detail. EJB 2.0 has a database locking feature for
    entity beans.

    -Balaji
  3. Balaji,

    I did not saw the DB lock feature of EJB 2.0, I will re-read the big spec !

    Else a few points about the Lease pattern:

    When a client need to allocate a resource on a server (a remote object, a lock, anything which use memory), the server must always be able to free the allocated resource even if the client crash.

    With RMI over JRMP if a client need a proxy, it receive a Lease: the server "rent" the resource to the client for a pre-defined duration (this value can be changed using a Java property).

    The client need to manage the Lease, and to renew it before it get expired: the client need to "ping" the remote resource on a regular basis.

    With RMI/JRMP you cannot control the Leases, everything is hidden. With JINI, the Lease is a specific interface, and you can control yourselves the remote resources, which is importat for JINI based systems which usualy include plug-and-play components.


    This pattern can be used to control resources such as a lock on an EJB server. The best solution is probably to externalize this code to a Lock Service implemented in a RMI object.

    Tibo.
  4. The design specified by u looks impressive. But i have question regarding this if we consider web scenerio. Suppose I have a module call candidate profile. I have a functional requirement that no body else should be able to view or update the profile when candidate himself is viewing/updating the profile. In this case get method of profile is called and the profile is locked and only can be unlock after set method for profile is called. In this case if the candidates closes his browser by mistake, the profile is locked but the candidate has no control on it. If he tries to view his profile, we won't be able to unless the specific time lapses and record is unlocked as specified by u.

    How can u achieve the required fucntionality and avoid this problem in this case???

    Gopesh
  5. Gopesh,

    You can:
     1- Remember the lock owner. The owner can get the StateHolder, even if the entity is locked.
     2- The lock need to have a time-out (see the message about Lease). The Lease allow short time-out.

    Tibo.
  6. My implementation of a pessimistic lock:

    public String ejbFindForUpdate(String skey) throws RemoteException, FinderException, LockedException {
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            ps = connection().prepareStatement(SQL_SELECT_FOR_UPDATE);
            ps.setString(1, skey);
            if(ps.executeUpdate() != 1) throw new ObjectNotFoundException();
        } catch (SQLException sqe) {
    if(sqe.getErrorCode() ==Constants.OBJ_IN_USE_CODE)
                    throw new LockedException(skey);
                else
                    throw new RuntimeException(sqe.getMessage());
        } finally {
            cleanup(ps, rs);
        }

        return skey;

    }
  7. yes, but what if you need your "select_for_update" includes a "join" ?

  8. Hi, I think your pattern is oke, but what happens when the server crashes. All the objects that are locked will still be locked after the server restarts (because the lock is stored in the db). How can I handle this?

    Wim Veninga.
  9. Long-Lived Pessimistic Transactions[ Go to top ]

       Server crash means all jdbc connections which are holding locks going to vanish.
  10. I have implemented the RMI version. You can register your
    RMI object with JNDI and look it up in a similar fashion
    as with an EJB. The main problem I see with this sort of
    thing is that if you get too many of these "special case"
    constructs going you may have maintenance difficulty.
  11. Hi,

    Sorry for adding more info, might be valuable though ;-) ...

    This problem has always been in transactional systems.

    Whatever lock you use, it must be associated to the current transaction.

    Let's admit that your primary key is a simple String. If it is not, concatenating its member (if members are of fixed length) will lead you to the single string hypothesis.

    Create in your RDBMS a table called APPLOCK, with only one column, the primary key, a String (as long as the longest primary key of your lockable Entity Beans PK2String).

    When you want to get an Entity Bean from the database (on ejbCreate for example), just to IN THE CURRENT TRANSACTION, insert the primary key string into the table APPLOCK. DO NOT COMMIT!

    When you want to release the lock, DELETE the primary key from that table. Normally at that point, the transaction should soon end.

    Now I explain.

    LOCKING: if someone has already inserted your key in APPLOC, you get on the INSERT, a DuplicateKey exception. EVEN IF THAT INSERT WAS NOT COMMITTED!!! This is a little breach in the Isolation property (remember ACID), but it avoids many errors. When you insert a row in a RDBMS, the index is locked in the current transaction, and any other thansaction that would read in that table would *not* see your row (uncommitted), but would get a Duplicated Key error when trying to insert that value...

    LOCK RELEASE: In most cases, the deletion of the key in APPLOCK should occur in that same transaction that performed the INSERT. If it did not, THEN you whould get a possibility of your lock to never be released.

    To avoid that, insert the expire time of the log (like currentTime+10min) in a second column of APPLOCK, and use a batch that deletes the old locks (and logs these deletions).

    If you release correctly the locks you set, the table should always be empty.

    OK, my solution should perhaps prefer short-lived Transactions.

    But it gives you one direction.

    For longer lived transactions, the locking should be visible from outside the transaction, the breach in Isolation is no longer sufficient. Use a Stateless EJB method that will use ANOTHER DataSource to the same (not necessary) database, checkint if the lock exists and is still valid, then inserting the lock in a table. In fact you should (to do it the right way) try to insert, if OK, go on. If fails, check the validity of the lock. If lock is not visible, then you can consider the object is locked but you cannot know (for next 10ms or so) by whom (the locker is inserting the lock, had not committed when you tried to read). Once the lock is inserted, commit (only) this INSERT.

    In that lock, there will be (still) the PK of the object (which is still the PK os the lock table), the time when the lock should expire, and application-wide data like username of the locker, reason code, whatever. Still in that stateless Session bean method, commit the transaction on that datasource (make sure this has no impact on the Container database. That Method should NOT run in the caller's transactional context. Attribute TX_NOT_SUPPORTED or TX_REQUIRES_NEW (if the datasource is known by the container) should be used.

    Hope I am clear enough (but impossible to draw here ;-)

    A++
    --
    Lionel MARTINEZ
  12. All patterns suggested here, using a database, are not satisfying. None of them solves the problem of a crashing client or a client just ended. The transaction solution has this problem:
    What, if a user gets data with one transaction (Web-Client in Html, connected to a servlet, servlet connected to an ejb-Server), editing data within an HTML-Form, and than send the data back over an servlet to the ejb-Server, wich tries to save the data within an _new_ transaction?

    The idea of an Locking-Service is the best one, I think, but not easy to develop.

    The __real__ problem is that the EJB standard does not give a standard-solution.
    Balaji wrote that the EJB-Standard 2.0 gives a solution.

    Can anybody post more information here?

    Bye, Frank
  13. Hi,

    Actually in this pattern, the main problem is to check that the lock is valid. I did not find any solution to check that the user's session is still alive. I looked for an API that could return the list of current user's sessions, or a way to list the active Statefull Session Beans instances. But I did not find any solution.

    Is there a way to do that, because this could solve the main issue of the pattern: lock timeout?

    Jacques

  14. To check if a client is still alive a nice solution come from JINI, which is use the concept of Lease.

    A Client session is a remote resource stored in the server memory. When the server send a proxy to a remote resource, the server "Rent" this resource for a fixed amount of time, such as 3 minutes. The client has to renew the Leases before expiration, such as after 2 minutes. Have a look on the JINI API spec to get a complete description of the Lease interfaces.

    Lease must be managed on the server side with a Lease Manager (a stateless sesison EJB, or a servlet).
    This design avoid the server to ping all the clients on a regular basis.
  15. I think the server should not ping the client or anything to check if the lease should be kept up.
    On the client a timer should run that can automatically trigger the refresh lease routine after a certain period.
    This could be combined with some user interaction after a number of automatic refreshes, like after an hour a messagebox could pop up, asking if the user still intends to update certain information.
    This would easily take care of client crashes or abandoned client sessions.
    To implement it in a rich client, it's fairly straight forward, however in thin clients it's also possible.
    For Internet Explorer, the XMLDSO object could be used with a timer javascript, it would be able to execute a jsp without refreshing the page, and it could check the returned XML for additional information related to the locks.
    For other thin clients, maybe a small applet would do this.

    regards,
    Balazs Fejes
  16. You talk about 'ping a client to check if he is still up', can you tell me how can I do that? I use a stand alone application in java?
    thanks
  17. What if a user is in the middle of editing his or her profile and is called away (for an unspecified amount)
    of time? This profile data would then be locked for that
    period of time? Is there a cascade effect to locking
    this resource ... e.g. is the profile data a single
    point of entry to other resources that need to be shared?
    Could it ever evolve into such a thing? What if
    the lock is never cleared for whatever reason?

    A simpler solution that avoids all of these issues
    (which are pitfalls) is to limit write access of the
    user's profile info to that user. Then create some
    sort of administrator user that has write access to
    to all the data profiles while allowing users to
    have an optimistic lock which would warn the admin
    he or she is about to change data that a user is
    working on.