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

RAIDb1_LPRF.java

00001 /**
00002  * C-JDBC: Clustered JDBC.
00003  * Copyright (C) 2002-2005 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.raidb1;
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.AbstractRequest;
00034 import org.objectweb.cjdbc.common.sql.SelectRequest;
00035 import org.objectweb.cjdbc.common.sql.StoredProcedure;
00036 import org.objectweb.cjdbc.common.xml.DatabasesXmlTags;
00037 import org.objectweb.cjdbc.controller.backend.DatabaseBackend;
00038 import org.objectweb.cjdbc.controller.cache.metadata.MetadataCache;
00039 import org.objectweb.cjdbc.controller.loadbalancer.policies.WaitForCompletionPolicy;
00040 import org.objectweb.cjdbc.controller.virtualdatabase.ControllerResultSet;
00041 import org.objectweb.cjdbc.controller.virtualdatabase.VirtualDatabase;
00042 
00043 /**
00044  * RAIDb-1 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. Write requests are broadcasted to all backends.
00050  * 
00051  * @author <a href="mailto:Emmanuel.Cecchet@inria.fr">Emmanuel Cecchet </a>
00052  * @version 1.0
00053  */
00054 public class RAIDb1_LPRF extends RAIDb1
00055 {
00056   // How the code is organized ?
00057   //
00058   // 1. Member variables
00059   // 2. Constructor(s)
00060   // 3. Request handling
00061   // 4. Debug/Monitoring
00062 
00063   /*
00064    * Constructors
00065    */
00066 
00067   /**
00068    * Creates a new RAIDb-1 Round Robin request load balancer.
00069    * 
00070    * @param vdb the virtual database this load balancer belongs to.
00071    * @param waitForCompletionPolicy how many backends must complete before
00072    *          returning the result?
00073    * @throws Exception if an error occurs
00074    */
00075   public RAIDb1_LPRF(VirtualDatabase vdb,
00076       WaitForCompletionPolicy waitForCompletionPolicy) throws Exception
00077   {
00078     super(vdb, waitForCompletionPolicy);
00079   }
00080 
00081   /*
00082    * Request Handling
00083    */
00084 
00085   /**
00086    * Selects the backend using a least pending request first policy. The backend
00087    * that has the shortest queue of currently executing queries is chosen to
00088    * execute this query.
00089    * 
00090    * @see org.objectweb.cjdbc.controller.loadbalancer.raidb1.RAIDb1#execReadRequest(SelectRequest,
00091    *      MetadataCache)
00092    */
00093   public ControllerResultSet execReadRequest(SelectRequest request,
00094       MetadataCache metadataCache) throws SQLException
00095   {
00096     return executeLPRF(request, true, "Request ", metadataCache);
00097   }
00098 
00099   /**
00100    * Selects the backend using a least pending request first policy. The backend
00101    * that has the shortest queue of currently executing queries is chosen to
00102    * execute this stored procedure.
00103    * 
00104    * @see org.objectweb.cjdbc.controller.loadbalancer.AbstractLoadBalancer#execReadOnlyReadStoredProcedure(StoredProcedure,
00105    *      MetadataCache)
00106    */
00107   public ControllerResultSet execReadOnlyReadStoredProcedure(
00108       StoredProcedure proc, MetadataCache metadataCache) throws SQLException
00109   {
00110     return executeLPRF(proc, false, "Stored procedure ", metadataCache);
00111   }
00112 
00113   /**
00114    * Common code to execute a SelectRequest or a StoredProcedure on a backend
00115    * chosen using a LPRF algorithm.
00116    * 
00117    * @param request a <code>SelectRequest</code> or
00118    *          <code>StoredProcedure</code>
00119    * @param isSelect true if it is a <code>SelectRequest</code>, false if it
00120    *          is a <code>StoredProcedure</code>
00121    * @param errorMsgPrefix the error message prefix, usually "Request " or
00122    *          "Stored procedure " ... failed because ...
00123    * @param metadataCache the metadataCache if any or null
00124    * @return a <code>ResultSet</code>
00125    * @throws SQLException if an error occurs
00126    */
00127   private ControllerResultSet executeLPRF(AbstractRequest request,
00128       boolean isSelect, String errorMsgPrefix, MetadataCache metadataCache)
00129       throws SQLException
00130   {
00131     // Choose a backend
00132     try
00133     {
00134       vdb.acquireReadLockBackendLists();
00135     }
00136     catch (InterruptedException e)
00137     {
00138       String msg = Translate.get(
00139           "loadbalancer.backendlist.acquire.readlock.failed", e);
00140       logger.error(msg);
00141       throw new SQLException(msg);
00142     }
00143 
00144     DatabaseBackend backend = null; // The
00145     // backend
00146     // that
00147     // will
00148     // execute
00149     // the
00150     // query
00151 
00152     // Note that vdb lock is released in the finally clause of this try/catch
00153     // block
00154     try
00155     {
00156       ArrayList backends = vdb.getBackends();
00157       int size = backends.size();
00158 
00159       if (size == 0)
00160         throw new SQLException(Translate.get(
00161             "loadbalancer.execute.no.backend.available", request.getId()));
00162 
00163       // Choose the backend that has the least pending requests
00164       int leastRequests = 0;
00165       for (int i = 0; i < size; i++)
00166       {
00167         DatabaseBackend b = (DatabaseBackend) backends.get(i);
00168         if (b.isReadEnabled())
00169         {
00170           int pending = b.getPendingRequests().size();
00171           if ((backend == null) || (pending < leastRequests))
00172           {
00173             backend = b;
00174             if (pending == 0)
00175               break; // Stop here we will never find a less loaded node
00176             else
00177               leastRequests = pending;
00178           }
00179         }
00180       }
00181 
00182       if (backend == null)
00183         throw new NoMoreBackendException(Translate.get(
00184             "loadbalancer.execute.no.backend.enabled", request.getId()));
00185     }
00186     catch (RuntimeException e)
00187     {
00188       String msg = Translate.get("loadbalancer.execute.find.backend.failed",
00189           new String[]{request.getSQLShortForm(vdb.getSQLShortFormLength()),
00190               e.getMessage()});
00191       logger.error(msg, e);
00192       throw new SQLException(msg);
00193     }
00194     finally
00195     {
00196       vdb.releaseReadLockBackendLists();
00197     }
00198 
00199     ControllerResultSet rs = null;
00200     // Execute the request on the chosen backend
00201     try
00202     {
00203       if (isSelect)
00204         rs = executeRequestOnBackend((SelectRequest) request, backend,
00205             metadataCache);
00206       else
00207         rs = executeStoredProcedureOnBackend((StoredProcedure) request,
00208             backend, metadataCache);
00209     }
00210     catch (UnreachableBackendException urbe)
00211     {
00212       // Try to execute query on different backend
00213       return executeLPRF(request, isSelect, errorMsgPrefix, metadataCache);
00214     }
00215     catch (SQLException se)
00216     {
00217       String msg = Translate.get("loadbalancer.something.failed", new String[]{
00218           errorMsgPrefix, String.valueOf(request.getId()), se.getMessage()});
00219       if (logger.isInfoEnabled())
00220         logger.info(msg);
00221       throw se;
00222     }
00223     catch (RuntimeException e)
00224     {
00225       String msg = Translate.get("loadbalancer.something.failed.on",
00226           new String[]{errorMsgPrefix,
00227               request.getSQLShortForm(vdb.getSQLShortFormLength()),
00228               backend.getName(), e.getMessage()});
00229       logger.error(msg, e);
00230       throw new SQLException(msg);
00231     }
00232 
00233     return rs;
00234   }
00235 
00236   /*
00237    * Debug/Monitoring
00238    */
00239 
00240   /**
00241    * Gets information about the request load balancer.
00242    * 
00243    * @return <code>String</code> containing information
00244    */
00245   public String getInformation()
00246   {
00247     // We don't lock since we don't need a top accurate value
00248     int size = vdb.getBackends().size();
00249 
00250     if (size == 0)
00251       return "RAIDb-1 Least Pending Request First load balancer: !!!Warning!!! No backend nodes found\n";
00252     else
00253       return "RAIDb-1 Least Pending Request First load balancer (" + size
00254           + " backends)\n";
00255   }
00256 
00257   /**
00258    * @see org.objectweb.cjdbc.controller.loadbalancer.raidb1.RAIDb1#getRaidb1Xml
00259    */
00260   public String getRaidb1Xml()
00261   {
00262     return "<" + DatabasesXmlTags.ELT_RAIDb_1_LeastPendingRequestsFirst + "/>";
00263   }
00264 
00265 }

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