001: /**
002: * Sequoia: Database clustering technology.
003: * Copyright (C) 2002-2004 French National Institute For Research In Computer
004: * Science And Control (INRIA).
005: * Copyright (C) 2005 AmicoSoft, Inc. dba Emic Networks
006: * Copyright (C) 2005-2006 Continuent, Inc.
007: * Contact: sequoia@continuent.org
008: *
009: * Licensed under the Apache License, Version 2.0 (the "License");
010: * you may not use this file except in compliance with the License.
011: * You may obtain a copy of the License at
012: *
013: * http://www.apache.org/licenses/LICENSE-2.0
014: *
015: * Unless required by applicable law or agreed to in writing, software
016: * distributed under the License is distributed on an "AS IS" BASIS,
017: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
018: * See the License for the specific language governing permissions and
019: * limitations under the License.
020: *
021: * Initial developer(s): Emmanuel Cecchet.
022: * Contributor(s): ______________________.
023: */package org.continuent.sequoia.controller.virtualdatabase.protocol;
024:
025: import java.io.Serializable;
026: import java.sql.SQLException;
027: import java.util.LinkedList;
028:
029: import org.continuent.sequoia.common.exceptions.NoMoreBackendException;
030: import org.continuent.sequoia.common.i18n.Translate;
031: import org.continuent.sequoia.common.log.Trace;
032: import org.continuent.sequoia.controller.loadbalancer.AllBackendsFailedException;
033: import org.continuent.sequoia.controller.requestmanager.TransactionMetaData;
034: import org.continuent.sequoia.controller.requestmanager.distributed.DistributedRequestManager;
035: import org.continuent.sequoia.controller.requests.AbstractRequest;
036: import org.continuent.sequoia.controller.requests.UnknownWriteRequest;
037:
038: /**
039: * Execute a distributed rollback.
040: *
041: * @author <a href="mailto:Emmanuel.Cecchet@inria.fr">Emmanuel Cecchet </a>
042: * @version 1.0
043: */
044: public class DistributedRollback extends DistributedTransactionMarker {
045: private static final long serialVersionUID = -8954391235872189513L;
046:
047: // Login that rollbacks the transaction. This is used in case the remote
048: // controller has to log the commit but didn't see the begin in which case it
049: // will not be able to retrieve the transaction marker metadata
050: private String login;
051:
052: /**
053: * Creates a new <code>Rollback</code> message.
054: *
055: * @param login login that rollback the transaction
056: * @param transactionId id of the transaction to commit
057: */
058: public DistributedRollback(String login, long transactionId) {
059: super (transactionId);
060: this .login = login;
061: }
062:
063: /**
064: * @see org.continuent.sequoia.controller.virtualdatabase.protocol.DistributedTransactionMarker#scheduleCommand(org.continuent.sequoia.controller.requestmanager.distributed.DistributedRequestManager)
065: */
066: public Object scheduleCommand(DistributedRequestManager drm)
067: throws SQLException {
068: LinkedList totalOrderQueue = drm.getVirtualDatabase()
069: .getTotalOrderQueue();
070: if (totalOrderQueue != null) {
071: synchronized (totalOrderQueue) {
072: totalOrderQueue.addLast(this );
073: }
074: }
075: return this ;
076: }
077:
078: /**
079: * Execution of a distributed rollback command on the specified
080: * <code>DistributedRequestManager</code>
081: *
082: * @param drm the DistributedRequestManager that will execute the rollback
083: * @return Boolean.TRUE if everything went fine or a SQLException if an error
084: * occured
085: * @throws SQLException if an error occurs
086: */
087: public Serializable executeCommand(DistributedRequestManager drm)
088: throws SQLException {
089: boolean hasBeenScheduled = false;
090: boolean transactionStartedOnThisController = true;
091: Long tid = new Long(transactionId);
092: TransactionMetaData tm;
093: try {
094: tm = drm.getTransactionMetaData(tid);
095: } catch (SQLException ignore) {
096: // The transaction was started before the controller joined the
097: // cluster, build a fake tm so that we will be able to log it.
098: transactionStartedOnThisController = false;
099: tm = new TransactionMetaData(transactionId, 0, login,
100: false, 0);
101: }
102:
103: Trace logger = drm.getLogger();
104: try {
105: if (transactionStartedOnThisController) {
106: drm.getScheduler().rollback(tm, this );
107: hasBeenScheduled = true;
108: }
109:
110: if (logger.isDebugEnabled())
111: logger.debug(Translate.get("transaction.rollback",
112: String.valueOf(tid)));
113:
114: // Send to load balancer
115: drm.getLoadBalancer().rollback(tm);
116:
117: // Update recovery log
118: drm.getRecoveryLog().logRequestCompletion(tm.getLogId(),
119: true, 0);
120:
121: // Invalidate the query result cache if this transaction has updated the
122: // cache or altered the schema
123: if ((drm.getResultCache() != null)
124: && (tm.altersQueryResultCache() || tm
125: .altersDatabaseSchema()))
126: drm.getResultCache().rollback(tm.getTransactionId());
127:
128: // Check for schema modifications that need to be rollbacked
129: if (tm.altersDatabaseSchema()) {
130: if (drm.getMetadataCache() != null)
131: drm.getMetadataCache().flushCache();
132: drm.setSchemaIsDirty(true);
133: }
134:
135: // Notify scheduler for completion
136: if (hasBeenScheduled)
137: drm.getScheduler().rollbackCompleted(tm, true);
138:
139: if (transactionStartedOnThisController) {
140: drm.completeTransaction(tid);
141: }
142: } catch (NoMoreBackendException e) {
143: if (logger.isDebugEnabled())
144: logger
145: .debug(Translate
146: .get(
147: "virtualdatabase.distributed.rollback.logging.only",
148: transactionId));
149: addRollbackFailureOnAllBackends(drm, hasBeenScheduled, tm);
150: throw e;
151: } catch (SQLException e) {
152: if (tm.isReadOnly()) {
153: if (logger.isWarnEnabled()) {
154: logger
155: .warn("Ignoring failure of rollback for read-only transaction, exception was: "
156: + e);
157: }
158:
159: if (hasBeenScheduled)
160: drm.getScheduler().rollbackCompleted(tm, true);
161:
162: return Boolean.TRUE;
163: }
164:
165: addRollbackFailureOnAllBackends(drm, hasBeenScheduled, tm);
166: logger
167: .warn(
168: Translate
169: .get("virtualdatabase.distributed.rollback.sqlexception"),
170: e);
171: return e;
172: } catch (RuntimeException re) {
173: addRollbackFailureOnAllBackends(drm, hasBeenScheduled, tm);
174: logger
175: .warn(
176: Translate
177: .get("virtualdatabase.distributed.rollback.exception"),
178: re);
179: throw new SQLException(re.getMessage());
180: } catch (AllBackendsFailedException e) {
181: addRollbackFailureOnAllBackends(drm, hasBeenScheduled, tm);
182: if (logger.isDebugEnabled())
183: logger
184: .debug(Translate
185: .get(
186: "virtualdatabase.distributed.commit.all.backends.locally.failed",
187: transactionId));
188: return e;
189: }
190: return Boolean.TRUE;
191: }
192:
193: private void addRollbackFailureOnAllBackends(
194: DistributedRequestManager drm, boolean hasBeenScheduled,
195: TransactionMetaData tm) {
196: AbstractRequest request = new UnknownWriteRequest("rollback",
197: false, 0, "\n");
198: request.setTransactionId(transactionId);
199: request.setLogId(tm.getLogId());
200: drm.addFailedOnAllBackends(request, hasBeenScheduled);
201: }
202:
203: /**
204: * @see java.lang.Object#toString()
205: */
206: public String toString() {
207: return "Rollback transaction " + transactionId;
208: }
209: }
|