KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > continuent > sequoia > controller > loadbalancer > raidb2 > RAIDb2_RR


1 /**
2  * Sequoia: Database clustering technology.
3  * Copyright (C) 2002-2004 French National Institute For Research In Computer
4  * Science And Control (INRIA).
5  * Contact: sequoia@continuent.org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  * Initial developer(s): Emmanuel Cecchet.
20  * Contributor(s): Julie Marguerite.
21  */

22
23 package org.continuent.sequoia.controller.loadbalancer.raidb2;
24
25 import java.sql.SQLException JavaDoc;
26 import java.util.ArrayList JavaDoc;
27 import java.util.Collection JavaDoc;
28
29 import org.continuent.sequoia.common.exceptions.NoMoreBackendException;
30 import org.continuent.sequoia.common.exceptions.UnreachableBackendException;
31 import org.continuent.sequoia.common.i18n.Translate;
32 import org.continuent.sequoia.common.xml.DatabasesXmlTags;
33 import org.continuent.sequoia.controller.backend.DatabaseBackend;
34 import org.continuent.sequoia.controller.backend.result.ControllerResultSet;
35 import org.continuent.sequoia.controller.backend.result.ExecuteResult;
36 import org.continuent.sequoia.controller.cache.metadata.MetadataCache;
37 import org.continuent.sequoia.controller.loadbalancer.policies.WaitForCompletionPolicy;
38 import org.continuent.sequoia.controller.loadbalancer.policies.createtable.CreateTablePolicy;
39 import org.continuent.sequoia.controller.requests.SelectRequest;
40 import org.continuent.sequoia.controller.requests.StoredProcedure;
41 import org.continuent.sequoia.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.continuent.sequoia.controller.loadbalancer.raidb2.RAIDb2#statementExecuteQuery(SelectRequest,
98    * MetadataCache)
99    */

