Documents
Resources
Learning Center
Upload
Plans & pricing Sign in
Sign Out
Get this document free

Offline Concurrency Patterns

VIEWS: 4 PAGES: 27

									Offline Concurrency Patterns




         M.Fowler, Chapter 16, “Enterprise Patterns”
    Learning Objectives
   Pessimistic Offline Lock
        Three phases implement
           •   Lock choosing
           •   Building a lock manager
           •   Defining protocol
        When to use it
        Example
   Optimistic Offline Lock
        UPDATE optimistic check
        When to use it
   Coarse-Grained Lock
        How its works
        Locking a shared version
        Locking the root
        Disadvantages
   Implicit Lock
        How its works
        A word of caution
              Pessimistic Offline Lock
      Prevent conflict between concurrent business transactions by allowing
       only one business transaction at a time to access data.

                  Martin’s Session                 Database                  David’s Session



                               getCustomer (129)


                                                                                               System
                               return customer 129                                             Transaction
                                                                                               Boundary
                                                              getCustomer (129)


                                edit customer
                                                          Error : customer locked
Business
Transaction
Boundary
    Pessimistic Offline Lock (cont)

Pessimistic Offline Lock prevent conflicts by avoiding them altogether.
It force a business transaction to acquire a lock on a piece of data before
it starts to use it.
Once you begin a business transaction you can be pretty sure it will end
without being bounced by concurrency control.



Implementation of Pessimistic Offline Lock in three phases :
     Determining what type of lock you need

     Building a lock manager

     Defining procedures for a business transaction to use lock
    Lock choosing
   Lock types
      Exclusive write lock – acquire lock in order to edit session data.
      Exclusive read lock – acquire a lock simply to load the record.
      Read/Write lock – combines the two lock types.

     The relationship of the read and write locks is the key to getting the
     best of both worlds:
             Read and write locks are mutually exclusive.
             Concurrent read locks are acceptable.


   Choosing the correct lock type
     -   Maximizing system concurrency
     -   Meeting business needs
     -   Minimizing code complexity
     Building a lock manager

The lock manager’s job is to grant or deny any request by a
business transaction to acquire or release a lock.
The manager is simply a table that maps locks to owners.

   A simple one might wrap an in-memory hash table or it might
    be a database table.
   You must have one and only one lock table (can use a singleton).
   The lock should remain private to the lock manager.
   Business transactions should interact only with the lock manager.
     Defining protocol
Defining the protocol according to which a business transaction
must use the lock manager.

   The protocol has to include :
      What to lock.- locking the ID (or the key)
      When to lock – before loading the data (getting the latest version of the locked item)
      When to release a lock – when the business transaction completes.
      How to act when a lock can’t be acquired – abort (the easiest course of action)
                the user should find this acceptable since it happen early in the transaction.
   When to use Pessimistic Offline Lock

Pessimistic Offline Lock is appropriate when :
       The chance of conflict between concurrent session is high.
        (a user should never have to throw away work).
       The cost of a conflict is too high regardless of its likelihood.

Don’t use these techniques if your business transactions fit within a
single system transaction (many system transaction pessimistic locking
techniques ship with the application and database server you're already
using – “SELECT FOR UPDATE” SQL).

Locking every entity in a system will almost surly create tremendous
data contention problems, so use it when it’s truly required.
       Example : Simple Lock Manager
table lock
   create table lock (lockableid bigint primary key, ownerid bigint)

