KickJava   Java API By Example, From Geeks To Geeks.

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


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): ______________________.
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 featuring (Least Pending Requests First
45  * load balancing algorithm).
46  * <p>
47  * The read requests coming from the request manager are sent to the node that
48  * has the Least pending read requests among the nodes that can execute the
49  * request.
50  *
51  * @author <a HREF="mailto:Emmanuel.Cecchet@inria.fr">Emmanuel Cecchet </a>
52  * @version 1.0
53  */

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

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_LPRF(VirtualDatabase vdb,
77       WaitForCompletionPolicy waitForCompletionPolicy,
78       CreateTablePolicy createTablePolicy) throws Exception JavaDoc
79   {
80     super(vdb, waitForCompletionPolicy, createTablePolicy);
81   }
82
83   /*
84    * Request Handling
85    */

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

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

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

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

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

376
377   /**
378    * Gets information about the request load balancer.
379    *
380    * @return <code>String</code> containing information
381    */

382   public String JavaDoc getInformation()
383   {
384     // We don't lock since we don't need a completely accurate value
385
int size = vdb.getBackends().size();
386
387     if (size == 0)
388       return "RAIDb-2 Least Pending Requests First load balancer: !!!Warning!!! No backend nodes found\n";
389     else
390       return "RAIDb-2 Least Pending Requests First load balancer (" + size
391           + " backends)\n";
392   }
393
394   /**
395    * @see org.objectweb.cjdbc.controller.loadbalancer.raidb2.RAIDb2#getRaidb2Xml
396    */

397   public String JavaDoc getRaidb2Xml()
398   {
399     return "<" + DatabasesXmlTags.ELT_RAIDb_2_LeastPendingRequestsFirst + "/>";
400   }
401 }
Popular Tags