100   public ControllerResultSet statementExecuteQuery(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 NoMoreBackendException(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       Collection JavaDoc tables = request.getFrom();
133       if (tables == null)
134         throw new SQLException JavaDoc(Translate.get("loadbalancer.from.not.found",
135             request.getSqlShortForm(vdb.getSqlShortFormLength())));
136
137       synchronized (this)
138       {
139         do
140         {
141           index = (index + 1) % size;
142           backend = (DatabaseBackend) backends.get(index);
143           if (backend.isReadEnabled())
144           {
145             enabledBackends++;
146             if (backend.hasTables(tables))
147               break;
148           }
149           maxTries--;
150         }
151         while (maxTries >= 0);
152       }
153
154       if (maxTries < 0)
155       { // No suitable backend found
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     catch (RuntimeException JavaDoc e)
165     {
166       String JavaDoc msg = Translate.get("loadbalancer.request.failed.on.backend",
167           new String JavaDoc[]{request.getSqlShortForm(vdb.getSqlShortFormLength()),
168               backend.getName(), e.getMessage()});
169       logger.error(msg, e);
170       throw new SQLException JavaDoc(msg);
171     }
172     finally
173     {
174       vdb.releaseReadLockBackendLists();
175     }
176
177     // Execute the request on the chosen backend
178
ControllerResultSet rs = null;
179     try
180     {
181       rs = executeReadRequestOnBackend(request, backend, metadataCache);
182     }
183     catch (UnreachableBackendException se)
184     {
185       // Try on another backend
186
return statementExecuteQuery(request, metadataCache);
187     }
188     catch (SQLException JavaDoc se)
189     {
190       String JavaDoc msg = Translate.get("loadbalancer.request.failed", new String JavaDoc[]{
191           String.valueOf(request.getId()), se.getMessage()});
192       if (logger.isInfoEnabled())
193         logger.info(msg);
194       throw new SQLException JavaDoc(msg);
195     }
196     catch (RuntimeException JavaDoc e)
197     {
198       String JavaDoc msg = Translate.get("loadbalancer.request.failed.on.backend",
199           new String JavaDoc[]{request.getSqlShortForm(vdb.getSqlShortFormLength()),
200               backend.getName(), e.getMessage()});
201       logger.error(msg, e);
202       throw new SQLException JavaDoc(msg);
203     }
204
205     return rs;
206   }
207
208   /**
209    * Chooses the node to execute the stored procedure using a round-robin
210    * algorithm. If the next node has not the needed stored procedure, we try the
211    * next one and so on until a suitable backend is found.
212    *
213    * @see org.continuent.sequoia.controller.loadbalancer.AbstractLoadBalancer#readOnlyCallableStatementExecuteQuery(StoredProcedure,
214    * MetadataCache)
215    */

216   public ControllerResultSet readOnlyCallableStatementExecuteQuery(
217       StoredProcedure proc, MetadataCache metadataCache) throws SQLException JavaDoc
218   {
219     return (ControllerResultSet) callStoredProcedure(proc,
220         CALLABLE_STATEMENT_EXECUTE_QUERY, metadataCache);
221   }
222
223   /**
224    * Chooses the node to execute the stored procedure using a round-robin
225    * algorithm. If the next node has not the needed stored procedure, we try the
226    * next one and so on until a suitable backend is found.
227    *
228    * @see org.continuent.sequoia.controller.loadbalancer.AbstractLoadBalancer#readOnlyCallableStatementExecute(org.continuent.sequoia.controller.requests.StoredProcedure,
229    * org.continuent.sequoia.controller.cache.metadata.MetadataCache)
230    */

231   public ExecuteResult readOnlyCallableStatementExecute(StoredProcedure proc,
232       MetadataCache metadataCache) throws SQLException JavaDoc
233   {
234     return (ExecuteResult) callStoredProcedure(proc,
235         CALLABLE_STATEMENT_EXECUTE, metadataCache);
236   }
237
238   /**
239    * Common code to execute a StoredProcedure using executeQuery or
240    * executeUpdate on a backend chosen using a LPRF algorithm.
241    *
242    * @param proc a <code>StoredProcedure</code>
243    * @param callType one of CALLABLE_STATEMENT_EXECUTE_QUERY or
244    * CALLABLE_STATEMENT_EXECUTE
245    * @param metadataCache the metadataCache if any or null
246    * @return a <code>ControllerResultSet</code> or an
247    * <code>ExecuteResult</code> object
248    * @throws SQLException if an error occurs
249    */

250   private Object JavaDoc callStoredProcedure(StoredProcedure proc, int callType,
251       MetadataCache metadataCache) throws SQLException JavaDoc
252   {
253     // Choose a backend
254
try
255     {
256       vdb.acquireReadLockBackendLists();
257     }
258     catch (InterruptedException JavaDoc e)
259     {
260       String JavaDoc msg = Translate.get(
261           "loadbalancer.backendlist.acquire.readlock.failed", e);
262       logger.error(msg);
263       throw new SQLException JavaDoc(msg);
264     }
265
266     DatabaseBackend backend = null; // The backend that will execute the query
267

268     // Note that vdb lock is released in the finally clause of this try/catch
269
// block
270
try
271     {
272       DatabaseBackend failedBackend = null;
273       SQLException JavaDoc failedException = null;
274       Object JavaDoc result = null;
275       do
276       {
277         ArrayList JavaDoc backends = vdb.getBackends();
278         int size = backends.size();
279
280         if (size == 0)
281           throw new NoMoreBackendException(Translate.get(
282               "loadbalancer.execute.no.backend.available", proc.getId()));
283
284         // Take the next backend that has the needed tables
285
int maxTries = size;
286         int enabledBackends = 0;
287
288         synchronized (this)
289         {
290           do
291           {
292             index = (index + 1) % size;
293             backend = (DatabaseBackend) backends.get(index);
294             if (backend.isReadEnabled())
295             {
296               enabledBackends++;
297               if ((backend != failedBackend)
298                   && backend.hasStoredProcedure(proc.getProcedureKey(), proc
299                       .getNbOfParameters()))
300                 break;
301             }
302             maxTries--;
303           }
304           while (maxTries >= 0);
305         }
306
307         if (maxTries < 0)
308         { // No suitable backend found
309
if (enabledBackends == 0)
310             throw new SQLException JavaDoc(Translate.get(
311                 "loadbalancer.execute.no.backend.enabled", proc.getId()));
312           else if (failedBackend == null)
313             throw new SQLException JavaDoc(Translate.get(
314                 "loadbalancer.backend.no.required.storedprocedure", proc
315                     .getProcedureKey()));
316           else
317             // Bad query, the only backend that could execute it has failed
318
throw failedException;
319         }
320
321         // Execute the request on the chosen backend
322
boolean toDisable = false;
323         try
324         {
325           switch (callType)
326           {
327             case CALLABLE_STATEMENT_EXECUTE_QUERY :
328               result = executeStoredProcedureOnBackend(proc, true, backend,
329                   metadataCache);
330               break;
331             case CALLABLE_STATEMENT_EXECUTE :
332               result = executeStoredProcedureOnBackend(proc, false, backend,
333                   metadataCache);
334               break;
335             default :
336               throw new RuntimeException JavaDoc("Unhandled call type " + callType
337                   + " in executeLPRF");
338           }
339           if (failedBackend != null)
340           { // Previous backend failed
341
if (logger.isWarnEnabled())
342               logger.warn(Translate.get("loadbalancer.storedprocedure.status",
343                   new String JavaDoc[]{String.valueOf(proc.getId()), backend.getName(),
344                       failedBackend.getName()}));
345             toDisable = true;
346           }
347         }
348         catch (UnreachableBackendException se)
349         {
350           // Retry on an other backend.
351
continue;
352         }
353         catch (SQLException JavaDoc se)
354         {
355           if (failedBackend != null)
356           { // Bad query, no backend can execute it
357
String JavaDoc msg = Translate.get(
358                 "loadbalancer.storedprocedure.failed.twice", new String JavaDoc[]{
359                     String.valueOf(proc.getId()), se.getMessage()});
360             if (logger.isInfoEnabled())
361               logger.info(msg);
362             throw new SQLException JavaDoc(msg);
363           }
364           else
365           { // We are the first to fail on this query
366
failedBackend = backend;
367             failedException = se;
368             if (logger.isInfoEnabled())
369               logger.info(Translate.get(
370                   "loadbalancer.storedprocedure.failed.on.backend",
371                   new String JavaDoc[]{
372                       proc.getSqlShortForm(vdb.getSqlShortFormLength()),
373                       backend.getName(), se.getMessage()}));
374             continue;
375           }
376         }
377
378         if (toDisable)
379         { // retry has succeeded and we need to disable the first node that
380
// failed
381
try
382           {
383             if (logger.isWarnEnabled())
384               logger.warn(Translate.get("loadbalancer.backend.disabling",
385                   failedBackend.getName()));
386             disableBackend(failedBackend, true);
387           }
388           catch (SQLException JavaDoc ignore)
389           {
390           }
391           finally
392           {
393             failedBackend = null; // to exit the do{}while
394
}
395         }
396       }
397       while (failedBackend != null);
398       return result;
399     }
400     catch (Throwable JavaDoc e)
401     {
402       String JavaDoc msg = Translate.get(
403           "loadbalancer.storedprocedure.failed.on.backend", new String JavaDoc[]{
404               proc.getSqlShortForm(vdb.getSqlShortFormLength()),
405               backend.getName(), e.getMessage()});
406       logger.fatal(msg, e);
407       throw new SQLException JavaDoc(msg);
408     }
409     finally
410     {
411       vdb.releaseReadLockBackendLists();
412     }
413   }
414
415   /*
416    * Debug/Monitoring
417    */

418
419   /**
420    * Gets information about the request load balancer.
421    *
422    * @return <code>String</code> containing information
423    */

424   public String JavaDoc getInformation()
425   {
426     // We don't lock since we don't need a completely accurate value
427
int size = vdb.getBackends().size();
428
429     if (size == 0)
430       return "RAIDb-2 Round-Robin Request load balancer: !!!Warning!!! No backend nodes found\n";
431     else
432       return "RAIDb-2 Round-Robin Request load balancer (" + size
433           + " backends)\n";
434   }
435
436   /**
437    * @see org.continuent.sequoia.controller.loadbalancer.raidb2.RAIDb2#getRaidb2Xml
438    */

439   public String JavaDoc getRaidb2Xml()
440   {
441     return "<" + DatabasesXmlTags.ELT_RAIDb_2_RoundRobin + "/>";
442   }
443 }
Popular Tags