KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > objectweb > cjdbc > controller > loadbalancer > raidb2 > RAIDb2_RR


1 /**
2  * C-JDBC: Clustered JDBC.
3  * Copyright (C) 2002-2004 French National Institute For Research In Computer
4  * Science And Control (INRIA).
5  * Contact: c-jdbc@objectweb.org
6  *
7  * This library is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU Lesser General Public License as published by the
9  * Free Software Foundation; either version 2.1 of the License, or any later
10  * version.
11  *
12  * This library is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
15  * for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public License
18  * along with this library; if not, write to the Free Software Foundation,
19  * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
20  *
21  * Initial developer(s): Emmanuel Cecchet.
22  * Contributor(s): Julie Marguerite.
23  */

24
25 package org.objectweb.cjdbc.controller.loadbalancer.raidb2;
26
27 import java.sql.SQLException JavaDoc;
28 import java.util.ArrayList JavaDoc;
29
30 import org.objectweb.cjdbc.common.exceptions.NoMoreBackendException;
31 import org.objectweb.cjdbc.common.exceptions.UnreachableBackendException;
32 import org.objectweb.cjdbc.common.i18n.Translate;
33 import org.objectweb.cjdbc.common.sql.SelectRequest;
34 import org.objectweb.cjdbc.common.sql.StoredProcedure;
35 import org.objectweb.cjdbc.common.xml.DatabasesXmlTags;
36 import org.objectweb.cjdbc.controller.backend.DatabaseBackend;
37 import org.objectweb.cjdbc.controller.cache.metadata.MetadataCache;
38 import org.objectweb.cjdbc.controller.loadbalancer.policies.WaitForCompletionPolicy;
39 import org.objectweb.cjdbc.controller.loadbalancer.policies.createtable.CreateTablePolicy;
40 import org.objectweb.cjdbc.controller.virtualdatabase.ControllerResultSet;
41 import org.objectweb.cjdbc.controller.virtualdatabase.VirtualDatabase;
42
43 /**
44  * RAIDb-2 Round Robin load balancer.
45  * <p>
46  * The read requests coming from the request manager are sent in a round robin
47  * to the backend nodes. Write requests are broadcasted to all backends.
48  *
49  * @author <a HREF="mailto:Emmanuel.Cecchet@inria.fr">Emmanuel Cecchet </a>
50  * @author <a HREF="mailto:Julie.Marguerite@inria.fr">Julie Marguerite </a>
51  * @version 1.0
52  */

