001: /*
002:
003: Derby - Class org.apache.derby.impl.store.raw.data.ContainerOperation
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to you under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021:
022: package org.apache.derby.impl.store.raw.data;
023:
024: import org.apache.derby.iapi.services.io.FormatIdUtil;
025: import org.apache.derby.iapi.services.io.StoredFormatIds;
026: import org.apache.derby.iapi.services.sanity.SanityManager;
027:
028: import org.apache.derby.iapi.store.raw.Compensation;
029: import org.apache.derby.iapi.store.raw.ContainerHandle;
030: import org.apache.derby.iapi.store.raw.LockingPolicy;
031: import org.apache.derby.iapi.store.raw.Transaction;
032: import org.apache.derby.iapi.store.raw.Undoable;
033:
034: import org.apache.derby.iapi.store.raw.xact.RawTransaction;
035: import org.apache.derby.iapi.store.raw.data.RawContainerHandle;
036: import org.apache.derby.iapi.store.raw.log.LogInstant;
037:
038: import org.apache.derby.iapi.error.StandardException;
039:
040: import org.apache.derby.iapi.util.ByteArray;
041:
042: import java.io.ObjectOutput;
043: import java.io.InputStream;
044: import java.io.ObjectInput;
045: import java.io.IOException;
046: import org.apache.derby.iapi.services.io.LimitObjectInput;
047:
048: /**
049: Log operation to create, drop or remove a container.
050:
051: Both the doMe or the undoMe of a create actually caused the container
052: header to be modified and flushed before the log record is flushed. This
053: is necessary for 2 reasons, one is that of ensuring enough disk space, and
054: the other is because unlike any other operation, the log record create
055: container is in the log stream before the container is in the container
056: cache. What this mean is that if a checkpoint started after the container
057: operation but before the container is kept or is dirtied in the container
058: cache, then checkpoint will not know to wait for the container to be kept
059: or cleaned. The checkpoint will erroneous assume that the operation does
060: not need to be redone since its log instant is before the checkpoint but in
061: fact the change has not been flushed to disk.
062:
063: A drop or remove container does not have this problem. The container exist
064: and is in kept state when the operation is logged so the checkpoint will
065: not overlook it and it doesn't need to flush the container header. In the
066: case of remove, the stub is flushed for a different reason - that of
067: ensuring disk space.
068:
069: */
070: public class ContainerOperation extends ContainerBasicOperation
071: implements Undoable {
072: protected byte operation; // create, drop, or remove
073:
074: // in previous version of contianerOperation, there may not
075: // be a createByteArray
076: transient protected boolean hasCreateByteArray = true;
077:
078: protected ByteArray createByteArray; // information necessary to
079: // recreate the container
080:
081: protected static final byte CREATE = (byte) 1;
082: protected static final byte DROP = (byte) 2;
083: protected static final byte REMOVE = (byte) 4;
084:
085: protected ContainerOperation(RawContainerHandle hdl, byte operation)
086: throws StandardException {
087: super (hdl);
088: this .operation = operation;
089: }
090:
091: /*
092: * Formatable methods
093: */
094:
095: // no-arg constructor, required by Formatable
096: public ContainerOperation() {
097: super ();
098: }
099:
100: public void writeExternal(ObjectOutput out) throws IOException {
101: super .writeExternal(out);
102: out.writeByte(operation);
103:
104: if (operation == CREATE) {
105: try {
106: createByteArray = containerHdl.logCreateContainerInfo();
107: } catch (StandardException se) {
108: throw new IOException(se.toString());
109: }
110:
111: createByteArray.writeExternal(out);
112: }
113: }
114:
115: /**
116: @exception IOException cannot read log record from log stream
117: @exception ClassNotFoundException cannot read ByteArray object
118: */
119: public void readExternal(ObjectInput in) throws IOException,
120: ClassNotFoundException {
121: super .readExternal(in);
122: operation = in.readByte();
123:
124: if (operation == CREATE && hasCreateByteArray) {
125: createByteArray = new ByteArray();
126: createByteArray.readExternal(in);
127: }
128: }
129:
130: /**
131: Return my format identifier.
132: */
133: public int getTypeFormatId() {
134: return StoredFormatIds.LOGOP_CONTAINER;
135: }
136:
137: /*
138: * override ContainerBasicOperation's findContainerForRedoRecovery
139: */
140: /**
141: Find container for load tran.
142: <p>
143: If we are in load tran, and the operation is a create, the container
144: may not (should not?) exist yet. We need to recreate it.
145:
146: @exception StandardException Standard Cloudscape policy.
147: */
148: protected RawContainerHandle findContainerForRedoRecovery(
149: RawTransaction xact) throws StandardException {
150: if (SanityManager.DEBUG)
151: SanityManager
152: .ASSERT(createByteArray != null,
153: "cannot reCreate container in load tran, createByteArray is null");
154:
155: long sid = containerId.getSegmentId();
156: long cid = containerId.getContainerId();
157:
158: xact
159: .reCreateContainerForRedoRecovery(sid, cid,
160: createByteArray);
161:
162: // now we should be able to open this container
163: return xact.openDroppedContainer(containerId,
164: (LockingPolicy) null);
165: }
166:
167: /**
168: @exception StandardException Standard Cloudscape error policy
169: */
170: public final void doMe(Transaction tran, LogInstant instant,
171: LimitObjectInput in) throws StandardException {
172:
173: switch (operation) {
174: case DROP:
175: containerHdl.dropContainer(instant, true);
176: //
177: // RESOLVE: if it hasn't been stubbified, even at redo time, we will
178: // want to earmark this as a post commit work because we know it will
179: // not be wasted effort.
180: //
181: break;
182:
183: case REMOVE:
184: containerHdl.removeContainer(instant);
185: break;
186:
187: case CREATE:
188: break;
189: // nothing to do with create container, it has already been synced to
190: // disk. If the container is subsequently dropped or even removed,
191: // that's fine too. Don't bother to find it.
192: }
193:
194: releaseResource(tran);
195: }
196:
197: /**
198: Undo of create, drop or remove
199:
200: @param tran the transaction that is undoing this operation
201: @param hdl the container handle. This is found here during runtime
202: undo - in which case we made the CLR and passed in the containerHdl
203: found in generateUndo and it is passed back to this; or it is found in
204: the CLR's needsRedo and is passed in and this operation never found the
205: container. Either case, release resource at the end is safe
206: @param CLRInstant the log instant of the CLR
207: @param in optional data
208:
209: @exception StandardException Standard Cloudscape error policy
210: */
211: public void undoMe(Transaction tran, RawContainerHandle hdl,
212: LogInstant CLRInstant, LimitObjectInput in)
213: throws StandardException {
214: switch (operation) {
215: case DROP:
216: if (SanityManager.DEBUG) {
217: SanityManager.ASSERT(hdl != null,
218: "container handle is null");
219: SanityManager
220: .ASSERT(
221: hdl.getContainerStatus() != RawContainerHandle.COMMITTED_DROP,
222: "Undoing a drop but the container status is not dropped");
223: }
224: hdl.dropContainer(CLRInstant, false); // not dropped
225: break;
226:
227: case CREATE:
228: // remove the container
229: hdl.removeContainer(CLRInstant);
230: break;
231:
232: case REMOVE:
233: if (SanityManager.DEBUG) {
234: SanityManager
235: .THROWASSERT("cannot undo REMOVE, should not have generated a CLR in the first place");
236: }
237: break;
238: }
239: releaseResource(tran);
240:
241: }
242:
243: /**
244: @see org.apache.derby.iapi.store.raw.Undoable
245: @exception StandardException Standard Cloudscape error policy
246: */
247: public Compensation generateUndo(Transaction tran,
248: LimitObjectInput in) throws StandardException {
249: if (operation == REMOVE)
250: return null; // cannot undo REMOVE
251: else {
252: RawContainerHandle undoContainerHandle = findContainer(tran);
253:
254: // mark the container as pre-dirtied so that if a checkpoint
255: // happens after the log record is sent to the log stream, the
256: // cache cleaning will wait for this change.
257: //
258: // RESOLVE: don't do this now because if undo failed, this
259: // container will be "stuck" in the preDirty state and checkpoint
260: // will be stuck
261: // undoContainerHandle.preDirty(true);
262: //
263:
264: return new ContainerUndoOperation(undoContainerHandle, this );
265: }
266: }
267:
268: /** debug */
269: public String toString() {
270: if (SanityManager.DEBUG) {
271: String str = super .toString();
272: switch (operation) {
273: case CREATE:
274: str += " CREATE container " + containerId;
275: break;
276: case DROP:
277: str += " DROP container " + containerId;
278: break;
279: case REMOVE:
280: str += " REMOVE container " + containerId;
281: break;
282: }
283: return str;
284: } else
285: return null;
286: }
287:
288: }
|