class ExclusiveReadLockManagerDBImpl implements ExclusiveLockManager...
       private static final String INSERT_SQL =
                   "insert into lock values(?,?)";
       private static final DELETE_SINGLE_SQL =
                   "delete from lock where lockableid = ? and ownerid = ?";
       private static final DELETE_ALL_SQL =
                   "delete from lock where ownerid = ? ";
       private static final String CHECK_SQL =
                   "select lockableid from lock where lockableid = ? and ownerid = ? ";
       public void acquireLock (Long lockable, String owner) throws ConcurrencyException{
       if (!hasLock (lockable,owner)) {
                   connection conn = null ;
                   PreparedStatement pstmt = null;
                   try
                   {
                                      conn = ConnectionManager.INSTANCE.getConnection();
                                      pstmt = conn.prepareStatment(INSERT_SQL);
                                      pstmt.setLong (1, lockable.longValue());
                                      pstmt.setString (2, owner);
                                      pstmt.executeUpdate();
                   }catch (SQLException sqlEx) {
                                      throw new ConcurrencyException ("unable to lock " + lockable);
                   }finally {
                                      closeDBResources (conn,pstmt);
                   }
       }}
       Example : Simple Lock Manager (cont)
public void releaseLock (Long lockable, String owner){
       Connection conn = null ;
       PreparedStatement pstmt = null ;
       try {
                  conn = ConnectionManager.INSTANCE.getConnection();
                  pstmt = conn.prepareStatment(DELETE_SINGLE_SQL);
                  pstmt.setLong (1, lockable.longValue());
                  pstmt.setString (2, owner);
                  pstmt.executeUpdate();
       }catch (SQLException sqlEx) {
                  throw new ConcurrencyException ("unexpected error releasing lock on " + lockable);
       }finally {
                  closeDBResources (conn,pstmt);
}}



class AppSession...                                                        class AppSessionManager...
       private String user ;                                                      private static ThreadLocal current = new ThreadLocal();
       private String id ;                                                        public static AppSession getSession(){
       private IdentityMap imap ;                                                             return (AppSession) current.get();
       public AppSession (String user, String id, IdentityMap imap){              }
                   this.user = user ;                                             public static void setSession (AppSession session){
                   this.id = id ;                                                             current.set(session);
                   this.imap = imap ;                                             }
       }
       Example : Simple Lock Manager (cont)
