KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > continuent > sequoia > controller > loadbalancer > raidb1 > RAIDb1_WRR


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, Gaetano Mazzeo.
21  */

22
23 package org.continuent.sequoia.controller.loadbalancer.raidb1;
24
25 import java.sql.SQLException JavaDoc;
26 import java.util.ArrayList JavaDoc;
27 import java.util.HashMap 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.WeightedBalancer;
38 import org.continuent.sequoia.controller.loadbalancer.policies.WaitForCompletionPolicy;
39 import org.continuent.sequoia.controller.requests.AbstractRequest;
40 import org.continuent.sequoia.controller.requests.SelectRequest;
41 import org.continuent.sequoia.controller.requests.StoredProcedure;
42 import org.continuent.sequoia.controller.virtualdatabase.VirtualDatabase;
43
44 /**
45  * RAIDb-1 Weighted Round Robin load balancer
46  * <p>
47  * The read requests coming from the request manager are sent to the backend
48  * nodes using a weighted round robin. Write requests are broadcasted to all
49  * backends.
50  * <p>
51  * The weighted round-robin works as follows. If the backend weight is set to 0,
52  * no read requests are sent to this backend unless it is the last one available
53  * on this controller. The load balancer maintains a current weight that is
54  * increased by one each time a new read request is executed. <br>
55  * If backend1 has a weight of 5 and backend2 a weight of 10, backend1 will
56  * receive the 5 first requests and backend2 the next 10 requests. Then we
57  * restart with backend1. Be careful that large weight values will heavily load
58  * backends in turn but will probably not balance the load in an effective way.
59  *
60  * @author <a HREF="mailto:Emmanuel.Cecchet@inria.fr">Emmanuel Cecchet </a>
61  * @author <a HREF="mailto:Julie.Marguerite@inria.fr">Julie Marguerite </a>
62  * @author <a HREF="mailto:Nicolas.Modrzyk@inrialpes.fr">Nicolas Modrzyk </a>
63  * @version 1.0
64  */

