001: /**
002: * Sequoia: Database clustering technology.
003: * Copyright (C) 2002-2004 French National Institute For Research In Computer
004: * Science And Control (INRIA).
005: * Contact: sequoia@continuent.org
006: *
007: * Licensed under the Apache License, Version 2.0 (the "License");
008: * you may not use this file except in compliance with the License.
009: * You may obtain a copy of the License at
010: *
011: * http://www.apache.org/licenses/LICENSE-2.0
012: *
013: * Unless required by applicable law or agreed to in writing, software
014: * distributed under the License is distributed on an "AS IS" BASIS,
015: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016: * See the License for the specific language governing permissions and
017: * limitations under the License.
018: *
019: * Initial developer(s): Emmanuel Cecchet.
020: * Contributor(s): Julie Marguerite.
021: */package org.continuent.sequoia.controller.loadbalancer.raidb1;
022:
023: import java.sql.SQLException;
024: import java.util.ArrayList;
025:
026: import org.continuent.sequoia.common.exceptions.NoMoreBackendException;
027: import org.continuent.sequoia.common.exceptions.UnreachableBackendException;
028: import org.continuent.sequoia.common.i18n.Translate;
029: import org.continuent.sequoia.common.xml.DatabasesXmlTags;
030: import org.continuent.sequoia.controller.backend.DatabaseBackend;
031: import org.continuent.sequoia.controller.backend.result.ControllerResultSet;
032: import org.continuent.sequoia.controller.backend.result.ExecuteResult;
033: import org.continuent.sequoia.controller.cache.metadata.MetadataCache;
034: import org.continuent.sequoia.controller.loadbalancer.policies.WaitForCompletionPolicy;
035: import org.continuent.sequoia.controller.requests.AbstractRequest;
036: import org.continuent.sequoia.controller.requests.SelectRequest;
037: import org.continuent.sequoia.controller.requests.StoredProcedure;
038: import org.continuent.sequoia.controller.virtualdatabase.VirtualDatabase;
039:
040: /**
041: * RAIDb-1 Round Robin load balancer
042: * <p>
043: * The read requests coming from the Request Manager are sent in a round robin
044: * to the backend nodes. Write requests are broadcasted to all backends.
045: *
046: * @author <a href="mailto:Emmanuel.Cecchet@inria.fr">Emmanuel Cecchet </a>
047: * @author <a href="mailto:Julie.Marguerite@inria.fr">Julie Marguerite </a>
048: * @version 1.0
049: */
050: public class RAIDb1_RR extends RAIDb1 {
051: /*
052: * How the code is organized ? 1. Member variables 2. Constructor(s) 3.
053: * Request handling 4. Debug/Monitoring
054: */
055:
056: private int index; // index in the backend vector the Round-Robin
057:
058: /*
059: * Constructors
060: */
061:
062: /**
063: * Creates a new RAIDb-1 Round Robin request load balancer.
064: *
065: * @param vdb the virtual database this load balancer belongs to.
066: * @param waitForCompletionPolicy How many backends must complete before
067: * returning the result?
068: * @throws Exception if an error occurs
069: */
070: public RAIDb1_RR(VirtualDatabase vdb,
071: WaitForCompletionPolicy waitForCompletionPolicy)
072: throws Exception {
073: super (vdb, waitForCompletionPolicy);
074: index = -1;
075: }
076:
077: /*
078: * Request Handling
079: */
080:
081: /**
082: * Selects the backend using a simple round-robin algorithm and executes the
083: * read request.
084: *
085: * @see org.continuent.sequoia.controller.loadbalancer.raidb1.RAIDb1#statementExecuteQuery(SelectRequest,
086: * MetadataCache)
087: */
088: public ControllerResultSet execSingleBackendReadRequest(
089: SelectRequest request, MetadataCache metadataCache)
090: throws SQLException {
091: return (ControllerResultSet) executeRoundRobinRequest(request,
092: STATEMENT_EXECUTE_QUERY, "Request ", metadataCache);
093: }
094:
095: /**
096: * Selects the backend using a simple round-robin algorithm and executes the
097: * read request.
098: *
099: * @see org.continuent.sequoia.controller.loadbalancer.AbstractLoadBalancer#readOnlyCallableStatementExecuteQuery(StoredProcedure,
100: * MetadataCache)
101: */
102: public ControllerResultSet readOnlyCallableStatementExecuteQuery(
103: StoredProcedure proc, MetadataCache metadataCache)
104: throws SQLException {
105: return (ControllerResultSet) executeRoundRobinRequest(proc,
106: CALLABLE_STATEMENT_EXECUTE_QUERY, "Stored procedure ",
107: metadataCache);
108: }
109:
110: /**
111: * Selects the backend using a simple round-robin algorithm. The backend that
112: * has the shortest queue of currently executing queries is chosen to execute
113: * this stored procedure.
114: *
115: * @see org.continuent.sequoia.controller.loadbalancer.AbstractLoadBalancer#readOnlyCallableStatementExecute(StoredProcedure,
116: * MetadataCache)
117: */
118: public ExecuteResult readOnlyCallableStatementExecute(
119: StoredProcedure proc, MetadataCache metadataCache)
120: throws SQLException {
121: return (ExecuteResult) executeRoundRobinRequest(proc,
122: CALLABLE_STATEMENT_EXECUTE, "Stored procedure ",
123: metadataCache);
124: }
125:
126: /**
127: * Common code to execute a SelectRequest or a StoredProcedure on a backend
128: * chosen using a round-robin algorithm.
129: *
130: * @param request a <code>SelectRequest</code> or
131: * <code>StoredProcedure</code>
132: * @param callType one of STATEMENT_EXECUTE_QUERY,
133: * CALLABLE_STATEMENT_EXECUTE_QUERY or CALLABLE_STATEMENT_EXECUTE
134: * @param errorMsgPrefix the error message prefix, usually "Request " or
135: * "Stored procedure " ... failed because ...
136: * @param metadataCache a metadataCache if any or null
137: * @return a <code>ResultSet</code>
138: * @throws SQLException if an error occurs
139: */
140: private Object executeRoundRobinRequest(AbstractRequest request,
141: int callType, String errorMsgPrefix,
142: MetadataCache metadataCache) throws SQLException {
143: // Choose a backend
144: try {
145: vdb.acquireReadLockBackendLists();
146: } catch (InterruptedException e) {
147: String msg = Translate.get(
148: "loadbalancer.backendlist.acquire.readlock.failed",
149: e);
150: logger.error(msg);
151: throw new SQLException(msg);
152: }
153:
154: // The backend that will execute the query
155: DatabaseBackend backend = null;
156:
157: // Note that vdb lock is released in the finally clause of this try/catch
158: // block
159: try {
160: ArrayList backends = vdb.getBackends();
161: int size = backends.size();
162:
163: if (size == 0)
164: throw new NoMoreBackendException(Translate.get(
165: "loadbalancer.execute.no.backend.available",
166: request.getId()));
167:
168: // Take the next backend
169: int maxTries = size;
170: synchronized (this ) {
171: do {
172: index = (index + 1) % size;
173: backend = (DatabaseBackend) backends.get(index);
174: maxTries--;
175: } while ((!backend.isReadEnabled() && maxTries >= 0));
176: }
177:
178: if (maxTries < 0)
179: throw new NoMoreBackendException(Translate.get(
180: "loadbalancer.execute.no.backend.enabled",
181: request.getId()));
182: } catch (RuntimeException e) {
183: String msg = Translate.get(
184: "loadbalancer.execute.find.backend.failed",
185: new String[] {
186: request.getSqlShortForm(vdb
187: .getSqlShortFormLength()),
188: e.getMessage() });
189: logger.error(msg, e);
190: throw new SQLException(msg);
191: } finally {
192: vdb.releaseReadLockBackendLists();
193: }
194:
195: // Execute the request on the chosen backend
196: try {
197: switch (callType) {
198: case STATEMENT_EXECUTE_QUERY:
199: return executeRequestOnBackend((SelectRequest) request,
200: backend, metadataCache);
201: case CALLABLE_STATEMENT_EXECUTE_QUERY:
202: return executeStoredProcedureOnBackend(
203: (StoredProcedure) request, true, backend,
204: metadataCache);
205: case CALLABLE_STATEMENT_EXECUTE:
206: return executeStoredProcedureOnBackend(
207: (StoredProcedure) request, false, backend,
208: metadataCache);
209: default:
210: throw new RuntimeException("Unhandled call type "
211: + callType + " in executeRoundRobin");
212: }
213: } catch (UnreachableBackendException urbe) {
214: // Try to execute query on different backend
215: return executeRoundRobinRequest(request, callType,
216: errorMsgPrefix, metadataCache);
217: } catch (SQLException se) {
218: String msg = Translate.get("loadbalancer.something.failed",
219: new String[] { errorMsgPrefix,
220: String.valueOf(request.getId()),
221: se.getMessage() });
222: if (logger.isInfoEnabled())
223: logger.info(msg);
224: throw se;
225: } catch (RuntimeException e) {
226: String msg = Translate.get(
227: "loadbalancer.something.failed.on", new String[] {
228: errorMsgPrefix,
229: request.getSqlShortForm(vdb
230: .getSqlShortFormLength()),
231: backend.getName(), e.getMessage() });
232: logger.error(msg, e);
233: throw new SQLException(msg);
234: }
235: }
236:
237: /*
238: * Debug/Monitoring
239: */
240:
241: /**
242: * Gets information about the request load balancer.
243: *
244: * @return <code>String</code> containing information
245: */
246: public String getInformation() {
247: // We don't lock since we don't need a top accurate value
248: int size = vdb.getBackends().size();
249:
250: if (size == 0)
251: return "RAIDb-1 Round-Robin Request load balancer: !!!Warning!!! No backend nodes found\n";
252: else
253: return "RAIDb-1 Round-Robin Request load balancer (" + size
254: + " backends)\n";
255: }
256:
257: /**
258: * @see org.continuent.sequoia.controller.loadbalancer.raidb1.RAIDb1#getRaidb1Xml
259: */
260: public String getRaidb1Xml() {
261: return "<" + DatabasesXmlTags.ELT_RAIDb_1_RoundRobin + "/>";
262: }
263:
264: }
|