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.tasks;
022:
023: import java.sql.Connection;
024: import java.sql.SQLException;
025:
026: import org.continuent.sequoia.common.i18n.Translate;
027: import org.continuent.sequoia.common.log.Trace;
028: import org.continuent.sequoia.controller.backend.DatabaseBackend;
029: import org.continuent.sequoia.controller.connection.AbstractConnectionManager;
030: import org.continuent.sequoia.controller.connection.PooledConnection;
031: import org.continuent.sequoia.controller.loadbalancer.BackendWorkerThread;
032: import org.continuent.sequoia.controller.requestmanager.TransactionMetaData;
033: import org.continuent.sequoia.controller.requests.AbstractRequest;
034: import org.continuent.sequoia.controller.requests.UnknownWriteRequest;
035:
036: /**
037: * Task to rollback a transaction.
038: *
039: * @author <a href="mailto:Emmanuel.Cecchet@inria.fr">Emmanuel Cecchet </a>
040: * @author <a href="mailto:Julie.Marguerite@inria.fr">Julie Marguerite </a>
041: * @version 1.0
042: */
043: public class RollbackTask extends AbstractTask {
044: // Transaction metadata (login, transaction id, timeout)
045: private TransactionMetaData tm;
046:
047: static Trace endUserLogger = Trace
048: .getLogger("org.continuent.sequoia.enduser");
049:
050: /**
051: * Commits a transaction given a login and a transaction id.
052: *
053: * @param nbToComplete number of threads that must succeed before returning
054: * @param totalNb total number of threads
055: * @param tm transaction metadata
056: * @throws NullPointerException if tm is null
057: */
058: public RollbackTask(int nbToComplete, int totalNb,
059: TransactionMetaData tm) throws NullPointerException {
060: super (nbToComplete, totalNb, tm.isPersistentConnection(), tm
061: .getPersistentConnectionId());
062: if (tm == null)
063: throw new NullPointerException(
064: "Unexpected null metadata in RollbackTask");
065: this .tm = tm;
066: }
067:
068: /**
069: * Rollbacks a transaction with the given backend thread.
070: *
071: * @param backendThread the backend thread that will execute the task
072: * @throws SQLException if an error occurs
073: */
074: public void executeTask(BackendWorkerThread backendThread)
075: throws SQLException {
076: DatabaseBackend backend = backendThread.getBackend();
077: Long lTid = new Long(tm.getTransactionId());
078:
079: AbstractConnectionManager cm = backend.getConnectionManager(tm
080: .getLogin());
081: if (cm == null) {
082: SQLException se = new SQLException(
083: "No Connection Manager for Virtual Login:"
084: + tm.getLogin());
085: try {
086: notifyFailure(backendThread, -1, se);
087: } catch (SQLException ignore) {
088:
089: }
090: throw se;
091: }
092:
093: PooledConnection pc = cm.retrieveConnectionForTransaction(tm
094: .getTransactionId());
095:
096: // Sanity check
097: if (pc == null) { // Bad connection
098: backend.stopTransaction(lTid);
099: SQLException se = new SQLException(
100: "Unable to retrieve connection for transaction "
101: + tm.getTransactionId());
102: try { // All backends failed, just ignore
103: if (!notifyFailure(backendThread, tm.getTimeout(), se))
104: return;
105: } catch (SQLException ignore) {
106: }
107: // Disable this backend (it is no more in sync) by killing the backend
108: // thread
109: backendThread.getLoadBalancer().disableBackend(backend,
110: true);
111: String msg = "Failed to rollback transaction "
112: + tm.getTransactionId() + " on backend "
113: + backend.getName() + " but " + getSuccess()
114: + " succeeded (" + se + ")";
115: backendThread.getLogger().error(msg);
116: endUserLogger.error(Translate
117: .get("loadbalancer.backend.disabling", backend
118: .getName()));
119: throw new SQLException(msg);
120: }
121:
122: // The intent with this sync block is to cope with the specific
123: // situation where two RollbackTasks for the same transaction are executing
124: // in parallel.
125: synchronized (pc) {
126: if (backend.isStartedTransaction(lTid)) {
127: try {
128: Connection c = pc.getConnection();
129: c.rollback();
130: if (tm.altersDatabaseSchema()) { // Flag the schema as dirty in case the transaction contained a DDL
131: UnknownWriteRequest fakeRequest = new UnknownWriteRequest(
132: "rollback " + getTransactionId(),
133: false, 0, null);
134: fakeRequest.setLogin(tm.getLogin());
135: fakeRequest.setIsAutoCommit(false);
136: fakeRequest
137: .setTransactionId(getTransactionId());
138: fakeRequest
139: .setPersistentConnection(isPersistentConnection());
140: fakeRequest
141: .setPersistentConnectionId(getPersistentConnectionId());
142: backendThread.getBackend().setSchemaIsDirty(
143: true, fakeRequest);
144: }
145: c.setAutoCommit(true);
146: } catch (Exception e) {
147: try {
148: if (!notifyFailure(backendThread, tm
149: .getTimeout(), new SQLException(e
150: .getMessage())))
151: return;
152: } catch (SQLException ignore) {
153: }
154: // Disable this backend (it is no more in sync) by killing the backend
155: // thread
156: backendThread.getLoadBalancer().disableBackend(
157: backend, true);
158: String msg = "Failed to rollback transaction "
159: + tm.getTransactionId() + " on backend "
160: + backend.getName() + " but "
161: + getSuccess() + " succeeded (" + e + ")";
162: backendThread.getLogger().error(msg);
163: endUserLogger.error(Translate.get(
164: "loadbalancer.backend.disabling", backend
165: .getName()));
166: throw new SQLException(msg);
167: } finally {
168: cm.releaseConnectionForTransaction(tm
169: .getTransactionId());
170: backend.stopTransaction(lTid);
171: backend.getTaskQueues()
172: .releaseLocksAndCheckForPriorityInversion(
173: tm);
174: }
175: } else {
176: if (backendThread.getLogger().isWarnEnabled())
177: backendThread
178: .getLogger()
179: .warn(
180: "Transaction "
181: + lTid
182: + " not found when trying to rollback (probably because of a concurrent rollback)");
183: }
184: notifySuccess(backendThread);
185: }
186: }
187:
188: /**
189: * @see org.continuent.sequoia.controller.loadbalancer.tasks.AbstractTask#getRequest()
190: */
191: public AbstractRequest getRequest() {
192: return null;
193: }
194:
195: /**
196: * @see org.continuent.sequoia.controller.loadbalancer.tasks.AbstractTask#getTransactionId()
197: */
198: public long getTransactionId() {
199: return tm.getTransactionId();
200: }
201:
202: /**
203: * @see org.continuent.sequoia.controller.loadbalancer.tasks.AbstractTask#isAutoCommit()
204: */
205: public boolean isAutoCommit() {
206: return false;
207: }
208:
209: /**
210: * @see java.lang.Object#equals(java.lang.Object)
211: */
212: public boolean equals(Object other) {
213: if ((other == null) || !(other instanceof RollbackTask))
214: return false;
215:
216: RollbackTask rollback = (RollbackTask) other;
217: return this .getTransactionId() == rollback.getTransactionId();
218: }
219:
220: /**
221: * Returns the transaction metadata value.
222: *
223: * @return Returns the tm.
224: */
225: public final TransactionMetaData getTransactionMetaData() {
226: return tm;
227: }
228:
229: /**
230: * @see java.lang.Object#hashCode()
231: */
232: public int hashCode() {
233: return (int) this .getTransactionId();
234: }
235:
236: /**
237: * @see java.lang.Object#toString()
238: */
239: public String toString() {
240: return "RollbackTask (" + tm.getTransactionId() + ")";
241: }
242: }
|