001: /*
002:
003: Derby - Class org.apache.derby.impl.store.access.heap.HeapPostCommit
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.access.heap;
023:
024: import org.apache.derby.iapi.services.context.ContextManager;
025: import org.apache.derby.iapi.services.daemon.Serviceable;
026: import org.apache.derby.iapi.services.monitor.Monitor;
027: import org.apache.derby.iapi.services.sanity.SanityManager;
028: import org.apache.derby.iapi.error.StandardException;
029:
030: import org.apache.derby.iapi.store.access.conglomerate.TransactionManager;
031:
032: import org.apache.derby.iapi.store.access.AccessFactory;
033: import org.apache.derby.iapi.store.access.AccessFactoryGlobals;
034:
035: import org.apache.derby.iapi.store.access.ConglomerateController;
036: import org.apache.derby.iapi.store.access.DynamicCompiledOpenConglomInfo;
037: import org.apache.derby.iapi.store.access.Qualifier;
038: import org.apache.derby.iapi.store.access.RowUtil;
039: import org.apache.derby.iapi.store.access.TransactionController;
040:
041: import org.apache.derby.iapi.store.raw.ContainerHandle;
042: import org.apache.derby.iapi.store.raw.FetchDescriptor;
043: import org.apache.derby.iapi.store.raw.LockingPolicy;
044: import org.apache.derby.iapi.store.raw.Page;
045: import org.apache.derby.iapi.store.raw.RecordHandle;
046: import org.apache.derby.iapi.store.raw.Transaction;
047:
048: import org.apache.derby.iapi.reference.SQLState;
049:
050: /**
051:
052: The HeapPostCommit class implements the Serviceable protocol.
053:
054: In it's role as a Serviceable object, it stores the state necessary to
055: find a page in a heap that may have committed delete's to reclaim.
056:
057: It looks up the page described, and reclaims space in the conglomerate.
058: It first trys to clean up any deleted commits on the page. It will then
059: deallocate the page if no rows remain on the page. All work is done while
060: holding the latch on the page, and locks are never "waited" on while holding
061: this latch.
062:
063: This implementation uses record level locking to reclaim the space.
064: For the protocols to work correctly all other heap methods must be
065: prepared for a record or a page to "disappear" if they don't hold a latch and/or
066: a lock. An example of the problem case is a scan which does not hold locks
067: on it's current position (group scan works this way), which is positioned
068: on a row deleted by another xact, it must be prepared to continue the
069: scan after getting an error if the current page/row disapppears.
070:
071: **/
072:
073: class HeapPostCommit implements Serviceable {
074: /**************************************************************************
075: * Fields of the class
076: **************************************************************************
077: */
078:
079: private AccessFactory access_factory = null;
080: private Heap heap = null;
081: private long page_number = ContainerHandle.INVALID_PAGE_NUMBER;
082:
083: /**************************************************************************
084: * Constructors for This class:
085: **************************************************************************
086: */
087: HeapPostCommit(AccessFactory access_factory, Heap heap,
088: long input_page_number) {
089: this .access_factory = access_factory;
090: this .heap = heap;
091: this .page_number = input_page_number;
092: }
093:
094: /**************************************************************************
095: * Private/Protected methods of This class:
096: **************************************************************************
097: */
098:
099: /**
100: * Reclaim space taken up by committed deleted rows.
101: * <p>
102: * This routine assumes it has been called by an internal transaction which
103: * has performed no work so far, and that it has an exclusive intent table
104: * lock. It will attempt obtain exclusive row locks on deleted rows, where
105: * successful those rows can be reclaimed as they must be "committed
106: * deleted" rows.
107: * <p>
108: * This routine will latch the page and hold the latch due to interface
109: * requirement from Page.purgeAtSlot.
110: *
111: * @param heap_control The heap, already opened.
112: * @param pageno number of page to look for committed deletes.
113: *
114: * @see Page#purgeAtSlot
115: * @exception StandardException Standard exception policy.
116: **/
117: private final void purgeCommittedDeletes(
118: HeapController heap_control, long pageno)
119: throws StandardException {
120: // The following can fail either if it can't get the latch or
121: // somehow the page requested no longer exists.
122:
123: //resolve - what will happen if the user page doesnt exist
124:
125: // wait to get the latch on the page
126: Page page = heap_control.getUserPageWait(pageno);
127: boolean purgingDone = false;
128:
129: if (page != null) {
130: try {
131: // The number records that can be reclaimed is:
132: // total recs - recs_not_deleted
133: int num_possible_commit_delete = page.recordCount()
134: - page.nonDeletedRecordCount();
135:
136: if (num_possible_commit_delete > 0) {
137: // loop backward so that purges which affect the slot table
138: // don't affect the loop (ie. they only move records we
139: // have already looked at).
140: for (int slot_no = page.recordCount() - 1; slot_no >= 0; slot_no--) {
141: boolean row_is_committed_delete = page
142: .isDeletedAtSlot(slot_no);
143:
144: if (row_is_committed_delete) {
145: // At this point we only know that the row is
146: // deleted, not whether it is committed.
147:
148: // see if we can purge the row, by getting an
149: // exclusive lock on the row. If it is marked
150: // deleted and we can get this lock, then it
151: // must be a committed delete and we can purge
152: // it.
153:
154: RecordHandle rh = page.fetchFromSlot(
155: (RecordHandle) null, slot_no,
156: RowUtil.EMPTY_ROW,
157: RowUtil.EMPTY_ROW_FETCH_DESCRIPTOR,
158: true);
159:
160: row_is_committed_delete = heap_control
161: .lockRowAtSlotNoWaitExclusive(rh);
162:
163: if (row_is_committed_delete) {
164: purgingDone = true;
165:
166: page.purgeAtSlot(slot_no, 1, false);
167:
168: if (SanityManager.DEBUG) {
169: if (SanityManager
170: .DEBUG_ON("verbose_heap_post_commit")) {
171: SanityManager.DEBUG_PRINT(
172: "HeapPostCommit",
173: "Purging row["
174: + slot_no + "]"
175: + "on page:"
176: + pageno
177: + ".\n");
178: }
179: }
180: }
181: }
182: }
183: }
184: if (page.recordCount() == 0) {
185: purgingDone = true;
186:
187: // Deallocate the current page with 0 rows on it.
188: heap_control.removePage(page);
189:
190: // removePage guarantees to unlatch the page even if an
191: // exception is thrown. The page is protected against reuse
192: // because removePage locks it with a dealloc lock, so it
193: // is OK to release the latch even after a purgeAtSlot is
194: // called.
195: // @see ContainerHandle#removePage
196:
197: if (SanityManager.DEBUG) {
198: if (SanityManager
199: .DEBUG_ON("verbose_heap_post_commit")) {
200: SanityManager.DEBUG_PRINT("HeapPostCommit",
201: "Calling Heap removePage().; pagenumber="
202: + pageno + "\n");
203: }
204: }
205: }
206: } finally {
207: // If no purge happened on the page and the page is not
208: // removed, feel free to unlatch it. Otherwise, let
209: // transaction commit take care of it.
210: if (!purgingDone) {
211: page.unlatch();
212: page = null;
213: }
214: }
215: } else {
216: if (SanityManager.DEBUG) {
217: if (SanityManager.DEBUG_ON("verbose_heap_post_commit")) {
218: SanityManager.DEBUG_PRINT("HeapPostCommit",
219: "Get No Wait returned null. page num = "
220: + pageno + "\n");
221:
222: SanityManager.showTrace(new Throwable());
223: }
224: }
225: }
226: return;
227: }
228:
229: /**************************************************************************
230: * Public Methods implementing the Serviceable interface:
231: **************************************************************************
232: */
233:
234: /**
235: * The urgency of this post commit work.
236: * <p>
237: * This determines where this Serviceable is put in the post commit
238: * queue. Post commit work in the heap can be safely delayed until there
239: * is not user work to do.
240: *
241: * @return false, this work should not be serviced ASAP
242: **/
243: public boolean serviceASAP() {
244: return (true);
245: }
246:
247: // @return true, if this work needs to be done on a user thread immediately
248: public boolean serviceImmediately() {
249: return false;
250: }
251:
252: /**
253: * perform the work described in the postcommit work.
254: * <p>
255: * In this implementation the only work that can be executed by this
256: * post commit processor is this class itself.
257: * <p>
258: *
259: * @return Returns Serviceable.DONE when work has completed, or
260: * returns Serviceable.REQUEUE if work needs to be requeued.
261: *
262: * @param contextMgr the context manager started by the post commit daemon
263: *
264: * @exception StandardException Standard exception policy.
265: **/
266: public int performWork(ContextManager contextMgr)
267: throws StandardException {
268: TransactionManager tc = (TransactionManager) this .access_factory
269: .getAndNameTransaction(contextMgr,
270: AccessFactoryGlobals.SYS_TRANS_NAME);
271:
272: TransactionManager internal_xact = tc.getInternalTransaction();
273:
274: // only requeue if work was not completed in this try.
275: boolean requeue_work = false;
276:
277: HeapController heapcontroller;
278:
279: if (SanityManager.DEBUG) {
280: if (SanityManager.DEBUG_ON("verbose_heap_post_commit"))
281: SanityManager.DEBUG_PRINT("HeapPostCommit",
282: "starting internal xact\n");
283: }
284:
285: try {
286: // This call will attempt to open the heap table locked with
287: // table level IX mode, preparing to do record level locked space
288: // reclamation.
289: //
290: // The call will either succeed immediately, or throw an exception
291: // which could mean the container does not exist or that the lock
292: // could not be granted immediately.
293:
294: //Reversed the fix for 4255:
295: //page reclaimation is done asynchronosly by raswstore daemon
296: //not good to WAIT FOR LOCKS , as it can freeze the daemon
297: //If we can not get the lock this reclamation request will
298: //requeued.
299:
300: heapcontroller = (HeapController) heap
301: .open(
302: internal_xact,
303: internal_xact.getRawStoreXact(),
304: false,
305: ContainerHandle.MODE_FORUPDATE
306: | ContainerHandle.MODE_LOCK_NOWAIT,
307: TransactionController.MODE_RECORD,
308: internal_xact
309: .getRawStoreXact()
310: .newLockingPolicy(
311: LockingPolicy.MODE_RECORD,
312: TransactionController.ISOLATION_REPEATABLE_READ,
313: true), heap,
314: (DynamicCompiledOpenConglomInfo) null);
315:
316: // We got a table intent lock, all deleted rows we encounter can
317: // be reclaimed, once an "X" row lock is obtained on them.
318:
319: // Process all the rows on the page while holding the latch.
320: purgeCommittedDeletes(heapcontroller, this .page_number);
321:
322: } catch (StandardException se) {
323: // exception might have occured either container got dropper or lock not granted.
324: // It is possible by the time this post commit work gets scheduled
325: // that the container has been dropped and that the open container
326: // call will return null - in this case just return assuming no
327: // work to be done.
328:
329: //If this expcetion is because lock could not be obtained , work is requeued.
330: if (se.getMessageId().equals(SQLState.LOCK_TIMEOUT)
331: || se.getMessageId().equals(SQLState.DEADLOCK)) {
332: requeue_work = true;
333: }
334:
335: // Do not close the controller because that will unlatch the
336: // page. Let the commit and destroy do release the latch and
337: // close the controller.
338: // heapcontroller.close();
339: }
340:
341: // It is ok to not sync this post work. If no subsequent log record
342: // is sync'd to disk then it is ok that this transaction not make
343: // it to the database. If any subsequent transaction is sync'd to
344: // the log file, then this transaction will be sync'd as part of that
345: // work.
346:
347: internal_xact.commitNoSync(Transaction.RELEASE_LOCKS);
348: internal_xact.destroy();
349:
350: if (SanityManager.DEBUG) {
351: if (SanityManager.DEBUG_ON("verbose_heap_post_commit")) {
352: if (requeue_work)
353: SanityManager.DEBUG_PRINT("HeapPostCommit",
354: "requeueing on page num = " + page_number);
355: }
356: }
357:
358: return (requeue_work ? Serviceable.REQUEUE : Serviceable.DONE);
359: }
360: }
|