001: /**
002: * Sequoia: Database clustering technology.
003: * Copyright (C) 2006 Continuent, Inc.
004: * Contact: sequoia@continuent.org
005: *
006: * Licensed under the Apache License, Version 2.0 (the "License");
007: * you may not use this file except in compliance with the License.
008: * You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing, software
013: * distributed under the License is distributed on an "AS IS" BASIS,
014: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: *
018: * Initial developer(s): Emmanuel Cecchet.
019: * Contributor(s): Damian Arregui.
020: */package org.continuent.sequoia.controller.virtualdatabase.protocol;
021:
022: import java.io.Serializable;
023: import java.util.ArrayList;
024: import java.util.Iterator;
025: import java.util.LinkedList;
026:
027: import org.continuent.hedera.common.Member;
028: import org.continuent.sequoia.common.exceptions.ControllerException;
029: import org.continuent.sequoia.common.exceptions.VirtualDatabaseException;
030: import org.continuent.sequoia.common.i18n.Translate;
031: import org.continuent.sequoia.common.jmx.management.BackendInfo;
032: import org.continuent.sequoia.common.jmx.management.BackendState;
033: import org.continuent.sequoia.common.log.Trace;
034: import org.continuent.sequoia.controller.backend.DatabaseBackend;
035: import org.continuent.sequoia.controller.scheduler.AbstractScheduler;
036: import org.continuent.sequoia.controller.virtualdatabase.DistributedVirtualDatabase;
037: import org.continuent.sequoia.controller.virtualdatabase.VirtualDatabase;
038:
039: /**
040: * This class defines a DisableBackendsAndSetCheckpoint message. Writes,
041: * Transactions and Persistent connections must be suspended before this request
042: * is issued. When executed it does the following things:
043: * <ol>
044: * <li>Set checkpoint
045: * <li>Disable backends if needed
046: * <li>Resume writes, transactions and persistent connections.
047: * </ol>
048: * <p>
049: *
050: * @author <a href="mailto:emmanuel.cecchet@continuent.com">Emmanuel Cecchet</a>
051: * @author <a href="mailto:damian.arregui@continuent.com">Damian Arregui</a>
052: * @author <a href="mailto:ralph.hannus@continuent.com">Ralph hannus</a>
053: * @version 1.0
054: */
055: public class DisableBackendsAndSetCheckpoint extends
056: DistributedVirtualDatabaseMessage {
057: private static final long serialVersionUID = 5296233035014770268L;
058:
059: private ArrayList backendInfos;
060: private String checkpointName;
061: private transient LinkedList totalOrderQueue;
062:
063: /**
064: * Creates a new <code>DisableBackendsAndSetCheckpoint</code> object
065: *
066: * @param backendInfos list of Backend Info to be disabled, if the list is
067: * empty no backends are disabled
068: * @param checkpointName name of the checkpoint to be set
069: */
070: public DisableBackendsAndSetCheckpoint(
071: ArrayList/* <BackendInfo> */backendInfos,
072: String checkpointName) {
073: this .backendInfos = backendInfos;
074: this .checkpointName = checkpointName;
075: }
076:
077: /**
078: * @see org.continuent.sequoia.controller.virtualdatabase.protocol.DistributedVirtualDatabaseMessage#handleMessageSingleThreaded(org.continuent.sequoia.controller.virtualdatabase.DistributedVirtualDatabase,
079: * org.continuent.hedera.common.Member)
080: */
081: public Object handleMessageSingleThreaded(
082: DistributedVirtualDatabase dvdb, Member sender) {
083: totalOrderQueue = dvdb.getTotalOrderQueue();
084: if (totalOrderQueue == null)
085: return new VirtualDatabaseException(Translate.get(
086: "virtualdatabase.no.total.order.queue", dvdb
087: .getVirtualDatabaseName()));
088:
089: synchronized (totalOrderQueue) {
090: SuspendWritesMessage request = new SuspendWritesMessage(
091: "Disable " + checkpointName);
092: totalOrderQueue.addLast(request);
093: return request;
094: }
095: }
096:
097: /**
098: * @see org.continuent.sequoia.controller.virtualdatabase.protocol.DistributedVirtualDatabaseMessage#handleMessageMultiThreaded(org.continuent.sequoia.controller.virtualdatabase.DistributedVirtualDatabase,
099: * org.continuent.hedera.common.Member, java.lang.Object)
100: */
101: public Serializable handleMessageMultiThreaded(
102: DistributedVirtualDatabase dvdb, Member sender,
103: Object handleMessageSingleThreadedResult) {
104: Trace logger = dvdb.getLogger();
105:
106: // Wait for our turn to execute
107: boolean found = dvdb.getRequestManager().getLoadBalancer()
108: .waitForTotalOrder(handleMessageSingleThreadedResult,
109: false);
110:
111: AbstractScheduler scheduler = dvdb.getRequestManager()
112: .getScheduler();
113: // Remove ourselves from the queue to allow others to complete if needed
114: if (!found)
115: logger
116: .error("Disable backend "
117: + backendInfos.toString()
118: + " was not found in total order queue, posting out of order ("
119: + checkpointName + ")");
120: else
121: dvdb.getRequestManager().getLoadBalancer()
122: .removeObjectFromAndNotifyTotalOrderQueue(
123: handleMessageSingleThreadedResult);
124:
125: if (!dvdb.hasRecoveryLog()) {
126: // Resume transactions, writes and persistent connections
127: scheduler
128: .resumeWritesTransactionsAndPersistentConnections();
129: return new VirtualDatabaseException(Translate
130: .get("virtualdatabase.no.recovery.log"));
131: }
132:
133: try {
134: // Insert the checkpoint
135: dvdb.getRequestManager().getRecoveryLog().storeCheckpoint(
136: checkpointName);
137: logger.info(Translate.get("recovery.checkpoint.stored",
138: checkpointName));
139:
140: ControllerException disableStatus = null;
141: // Start disabling the backend locally
142: if (dvdb.isLocalSender(sender)) {
143: Iterator iter = backendInfos.iterator();
144: while (iter.hasNext()) {
145: BackendInfo backendInfo = (BackendInfo) iter.next();
146:
147: // Get the real backend instance
148: DatabaseBackend db = dvdb.getAndCheckBackend(
149: backendInfo.getName(),
150: VirtualDatabase.NO_CHECK_BACKEND);
151:
152: boolean cleanState = false;
153: if (!(db.isReadEnabled() || db.isWriteEnabled())) {
154: if (db.isDisabled())
155: continue; // Backend already disabled, ignore
156:
157: // Else the backend is in a transition state (backuping, ...)
158: String message = "Backend "
159: + db.getName()
160: + " in transition state ("
161: + BackendState.description(db
162: .getStateValue())
163: + "), switching backend to unknown state";
164: disableStatus = new ControllerException(message);
165: logger.error(message);
166: db.setState(BackendState.UNKNOWN);
167: } else {
168: // Signal the backend should not begin any new transaction
169: db.setState(BackendState.DISABLING);
170: logger.info(Translate
171: .get("backend.state.disabling", db
172: .getName()));
173:
174: // Sanity checks
175: cleanState = (db.getActiveTransactions().size() == 0)
176: && (!db.hasPersistentConnections());
177:
178: if (!cleanState) {
179: String message = "Open transactions or persistent connections detected when disabling backend "
180: + db.getName()
181: + ", switching backend to unknown state";
182: // let the user known that the disable was not clean
183: disableStatus = new ControllerException(
184: message);
185: logger.error(message);
186: logger.error(db.getName()
187: + " open transactions: "
188: + db.getActiveTransactions());
189: logger.error(db.getName()
190: + " open persistent connections: "
191: + db.hasPersistentConnections());
192: db.setState(BackendState.UNKNOWN);
193: }
194: }
195:
196: // Now we can safely disable the backend since all transactions have
197: // completed
198: dvdb.getRequestManager().getLoadBalancer()
199: .disableBackend(db, false);
200:
201: if (cleanState) {
202: // Update the last known checkpoint
203: db.setLastKnownCheckpoint(checkpointName);
204: logger
205: .info(Translate.get(
206: "backend.state.disabled", db
207: .getName()));
208: }
209: }
210: }
211:
212: // update remote backend tables
213: dvdb.handleRemoteDisableBackendsNotification(backendInfos,
214: sender);
215: return disableStatus;
216: } catch (Exception e) {
217: return new ControllerException(e);
218: }
219: }
220: }
|