65 public class RAIDb1_WRR extends RAIDb1
66 {
67   //
68
// How the code is organized ?
69
//
70
// 1. Member variables
71
// 2. Constructor(s)
72
// 3. Request handling
73
// 4. Debug/Monitoring
74
//
75

76   private HashMap JavaDoc weights = new HashMap JavaDoc();
77   private int currentWeight;
78
79   /*
80    * Constructors
81    */

82
83   /**
84    * Creates a new RAIDb-1 Weighted Round Robin request load balancer.
85    *
86    * @param vdb the virtual database this load balancer belongs to.
87    * @param waitForCompletionPolicy How many backends must complete before
88    * returning the result?
89    * @throws Exception if an error occurs
90    */

91   public RAIDb1_WRR(VirtualDatabase vdb,
92       WaitForCompletionPolicy waitForCompletionPolicy) throws Exception JavaDoc
93   {
94     super(vdb, waitForCompletionPolicy);
95     currentWeight = 0;
96   }
97
98   /*
99    * Request Handling
100    */

101
102   /**
103    * Selects the backend using a weighted round-robin algorithm and executes the
104    * read request.
105    *
106    * @see org.continuent.sequoia.controller.loadbalancer.raidb1.RAIDb1#statementExecuteQuery(SelectRequest,
107    * MetadataCache)
108    */

109   public ControllerResultSet execSingleBackendReadRequest(
110       SelectRequest request, MetadataCache metadataCache) throws SQLException JavaDoc
111   {
112     return (ControllerResultSet) executeWRR(request, STATEMENT_EXECUTE_QUERY,
113         "Request ", metadataCache);
114   }
115
116   /**
117    * Selects the backend using a weighted round-robin algorithm. The backend
118    * that has the shortest queue of currently executing queries is chosen to
119    * execute this stored procedure.
120    *
121    * @see org.continuent.sequoia.controller.loadbalancer.AbstractLoadBalancer#readOnlyCallableStatementExecuteQuery(StoredProcedure,
122    * MetadataCache)
123    */

124   public ControllerResultSet readOnlyCallableStatementExecuteQuery(
125       StoredProcedure proc, MetadataCache metadataCache) throws SQLException JavaDoc
126   {
127     return (ControllerResultSet) executeWRR(proc,
128         CALLABLE_STATEMENT_EXECUTE_QUERY, "Stored procedure ", metadataCache);
129   }
130
131   /**
132    * Selects the backend using a weighted round-robin algorithm. The backend
133    * that has the shortest queue of currently executing queries is chosen to
134    * execute this stored procedure.
135    *
136    * @see org.continuent.sequoia.controller.loadbalancer.AbstractLoadBalancer#readOnlyCallableStatementExecute(StoredProcedure,
137    * MetadataCache)
138    */

139   public ExecuteResult readOnlyCallableStatementExecute(StoredProcedure proc,
140       MetadataCache metadataCache) throws SQLException JavaDoc
141   {
142     return (ExecuteResult) executeWRR(proc, CALLABLE_STATEMENT_EXECUTE,
143         "Stored procedure ", metadataCache);
144   }
145
146   /**
147    * Common code to execute a SelectRequest or a StoredProcedure on a backend
148    * chosen using a weighted round-robin algorithm.
149    *
150    * @param request a <code>SelectRequest</code> or
151    * <code>StoredProcedure</code>
152    * @param callType one of STATEMENT_EXECUTE_QUERY,
153    * CALLABLE_STATEMENT_EXECUTE_QUERY or CALLABLE_STATEMENT_EXECUTE
154    * @param errorMsgPrefix the error message prefix, usually "Request " or
155    * "Stored procedure " ... failed because ...
156    * @param metadataCache a metadataCache if any or null
157    * @return a <code>ResultSet</code>
158    * @throws SQLException if an error occurs
159    */

160   private Object JavaDoc executeWRR(AbstractRequest request, int callType,
161       String JavaDoc errorMsgPrefix, MetadataCache metadataCache) throws SQLException JavaDoc
162   {
163     // Choose a backend
164
try
165     {
166       vdb.acquireReadLockBackendLists();
167     }
168     catch (InterruptedException JavaDoc e)
169     {
170       String JavaDoc msg = Translate.get(
171           "loadbalancer.backendlist.acquire.readlock.failed", e);
172       logger.error(msg);
173       throw new SQLException JavaDoc(msg);
174     }
175
176     DatabaseBackend backend = null;
177
178     // Note that vdb lock is released in the finally clause of this try/catch
179
// block
180
try
181     {
182       ArrayList JavaDoc backends = vdb.getBackends();
183       int size = backends.size();
184
185       if (size == 0)
186         throw new NoMoreBackendException(Translate.get(
187             "loadbalancer.execute.no.backend.available", request.getId()));
188
189       // Choose the backend (WRR algorithm starts here)
190
int w = 0; // cumulative weight
191
boolean backendFound = false;
192       for (int i = 0; i < size; i++)
193       {
194         DatabaseBackend b = (DatabaseBackend) backends.get(i);
195         if (b.isReadEnabled())
196         {
197           if (backend == null)
198             backend = b; // Fallback if no backend found
199

200           // Add the weight of this backend
201
Integer JavaDoc weight = (Integer JavaDoc) weights.get(b.getName());
202           if (weight == null)
203             logger.error("No weight defined for backend " + b.getName());
204           else
205           {
206             int backendWeight = weight.intValue();
207             if (backendWeight == 0)
208               continue; // Weight of 0, avoid using that backend
209
else
210               w += backendWeight;
211           }
212
213           // Ok we reached the needed weight, take this backend
214
if (currentWeight < w)
215           {
216             backend = b;
217             backendFound = true;
218             break;
219           }
220         }
221       }
222
223       if (backend == null)
224         throw new NoMoreBackendException(Translate.get(
225             "loadbalancer.execute.no.backend.enabled", request.getId()));
226
227       // We are over the total weight and we are using the
228
// first available node. Let's reset the index to 1
229
// since we used this first node (0++).
230
if (backendFound)
231         currentWeight++; // Next time take the next
232
else
233         currentWeight = 1;
234     }
235     catch (RuntimeException JavaDoc e)
236     {
237       String JavaDoc msg = Translate.get("loadbalancer.execute.find.backend.failed",
238           new String JavaDoc[]{request.getSqlShortForm(vdb.getSqlShortFormLength()),
239               e.getMessage()});
240       logger.error(msg, e);
241       throw new SQLException JavaDoc(msg);
242     }
243     finally
244     {
245       vdb.releaseReadLockBackendLists();
246     }
247
248     // Execute the request on the chosen backend
249
try
250     {
251       switch (callType)
252       {
253         case STATEMENT_EXECUTE_QUERY :
254           return executeRequestOnBackend((SelectRequest) request, backend,
255               metadataCache);
256         case CALLABLE_STATEMENT_EXECUTE_QUERY :
257           return executeStoredProcedureOnBackend((StoredProcedure) request,
258               true, backend, metadataCache);
259         case CALLABLE_STATEMENT_EXECUTE :
260           return executeStoredProcedureOnBackend((StoredProcedure) request,
261               false, backend, metadataCache);
262         default :
263           throw new RuntimeException JavaDoc("Unhandled call type " + callType
264               + " in executeRoundRobin");
265       }
266     }
267     catch (UnreachableBackendException urbe)
268     {
269       // Try to execute query on different backend
270
return executeWRR(request, callType, errorMsgPrefix, metadataCache);
271     }
272     catch (SQLException JavaDoc se)
273     {
274       String JavaDoc msg = Translate.get("loadbalancer.something.failed", new String JavaDoc[]{
275           errorMsgPrefix, String.valueOf(request.getId()), se.getMessage()});
276       if (logger.isInfoEnabled())
277         logger.info(msg);
278       throw se;
279     }
280     catch (RuntimeException JavaDoc e)
281     {
282       String JavaDoc msg = Translate.get("loadbalancer.something.failed.on",
283           new String JavaDoc[]{errorMsgPrefix,
284               request.getSqlShortForm(vdb.getSqlShortFormLength()),
285               backend.getName(), e.getMessage()});
286       logger.error(msg, e);
287       throw new SQLException JavaDoc(msg);
288     }
289   }
290
291   /*
292    * Backends management
293    */

294
295   /**
296    * @see org.continuent.sequoia.controller.loadbalancer.AbstractLoadBalancer#setWeight(String,
297    * int)
298    */

299   public void setWeight(String JavaDoc name, int w) throws SQLException JavaDoc
300   {
301     if (logger.isDebugEnabled())
302       logger.debug(Translate.get("loadbalancer.weight.set", new String JavaDoc[]{
303           String.valueOf(w), name}));
304
305     weights.put(name, new Integer JavaDoc(w));
306   }
307
308   /*
309    * Debug/Monitoring
310    */

311
312   /**
313    * Gets information about the request load balancer.
314    *
315    * @return <code>String</code> containing information
316    */

317   public String JavaDoc getInformation()
318   {
319     // We don't lock since we don't need a top accurate value
320
int size = vdb.getBackends().size();
321
322     if (size == 0)
323       return "RAIDb-1 with Weighted Round Robin Request load balancer: !!!Warning!!! No backend nodes found\n";
324     else
325       return "RAIDb-1 Weighted Round-Robin Request load balancer (" + size
326           + " backends)\n";
327   }
328
329   /**
330    * @see org.continuent.sequoia.controller.loadbalancer.raidb1.RAIDb1#getRaidb1Xml
331    */

332   public String JavaDoc getRaidb1Xml()
333   {
334     return WeightedBalancer.getRaidbXml(weights,
335         DatabasesXmlTags.ELT_RAIDb_1_WeightedRoundRobin);
336   }
337
338 }
339
Popular Tags