You are viewing a plain text version of this content. The canonical link for it is here.
Posted to derby-commits@db.apache.org by ka...@apache.org on 2009/03/16 14:55:50 UTC
svn commit: r754894 [1/6] - in /db/derby/code/trunk/java:
engine/org/apache/derby/iapi/store/access/conglomerate/
engine/org/apache/derby/iapi/store/raw/
engine/org/apache/derby/impl/store/access/
engine/org/apache/derby/impl/store/access/btree/ engine...
Author: kahatlen
Date: Mon Mar 16 13:55:49 2009
New Revision: 754894
URL: http://svn.apache.org/viewvc?rev=754894&view=rev
Log:
DERBY-2991: Index split deadlock
This is the main fix (patch d2991-2b.diff) which removes the scan lock
mechanism that caused the deadlocks and instead saves the scan
position by key whenever a scan loses its latches.
Modified:
db/derby/code/trunk/java/engine/org/apache/derby/iapi/store/access/conglomerate/ScanManager.java
db/derby/code/trunk/java/engine/org/apache/derby/iapi/store/access/conglomerate/TransactionManager.java
db/derby/code/trunk/java/engine/org/apache/derby/iapi/store/raw/Page.java
db/derby/code/trunk/java/engine/org/apache/derby/iapi/store/raw/RecordHandle.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/RAMTransaction.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/BTreeController.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/BTreeForwardScan.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/BTreeLockingPolicy.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/BTreeMaxScan.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/BTreePostCommit.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/BTreeRowPosition.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/BTreeScan.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/LeafControlRow.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/OpenBTree.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/index/B2INoLocking.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/index/B2IRowLocking1.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/index/B2IRowLocking2.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/index/B2IRowLocking3.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/index/B2IRowLockingRR.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/heap/HeapRowLocation.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/heap/HeapScan.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/sort/Scan.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/store/raw/data/BasePage.java
db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/RowLockIso.out
db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/readlocks.out
db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/rlliso2multi.out
db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/rlliso3multi.out
db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/updatelocksJDBC30.out
db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/store/rlliso2multi.sql
db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/store/rlliso3multi.sql
db/derby/code/trunk/java/testing/org/apache/derbyTesting/unitTests/store/T_RawStoreFactory.java
db/derby/code/trunk/java/testing/org/apache/derbyTesting/unitTests/store/T_b2i.java
Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/store/access/conglomerate/ScanManager.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/store/access/conglomerate/ScanManager.java?rev=754894&r1=754893&r2=754894&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/store/access/conglomerate/ScanManager.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/store/access/conglomerate/ScanManager.java Mon Mar 16 13:55:49 2009
@@ -170,21 +170,4 @@
int[] key_column_numbers,
BackingStoreHashtable hash_table)
throws StandardException;
-
-
- /**
- * Do work necessary to maintain the current position in the scan.
- * <p>
- * The latched page in the conglomerate "congomid" is changing, do
- * whatever is necessary to maintain the current position of the scan.
- * For some conglomerates this may be a no-op.
- * <p>
- *
- * @param conglom Conglomerate object of the conglomerate being changed.
- * @param page Page in the conglomerate being changed.
- *
- * @exception StandardException Standard exception policy.
- **/
- public void savePosition(Conglomerate conglom, Page page)
- throws StandardException;
}
Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/store/access/conglomerate/TransactionManager.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/store/access/conglomerate/TransactionManager.java?rev=754894&r1=754893&r2=754894&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/store/access/conglomerate/TransactionManager.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/store/access/conglomerate/TransactionManager.java Mon Mar 16 13:55:49 2009
@@ -22,12 +22,9 @@
package org.apache.derby.iapi.store.access.conglomerate;
import org.apache.derby.iapi.services.daemon.Serviceable;
-import org.apache.derby.iapi.store.access.AccessFactory;
import org.apache.derby.iapi.store.access.ConglomerateController;
import org.apache.derby.iapi.store.access.SortController;
import org.apache.derby.iapi.store.access.TransactionController;
-import org.apache.derby.iapi.store.raw.LockingPolicy;
-import org.apache.derby.iapi.store.raw.Page;
import org.apache.derby.iapi.store.raw.Transaction;
import org.apache.derby.iapi.error.StandardException;
@@ -172,23 +169,4 @@
**/
public Transaction getRawStoreXact()
throws StandardException;
-
-
- /**
- * Do work necessary to maintain the current position in all the scans.
- * <p>
- * The latched page in the conglomerate "congomid" is changing, do
- * whatever is necessary to maintain the current position of all the
- * scans open in this transaction.
- * <p>
- * For some conglomerates this may be a no-op.
- * <p>
- *
- * @param conglom Conglomerate object of the conglomerate being changed.
- * @param page Page in the conglomerate being changed.
- *
- * @exception StandardException Standard exception policy.
- **/
- public void saveScanPositions(Conglomerate conglom, Page page)
- throws StandardException;
}
Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/store/raw/Page.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/store/raw/Page.java?rev=754894&r1=754893&r2=754894&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/store/raw/Page.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/store/raw/Page.java Mon Mar 16 13:55:49 2009
@@ -139,14 +139,6 @@
public RecordHandle getInvalidRecordHandle();
/**
- * Get a record id protection handle.
- *
- * @return a record id protection handle
- * @see RecordHandle#RECORD_ID_PROTECTION_HANDLE
- */
- public RecordHandle getProtectionRecordHandle();
-
- /**
* Return a record handle for the given constant record id.
* <p>
* Return a record handle that doesn't represent a record but rather has
@@ -1081,6 +1073,30 @@
MT - latched
*/
+ /**
+ * Set a hint in the page object to indicate that scans positioned on it
+ * need to reposition. Only called on B-tree pages.
+ */
+ void setRepositionNeeded();
+
+ /**
+ * Check if this page has been changed in such a way that scans that are
+ * positioned on it will have to reposition. Only called on B-tree pages.
+ *
+ * @param version the version number of the page when the scan positioned
+ * on it (after which version the page should not have changed in a way
+ * that requires repositioning)
+ * @return {@code true} if a scan that was positioned on the page at page
+ * version {@code version} needs to reposition; {@code false} otherwise
+ */
+ boolean isRepositionNeeded(long version);
+
+ /**
+ * Get the current version number of the page.
+ *
+ * @return page version number
+ */
+ long getPageVersion();
/*
* time stamp - for those implmentation that supports it
Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/store/raw/RecordHandle.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/store/raw/RecordHandle.java?rev=754894&r1=754893&r2=754894&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/store/raw/RecordHandle.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/store/raw/RecordHandle.java Mon Mar 16 13:55:49 2009
@@ -47,11 +47,10 @@
public static final int INVALID_RECORD_HANDLE = 0;
/**
- A lock with this recordHandle protects all the recordIds in the page.
- No recordId can disappear while this lock is held.
- New recordIds may appear while this lock is held.
- */
- public static final int RECORD_ID_PROTECTION_HANDLE = 1;
+ * Reserved for future use - name it and define it when you have a need
+ * to use one.
+ */
+ public static final int RESERVED1_RECORD_HANDLE = 1;
/**
A lock with this recordHandle protects this deallocated page from
Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/RAMTransaction.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/RAMTransaction.java?rev=754894&r1=754893&r2=754894&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/RAMTransaction.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/RAMTransaction.java Mon Mar 16 13:55:49 2009
@@ -35,7 +35,6 @@
import org.apache.derby.iapi.services.daemon.Serviceable;
import org.apache.derby.iapi.services.locks.CompatibilitySpace;
-import org.apache.derby.iapi.services.monitor.Monitor;
import org.apache.derby.iapi.services.sanity.SanityManager;
import org.apache.derby.iapi.error.StandardException;
import org.apache.derby.iapi.store.access.conglomerate.Conglomerate;
@@ -71,7 +70,6 @@
import org.apache.derby.iapi.store.raw.Loggable;
-import org.apache.derby.iapi.store.raw.Page;
import org.apache.derby.iapi.store.raw.Transaction;
import org.apache.derby.iapi.types.DataValueDescriptor;
@@ -86,7 +84,6 @@
import java.io.Serializable;
// debugging
-import org.apache.derby.iapi.services.stream.HeaderPrintWriter;
public class RAMTransaction
implements XATransactionController, TransactionManager
@@ -2372,53 +2369,6 @@
return(rawtran);
}
-
- /**
- * Do work necessary to maintain the current position in all the scans.
- * <p>
- * The latched page in the conglomerate "congomid" is changing, do
- * whatever is necessary to maintain the current position of all the
- * scans open in this transaction.
- * <p>
- * For some conglomerates this may be a no-op.
- * <p>
- *
- * @param conglom Conglomerate being changed.
- * @param page Page in the conglomerate being changed.
- *
- * @exception StandardException Standard exception policy.
- **/
- public void saveScanPositions(Conglomerate conglom, Page page)
- throws StandardException
- {
- for (Iterator it = scanControllers.iterator(); it.hasNext(); )
- {
- Object o = it.next();
-
- if (SanityManager.DEBUG)
- {
- // The following debugging code is here because the following
- // (ScanManager) cast is occasionally causing a
- // java.lang.ClassCastException.
-
- if (!(o instanceof ScanManager))
- {
- HeaderPrintWriter istream = Monitor.getStream();
-
- if (o == null)
- istream.println("next element was null\n");
- else
- istream.println("non ScanManager on list: " + o);
-
- istream.println(
- "Current list of open scans: " + debugOpened());
- }
- }
- ScanManager sm = (ScanManager) o;
- sm.savePosition(conglom, page);
- }
- }
-
public FileResource getFileHandler() {
return rawtran.getFileHandler();
}
Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/BTreeController.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/BTreeController.java?rev=754894&r1=754893&r2=754894&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/BTreeController.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/BTreeController.java Mon Mar 16 13:55:49 2009
@@ -141,13 +141,8 @@
int num_possible_commit_delete =
leaf.page.recordCount() - 1 - leaf.page.nonDeletedRecordCount();
- if ((num_possible_commit_delete > 0) &&
- (btree_locking_policy.lockScanForReclaimSpace(leaf)))
+ if (num_possible_commit_delete > 0)
{
- // Need to get an exclusive scan lock on the page before we can
- // do any sort of purges, otherwise other concurrent scans would
- // not work. If we can't get the lock NOWAIT, just give up on
- // purging rows and do the split without reclaiming rows.
Page page = leaf.page;
@@ -192,9 +187,15 @@
}
finally
{
- if (controlRow != null)
+ if (controlRow != null) {
+ if (purged_at_least_one_row) {
+ // Set a hint in the page that scans positioned on it
+ // need to reposition because rows have disappeared from
+ // the page.
+ controlRow.page.setRepositionNeeded();
+ }
controlRow.release();
-
+ }
}
return(purged_at_least_one_row);
@@ -724,7 +725,7 @@
latch_released =
test_errors(
this,
- "BTreeController_doIns", false,
+ "BTreeController_doIns", null,
this.getLockingPolicy(),
targetleaf, latch_released);
}
Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/BTreeForwardScan.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/BTreeForwardScan.java?rev=754894&r1=754893&r2=754894&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/BTreeForwardScan.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/BTreeForwardScan.java Mon Mar 16 13:55:49 2009
@@ -29,7 +29,6 @@
import org.apache.derby.iapi.store.access.ScanController;
-import org.apache.derby.iapi.store.raw.Page;
import org.apache.derby.iapi.store.raw.RecordHandle;
import org.apache.derby.iapi.types.DataValueDescriptor;
@@ -199,12 +198,14 @@
// will be null), or when stopKeyValue is reached/passed. Along the
// way apply qualifiers to skip rows which don't qualify.
+ leaf_loop:
while (pos.current_leaf != null)
{
// System.out.println(
// "1 of fetchSet loop, ret_row_count = " + ret_row_count +
// "fetch_row = " + fetch_row);
+ slot_loop:
while ((pos.current_slot + 1) < pos.current_leaf.page.recordCount())
{
@@ -304,7 +305,6 @@
boolean latch_released =
!this.getLockingPolicy().lockScanRow(
this, this.getConglomerate(), pos,
- false,
init_lock_fetch_desc,
pos.current_lock_template,
pos.current_lock_row_loc,
@@ -316,7 +316,7 @@
latch_released =
test_errors(
this,
- "BTreeScan_fetchNextGroup", false,
+ "BTreeScan_fetchNextGroup", pos,
this.getLockingPolicy(),
pos.current_leaf, latch_released);
}
@@ -328,15 +328,42 @@
// is null until after the lock is granted.
pos.current_rh = rh;
- if (latch_released)
+ while (latch_released)
{
// lost latch on page in order to wait for row lock.
- // Because we have scan lock on page, we need only
- // call reposition() which will use the saved record
- // handle to reposition to the same spot on the page.
- // We don't have to search the
- // tree again, as we have the a scan lock on the page
- // which means the current_rh is valid to reposition on.
+ // reposition() will take care of the complexity with
+ // finding the correct spot to position on if the row
+ // has been moved to another page.
+
+ if (!reposition(pos, false))
+ {
+ // Could not position on the exact same row that was
+ // saved, which means that it has been purged.
+ // Reposition on the row immediately to the left of
+ // the purged row instead.
+ if (!reposition(pos, true))
+ {
+ if (SanityManager.DEBUG)
+ {
+ SanityManager.THROWASSERT(
+ "Cannot fail with 2nd param true");
+ }
+ // reposition will set pos.current_leaf to null if
+ // it returns false, so if this ever does fail in
+ // delivered code, expect a NullPointerException at
+ // the top of this loop when we call recordCount().
+ }
+
+ // Now we're positioned to the left of our saved
+ // position. Go to the top of the loop so that we move
+ // the scan to the next row and release the lock on
+ // the purged row.
+ continue slot_loop;
+ }
+
+ // At this point, the scan is positioned and the latch
+ // is held.
+ latch_released = false;
if (this.getConglomerate().isUnique())
{
@@ -355,25 +382,6 @@
// lock, and the row location we fetched earlier in
// this loop is invalid.
- while (latch_released)
- {
- if (!reposition(pos, false))
- {
- if (SanityManager.DEBUG)
- {
- // can't fail while with scan lock
- SanityManager.THROWASSERT(
- "can not fail holding scan lock.");
- }
-
- // reposition will set pos.current_leaf to
- // null, if it returns false so if the this
- // ever does fail in delivered code, expect
- // a null pointer exception on the next line,
- // trying to call fetchFromSlot().
-
- }
-
pos.current_leaf.page.fetchFromSlot(
(RecordHandle) null,
pos.current_slot, fetch_row,
@@ -385,32 +393,10 @@
this,
this.getConglomerate(),
pos,
- false,
init_lock_fetch_desc,
pos.current_lock_template,
pos.current_lock_row_loc,
false, init_forUpdate, lock_operation);
- }
- }
- else
- {
- if (!reposition(pos, false))
- {
- if (SanityManager.DEBUG)
- {
- // can't fail while with scan lock
- SanityManager.THROWASSERT(
- "can not fail holding scan lock.");
- }
-
- // reposition will set pos.current_leaf to
- // null, if it returns false so if the this
- // ever does fail in delivered code, expect
- // a null pointer exception on the next line,
- // trying to call isDeletedAtSlot().
-
- }
-
}
}
@@ -446,6 +432,16 @@
ret_row_count++;
stat_numrows_qualified++;
+ final boolean doneWithGroup = max_rowcnt <= ret_row_count;
+
+ if (doneWithGroup) {
+ if (SanityManager.DEBUG) {
+ SanityManager.ASSERT(pos == scan_position);
+ }
+ int[] vcols = init_fetchDesc.getValidColumnsArray();
+ savePositionAndReleasePage(fetch_row, vcols);
+ }
+
if (hash_table != null)
{
if (hash_table.putRow(false, fetch_row))
@@ -456,15 +452,8 @@
fetch_row = null;
}
- if (max_rowcnt <= ret_row_count)
+ if (doneWithGroup)
{
- // current_slot is invalid after releasing latch
- pos.current_slot = Page.INVALID_SLOT_NUMBER;
-
- // exit fetch row loop and return to the client.
- pos.current_leaf.release();
- pos.current_leaf = null;
-
return(ret_row_count);
}
}
Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/BTreeLockingPolicy.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/BTreeLockingPolicy.java?rev=754894&r1=754893&r2=754894&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/BTreeLockingPolicy.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/BTreeLockingPolicy.java Mon Mar 16 13:55:49 2009
@@ -73,96 +73,12 @@
{
/**************************************************************************
* Abstract Protected lockScan*() locking methods of BTree:
- * lockScan - lock the scan page
- * lockScanForReclaimSpace - lock page for reclaiming deleted rows.
- * lockScanRow - lock row and possibly the scan page
- * unlockScan - unlock the scan page
+ * lockScanRow - lock row
* unlockScanRecordAfterRead- unlock the scan record
**************************************************************************
*/
/**
- * Lock the current leaf page.
- * <p>
- * Logically lock the record id's on a leaf page. This protocol is used
- * by splits/row purgers and scans to coordinate between themselves.
- * <p>
- * Anyone who wants to either move rows off of a btree page or, purge
- * them from existence must first call this routine with "forUpdate"
- * true. This will result in a lock request which will block on other
- * processes which cannot work if rows move off the page or disappear.
- * It is expected that the this routine will only be called for update
- * by very short term internal transactions which will commit immediately
- * after doing their work and give up the exclusive lock quickly.
- * <p>
- * Currently scans can position themselves in one of 2 ways, either by
- * saving the record handle of a record when they give up the latch on
- * the page, or by saving the entire row. If they save the record handle
- * then they must call this routine with "forUpdate" false, to get a
- * lock which will protect the record handle they are using from moving
- * off the page or disapearing. This is also why aborts of inserts must
- * be done by marking the rows deleted, rather than purging them.
- * It is expected that scanner's will release this lock once they move
- * off the page they are looking at. They do this by calling
- * unlockScan().
- * <p>
- * This lock enforces the same lock/latch protocol as btree row locks.
- * On return the lock has been obtained. Return status indicates if the
- * lock was waited for, which will mean a latch(s) were dropped while
- * waiting.
- * In general a false status means that the caller will either have
- * to research the tree unless some protocol has been implemented that
- * insures that the row will not have moved while the latch was dropped.
- * <p>
- * This routine requests a special row on the RECORD_ID_PROTECTION_HANDLE
- * row id. If the lock is granted the routine will return true.
- * If the lock cannot be granted NOWAIT, then the routine will release
- * the latch on "current_leaf" and "aux_control_row" (if
- * aux_control_row is non-null), and then it will request a WAIT lock on
- * the row.
- *
- * @param current_leaf The lock is associated with this page in the
- * btree. This control row is unlatched if the
- * routine has to wait on the lock.
- * @param aux_control_row If non-null, this control row is unlatched
- * if the routine has to wait on the lock.
- * @param forUpdate Whether to wait for lock.
- * @param lock_operation For what operation are we requesting the lock,
- * this should be one of the following 4 options:
- * LOCK_READ [read lock],
- * (LOCK_INS | LOCK_UPD) [ lock for insert],
- * (LOCK_INSERT_PREVKEY | LOCK_UPD) [lock for
- * previous key to insert],
- * (LOCK_UPD) [lock for delete or replace]
- *
- * @exception StandardException Standard exception policy.
- **/
- abstract public boolean lockScan(
- LeafControlRow current_leaf,
- ControlRow aux_control_row,
- boolean forUpdate,
- int lock_operation)
- throws StandardException;
-
-
- /**
- * Lock a control row page for reclaiming deleted rows.
- * <p>
- * When reclaiming deleted rows during split need to get an exclusive
- * scan lock on the page, which will mean there are no other scans
- * positioned on the page. If there are other scans positioned, just
- * give up on reclaiming space now.
- *
- * @return true if lock was granted nowait, else false and not lock was
- * granted.
- *
- * @exception StandardException Standard exception policy.
- **/
- abstract public boolean lockScanForReclaimSpace(
- LeafControlRow current_leaf)
- throws StandardException;
-
- /**
* Lock a btree row to determine if it is a committed deleted row.
* <p>
* Request an exclusive lock on the row located at the given slot, NOWAIT.
@@ -188,16 +104,12 @@
* Lock a row as part of doing the scan.
* <p>
* Lock the row at the given slot (or the previous row if slot is 0).
- * Get the scan lock on the page if "request_scan_lock" is true.
* <p>
* If this routine returns true all locks were acquired while maintaining
* the latch on leaf. If this routine returns false, locks may or may
* not have been acquired, and the routine should be called again after
* the client has researched the tree to reget the latch on the
* appropriate page.
- * (p>
- * As a side effect stores the value of the record handle of the current
- * scan lock.
*
* @return Whether locks were acquired without releasing latch on leaf.
*
@@ -205,8 +117,6 @@
* used if routine has to scan backward.
* @param btree the conglomerate info.
* @param pos Description of position of row to lock.
- * @param request_scan_lock Whether to request the page scan lock, should
- * only be requested once per page in the scan.
* @param lock_template A scratch area to use to read in rows.
* @param previous_key_lock Is this a previous key lock call?
* @param forUpdate Is the scan for update or for read only.
@@ -224,7 +134,6 @@
OpenBTree open_btree,
BTree btree,
BTreeRowPosition pos,
- boolean request_scan_lock,
FetchDescriptor lock_fetch_desc,
DataValueDescriptor[] lock_template,
RowLocation lock_row_loc,
@@ -248,19 +157,6 @@
boolean forUpdate)
throws StandardException;
- /**
- * Release the lock gotten by calling lockScan. This call can only be
- * made to release read scan locks, write scan locks must be held until
- * end of transaction.
- * <p>
- *
- * @param protectionHandle a <code>RecordHandle</code> that, when locked,
- * protects all the record ids on a page
- * @see RecordHandle#RECORD_ID_PROTECTION_HANDLE
- *
- **/
- abstract public void unlockScan(RecordHandle protectionHandle);
-
/**************************************************************************
* Abstract Protected lockNonScan*() locking methods of BTree:
Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/BTreeMaxScan.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/BTreeMaxScan.java?rev=754894&r1=754893&r2=754894&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/BTreeMaxScan.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/BTreeMaxScan.java Mon Mar 16 13:55:49 2009
@@ -77,7 +77,6 @@
* @exception StandardException Standard exception policy.
**/
private boolean fetchMaxRowFromBeginning(
- BTreeRowPosition pos,
DataValueDescriptor[] fetch_row)
throws StandardException
{
@@ -99,6 +98,8 @@
(DataValueDescriptor[]) null,
ScanController.NA);
+ BTreeRowPosition pos = scan_position;
+
positionAtStartForForwardScan(pos);
// At this point:
@@ -111,8 +112,10 @@
// seen.
boolean nulls_not_reached = true;
+ leaf_loop:
while ((pos.current_leaf != null) && nulls_not_reached)
{
+ slot_loop:
while ((pos.current_slot + 1) < pos.current_leaf.page.recordCount())
{
// unlock the previous row if doing read.
@@ -145,7 +148,6 @@
boolean latch_released =
!this.getLockingPolicy().lockScanRow(
this, this.getConglomerate(), pos,
- false,
init_lock_fetch_desc,
pos.current_lock_template,
pos.current_lock_row_loc,
@@ -157,7 +159,7 @@
latch_released =
test_errors(
this,
- "BTreeMaxScan_fetchNextGroup", false,
+ "BTreeMaxScan_fetchNextGroup", pos,
this.getLockingPolicy(),
pos.current_leaf, latch_released);
}
@@ -172,20 +174,35 @@
if (latch_released)
{
// lost latch on page in order to wait for row lock.
- // Because we have scan lock on page, we need only
- // call reposition() which will use the saved record
+ // Call reposition() which will use the saved record
// handle to reposition to the same spot on the page.
- // We don't have to search the
- // tree again, as we have the a scan lock on the page
- // which means the current_rh is valid to reposition on.
+ // If the row is no longer on the page, reposition()
+ // will take care of searching the tree and position
+ // on the correct page.
if (!reposition(pos, false))
{
- if (SanityManager.DEBUG)
+ // Could not position on the exact same row that was
+ // saved, which means that it has been purged.
+ // Reposition on the row immediately to the left of
+ // the purged row instead.
+ if (!reposition(pos, true))
{
- // can't fail while with scan lock
- SanityManager.THROWASSERT(
- "can not fail holding scan lock.");
+ if (SanityManager.DEBUG)
+ {
+ SanityManager.THROWASSERT(
+ "Cannot fail with 2nd param true");
+ }
+ // reposition will set pos.current_leaf to null if
+ // it returns false, so if this ever does fail in
+ // delivered code, expect a NullPointerException at
+ // the top of this loop when we call recordCount().
}
+
+ // Now we're positioned to the left of our saved
+ // position. Go to the top of the loop so that we move
+ // the scan to the next row and release the lock on
+ // the purged row.
+ continue slot_loop;
}
}
@@ -285,7 +302,6 @@
SanityManager.ASSERT(this.scan_state == SCAN_INIT);
SanityManager.ASSERT(pos.current_rh == null);
SanityManager.ASSERT(pos.current_positionKey == null);
- SanityManager.ASSERT(pos.current_scan_protectionHandle == null);
}
// Loop until you can lock the row previous to the first row to be
@@ -325,14 +341,12 @@
// can be made depending on isolation level.
//
// Note that this is not a "previous key" lock as the row we are
- // locking is the max row to return. Get the scan lock at the
- // same time.
+ // locking is the max row to return.
pos.current_slot--;
boolean latch_released =
!this.getLockingPolicy().lockScanRow(
this, this.getConglomerate(), pos,
- true,
init_lock_fetch_desc,
pos.current_lock_template,
pos.current_lock_row_loc,
@@ -345,7 +359,7 @@
latch_released =
test_errors(
this,
- "BTreeMaxScan_positionAtStartPosition", true,
+ "BTreeMaxScan_positionAtStartPosition", pos,
this.getLockingPolicy(), pos.current_leaf, latch_released);
}
@@ -363,8 +377,6 @@
}
this.scan_state = SCAN_INPROGRESS;
- pos.current_scan_protectionHandle =
- pos.current_leaf.page.getProtectionRecordHandle();
if (SanityManager.DEBUG)
SanityManager.ASSERT(pos.current_leaf != null);
@@ -460,7 +472,6 @@
boolean latch_released =
!this.getLockingPolicy().lockScanRow(
this, this.getConglomerate(), pos,
- false,
init_lock_fetch_desc,
pos.current_lock_template,
pos.current_lock_row_loc,
@@ -531,7 +542,7 @@
if (!max_found)
{
// max row in table was not last row in table
- max_found = fetchMaxRowFromBeginning(scan_position, fetch_row);
+ max_found = fetchMaxRowFromBeginning(fetch_row);
}
return(max_found);
Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/BTreePostCommit.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/BTreePostCommit.java?rev=754894&r1=754893&r2=754894&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/BTreePostCommit.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/BTreePostCommit.java Mon Mar 16 13:55:49 2009
@@ -384,6 +384,10 @@
}
page.purgeAtSlot(slot_no, 1, true);
+ // Tell scans positioned on this page to reposition
+ // because the row they are positioned on may have
+ // disappeared.
+ page.setRepositionNeeded();
if (SanityManager.DEBUG)
{
@@ -474,17 +478,12 @@
int num_possible_commit_delete =
leaf.page.recordCount() - 1 - leaf.page.nonDeletedRecordCount();
- if ((num_possible_commit_delete > 0) &&
- (btree_locking_policy.lockScanForReclaimSpace(leaf)))
+ if (num_possible_commit_delete > 0)
{
DataValueDescriptor[] scratch_template =
open_btree.getRuntimeMem().get_template(
open_btree.getRawTran());
- // Need to get an exclusive scan lock on the page before we can
- // do any sort of purges, otherwise other concurrent scans would
- // not work. If we can't get the lock NOWAIT, just give up on
- // purging rows.
Page page = leaf.page;
@@ -511,6 +510,10 @@
{
// the row is a committed deleted row, purge it.
page.purgeAtSlot(slot_no, 1, true);
+ // Tell scans positioned on this page to reposition
+ // because the row they are positioned on may have
+ // disappeared.
+ page.setRepositionNeeded();
}
}
}
Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/BTreeRowPosition.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/BTreeRowPosition.java?rev=754894&r1=754893&r2=754894&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/BTreeRowPosition.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/BTreeRowPosition.java Mon Mar 16 13:55:49 2009
@@ -22,13 +22,15 @@
package org.apache.derby.impl.store.access.btree;
+import org.apache.derby.iapi.error.StandardException;
+import org.apache.derby.iapi.services.io.FormatableBitSet;
import org.apache.derby.iapi.services.sanity.SanityManager;
import org.apache.derby.iapi.store.raw.Page;
-import org.apache.derby.iapi.store.raw.RecordHandle;
import org.apache.derby.iapi.store.access.RowUtil;
+import org.apache.derby.iapi.store.raw.FetchDescriptor;
import org.apache.derby.iapi.types.DataValueDescriptor;
import org.apache.derby.iapi.types.RowLocation;
@@ -46,19 +48,39 @@
**************************************************************************
*/
public DataValueDescriptor[] current_positionKey;
- public RecordHandle current_scan_protectionHandle;
public LeafControlRow current_leaf;
protected LeafControlRow next_leaf;
public DataValueDescriptor[] current_lock_template;
public RowLocation current_lock_row_loc;
+ /** The scan that owns this position object. */
+ private final BTreeScan parent;
+
+ /**
+ * The version number of the leaf page when this position was saved by
+ * key. Only valid if {@link #current_positionKey} is non-null. This value
+ * is used to decide whether repositioning should be performed by using
+ * the key, or if {@link #current_rh} could be used directly.
+ */
+ long versionWhenSaved;
+
+ /** Cached template for saving this position by key. */
+ private DataValueDescriptor[] positionKey_template;
+
+ /**
+ * Cached fetch descriptor that can be used to fetch the key columns that
+ * are not already fetched by the scan. The fetch descriptor is used when
+ * this position is about to be saved by its full key.
+ */
+ private FetchDescriptor savedFetchDescriptor;
+
/**************************************************************************
* Constructors for This class:
**************************************************************************
*/
- public BTreeRowPosition()
+ public BTreeRowPosition(BTreeScan parent)
{
- super();
+ this.parent = parent;
}
/**************************************************************************
@@ -80,6 +102,12 @@
public final void unlatch()
{
+ // This method is never called for a BTreeRowPosition. If it is ever
+ // used, make sure that the key is saved first, unless the scan won't
+ // use that page again. DERBY-2991
+ if (SanityManager.DEBUG) {
+ SanityManager.THROWASSERT("Did you really call me?!?");
+ }
if (current_leaf != null)
{
current_leaf.release();
@@ -88,6 +116,83 @@
current_slot = Page.INVALID_SLOT_NUMBER;
}
+ /**
+ * Save this position by key and release the latch on the current leaf.
+ * @throws StandardException if an error occurs while saving the position
+ * @see BTreeScan#savePositionAndReleasePage()
+ */
+ public void saveMeAndReleasePage() throws StandardException {
+ if (SanityManager.DEBUG) {
+ SanityManager.ASSERT(parent.scan_position == this);
+ }
+ parent.savePositionAndReleasePage();
+ }
+
+ /**
+ * Get a template into which the position key can be copied. The value
+ * is cached, so two calls to this method on the same object will return
+ * the same object.
+ *
+ * @return an array into which the position key can be copied
+ * @throws StandardException if an error occurs while allocating the
+ * template array
+ */
+ DataValueDescriptor[] getKeyTemplate() throws StandardException {
+ if (positionKey_template == null) {
+ positionKey_template = parent.getRuntimeMem().
+ get_row_for_export(parent.getRawTran());
+ }
+ return positionKey_template;
+ }
+
+ /**
+ * Get a fetch descriptor that can be used to fetch the missing columns
+ * in a partial key. The fetch descriptor is only created on the first
+ * call to this method. The returned descriptor will be cached, so
+ * subsequent calls will return the same descriptor and the arguments
+ * to this method should be the same between invokations.
+ *
+ * @param vcols an array which tells which columns the partial key contains
+ * (valid columns have non-zero values in the array)
+ * @param fullLength the length of the full key to create a fetch
+ * descriptor for (may be greater than {@code vcols.length})
+ * @return a fetch descriptor
+ */
+ FetchDescriptor getFetchDescriptorForSaveKey(int[] vcols, int fullLength) {
+ if (savedFetchDescriptor == null) {
+ FormatableBitSet columns = new FormatableBitSet(fullLength);
+ for (int i = 0; i < vcols.length; i++) {
+ if (vcols[i] == 0) {
+ // partial key does not have a valid value for this
+ // column, add it to the set of columns to fetch
+ columns.set(i);
+ }
+ }
+ // also fetch the columns behind the ones in the partial key
+ for (int i = vcols.length; i < fullLength; i++) {
+ columns.set(i);
+ }
+ savedFetchDescriptor =
+ new FetchDescriptor(fullLength, columns, null);
+ }
+
+ // Verify that the cached fetch descriptor matches the arguments
+ // (will fail if this method is not called with the same parameters
+ // as when the descriptor was created and cached).
+ if (SanityManager.DEBUG) {
+ FormatableBitSet fetchCols = savedFetchDescriptor.getValidColumns();
+ SanityManager.ASSERT(fullLength == fetchCols.size());
+ for (int i = 0; i < vcols.length; i++) {
+ SanityManager.ASSERT((vcols[i] == 0) == fetchCols.get(i));
+ }
+ for (int i = vcols.length; i < fullLength; i++) {
+ SanityManager.ASSERT(fetchCols.get(i));
+ }
+ }
+
+ return savedFetchDescriptor;
+ }
+
public final String toString()
{
String ret_string = null;
@@ -98,8 +203,6 @@
super.toString() +
"current_positionKey = " + current_positionKey +
";key = " + RowUtil.toString(current_positionKey) +
- ";current_scan_protectionHandle" +
- current_scan_protectionHandle +
";next_leaf" + next_leaf +
";current_leaf" + current_leaf;
}
Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/BTreeScan.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/BTreeScan.java?rev=754894&r1=754893&r2=754894&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/BTreeScan.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/BTreeScan.java Mon Mar 16 13:55:49 2009
@@ -27,7 +27,6 @@
import org.apache.derby.iapi.error.StandardException;
-import org.apache.derby.iapi.store.access.conglomerate.Conglomerate;
import org.apache.derby.iapi.store.access.conglomerate.LogicalUndo;
import org.apache.derby.iapi.store.access.conglomerate.ScanManager;
import org.apache.derby.iapi.store.access.conglomerate.TransactionManager;
@@ -257,7 +256,7 @@
// reset the "current" position to starting condition.
// RESOLVE (mmm) - "compile" this.
- scan_position = new BTreeRowPosition();
+ scan_position = new BTreeRowPosition(this);
scan_position.init();
@@ -348,7 +347,6 @@
(scan_state == SCAN_INIT) || (scan_state == SCAN_HOLD_INIT));
SanityManager.ASSERT(pos.current_rh == null);
SanityManager.ASSERT(pos.current_positionKey == null);
- SanityManager.ASSERT(pos.current_scan_protectionHandle == null);
}
// Loop until you can lock the row previous to the first row to be
@@ -436,25 +434,12 @@
latch_released =
!this.getLockingPolicy().lockScanRow(
this, this.getConglomerate(), pos,
- true,
init_lock_fetch_desc,
pos.current_lock_template,
pos.current_lock_row_loc,
true, init_forUpdate,
lock_operation);
}
- else
- {
- // Don't need to lock the "previous key" but still need to get
- // the scan lock to protect the position in the btree.
-
- latch_released =
- !this.getLockingPolicy().lockScan(
- pos.current_leaf, // the page we are positioned on.
- (ControlRow) null, // no other page to unlatch
- false, // lock for read.
- lock_operation); // not used.
- }
// special test to see if latch release code works
if (SanityManager.DEBUG)
@@ -462,7 +447,9 @@
latch_released =
test_errors(
this,
- "BTreeScan_positionAtStartPosition", true,
+ "BTreeScan_positionAtStartPosition",
+ null, // no need to save the position, since we'll
+ // retry the operation if the latch is released
this.getLockingPolicy(),
pos.current_leaf, latch_released);
}
@@ -481,8 +468,6 @@
}
this.scan_state = SCAN_INPROGRESS;
- pos.current_scan_protectionHandle =
- pos.current_leaf.page.getProtectionRecordHandle();
if (SanityManager.DEBUG)
SanityManager.ASSERT(pos.current_leaf != null);
@@ -514,7 +499,6 @@
SanityManager.ASSERT(pos.current_rh == null);
SanityManager.ASSERT(pos.current_positionKey == null);
- SanityManager.ASSERT(pos.current_scan_protectionHandle == null);
}
// Loop until you can lock the row previous to the first row to be
@@ -609,7 +593,6 @@
boolean latch_released =
!this.getLockingPolicy().lockScanRow(
this, this.getConglomerate(), pos,
- true,
init_lock_fetch_desc,
pos.current_lock_template,
pos.current_lock_row_loc,
@@ -621,7 +604,7 @@
latch_released =
test_errors(
this,
- "BTreeScan_positionAtStartPosition", true,
+ "BTreeScan_positionAtStartPosition", pos,
this.getLockingPolicy(), pos.current_leaf, latch_released);
}
@@ -639,8 +622,6 @@
}
this.scan_state = SCAN_INPROGRESS;
- pos.current_scan_protectionHandle =
- pos.current_leaf.page.getProtectionRecordHandle();
if (SanityManager.DEBUG)
SanityManager.ASSERT(pos.current_leaf != null);
@@ -654,8 +635,7 @@
* <p>
* Position to next page, keeping latch on previous page until we have
* latch on next page. This routine releases the latch on current_page
- * once it has successfully gotten both the latch on the next page and
- * the scan lock on the next page.
+ * once it has successfully gotten the latch on the next page.
*
* @param pos current row position of the scan.
*
@@ -665,55 +645,11 @@
BTreeRowPosition pos)
throws StandardException
{
- // RESOLVE (mikem) - not sure but someday in the future this
- // assert may not be true, but for now we always have the scan
- // lock when we call this routine.
- if (SanityManager.DEBUG)
- SanityManager.ASSERT(pos.current_scan_protectionHandle != null);
-
- while (true)
- {
- if ((pos.next_leaf =
- (LeafControlRow) pos.current_leaf.getRightSibling(this)) == null)
- {
- break;
- }
- boolean latch_released =
- !this.getLockingPolicy().lockScan(
- pos.next_leaf,
- (LeafControlRow) null, // no other latch currently
- false /* not for update */,
- ConglomerateController.LOCK_READ); // get read scan lock.
-
- // TESTING CODE:
- if (SanityManager.DEBUG)
- {
- latch_released =
- test_errors(
- this,
- "BTreeScan_positionAtNextPage", true,
- this.getLockingPolicy(), pos.next_leaf, latch_released);
- }
-
- if (!latch_released)
- {
- break;
- }
- }
+ pos.next_leaf = (LeafControlRow) pos.current_leaf.getRightSibling(this);
- // Now that we either have both latch and scan lock on next leaf, or
- // there is no next leaf we can release scan and latch on current page.
- if (SanityManager.DEBUG)
- {
- if (pos.current_scan_protectionHandle.getPageNumber() !=
- pos.current_leaf.page.getPageNumber()) {
- SanityManager.THROWASSERT(
- "pos.current_scan_protectionHandle = " +
- pos.current_scan_protectionHandle +
- "pos.current_leaf = " + pos.current_leaf);
- }
- }
+ // Now that we either have the latch on next leaf, or there is no next
+ // leaf, we can release the latch on the current page.
// unlock the previous row if doing read.
if (pos.current_rh != null)
@@ -722,14 +658,9 @@
pos, init_forUpdate);
}
- unlockCurrentScan(pos);
pos.current_leaf.release();
pos.current_leaf = pos.next_leaf;
- pos.current_scan_protectionHandle =
- (pos.current_leaf == null) ?
- null : pos.current_leaf.page.getProtectionRecordHandle();
-
// set up for scan to continue at beginning of next page.
pos.current_slot = Page.FIRST_SLOT_NUMBER;
pos.current_rh = null;
@@ -782,34 +713,11 @@
// unlock (thus why we only do the following code if we
// "don't" have lock, ie. pos.current_leaf== null).
- if (!reposition(pos, false))
- {
- if (SanityManager.DEBUG)
- {
- SanityManager.THROWASSERT(
- "can not fail while holding update row lock.");
- }
- }
-
this.getLockingPolicy().unlockScanRecordAfterRead(
pos, init_forUpdate);
-
- pos.current_rh = null;
- pos.current_leaf.release();
- pos.current_leaf = null;
}
}
-
- // Need to do this unlock in any case, until lock manager provides
- // a way to release locks associated with a compatibility space. This
- // scan lock is special, as it is a lock on the btree container rather
- // than the heap container. The open container on the btree actually
- // has a null locking policy so the close of that container does not
- // release this lock, need to explicitly unlock it here or when the
- // scan is closed as part of the abort the lock will not be released.
- unlockCurrentScan(pos);
-
pos.current_slot = Page.INVALID_SLOT_NUMBER;
pos.current_rh = null;
pos.current_positionKey = null;
@@ -835,15 +743,6 @@
throws StandardException
{
- // Need to do this unlock in any case, until lock manager provides
- // a way to release locks associated with a compatibility space. This
- // scan lock is special, as it is a lock on the btree container rather
- // than the heap container. The open container on the btree actually
- // has a null locking policy so the close of that container does not
- // release this lock, need to explicitly unlock it here or when the
- // scan is closed as part of the abort the lock will not be released.
- unlockCurrentScan(pos);
-
pos.current_slot = Page.INVALID_SLOT_NUMBER;
pos.current_rh = null;
pos.current_positionKey = null;
@@ -1000,8 +899,7 @@
* scan it is necessary to research the tree from the top using
* the copy of the row.
*
- * If the scan has saved it's position by key (and thus has given up the
- * scan lock on the page), there are a few cases where it is possible that
+ * There are a few cases where it is possible that
* the key no longer exists in the table. In the case of a scan held
* open across commit it is easy to imagine that the row the scan was
* positioned on could be deleted and subsequently purged from the table
@@ -1011,7 +909,7 @@
* opens scan and positions on row (1,2), transaction 2 deletes (1,2) and
* commits, transaction 1 inserts (1,3) which goes to same page as (1,2)
* and is going to cause a split, transaction 1 saves scan position as
- * key, gives up scan lock and then purges row (1, 2), when transaction
+ * key, and then purges row (1, 2), when transaction
* 1 resumes scan (1, 2) no longer exists. missing_row_for_key_ok
* parameter is added as a sanity check to make sure it ok that
* repositioning does not go to same row that we were repositioned on.
@@ -1048,17 +946,17 @@
new Integer(this.scan_state));
}
- // Either current_rh or positionKey is valid - the other is null.
+ // positionKey is always valid
if (SanityManager.DEBUG)
{
- if ((pos.current_rh == null) != (pos.current_positionKey != null))
+ if (pos.current_positionKey == null)
SanityManager.THROWASSERT(
"pos.current_rh = (" + pos.current_rh + "), " +
"pos.current_positionKey = (" +
pos.current_positionKey + ").");
}
- if (!((pos.current_rh == null) == (pos.current_positionKey != null)))
+ if (pos.current_positionKey == null)
{
throw StandardException.newException(
SQLState.BTREE_SCAN_INTERNAL_ERROR,
@@ -1066,102 +964,72 @@
new Boolean(pos.current_positionKey == null));
}
- if (pos.current_positionKey == null)
+ // If current_rh is non-null, we know the exact physical position of
+ // the scan before the latch on the leaf was released. Check if the
+ // row is still on that position so that we don't need to renavigate
+ // from the root of the B-tree in the common case.
+ if (pos.current_rh != null)
{
// Reposition to remembered spot on page.
- if (SanityManager.DEBUG)
- SanityManager.ASSERT(pos.current_scan_protectionHandle != null);
- pos.current_leaf = (LeafControlRow)
- ControlRow.get(this, pos.current_rh.getPageNumber());
- pos.current_slot =
- pos.current_leaf.page.getSlotNumber(pos.current_rh);
+ // Get the page object. If getPage() returns null, the page is
+ // not valid (could for instance have been removed by compress
+ // table) so we need to reposition by key instead.
+ Page page = container.getPage(pos.current_rh.getPageNumber());
+ if (page != null) {
+ ControlRow row =
+ ControlRow.getControlRowForPage(container, page);
+ if (row instanceof LeafControlRow &&
+ !row.page.isRepositionNeeded(pos.versionWhenSaved)) {
+ // No rows have been moved off the page after we released
+ // the latch, and the page is still a leaf page. No need
+ // to reposition by key.
+ pos.current_leaf = (LeafControlRow) row;
+ pos.current_slot = row.page.getSlotNumber(pos.current_rh);
+ pos.current_positionKey = null;
+ return true;
+ }
+ // We couldn't use the position specified by current_rh, so we
+ // need to reposition by key and may find the row on another
+ // page. Therefore, give up the latch on this page.
+ row.release();
+ }
}
- else
- {
- // RESOLVE (mikem) - not sure but someday in the future this
- // assert may not be true, but for now we always release the
- // scan lock when we save the row away as the current position.
- if (SanityManager.DEBUG)
- SanityManager.ASSERT(pos.current_scan_protectionHandle == null);
- SearchParameters sp =
+ SearchParameters sp =
new SearchParameters(
pos.current_positionKey,
// this is a full key search, so this arg is not used.
SearchParameters.POSITION_LEFT_OF_PARTIAL_KEY_MATCH,
init_template, this, false);
- // latch/lock loop, continue until you can get scan lock on page
- // while holding page latched without waiting.
-
-
- boolean latch_released;
- do
- {
- pos.current_leaf = (LeafControlRow)
+ pos.current_leaf = (LeafControlRow)
ControlRow.get(this, BTree.ROOTPAGEID).search(sp);
- if (sp.resultExact || missing_row_for_key_ok)
- {
- // RESOLVE (mikem) - we could have a scan which always
- // maintained it's position by key value, or we could
- // optimize and delay this lock until we were about to
- // give up the latch. But it is VERY likely we will get
- // the lock since we have the latch on the page.
- //
- // In order to be successfully positioned we must get the
- // scan lock again.
- latch_released =
- !this.getLockingPolicy().lockScan(
- pos.current_leaf,
- (LeafControlRow) null, // no other latch currently
- false /* not for update */,
- ConglomerateController.LOCK_READ); // read lock on scan position
-
- // TESTING CODE:
- if (SanityManager.DEBUG)
- {
- latch_released =
- test_errors(
- this,
- "BTreeScan_reposition", true,
- this.getLockingPolicy(),
- pos.current_leaf, latch_released);
- }
- }
- else
- {
- // Did not find key to exactly position on.
+ if (!sp.resultExact && !missing_row_for_key_ok)
+ {
+ // Did not find key to exactly position on.
- pos.current_leaf.release();
- pos.current_leaf = null;
- return(false);
- }
+ pos.current_leaf.release();
+ pos.current_leaf = null;
+ return (false);
+ }
- } while (latch_released);
+ pos.current_slot = sp.resultSlot;
- pos.current_scan_protectionHandle =
- pos.current_leaf.page.getProtectionRecordHandle();
- pos.current_slot = sp.resultSlot;
- pos.current_positionKey = null;
+ // Need to update current_rh to the new position. current_rh should
+ // only be non-null if the row was locked when the position was saved,
+ // so we don't set it here if its old value is null.
+ if (pos.current_rh != null) {
+ pos.current_rh = pos.current_leaf.page.
+ getRecordHandleAtSlot(pos.current_slot);
}
+ pos.current_positionKey = null;
+
return(true);
}
- /**
- * Unlock the scan protection row for the current scan.
- *
- * @param pos position of the scan
- */
- private void unlockCurrentScan(BTreeRowPosition pos) {
- if (pos.current_scan_protectionHandle != null) {
- getLockingPolicy().unlockScan(pos.current_scan_protectionHandle);
- pos.current_scan_protectionHandle = null;
- }
- }
-
/*
** Public Methods of BTreeScan
*/
@@ -1340,7 +1208,6 @@
boolean latch_released =
!this.getLockingPolicy().lockScanRow(
this, this.getConglomerate(), scan_position,
- false,
init_lock_fetch_desc,
scan_position.current_lock_template,
scan_position.current_lock_row_loc,
@@ -1349,12 +1216,9 @@
if (latch_released)
{
// lost latch on page in order to wait for row lock.
- // Because we have scan lock on page, we need only
- // call reposition() which will use the saved record
- // handle to reposition to the same spot on the page.
- // We don't have to search the
- // tree again, as we have the a scan lock on the page
- // which means the current_rh is valid to reposition on.
+ // reposition() will take care of the complexity of
+ // positioning on the correct page if the row has been
+ // moved to another page.
if (reposition(scan_position, false))
{
throw StandardException.newException(
@@ -1414,8 +1278,7 @@
if (scan_position.current_leaf != null)
{
// release latch on page
- scan_position.current_leaf.release();
- scan_position.current_leaf = null;
+ savePositionAndReleasePage();
}
}
@@ -1515,8 +1378,7 @@
if (scan_position.current_leaf != null)
{
// release latch on page.
- scan_position.current_leaf.release();
- scan_position.current_leaf = null;
+ savePositionAndReleasePage();
}
}
}
@@ -1592,8 +1454,7 @@
if (scan_position.current_leaf != null)
{
// release latch on page.
- scan_position.current_leaf.release();
- scan_position.current_leaf = null;
+ savePositionAndReleasePage();
}
}
@@ -1707,8 +1568,7 @@
if (scan_position.current_leaf != null)
{
// release latch on page.
- scan_position.current_leaf.release();
- scan_position.current_leaf = null;
+ savePositionAndReleasePage();
}
}
@@ -2097,35 +1957,10 @@
if (scan_position.current_rh != null)
{
- // reposition to get record handle if we don't have it.
-
- if (!reposition(scan_position, false))
- {
- if (SanityManager.DEBUG)
- {
- SanityManager.THROWASSERT(
- "can not fail while holding update row lock.");
- }
- }
-
this.getLockingPolicy().unlockScanRecordAfterRead(
scan_position, init_forUpdate);
-
- scan_position.current_rh = null;
- scan_position.current_leaf.release();
- scan_position.current_leaf = null;
}
-
- // Need to do this unlock in any case, until lock manager provides
- // a way to release locks associated with a compatibility space. This
- // scan lock is special, as it is a lock on the btree container rather
- // than the heap container. The open container on the btree actually
- // has a null locking policy so the close of that container does not
- // release this lock, need to explicitly unlock it here or when the
- // scan is closed as part of the abort the lock will not be released.
- unlockCurrentScan(scan_position);
-
scan_position.current_slot = Page.INVALID_SLOT_NUMBER;
scan_position.current_rh = null;
scan_position.current_positionKey = null;
@@ -2263,16 +2098,16 @@
if (SanityManager.DEBUG)
{
SanityManager.ASSERT(scan_position != null);
+ SanityManager.ASSERT(
+ scan_position.current_positionKey != null,
+ "Position must be saved by key when tx ends");
}
- if (scan_position.current_positionKey == null)
- {
- // save position of scan by key rather than location so
- // that we can recover if the page with the position
- // disappears while we don't have a scan lock.
+ // When the transaction ends, we release all the locks
+ // obtained in this scan, so the row we're positioned on is
+ // no longer locked.
+ scan_position.current_rh = null;
- savePosition();
- }
this.scan_state = SCAN_HOLD_INPROGRESS;
}
else if (this.scan_state == SCAN_INIT)
@@ -2287,181 +2122,95 @@
}
/**
- * Do work necessary to maintain the current position in the scan.
- * <p>
- * Save the current position of the scan as a key.
- * Do whatever is necessary to maintain the current position of the scan.
- * For some conglomerates this may be a no-op.
- *
- * <p>
- * @exception StandardException Standard exception policy.
- **/
- private void savePosition()
- throws StandardException
- {
- if (this.scan_state == SCAN_INPROGRESS)
- {
- // Either current_rh or positionKey is valid - the other is null.
- if (SanityManager.DEBUG)
- {
- SanityManager.ASSERT(
- (scan_position.current_rh == null) ==
- (scan_position.current_positionKey != null));
+ * Save the current scan position by key and release the latch on the leaf
+ * that's being scanned. This method should be called if the latch on a
+ * leaf needs to be released in the middle of the scan. The scan can
+ * later reposition to the saved position by calling {@code reposition()}.
+ *
+ * @param partialKey known parts of the key that should be saved, or
+ * {@code null} if the entire key is unknown and will have to be fetched
+ * from the page
+ * @param vcols an array which tells which columns of the partial key are
+ * valid (key columns that have 0 in this array are not valid, and their
+ * values must be fetched from the page), or {@code null} if all the
+ * columns are valid
+ * @throws StandardException if an error occurs while saving the position
+ * @see #reposition(BTreeRowPosition, boolean)
+ */
+ void savePositionAndReleasePage(DataValueDescriptor[] partialKey,
+ int[] vcols)
+ throws StandardException {
+
+ final Page page = scan_position.current_leaf.getPage();
+
+ if (SanityManager.DEBUG) {
+ SanityManager.ASSERT(page.isLatched(), "Page is not latched");
+ SanityManager.ASSERT(scan_position.current_positionKey == null,
+ "Scan position already saved");
+
+ if (partialKey == null) {
+ SanityManager.ASSERT(vcols == null);
+ }
+ if (vcols != null) {
+ SanityManager.ASSERT(partialKey != null);
+ SanityManager.ASSERT(vcols.length <= partialKey.length);
}
+ }
- try
- {
- if (scan_position.current_rh != null)
- {
- // if scan position is not saved by key, then make it so.
-
- // must reposition to get the page latched.
-
- if (reposition(scan_position, false))
- {
- scan_position.current_positionKey =
- runtime_mem.get_row_for_export(getRawTran());
-
-
- Page page = scan_position.current_leaf.getPage();
-
-
- RecordHandle rh =
- page.fetchFromSlot(
- (RecordHandle) null,
- page.getSlotNumber(scan_position.current_rh),
- scan_position.current_positionKey,
- (FetchDescriptor) null,
- true);
-
- if (SanityManager.DEBUG)
- {
- SanityManager.ASSERT(rh != null);
- }
-
- scan_position.current_rh = null;
- scan_position.current_slot = Page.INVALID_SLOT_NUMBER;
-
- // release scan lock now that the row is saved away.
- unlockCurrentScan(scan_position);
-
- }
- else
- {
- // this should never happen as we hold the scan lock
- // on the page while maintaining the position by
- // recordhandle - reposition should always work in this
- // case.
-
- if (SanityManager.DEBUG)
- SanityManager.THROWASSERT(
- "Must always be able to reposition.");
+ try {
+ DataValueDescriptor[] fullKey = scan_position.getKeyTemplate();
+
+ FetchDescriptor fetchDescriptor = null;
+ boolean haveAllColumns = false;
+ if (partialKey != null) {
+ int copiedCols = 0;
+ final int partialKeyLength =
+ (vcols == null) ? partialKey.length : vcols.length;
+ for (int i = 0; i < partialKeyLength; i++) {
+ if (vcols == null || vcols[i] != 0) {
+ fullKey[i].setValue(partialKey[i]);
+ copiedCols++;
}
}
-
- }
- finally
- {
-
- if (scan_position.current_leaf != null)
- {
- // release latch on page
- scan_position.current_leaf.release();
- scan_position.current_leaf = null;
+ if (copiedCols < fullKey.length) {
+ fetchDescriptor =
+ scan_position.getFetchDescriptorForSaveKey(
+ vcols, fullKey.length);
+ } else {
+ haveAllColumns = true;
}
}
- }
-
- }
-
- /**
- * Do work necessary to maintain the current position in the scan.
- * <p>
- * The latched page in the conglomerate "congomid" is changing, do
- * whatever is necessary to maintain the current position of the scan.
- * For some conglomerates this may be a no-op.
- * <p>
- *
- * @param conglom Conglomerate object of the conglomerate being changed.
- * @param page Page in the conglomerate being changed.
- *
- * @exception StandardException Standard exception policy.
- **/
- public void savePosition(Conglomerate conglom, Page page)
- throws StandardException
- {
- // page should be latched by split. This scan is assuming that latch
- // and reading off it's key from the page under the split's latch.
- // A lock should have already been gotten on this row.
-
-
- if (SanityManager.DEBUG)
- {
- SanityManager.ASSERT(page.isLatched());
- }
-
- /*
- System.out.println(
- "Saving position in btree at top: " +
- " this.conglomerate = " + this.conglomerate +
- " this.scan_state = " + this.scan_state);
- SanityManager.DEBUG_PRINT("savePosition()",
- "Saving position in btree at top: " +
- " this.conglomerate = " + this.conglomerate +
- " this.scan_state = " + this.scan_state);
- */
-
- if ((this.getConglomerate() == conglom) &&
- (this.scan_state == SCAN_INPROGRESS))
- {
- // Either current_rh or positionKey is valid - the other is null.
- if (SanityManager.DEBUG)
- {
- SanityManager.ASSERT(
- (scan_position.current_rh == null) ==
- (scan_position.current_positionKey != null));
- }
-
- /*
- SanityManager.DEBUG_PRINT("savePosition()",
- "Saving position in btree: " +
- ";current_scan_pageno = " + this.current_scan_pageno +
- "this.current_rh = " + this.current_rh +
- ";page.getPageNumber() = " + page.getPageNumber() +
- ((this.current_rh != null) ?
- (";this.current_rh.getPageNumber() = " +
- this.current_rh.getPageNumber()) : ""));
- */
-
- if (scan_position.current_rh != null &&
- page.getPageNumber() ==
- scan_position.current_rh.getPageNumber())
- {
- scan_position.current_positionKey =
- runtime_mem.get_row_for_export(getRawTran());
-
- RecordHandle rh =
- page.fetchFromSlot(
+ if (!haveAllColumns) {
+ RecordHandle rh = page.fetchFromSlot(
(RecordHandle) null,
- page.getSlotNumber(scan_position.current_rh),
- scan_position.current_positionKey,
- (FetchDescriptor) null,
+ scan_position.current_slot,
+ fullKey,
+ fetchDescriptor,
true);
- if (SanityManager.DEBUG)
- {
- SanityManager.ASSERT(rh != null);
+ if (SanityManager.DEBUG) {
+ SanityManager.ASSERT(rh != null, "Row not found");
}
+ }
- scan_position.current_rh = null;
- scan_position.current_slot = Page.INVALID_SLOT_NUMBER;
+ scan_position.current_positionKey = fullKey;
+ // Don't null out current_rh, we might be able to use it later if
+ // no rows are moved off the page.
+ //scan_position.current_rh = null;
+ scan_position.versionWhenSaved = page.getPageVersion();
+ scan_position.current_slot = Page.INVALID_SLOT_NUMBER;
- // release the scan lock now that we have saved away the row.
- unlockCurrentScan(scan_position);
- }
+ } finally {
+ scan_position.current_leaf.release();
+ scan_position.current_leaf = null;
}
- }
+ }
+
+ /** Shortcut for for savePositionAndReleasePage(null,null). */
+ void savePositionAndReleasePage() throws StandardException {
+ savePositionAndReleasePage(null, null);
+ }
public RecordHandle getCurrentRecordHandleForDebugging()
{
Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/LeafControlRow.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/LeafControlRow.java?rev=754894&r1=754893&r2=754894&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/LeafControlRow.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/store/access/btree/LeafControlRow.java Mon Mar 16 13:55:49 2009
@@ -629,38 +629,6 @@
branchrow.getRow(), splitrow, flag));
}
- // Before moving the rows on the page, while having the latch on the
- // page, notify btree scans that the rows on this page may be moving
- // onto another page.
- //
- // RESOLVE (mikem) - need to pass conlgomid.
- // RESOLVE (mikem) - some optimization later, we only need to notify
- // the scans which are positioned on moving rows.
- if (SanityManager.DEBUG)
- SanityManager.ASSERT(open_btree.init_open_user_scans != null);
-
- open_btree.init_open_user_scans.saveScanPositions(
- open_btree.getConglomerate(), this.page);
-
- // Get exclusive RECORD_ID_PROTECTION_HANDLE lock to make sure that
- // we wait for scans in other transactions to move off of this page
- // before we split.
-
- if (!open_btree.getLockingPolicy().lockScan(
- this, parent_page, true /* for update */,
- ConglomerateController.LOCK_UPD))
- {
- // we had to give up latches on this and parent_page to get the
- // split lock. Redo the whole split pass as we have lost our
- // latches. Just returning is ok, as the caller can not assume
- // that split has succeeded in making space. Note that at this
- // point in the split no write work has been done in the current
- // internal transaction, so giving up here is fairly cheap.
-
- // RESOLVE RLL PERFORMANCE - we could keep a stack of visited
- // pages so as to not have to redo the complete search.
- return(current_leaf_pageno);
- }
// Create a new leaf page under the parent.
LeafControlRow newleaf =
@@ -786,6 +754,10 @@
}
}
+ // Set a hint in the page that any scan positioned on it needs
+ // to reposition because rows may have moved off the page.
+ page.setRepositionNeeded();
+
// At this point a unit of work in the split down the tree has
// been performed in an internal transaction. This work must
// be committed before any latches are released.
@@ -827,36 +799,6 @@
BranchControlRow branchroot = null;
LeafControlRow newleaf = null;
-
- // Before moving the rows on the page, while having the latch on the
- // page, notify btree scans that the rows on this page may be moving
- // onto another page.
- //
- open_btree.init_open_user_scans.saveScanPositions(
- open_btree.getConglomerate(), leafroot.page);
-
- // Get exclusive RECORD_ID_PROTECTION_HANDLE lock to make sure that
- // we wait for scans in other transactions to move off of this page
- // before we grow root. If we don't wait, scanners in other
- // transactions may be positioned on the leaf page which we are
- // about to make into a branch page.
-
- if (!open_btree.getLockingPolicy().lockScan(
- leafroot, (ControlRow) null,
- true /* for update */,
- ConglomerateController.LOCK_UPD))
- {
- // We had to give up latches on leafroot to get the
- // split lock. Redo the whole split pass as we have lost our
- // latches - which may mean that the root has grown when we gave
- // up the latch. Just returning is ok, as the caller can not assume
- // that grow root has succeeded in making space. Note that at this
- // point in the split no write work has been done in the current
- // internal transaction, so giving up here is fairly cheap.
-
- return;
- }
-
// Allocate a new leaf page under the existing leaf root.
newleaf = LeafControlRow.allocate(open_btree, leafroot);
@@ -941,6 +883,10 @@
branchroot.checkConsistency(open_btree, null, false);
}
}
+
+ // Set a hint in the page that any scan positioned on it needs
+ // to reposition because the page is no longer a leaf.
+ branchroot.page.setRepositionNeeded();
// At this point a unit of work in the split down the tree has
// been performed in an internal transaction. This work must