53 public class RAIDb2_RR extends RAIDb2
54 {
55   /*
56    * How the code is organized ? 1. Member variables 2. Constructor(s) 3.
57    * Request handling 4. Debug/Monitoring
58    */

59
60   private int index; // index in the backend vector the Round-Robin
61

62   /*
63    * Constructors
64    */

65
66   /**
67    * Creates a new RAIDb-2 Round Robin request load balancer.
68    *
69    * @param vdb the virtual database this load balancer belongs to.
70    * @param waitForCompletionPolicy How many backends must complete before
71    * returning the result?
72    * @param createTablePolicy The policy defining how 'create table' statements
73    * should be handled
74    * @exception Exception if an error occurs
75    */

76   public RAIDb2_RR(VirtualDatabase vdb,
77       WaitForCompletionPolicy waitForCompletionPolicy,
78       CreateTablePolicy createTablePolicy) throws Exception JavaDoc
79   {
80     super(vdb, waitForCompletionPolicy, createTablePolicy);
81     index = -1;
82   }
83
84   /*
85    * Request Handling
86    */

87
88   /**
89    * Chooses the node to execute the request using a round-robin algorithm. If
90    * the next node has not the tables needed to execute the requests, we try the
91    * next one and so on until a suitable backend is found.
92    *
93    * @param request an <code>SelectRequest</code>
94    * @param metadataCache cached metadata to use to construct the result set
95    * @return the corresponding <code>java.sql.ResultSet</code>
96    * @exception SQLException if an error occurs
97    * @see org.objectweb.cjdbc.controller.loadbalancer.raidb2.RAIDb2#execReadRequest(SelectRequest,
98    * MetadataCache)
99    */

100   public ControllerResultSet execReadRequest(SelectRequest request,
101       MetadataCache metadataCache) throws SQLException JavaDoc
102   {
103     // Choose a backend
104
try
105     {
106       vdb.acquireReadLockBackendLists();
107     }
108     catch (InterruptedException JavaDoc e)
109     {
110       String JavaDoc msg = Translate.get(
111           "loadbalancer.backendlist.acquire.readlock.failed", e);
112       logger.error(msg);
113       throw new SQLException JavaDoc(msg);
114     }
115
116     DatabaseBackend backend = null; // The backend that will execute the query
117

118     // Note that vdb lock is released in the finally clause of this try/catch
119
// block
120
try
121     {
122       ArrayList JavaDoc backends = vdb.getBackends();
123       int size = backends.size();
124
125       if (size == 0)
126         throw new SQLException JavaDoc(Translate.get(
127             "loadbalancer.execute.no.backend.available", request.getId()));
128
129       // Take the next backend that has the needed tables
130
int maxTries = size;
131       int enabledBackends = 0;
132       ArrayList JavaDoc tables = request.getFrom();
133
134       synchronized (this)
135       {
136         do
137         {
138           index = (index + 1) % size;
139           backend = (DatabaseBackend) backends.get(index);
140           if (backend.isReadEnabled())
141           {
142             enabledBackends++;
143             if (backend.hasTables(tables))
144               break;
145           }
146           maxTries--;
147         }
148         while (maxTries >= 0);
149       }
150
151       if (maxTries < 0)
152       { // No suitable backend found
153
if (enabledBackends == 0)
154           throw new NoMoreBackendException(Translate.get(
155               "loadbalancer.execute.no.backend.enabled", request.getId()));
156         else
157           throw new SQLException JavaDoc(Translate.get(
158               "loadbalancer.backend.no.required.tables", tables.toString()));
159       }
160     }
161     catch (RuntimeException JavaDoc e)
162     {
163       String JavaDoc msg = Translate.get("loadbalancer.request.failed.on.backend",
164           new String JavaDoc[]{request.getSQLShortForm(vdb.getSQLShortFormLength()),
165               backend.getName(), e.getMessage()});
166       logger.error(msg, e);
167       throw new SQLException JavaDoc(msg);
168     }
169     finally
170     {
171       vdb.releaseReadLockBackendLists();
172     }
173
174     // Execute the request on the chosen backend
175
ControllerResultSet rs = null;
176     try
177     {
178       rs = executeRequestOnBackend(request, backend, metadataCache);
179     }
180     catch (UnreachableBackendException se)
181     {
182       // Try on another backend
183
return execReadRequest(request, metadataCache);
184     }
185     catch (SQLException JavaDoc se)
186     {
187       String JavaDoc msg = Translate.get("loadbalancer.request.failed", new String JavaDoc[]{
188           String.valueOf(request.getId()), se.getMessage()});
189       if (logger.isInfoEnabled())
190         logger.info(msg);
191       throw new SQLException JavaDoc(msg);
192     }
193     catch (RuntimeException JavaDoc e)
194     {
195       String JavaDoc msg = Translate.get("loadbalancer.request.failed.on.backend",
196           new String JavaDoc[]{request.getSQLShortForm(vdb.getSQLShortFormLength()),
197               backend.getName(), e.getMessage()});
198       logger.error(msg, e);
199       throw new SQLException JavaDoc(msg);
200     }
201
202     return rs;
203   }
204
205   /**
206    * Chooses the node to execute the stored procedure using a round-robin
207    * algorithm. If the next node has not the needed stored procedure, we try the
208    * next one and so on until a suitable backend is found.
209    *
210    * @see org.objectweb.cjdbc.controller.loadbalancer.AbstractLoadBalancer#execReadOnlyReadStoredProcedure(StoredProcedure,
211    * MetadataCache)
212    */

213   public ControllerResultSet execReadOnlyReadStoredProcedure(
214       StoredProcedure proc, MetadataCache metadataCache) throws SQLException JavaDoc
215   {
216     // Choose a backend
217
try
218     {
219       vdb.acquireReadLockBackendLists();
220     }
221     catch (InterruptedException JavaDoc e)
222     {
223       String JavaDoc msg = Translate.get(
224           "loadbalancer.backendlist.acquire.readlock.failed", e);
225       logger.error(msg);
226       throw new SQLException JavaDoc(msg);
227     }
228
229     DatabaseBackend backend = null; // The backend that will execute the query
230

231     // Note that vdb lock is released in the finally clause of this try/catch
232
// block
233
try
234     {
235       DatabaseBackend failedBackend = null;
236       SQLException JavaDoc failedException = null;
237       ControllerResultSet rs = null;
238       do
239       {
240         ArrayList JavaDoc backends = vdb.getBackends();
241         int size = backends.size();
242
243         if (size == 0)
244           throw new SQLException JavaDoc(Translate.get(
245               "loadbalancer.execute.no.backend.available", proc.getId()));
246
247         // Take the next backend that has the needed tables
248
int maxTries = size;
249         int enabledBackends = 0;
250
251         synchronized (this)
252         {
253           do
254           {
255             index = (index + 1) % size;
256             backend = (DatabaseBackend) backends.get(index);
257             if (backend.isReadEnabled())
258             {
259               enabledBackends++;
260               if ((backend != failedBackend)
261                   && backend.hasStoredProcedure(proc.getProcedureName()))
262                 break;
263             }
264             maxTries--;
265           }
266           while (maxTries >= 0);
267         }
268
269         if (maxTries < 0)
270         { // No suitable backend found
271
if (enabledBackends == 0)
272             throw new SQLException JavaDoc(Translate.get(
273                 "loadbalancer.execute.no.backend.enabled", proc.getId()));
274           else if (failedBackend == null)
275             throw new SQLException JavaDoc(Translate.get(
276                 "loadbalancer.backend.no.required.storedprocedure", proc
277                     .getProcedureName()));
278           else
279             // Bad query, the only backend that could execute it has failed
280
throw failedException;
281         }
282
283         // Execute the request on the chosen backend
284
boolean toDisable = false;
285         try
286         {
287           rs = executeStoredProcedureOnBackend(proc, backend, metadataCache);
288           if (failedBackend != null)
289           { // Previous backend failed
290
if (logger.isWarnEnabled())
291               logger.warn(Translate.get("loadbalancer.storedprocedure.status",
292                   new String JavaDoc[]{String.valueOf(proc.getId()), backend.getName(),
293                       failedBackend.getName()}));
294             toDisable = true;
295           }
296         }
297         catch (UnreachableBackendException se)
298         {
299           // Retry on an other backend.
300
continue;
301         }
302         catch (SQLException JavaDoc se)
303         {
304           if (failedBackend != null)
305           { // Bad query, no backend can execute it
306
String JavaDoc msg = Translate.get(
307                 "loadbalancer.storedprocedure.failed.twice", new String JavaDoc[]{
308                     String.valueOf(proc.getId()), se.getMessage()});
309             if (logger.isInfoEnabled())
310               logger.info(msg);
311             throw new SQLException JavaDoc(msg);
312           }
313           else
314           { // We are the first to fail on this query
315
failedBackend = backend;
316             failedException = se;
317             if (logger.isInfoEnabled())
318               logger.info(Translate.get(
319                   "loadbalancer.storedprocedure.failed.on.backend",
320                   new String JavaDoc[]{
321                       proc.getSQLShortForm(vdb.getSQLShortFormLength()),
322                       backend.getName(), se.getMessage()}));
323             continue;
324           }
325         }
326
327         if (toDisable)
328         { // retry has succeeded and we need to disable the first node that
329
// failed
330
try
331           {
332             if (logger.isWarnEnabled())
333               logger.warn(Translate.get("loadbalancer.backend.disabling",
334                   failedBackend.getName()));
335             disableBackend(failedBackend);
336           }
337           catch (SQLException JavaDoc ignore)
338           {
339           }
340           finally
341           {
342             failedBackend = null; // to exit the do{}while
343
}
344         }
345       }
346       while (failedBackend != null);
347       return rs;
348     }
349     catch (RuntimeException JavaDoc e)
350     {
351       String JavaDoc msg = Translate.get(
352           "loadbalancer.storedprocedure.failed.on.backend", new String JavaDoc[]{
353               proc.getSQLShortForm(vdb.getSQLShortFormLength()),
354               backend.getName(), e.getMessage()});
355       logger.fatal(msg, e);
356       throw new SQLException JavaDoc(msg);
357     }
358     finally
359     {
360       vdb.releaseReadLockBackendLists();
361     }
362   }
363
364   /*
365    * Debug/Monitoring
366    */

367
368   /**
369    * Gets information about the request load balancer.
370    *
371    * @return <code>String</code> containing information
372    */

373   public String JavaDoc getInformation()
374   {
375     // We don't lock since we don't need a completely accurate value
376
int size = vdb.getBackends().size();
377
378     if (size == 0)
379       return "RAIDb-2 Round-Robin Request load balancer: !!!Warning!!! No backend nodes found\n";
380     else
381       return "RAIDb-2 Round-Robin Request load balancer (" + size
382           + " backends)\n";
383   }
384
385   /**
386    * @see org.objectweb.cjdbc.controller.loadbalancer.raidb2.RAIDb2#getRaidb2Xml
387    */

388   public String JavaDoc getRaidb2Xml()
389   {
390     return "<" + DatabasesXmlTags.ELT_RAIDb_2_RoundRobin + "/>";
391   }
392 }
Popular Tags