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

RAIDb2_RR.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.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.
00045  * <p>
00046  * The read requests coming from the request manager are sent in a round robin
00047  * to the backend nodes. Write requests are broadcasted to all backends.
00048  * 
00049  * @author <a href="mailto:Emmanuel.Cecchet@inria.fr">Emmanuel Cecchet </a>
00050  * @author <a href="mailto:Julie.Marguerite@inria.fr">Julie Marguerite </a>
00051  * @version 1.0
00052  */
00053 public class RAIDb2_RR extends RAIDb2
00054 {
00055   /*
00056    * How the code is organized ? 1. Member variables 2. Constructor(s) 3.
00057    * Request handling 4. Debug/Monitoring
00058    */
00059 
00060   private int index; // index in the backend vector the Round-Robin
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_RR(VirtualDatabase vdb,
00077       WaitForCompletionPolicy waitForCompletionPolicy,
00078       CreateTablePolicy createTablePolicy) throws Exception
00079   {
00080     super(vdb, waitForCompletionPolicy, createTablePolicy);
00081     index = -1;
00082   }
00083 
00084   /*
00085    * Request Handling
00086    */
00087 
00088   /**
00089    * Chooses the node to execute the request using a round-robin algorithm. If
00090    * the next node has not the tables needed to execute the requests, we try the
00091    * next one and so on until a suitable backend is found.
00092    * 
00093    * @param request an <code>SelectRequest</code>
00094    * @param metadataCache cached metadata to use to construct the result set
00095    * @return the corresponding <code>java.sql.ResultSet</code>
00096    * @exception SQLException if an error occurs
00097    * @see org.objectweb.cjdbc.controller.loadbalancer.raidb2.RAIDb2#execReadRequest(SelectRequest,
00098    *      MetadataCache)
00099    */
00100   public ControllerResultSet execReadRequest(SelectRequest request,
00101       MetadataCache metadataCache) throws SQLException
00102   {
00103     // Choose a backend
00104     try
00105     {
00106       vdb.acquireReadLockBackendLists();
00107     }
00108     catch (InterruptedException e)
00109     {
00110       String msg = Translate.get(
00111           "loadbalancer.backendlist.acquire.readlock.failed", e);
00112       logger.error(msg);
00113       throw new SQLException(msg);
00114     }
00115 
00116     DatabaseBackend backend = null; // The backend that will execute the query
00117 
00118     // Note that vdb lock is released in the finally clause of this try/catch
00119     // block
00120     try
00121     {
00122       ArrayList backends = vdb.getBackends();
00123       int size = backends.size();
00124 
00125       if (size == 0)
00126         throw new SQLException(Translate.get(
00127             "loadbalancer.execute.no.backend.available", request.getId()));
00128 
00129       // Take the next backend that has the needed tables
00130       int maxTries = size;
00131       int enabledBackends = 0;
00132       ArrayList tables = request.getFrom();
00133 
00134       synchronized (this)
00135       {
00136         do
00137         {
00138           index = (index + 1) % size;
00139           backend = (DatabaseBackend) backends.get(index);
00140           if (backend.isReadEnabled())
00141           {
00142             enabledBackends++;
00143             if (backend.hasTables(tables))
00144               break;
00145           }
00146           maxTries--;
00147         }
00148         while (maxTries >= 0);
00149       }
00150 
00151       if (maxTries < 0)
00152       { // No suitable backend found
00153         if (enabledBackends == 0)
00154           throw new NoMoreBackendException(Translate.get(
00155               "loadbalancer.execute.no.backend.enabled", request.getId()));
00156         else
00157           throw new SQLException(Translate.get(
00158               "loadbalancer.backend.no.required.tables", tables.toString()));
00159       }
00160     }
00161     catch (RuntimeException e)
00162     {
00163       String msg = Translate.get("loadbalancer.request.failed.on.backend",
00164           new String[]{request.getSQLShortForm(vdb.getSQLShortFormLength()),
00165               backend.getName(), e.getMessage()});
00166       logger.error(msg, e);
00167       throw new SQLException(msg);
00168     }
00169     finally
00170     {
00171       vdb.releaseReadLockBackendLists();
00172     }
00173 
00174     // Execute the request on the chosen backend
00175     ControllerResultSet rs = null;
00176     try
00177     {
00178       rs = executeRequestOnBackend(request, backend, metadataCache);
00179     }
00180     catch (UnreachableBackendException se)
00181     {
00182       // Try on another backend
00183       return execReadRequest(request, metadataCache);
00184     }
00185     catch (SQLException se)
00186     {
00187       String msg = Translate.get("loadbalancer.request.failed", new String[]{
00188           String.valueOf(request.getId()), se.getMessage()});
00189       if (logger.isInfoEnabled())
00190         logger.info(msg);
00191       throw new SQLException(msg);
00192     }
00193     catch (RuntimeException e)
00194     {
00195       String msg = Translate.get("loadbalancer.request.failed.on.backend",
00196           new String[]{request.getSQLShortForm(vdb.getSQLShortFormLength()),
00197               backend.getName(), e.getMessage()});
00198       logger.error(msg, e);
00199       throw new SQLException(msg);
00200     }
00201 
00202     return rs;
00203   }
00204 
00205   /**
00206    * Chooses the node to execute the stored procedure using a round-robin
00207    * algorithm. If the next node has not the needed stored procedure, we try the
00208    * next one and so on until a suitable backend is found.
00209    * 
00210    * @see org.objectweb.cjdbc.controller.loadbalancer.AbstractLoadBalancer#execReadOnlyReadStoredProcedure(StoredProcedure,
00211    *      MetadataCache)
00212    */
00213   public ControllerResultSet execReadOnlyReadStoredProcedure(
00214       StoredProcedure proc, MetadataCache metadataCache) throws SQLException
00215   {
00216     // Choose a backend
00217     try
00218     {
00219       vdb.acquireReadLockBackendLists();
00220     }
00221     catch (InterruptedException e)
00222     {
00223       String msg = Translate.get(
00224           "loadbalancer.backendlist.acquire.readlock.failed", e);
00225       logger.error(msg);
00226       throw new SQLException(msg);
00227     }
00228 
00229     DatabaseBackend backend = null; // The backend that will execute the query
00230 
00231     // Note that vdb lock is released in the finally clause of this try/catch
00232     // block
00233     try
00234     {
00235       DatabaseBackend failedBackend = null;
00236       SQLException failedException = null;
00237       ControllerResultSet rs = null;
00238       do
00239       {
00240         ArrayList backends = vdb.getBackends();
00241         int size = backends.size();
00242 
00243         if (size == 0)
00244           throw new SQLException(Translate.get(
00245               "loadbalancer.execute.no.backend.available", proc.getId()));
00246 
00247         // Take the next backend that has the needed tables
00248         int maxTries = size;
00249         int enabledBackends = 0;
00250 
00251         synchronized (this)
00252         {
00253           do
00254           {
00255             index = (index + 1) % size;
00256             backend = (DatabaseBackend) backends.get(index);
00257             if (backend.isReadEnabled())
00258             {
00259               enabledBackends++;
00260               if ((backend != failedBackend)
00261                   && backend.hasStoredProcedure(proc.getProcedureName()))
00262                 break;
00263             }
00264             maxTries--;
00265           }
00266           while (maxTries >= 0);
00267         }
00268 
00269         if (maxTries < 0)
00270         { // No suitable backend found
00271           if (enabledBackends == 0)
00272             throw new SQLException(Translate.get(
00273                 "loadbalancer.execute.no.backend.enabled", proc.getId()));
00274           else if (failedBackend == null)
00275             throw new SQLException(Translate.get(
00276                 "loadbalancer.backend.no.required.storedprocedure", proc
00277                     .getProcedureName()));
00278           else
00279             // Bad query, the only backend that could execute it has failed
00280             throw failedException;
00281         }
00282 
00283         // Execute the request on the chosen backend
00284         boolean toDisable = false;
00285         try
00286         {
00287           rs = executeStoredProcedureOnBackend(proc, backend, metadataCache);
00288           if (failedBackend != null)
00289           { // Previous backend failed
00290             if (logger.isWarnEnabled())
00291               logger.warn(Translate.get("loadbalancer.storedprocedure.status",
00292                   new String[]{String.valueOf(proc.getId()), backend.getName(),
00293                       failedBackend.getName()}));
00294             toDisable = true;
00295           }
00296         }
00297         catch (UnreachableBackendException se)
00298         {
00299           // Retry on an other backend.
00300           continue;
00301         }
00302         catch (SQLException se)
00303         {
00304           if (failedBackend != null)
00305           { // Bad query, no backend can execute it
00306             String msg = Translate.get(
00307                 "loadbalancer.storedprocedure.failed.twice", new String[]{
00308                     String.valueOf(proc.getId()), se.getMessage()});
00309             if (logger.isInfoEnabled())
00310               logger.info(msg);
00311             throw new SQLException(msg);
00312           }
00313           else
00314           { // We are the first to fail on this query
00315             failedBackend = backend;
00316             failedException = se;
00317             if (logger.isInfoEnabled())
00318               logger.info(Translate.get(
00319                   "loadbalancer.storedprocedure.failed.on.backend",
00320                   new String[]{
00321                       proc.getSQLShortForm(vdb.getSQLShortFormLength()),
00322                       backend.getName(), se.getMessage()}));
00323             continue;
00324           }
00325         }
00326 
00327         if (toDisable)
00328         { // retry has succeeded and we need to disable the first node that
00329           // failed
00330           try
00331           {
00332             if (logger.isWarnEnabled())
00333               logger.warn(Translate.get("loadbalancer.backend.disabling",
00334                   failedBackend.getName()));
00335             disableBackend(failedBackend);
00336           }
00337           catch (SQLException ignore)
00338           {
00339           }
00340           finally
00341           {
00342             failedBackend = null; // to exit the do{}while
00343           }
00344         }
00345       }
00346       while (failedBackend != null);
00347       return rs;
00348     }
00349     catch (RuntimeException e)
00350     {
00351       String msg = Translate.get(
00352           "loadbalancer.storedprocedure.failed.on.backend", new String[]{
00353               proc.getSQLShortForm(vdb.getSQLShortFormLength()),
00354               backend.getName(), e.getMessage()});
00355       logger.fatal(msg, e);
00356       throw new SQLException(msg);
00357     }
00358     finally
00359     {
00360       vdb.releaseReadLockBackendLists();
00361     }
00362   }
00363 
00364   /*
00365    * Debug/Monitoring
00366    */
00367 
00368   /**
00369    * Gets information about the request load balancer.
00370    * 
00371    * @return <code>String</code> containing information
00372    */
00373   public String getInformation()
00374   {
00375     // We don't lock since we don't need a completely accurate value
00376     int size = vdb.getBackends().size();
00377 
00378     if (size == 0)
00379       return "RAIDb-2 Round-Robin Request load balancer: !!!Warning!!! No backend nodes found\n";
00380     else
00381       return "RAIDb-2 Round-Robin Request load balancer (" + size
00382           + " backends)\n";
00383   }
00384 
00385   /**
00386    * @see org.objectweb.cjdbc.controller.loadbalancer.raidb2.RAIDb2#getRaidb2Xml
00387    */
00388   public String getRaidb2Xml()
00389   {
00390     return "<" + DatabasesXmlTags.ELT_RAIDb_2_RoundRobin + "/>";
00391   }
00392 }

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