interface Command...
       public void init(HttpServletRequest req, HttpServletResponse rsp){
       public void process() throws Exception;

abstract class BusinessTransactionCommand implements Command...
       public void init (HttpServletRequest req, HttpServletResponse rsp){
                  this.req = req ;
                  this.rsp = rsp ;
       }
       protected void startNewBusinessTransaction(){
                  HttpSession httpSession = getReq().getSession(true);
                  AppSession appSession = (AppSession)httpSession.getAttribute(APP_SESSION);
                  if(appSession != null) {
                        ExclusiveReadLockManager.INSTANCE.relaseAllLocks(appSession.getId());
                  }
                  appSession = new AppSession(getReq().getRemoteUser(),httpSession.getId(), new IdentityMap());
                  AppSessionManager.setSession(appSession);
                  httpSession.setAttribute(APP_SESSION,appSession);
                  httpSession.setAttribute(LOCK_REMOVER, new LockRemover (appSession.getId()));
       }
       protected void continueBusinessTransaction(){
                  HttpSession httpSession = getReq().getSession();
                  AppSession appSession = (AppSession)httpSession.getAttribute(APP_SESSION);
                  AppSessionManager.setSession(appSession);
       }
       protected HttpServletRequest getReq(){
                  return req ;}
       protected HttpServletResponse getRsp(){
                  return rsp ;}
       Example : Simple Lock Manager (cont)
class LockRemover implements HttpSessionBindingListener...
       private String sessionId ;
       public LockRemover(String sessionId){
                   this.sessionId = sessionId ;
       }
       public void valueUnbound (HttpSessionBindingEvent event){
                   try {
                        beginSystemTransaction();
                        ExclusiveReadLockManager.INSTANCE.relaseAllLocks(this.sessionId);
                        commitSystemTransaction();
                   } catch (Exception e) {
                        handleSeriousError(e);
       }           }


class TransactionalCommand implements Command...
       public TransactionalCommand(Command impl){
                  this.impl = impl ;
       }
       public void process() throws Exception {
                  beginSystemTransaction();
                  try {
                       impl.process();
                       commitSystemTransaction();
                  } catch (Exception e) {
                       rollbackSystemTransaction():
                       throw e ;
       }          }
      Example : Simple Lock Manager (cont)
class ControllerServlet extends HttpServlet...

     protected void doGet(HttpServletRequest req. HttpServletResponse rsp)
               throws ServletException, IOException {
        try {
               String cmdName = req.getParameter("command") ;
               Command cmd = getCommand(cmdName);
               cmd.init(req,rsp);
               cmd.process();
        } catch (Exception e) {
               writeException(e, rsp.getWriter());
        }
     }
     private Command getCommand (String name) {
        try {
               String className = (String) command.get(name);
               Command cmd = (Command) Class.forName(className).newInstance();
               return new TransactionalCommand(cmd);
        } catch (Exception e) {
               e.printStackTrace ();
               throw new SystemExcaption("unable to create command object for "+ name);
     }}
      Example : Simple Lock Manager (cont)
class EditCustomerCommand implements Command...

     public void process() throws Exception {
                startNewBusinessTransaction();
                Long customerId = new Long(getReq().getParameter("customer_id"));
                ExclusiveReadLockManager.INSTANCE.acquireLock(customerId, AppSessionManager.getSession().getId());
                Mapper customerMapper = MapperRegistry.Instance.getMapper(Customer.class);
                Customer customer = (Customer) customerMapper.find(customerId);
                getReq().getSession().setAttribute("customer",customer);
                forword ("/editCustomer.jsp");
     }

class SaveCustomerCommand implements Command...

     public void process() throws Exception {
                continueBusinessTransaction();
                Customer customer = (Customer) getReq().getSession().getAttribute("customer");
                String name = getReq().getParameter("customerName");
                customer.setName(name);
                Mapper customerMapper = MapperRegistry.Instance.getMapper(Customer.class);
                customerMapper.update(customer) ;
                ExclusiveReadLockManager.INSTANCE.releaseLock(customer.getId(), AppSessionManager.getSession().getId());
                forword ("/customerSaved.jsp");
     }
              Optimistic Offline Lock
       Prevent conflict between concurrent business transactions by detecting
        a conflict and rolling back the transaction.

                         Martin’s Session                Database                     David’s Session
                                     getCustomer (129)

                                                                                                           System
                                     return customer 129                                                   Transaction
                                                                                                           Boundary
                                                                    getCustomer (129)

                                      edit customer
                                                                return customer 129
                                                                                                  edit customer

                                                                    update customer 129
Business
Transaction
Boundary                             update customer 129
                                                                                                   Business
              rollback                                                                             Transaction
                                   Failure: wrong customer version
                                                                                                   Boundary
    Optimistic Offline Lock (cont)

   Optimistic Offline Lock is obtained by validating that, in the time since a
    session loaded a record, another session hasn’t altered it.

   The most common implementation is to associate a version number with each
    record in your system (optional information : who last modified and when).

   The idea is to compare the version stored in your session data to the current
    version in the record data.

   Once the verification succeeds, all changes, including an increment of the
    version, can be committed.

   The version increment is what prevent inconsistent record data, as a session
    with an old version can’t acquire the lock.
    UPDATE optimistic check
   With an RDBMS data store the verification is a matter of adding the version
    number to the criteria of any SQL statements used to update or delete a record.
   A single SQL statement can both acquire the lock and update the record data.
   The final step is for the business transaction to inspect the row count returned
    by the SQL execution (row count 1 indicates success, 0 failure)

    Session                           Database
                                 SELECT
              Customer

                                                          System
              edit Customer                               Transaction
                                                          Boundary
                                 UPDATE
              Customer
              return row count
                                                 UPDATE Customer
          Check row count is 1                   SET…, version=(session’s copy of version + 1)
                                                 WHERE id = ? AND version = session’s copy of version
      When to use Optimistic Offline Lock

   Optimistic concurrency management is appropriate when the chance of
    conflict between any two business transactions is low.
   If conflicts are likely it’s not user friendly to announce one only when the
    user has finished his work and is ready to commit (eventually he’ll assume
    the failure of business transaction and stop using the system).

As optimistic locking is much easier to implement and not prone to the same
defects and runtime errors as a Pessimistic Offline Lock, consider using it as a
default approach to business transaction conflict management.
        Coarse-Grained Lock
     Lock a set of related object with a single lock




                         Customer
                              1
                                                   1          1
                                                                     Lock
                                       *
                                    Address



Object can often be edited as a group. Perhaps you have a customer and its set of addresses.
When using the application it make Sense to lock all of these items if you want to
lock any one of them.
       How It Works

 A Coarse-Grained Lock is a single lock that covers many objects.
 It not only simplifies the locking action itself but also frees you from
 having to load all the members of a group in order to lock them.


First step : Create a single point of contention for locking a group of objects.
         (this makes only one lock necessary for locking the entire set).

Second step : Provide the shortest path possible to finding that single lock point
        (in order to minimize the group members that must be identified and
        possibly loaded into memory in the process of obtaining that lock).
   Locking a shared version
Sharing a version
                  Customer            *                   1

                      1                           Version
                                            getValue()
                                            Increment()
                      *
                   Address                                1
                                      *
             (version = customer.version)



Locking a shared version
                 Customer            *
                                                      1
                  1                                           1   1   Pessimistic
                                                Version
                  *                                                      Lock

                  Address                             1
                                     *
            (version = customer.version)
      Locking the root

Using a root lock as a Coarse-Grained Lock makes it necessary to implement
navigation to the root in your object graph.
For example, in hierarchy the obvious root is the top level parent. To which you
can link the descendents directly.

                                      1               1
                    Customer                                  Lock
                         1

         root                     *
                               Address                    boundary



                        Locking the root
      Disadvantages

The shared lock and the root lock implementations of Coarse-Grained Lock
Both have their trade-offs.

   When using a relational database the shared lock carries the burden that
    almost all of your selects will require a join to the version table.

   Loading objects while navigating to the root can make a performance
    problems.
   Implicit Lock
 Allows framework or layer supertype code to acquire offline locks.



                 Business                                     Lock
                                      Framework
                Transaction                                  Manager


                          load customer
                                             lock customer

                                                  success

                         return customer
    Implicit Lock (cont)

The key of locking scheme is that there are no gaps in its use:
   Forgetting to write a single line of code that acquires a lock can render an
    entire offline locking scheme useless.
   Failing to retrieve a read lock means you might not get up-to-date
    session data.
   Failing to use a version count properly can result in unknowingly writing over
    someone’s changes.
   If an item might be locked anywhere it must be lock everywhere.


Because offline concurrency management is difficult to test, such errors
might go undetected by all of your test suites.
    Implicit Lock (cont)

   One solution is not allow developers to make such a mistake.
    Locking task that cannot be overlooked should be handled not
    explicitly by developers but implicitly by the application.

   Implementing Implicit Lock is a matter of factoring your code such
    that any locking mechanics that absolutely cannot be skipped can be
    carried out by your application framework.

   The fact that most enterprise application make use of frameworks,
    Layer Supertypes provide us opportunity to facilitate Implicit Lock.
    How it works
   First step :
        Assemble a list of what tasks are mandatory for a business transaction to work
         within your locking strategy.
         (the Pessimistic Offline Lock list will include items along the lines of acquiring any
         lock necessary to load a piece of data)
   Second step:
        These lock types most greatly limit system concurrency.
         We have to make sure that locks necessary for writing are acquired before
         changes are committed.

A word of caution about using the Implicit Lock :
     While it allows developers to ignore much of the locking mechanics it doesn’t
     allow them to ignore consequences.
     The danger with Implicit Lock is that business transactions can fail in unexpected
     ways once developers stop thinking about locking

								
To top