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

RAIDb1_WRR.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): Julie Marguerite.
00023  */
00024 
00025 package org.objectweb.cjdbc.controller.loadbalancer.raidb1;
00026 
00027 import java.sql.SQLException;
00028 import java.util.ArrayList;
00029 import java.util.HashMap;
00030 
00031 import org.objectweb.cjdbc.common.exceptions.NoMoreBackendException;
00032 import org.objectweb.cjdbc.common.exceptions.UnreachableBackendException;
00033 import org.objectweb.cjdbc.common.i18n.Translate;
00034 import org.objectweb.cjdbc.common.sql.AbstractRequest;
00035 import org.objectweb.cjdbc.common.sql.SelectRequest;
00036 import org.objectweb.cjdbc.common.sql.StoredProcedure;
00037 import org.objectweb.cjdbc.common.xml.DatabasesXmlTags;
00038 import org.objectweb.cjdbc.controller.backend.DatabaseBackend;
00039 import org.objectweb.cjdbc.controller.cache.metadata.MetadataCache;
00040 import org.objectweb.cjdbc.controller.loadbalancer.WeightedBalancer;
00041 import org.objectweb.cjdbc.controller.loadbalancer.policies.WaitForCompletionPolicy;
00042 import org.objectweb.cjdbc.controller.virtualdatabase.ControllerResultSet;
00043 import org.objectweb.cjdbc.controller.virtualdatabase.VirtualDatabase;
00044 
00045 /**
00046  * RAIDb-1 Weighted Round Robin load balancer
00047  * <p>
00048  * The read requests coming from the request manager are sent to the backend
00049  * nodes using a weighted round robin. Write requests are broadcasted to all
00050  * backends.
00051  * 
00052  * @author <a href="mailto:Emmanuel.Cecchet@inria.fr">Emmanuel Cecchet </a>
00053  * @author <a href="mailto:Julie.Marguerite@inria.fr">Julie Marguerite </a>
00054  * @author <a href="mailto:Nicolas.Modrzyk@inrialpes.fr">Nicolas Modrzyk </a>
00055  * @version 1.0
00056  */
00057 public class RAIDb1_WRR extends RAIDb1
00058 {
00059   /*
00060    * How the code is organized ? 1. Member variables 2. Constructor(s) 3.
00061    * Request handling 4. Debug/Monitoring
00062    */
00063 
00064   private HashMap weights = new HashMap();
00065   private int     index;                  // index in the backend
00066                                                         // vector the
00067 
00068   // Round-Robin
00069 
00070   /*
00071    * Constructors
00072    */
00073 
00074   /**
00075    * Creates a new RAIDb-1 Weighted Round Robin request load balancer.
00076    * 
00077    * @param vdb the virtual database this load balancer belongs to.
00078    * @param waitForCompletionPolicy How many backends must complete before
00079    *                    returning the result?
00080    * @throws Exception if an error occurs
00081    */
00082   public RAIDb1_WRR(VirtualDatabase vdb,
00083       WaitForCompletionPolicy waitForCompletionPolicy) throws Exception
00084   {
00085     super(vdb, waitForCompletionPolicy);
00086     index = -1;
00087   }
00088 
00089   /*
00090    * Request Handling
00091    */
00092 
00093   /**
00094    * Selects the backend using a weighted round-robin algorithm and executes the
00095    * read request.
00096    * 
00097    * @see org.objectweb.cjdbc.controller.loadbalancer.raidb1.RAIDb1#execReadRequest(SelectRequest,
00098    *             MetadataCache)
00099    */
00100   public ControllerResultSet execReadRequest(SelectRequest request,
00101       MetadataCache metadataCache) throws SQLException
00102   {
00103     return executeWRR(request, true, "Request ", metadataCache);
00104   }
00105 
00106   /**
00107    * Selects the backend using a least pending request first policy. The backend
00108    * that has the shortest queue of currently executing queries is chosen to
00109    * execute this stored procedure.
00110    * 
00111    * @see org.objectweb.cjdbc.controller.loadbalancer.AbstractLoadBalancer#execReadOnlyReadStoredProcedure(StoredProcedure,
00112    *             MetadataCache)
00113    */
00114   public ControllerResultSet execReadOnlyReadStoredProcedure(
00115       StoredProcedure proc, MetadataCache metadataCache) throws SQLException
00116   {
00117     return executeWRR(proc, false, "Stored procedure ", metadataCache);
00118   }
00119 
00120   /**
00121    * Common code to execute a SelectRequest or a StoredProcedure on a backend
00122    * chosen using a weighted round-robin algorithm.
00123    * 
00124    * @param request a <code>SelectRequest</code> or
00125    *                    <code>StoredProcedure</code>
00126    * @param isSelect true if it is a <code>SelectRequest</code>, false if it
00127    *                    is a <code>StoredProcedure</code>
00128    * @param errorMsgPrefix the error message prefix, usually "Request " or
00129    *                    "Stored procedure " ... failed because ...
00130    * @param metadataCache a metadataCache if any or null
00131    * @return a <code>ResultSet</code>
00132    * @throws SQLException if an error occurs
00133    */
00134   private ControllerResultSet executeWRR(AbstractRequest request,
00135       boolean isSelect, String errorMsgPrefix, MetadataCache metadataCache)
00136       throws SQLException
00137   {
00138     // Choose a backend
00139     try
00140     {
00141       vdb.acquireReadLockBackendLists();
00142     }
00143     catch (InterruptedException e)
00144     {
00145       String msg = Translate.get(
00146           "loadbalancer.backendlist.acquire.readlock.failed", e);
00147       logger.error(msg);
00148       throw new SQLException(msg);
00149     }
00150 
00151     DatabaseBackend backend = null;
00152 
00153     // Note that vdb lock is released in the finally clause of this try/catch
00154     // block
00155     try
00156     {
00157       ArrayList backends = vdb.getBackends();
00158       int size = backends.size();
00159 
00160       if (size == 0)
00161         throw new SQLException(Translate.get(
00162             "loadbalancer.execute.no.backend.available", request.getId()));
00163 
00164       // Choose the backend (WRR algorithm starts here)
00165       int w = 0; // cumulative weight
00166       for (int i = 0; i < size; i++)
00167       {
00168         DatabaseBackend b = (DatabaseBackend) backends.get(index);
00169         if (b.isReadEnabled())
00170         {
00171           if (backend == null)
00172             backend = b; // Fallback if no backend found
00173 
00174           // Add the weight of this backend
00175           Integer weight = (Integer) weights.get(b.getName());
00176           if (weight == null)
00177             logger.error("No weight defined for backend " + b.getName());
00178           else
00179             w += weight.intValue();
00180 
00181           // Ok we reached the needed weight, take this backend
00182           if (index <= w)
00183           {
00184             backend = b;
00185             index++; // Next time take the next
00186             break;
00187           }
00188         }
00189       }
00190 
00191       if (backend == null)
00192         throw new NoMoreBackendException(Translate.get(
00193             "loadbalancer.execute.no.backend.enabled", request.getId()));
00194 
00195       // We are over the total weight and we are using the
00196       // first available node. Let's reset the index to 1
00197       // since we used this first node (0++).
00198       if (index > w)
00199         index = 1;
00200     }
00201     catch (RuntimeException e)
00202     {
00203       String msg = Translate.get("loadbalancer.execute.find.backend.failed",
00204           new String[]{request.getSQLShortForm(vdb.getSQLShortFormLength()),
00205               e.getMessage()});
00206       logger.error(msg, e);
00207       throw new SQLException(msg);
00208     }
00209     finally
00210     {
00211       vdb.releaseReadLockBackendLists();
00212     }
00213 
00214     ControllerResultSet rs = null;
00215     // Execute the request on the chosen backend
00216     try
00217     {
00218       if (isSelect)
00219         rs = executeRequestOnBackend((SelectRequest) request, backend,
00220             metadataCache);
00221       else
00222         rs = executeStoredProcedureOnBackend((StoredProcedure) request,
00223             backend, metadataCache);
00224     }
00225     catch (UnreachableBackendException urbe)
00226     {
00227       // Try to execute query on different backend
00228       return executeWRR(request, isSelect, errorMsgPrefix, metadataCache);
00229     }
00230     catch (SQLException se)
00231     {
00232       String msg = Translate.get("loadbalancer.something.failed", new String[]{
00233           errorMsgPrefix, String.valueOf(request.getId()), se.getMessage()});
00234       if (logger.isInfoEnabled())
00235         logger.info(msg);
00236       throw se;
00237     }
00238     catch (RuntimeException e)
00239     {
00240       String msg = Translate.get("loadbalancer.something.failed.on",
00241           new String[]{errorMsgPrefix,
00242               request.getSQLShortForm(vdb.getSQLShortFormLength()),
00243               backend.getName(), e.getMessage()});
00244       logger.error(msg, e);
00245       throw new SQLException(msg);
00246     }
00247 
00248     return rs;
00249   }
00250 
00251   /*
00252    * Backends management
00253    */
00254 
00255   /**
00256    * @see org.objectweb.cjdbc.controller.loadbalancer.AbstractLoadBalancer#setWeight(String,
00257    *             int)
00258    */
00259   public void setWeight(String name, int w) throws SQLException
00260   {
00261     if (logger.isDebugEnabled())
00262       logger.debug(Translate.get("loadbalancer.weight.set", new String[]{
00263           String.valueOf(w), name}));
00264 
00265     weights.put(name, new Integer(w));
00266   }
00267 
00268   /*
00269    * Debug/Monitoring
00270    */
00271 
00272   /**
00273    * Gets information about the request load balancer.
00274    * 
00275    * @return <code>String</code> containing information
00276    */
00277   public String getInformation()
00278   {
00279     // We don't lock since we don't need a top accurate value
00280     int size = vdb.getBackends().size();
00281 
00282     if (size == 0)
00283       return "RAIDb-1 with Weighted Round Robin Request load balancer: !!!Warning!!! No backend nodes found\n";
00284     else
00285       return "RAIDb-1 Weighted Round-Robin Request load balancer (" + size
00286           + " backends)\n";
00287   }
00288 
00289   /**
00290    * @see org.objectweb.cjdbc.controller.loadbalancer.raidb1.RAIDb1#getRaidb1Xml
00291    */
00292   public String getRaidb1Xml()
00293   {
00294     return WeightedBalancer.getRaidbXml(weights,
00295         DatabasesXmlTags.ELT_RAIDb_1_WeightedRoundRobin);
00296   }
00297 
00298 }

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