Main Page | Packages | Class Hierarchy | Alphabetical List | Class List | File List | Class Members | Related Pages

RAIDb2_LPRF.java

00001 /**
00002  * C-JDBC: Clustered JDBC.
00003  * Copyright (C) 2002-2004 French National Institute For Research In Computer
00004  * Science And Control (INRIA).
00005  * Contact: c-jdbc@objectweb.org
00006  * 
00007  * This library is free software; you can redistribute it and/or modify it
00008  * under the terms of the GNU Lesser General Public License as published by the
00009  * Free Software Foundation; either version 2.1 of the License, or any later
00010  * version.
00011  * 
00012  * This library is distributed in the hope that it will be useful, but WITHOUT
00013  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00014  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
00015  * for more details.
00016  * 
00017  * You should have received a copy of the GNU Lesser General Public License
00018  * along with this library; if not, write to the Free Software Foundation,
00019  * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
00020  *
00021  * Initial developer(s): Emmanuel Cecchet.
00022  * Contributor(s): ______________________.
00023  */
00024 
00025 package org.objectweb.cjdbc.controller.loadbalancer.raidb2;
00026 
00027 import java.sql.SQLException;
00028 import java.util.ArrayList;
00029 
00030 import org.objectweb.cjdbc.common.exceptions.NoMoreBackendException;
00031 import org.objectweb.cjdbc.common.exceptions.UnreachableBackendException;
00032 import org.objectweb.cjdbc.common.i18n.Translate;
00033 import org.objectweb.cjdbc.common.sql.SelectRequest;
00034 import org.objectweb.cjdbc.common.sql.StoredProcedure;
00035 import org.objectweb.cjdbc.common.xml.DatabasesXmlTags;
00036 import org.objectweb.cjdbc.controller.backend.DatabaseBackend;
00037 import org.objectweb.cjdbc.controller.cache.metadata.MetadataCache;
00038 import org.objectweb.cjdbc.controller.loadbalancer.policies.WaitForCompletionPolicy;
00039 import org.objectweb.cjdbc.controller.loadbalancer.policies.createtable.CreateTablePolicy;
00040 import org.objectweb.cjdbc.controller.virtualdatabase.ControllerResultSet;
00041 import org.objectweb.cjdbc.controller.virtualdatabase.VirtualDatabase;
00042 
00043 /**
00044  * RAIDb-2 Round Robin load balancer featuring (Least Pending Requests First
00045  * load balancing algorithm).
00046  * <p>
00047  * The read requests coming from the request manager are sent to the node that
00048  * has the Least pending read requests among the nodes that can execute the
00049  * request.
00050  * 
00051  * @author <a href="mailto:Emmanuel.Cecchet@inria.fr">Emmanuel Cecchet </a>
00052  * @version 1.0
00053  */
00054 public class RAIDb2_LPRF extends RAIDb2
00055 {
00056 
00057   /*
00058    * How the code is organized ? 1. Member variables 2. Constructor(s) 3.
00059    * Request handling 4. Debug/Monitoring
00060    */
00061 
00062   /*
00063    * Constructors
00064    */
00065 
00066   /**
00067    * Creates a new RAIDb-2 Round Robin request load balancer.
00068    * 
00069    * @param vdb the virtual database this load balancer belongs to.
00070    * @param waitForCompletionPolicy how many backends must complete before
00071    *          returning the result?
00072    * @param createTablePolicy the policy defining how 'create table' statements
00073    *          should be handled
00074    * @exception Exception if an error occurs
00075    */
00076   public RAIDb2_LPRF(VirtualDatabase vdb,
00077       WaitForCompletionPolicy waitForCompletionPolicy,
00078       CreateTablePolicy createTablePolicy) throws Exception
00079   {
00080     super(vdb, waitForCompletionPolicy, createTablePolicy);
00081   }
00082 
00083   /*
00084    * Request Handling
00085    */
00086 
00087   /**
00088    * Chooses the node to execute the request using a round-robin algorithm. If
00089    * the next node has not the tables needed to execute the requests, we try the
00090    * next one and so on until a suitable backend is found.
00091    * 
00092    * @param request an <code>SelectRequest</code>
00093    * @param metadataCache cached metadata to use to construct the result set
00094    * @return the corresponding <code>java.sql.ResultSet</code>
00095    * @exception SQLException if an error occurs
00096    * @see org.objectweb.cjdbc.controller.loadbalancer.raidb2.RAIDb2#execReadRequest(SelectRequest,
00097    *      MetadataCache)
00098    */
00099   public ControllerResultSet execReadRequest(SelectRequest request,
00100       MetadataCache metadataCache) throws SQLException
00101   {
00102     // Choose a backend
00103     try
00104     {
00105       vdb.acquireReadLockBackendLists();
00106     }
00107     catch (InterruptedException e)
00108     {
00109       String msg = Translate.get(
00110           "loadbalancer.backendlist.acquire.readlock.failed", e);
00111       logger.error(msg);
00112       throw new SQLException(msg);
00113     }
00114 
00115     DatabaseBackend backend = null; // The backend that will execute the query
00116 
00117     // Note that vdb lock is released in the finally clause of this try/catch
00118     // block
00119     try
00120     {
00121       ArrayList backends = vdb.getBackends();
00122       int size = backends.size();
00123 
00124       if (size == 0)
00125         throw new SQLException(Translate.get(
00126             "loadbalancer.execute.no.backend.available", request.getId()));
00127 
00128       // Choose the backend that has the least pending requests
00129       int leastRequests = 0;
00130       int enabledBackends = 0;
00131       ArrayList tables = request.getFrom();
00132 
00133       for (int i = 0; i < size; i++)
00134       {
00135         DatabaseBackend b = (DatabaseBackend) backends.get(i);
00136         if (b.isReadEnabled())
00137         {
00138           enabledBackends++;
00139           if (b.hasTables(tables))
00140           {
00141             int pending = b.getPendingRequests().size();
00142             if (((backend == null) || (pending < leastRequests)))
00143             {
00144               backend = b;
00145               if (pending == 0)
00146                 break; // Stop here we will never find a less loaded node
00147               else
00148                 leastRequests = pending;
00149             }
00150           }
00151         }
00152       }
00153 
00154       if (backend == null)
00155       {
00156         if (enabledBackends == 0)
00157           throw new NoMoreBackendException(Translate.get(
00158               "loadbalancer.execute.no.backend.enabled", request.getId()));
00159         else
00160           throw new SQLException(Translate.get(
00161               "loadbalancer.backend.no.required.tables", tables.toString()));
00162       }
00163 
00164     }
00165     catch (RuntimeException e)
00166     {
00167       String msg = Translate.get("loadbalancer.request.failed.on.backend",
00168           new String[]{request.getSQLShortForm(vdb.getSQLShortFormLength()),
00169               backend.getName(), e.getMessage()});
00170       logger.error(msg, e);
00171       throw new SQLException(msg);
00172     }
00173     finally
00174     {
00175       vdb.releaseReadLockBackendLists();
00176     }
00177 
00178     // Execute the request on the chosen backend
00179     ControllerResultSet rs = null;
00180     try
00181     {
00182       rs = executeRequestOnBackend(request, backend, metadataCache);
00183     }
00184     catch (UnreachableBackendException se)
00185     {
00186       // Try on another backend
00187       return execReadRequest(request, metadataCache);
00188     }
00189     catch (SQLException se)
00190     {
00191       String msg = Translate.get("loadbalancer.request.failed", new String[]{
00192           String.valueOf(request.getId()), se.getMessage()});
00193       if (logger.isInfoEnabled())
00194         logger.info(msg);
00195       throw new SQLException(msg);
00196     }
00197     catch (RuntimeException e)
00198     {
00199       String msg = Translate.get("loadbalancer.request.failed.on.backend",
00200           new String[]{request.getSQLShortForm(vdb.getSQLShortFormLength()),
00201               backend.getName(), e.getMessage()});
00202       logger.error(msg, e);
00203       throw new SQLException(msg);
00204     }
00205 
00206     return rs;
00207   }
00208 
00209   /**
00210    * Chooses the node to execute the stored procedure using a LPRF algorithm. If
00211    * the next node has not the needed stored procedure, we try the next one and
00212    * so on until a suitable backend is found.
00213    * 
00214    * @see org.objectweb.cjdbc.controller.loadbalancer.AbstractLoadBalancer#execReadOnlyReadStoredProcedure(StoredProcedure,
00215    *      MetadataCache)
00216    */
00217   public ControllerResultSet execReadOnlyReadStoredProcedure(
00218       StoredProcedure proc, MetadataCache metadataCache) throws SQLException
00219   {
00220     // Choose a backend
00221     try
00222     {
00223       vdb.acquireReadLockBackendLists();
00224     }
00225     catch (InterruptedException e)
00226     {
00227       String msg = Translate.get(
00228           "loadbalancer.backendlist.acquire.readlock.failed", e);
00229       logger.error(msg);
00230       throw new SQLException(msg);
00231     }
00232 
00233     DatabaseBackend backend = null; // The backend that will execute the query
00234 
00235     // Note that vdb lock is released in the finally clause of this try/catch
00236     // block
00237     try
00238     {
00239       DatabaseBackend failedBackend = null;
00240       SQLException failedException = null;
00241       ControllerResultSet rs = null;
00242       do
00243       {
00244         ArrayList backends = vdb.getBackends();
00245         int size = backends.size();
00246 
00247         if (size == 0)
00248           throw new SQLException(Translate.get(
00249               "loadbalancer.execute.no.backend.available", proc.getId()));
00250 
00251         // Choose the backend that has the least pending requests
00252         int leastRequests = 0;
00253         int enabledBackends = 0;
00254 
00255         for (int i = 0; i < size; i++)
00256         {
00257           DatabaseBackend b = (DatabaseBackend) backends.get(i);
00258           if (b.isReadEnabled())
00259           {
00260             enabledBackends++;
00261             if (b.hasStoredProcedure(proc.getProcedureName()))
00262             {
00263               int pending = b.getPendingRequests().size();
00264               if ((b != failedBackend)
00265                   && ((backend == null) || (pending < leastRequests)))
00266               {
00267                 backend = b;
00268                 if (pending == 0)
00269                   break; // Stop here we will never find a less loaded node
00270                 else
00271                   leastRequests = pending;
00272               }
00273             }
00274           }
00275         }
00276 
00277         if (backend == null)
00278         {
00279           if (enabledBackends == 0)
00280             throw new SQLException(Translate
00281                 .get("loadbalancer.storedprocedure.backend.no.enabled", proc
00282                     .getId()));
00283           else if (failedBackend == null)
00284             throw new SQLException(Translate.get(
00285                 "loadbalancer.backend.no.required.storedprocedure", proc
00286                     .getProcedureName()));
00287           else
00288             // Bad query, the only backend that could execute it has failed
00289             throw failedException;
00290         }
00291 
00292         // Execute the request on the chosen backend
00293         boolean toDisable = false;
00294         try
00295         {
00296           rs = executeStoredProcedureOnBackend(proc, backend, metadataCache);
00297           if (failedBackend != null)
00298           { // Previous backend failed
00299             if (logger.isWarnEnabled())
00300               logger.warn(Translate.get("loadbalancer.storedprocedure.status",
00301                   new String[]{String.valueOf(proc.getId()), backend.getName(),
00302                       failedBackend.getName()}));
00303             toDisable = true;
00304           }
00305         }
00306         catch (UnreachableBackendException se)
00307         {
00308           // Retry on an other backend.
00309           continue;
00310         }
00311         catch (SQLException se)
00312         {
00313           if (failedBackend != null)
00314           { // Bad query, no backend can execute it
00315             String msg = Translate.get(
00316                 "loadbalancer.storedprocedure.failed.twice", new String[]{
00317                     String.valueOf(proc.getId()), se.getMessage()});
00318             if (logger.isInfoEnabled())
00319               logger.info(msg);
00320             throw new SQLException(msg);
00321           }
00322           else
00323           { // We are the first to fail on this query
00324             failedBackend = backend;
00325             failedException = se;
00326             if (logger.isInfoEnabled())
00327               logger.info(Translate.get(
00328                   "loadbalancer.storedprocedure.failed.on.backend",
00329                   new String[]{
00330                       proc.getSQLShortForm(vdb.getSQLShortFormLength()),
00331                       backend.getName(), se.getMessage()}));
00332             continue;
00333           }
00334         }
00335 
00336         if (toDisable)
00337         { // retry has succeeded and we need to disable the first node that
00338           // failed
00339           try
00340           {
00341             if (logger.isWarnEnabled())
00342               logger.warn(Translate.get("loadbalancer.backend.disabling",
00343                   failedBackend.getName()));
00344             disableBackend(failedBackend);
00345           }
00346           catch (SQLException ignore)
00347           {
00348           }
00349           finally
00350           {
00351             failedBackend = null; // to exit the do{}while
00352           }
00353         }
00354       }
00355       while (failedBackend != null);
00356       return rs;
00357     }
00358     catch (RuntimeException e)
00359     {
00360       String msg = Translate.get(
00361           "loadbalancer.storedprocedure.failed.on.backend", new String[]{
00362               proc.getSQLShortForm(vdb.getSQLShortFormLength()),
00363               backend.getName(), e.getMessage()});
00364       logger.fatal(msg, e);
00365       throw new SQLException(msg);
00366     }
00367     finally
00368     {
00369       vdb.releaseReadLockBackendLists();
00370     }
00371   }
00372 
00373   /*
00374    * Debug/Monitoring
00375    */
00376 
00377   /**
00378    * Gets information about the request load balancer.
00379    * 
00380    * @return <code>String</code> containing information
00381    */
00382   public String getInformation()
00383   {
00384     // We don't lock since we don't need a completely accurate value
00385     int size = vdb.getBackends().size();
00386 
00387     if (size == 0)
00388       return "RAIDb-2 Least Pending Requests First load balancer: !!!Warning!!! No backend nodes found\n";
00389     else
00390       return "RAIDb-2 Least Pending Requests First load balancer (" + size
00391           + " backends)\n";
00392   }
00393 
00394   /**
00395    * @see org.objectweb.cjdbc.controller.loadbalancer.raidb2.RAIDb2#getRaidb2Xml
00396    */
00397   public String getRaidb2Xml()
00398   {
00399     return "<" + DatabasesXmlTags.ELT_RAIDb_2_LeastPendingRequestsFirst + "/>";
00400   }
00401 }

Generated on Mon Apr 11 22:01:33 2005 for C-JDBC by  doxygen 1.3.9.1