diff --git a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/node-common-SqlMap.xml b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/node-common-SqlMap.xml index f636838e14..7a61f90485 100644 --- a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/node-common-SqlMap.xml +++ b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/node-common-SqlMap.xml @@ -116,6 +116,10 @@ + + + + @@ -404,6 +408,17 @@ + + update alf_node set + version = version + 1, + transaction_id = #{idOne} + where + id in + + #{item} + + + update alf_node_assoc set assoc_index = #{assocIndex} diff --git a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/node-select-children-SqlMap.xml b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/node-select-children-SqlMap.xml index c371e55481..69f78d34eb 100644 --- a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/node-select-children-SqlMap.xml +++ b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/node-select-children-SqlMap.xml @@ -11,4 +11,20 @@ + + \ No newline at end of file diff --git a/config/alfresco/ibatis/org.hibernate.dialect.MySQLInnoDBDialect/node-select-children-SqlMap.xml b/config/alfresco/ibatis/org.hibernate.dialect.MySQLInnoDBDialect/node-select-children-SqlMap.xml index ee4d233790..33ce29c1f4 100644 --- a/config/alfresco/ibatis/org.hibernate.dialect.MySQLInnoDBDialect/node-select-children-SqlMap.xml +++ b/config/alfresco/ibatis/org.hibernate.dialect.MySQLInnoDBDialect/node-select-children-SqlMap.xml @@ -4,13 +4,32 @@ - + + + + + diff --git a/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java b/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java index 542c55121d..11f3dba8ae 100644 --- a/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java +++ b/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java @@ -85,7 +85,6 @@ import org.alfresco.util.Pair; import org.alfresco.util.PropertyCheck; import org.alfresco.util.ReadWriteLockExecuter; import org.alfresco.util.SerializationUtils; -import org.apache.commons.lang.mutable.MutableInt; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.dao.ConcurrencyFailureException; @@ -448,56 +447,55 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO * where the child associations or nodes are modified en-masse. * * @param parentNodeId the parent node of all child nodes to be invalidated (may be null) + * @param touchNodes true to also touch the nodes * @return the number of child associations found (might be capped) */ - private int invalidateNodeChildrenCaches(Long parentNodeId) + private int invalidateNodeChildrenCaches(Long parentNodeId, boolean primary, boolean touchNodes) { - // Select all children - final MutableInt count = new MutableInt(0); - ChildAssocRefQueryCallback callback = new ChildAssocRefQueryCallback() + Long txnId = getCurrentTransaction().getId(); + + int count = 0; + List childNodeIds = new ArrayList(256); + Long minAssocIdInclusive = Long.MIN_VALUE; + while (minAssocIdInclusive != null) { - private boolean isClearOn = false; - - public boolean preLoadNodes() + childNodeIds.clear(); + List childAssocs = selectChildNodeIds( + parentNodeId, + Boolean.valueOf(primary), + minAssocIdInclusive, + 256); + // Remove the cache entries as we go + for (ChildAssocEntity childAssoc : childAssocs) { - return false; - } - - @Override - public boolean orderResults() - { - return false; - } - - public boolean handle( - Pair childAssocPair, - Pair parentNodePair, - Pair childNodePair) - { - if (isClearOn) + Long childAssocId = childAssoc.getId(); + if (childAssocId.compareTo(minAssocIdInclusive) < 0) { - // We have already decided to drop ALL cache entries - return false; + throw new RuntimeException("Query results did not increase for assoc ID"); } - else if (count.intValue() >= 1000) + else { - // That's enough. Instead of walking thousands of entries - // we just drop the cache at this stage - AbstractNodeDAOImpl.this.clearCaches(); - isClearOn = true; - return false; // No more, please + minAssocIdInclusive = new Long(childAssocId.longValue() + 1L); } - count.increment(); - invalidateNodeCaches(childNodePair.getFirst()); - return true; + // Invalidate the node cache + Long childNodeId = childAssoc.getChildNode().getId(); + childNodeIds.add(childNodeId); + invalidateNodeCaches(childNodeId); + count++; } - - public void done() + // Bring all the nodes into the transaction, if required + if (touchNodes) { - } - }; - selectChildAssocs(parentNodeId, null, null, null, null, null, callback); - return count.intValue(); + updateNodes(txnId, childNodeIds); + } + // Now break out if we didn't have the full set of results + if (childAssocs.size() < 256) + { + break; + } + } + // Done + return count; } /** @@ -508,10 +506,10 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO private void invalidateNodeCaches(Long nodeId) { // Take the current value from the nodesCache and use that to invalidate the other caches - Pair nodePair = nodesCache.getByKey(nodeId); - if (nodePair != null) + Node node = nodesCache.getValue(nodeId); + if (node != null) { - NodeVersionKey nodeVersionKey = nodePair.getSecond().getNodeVersionKey(); + NodeVersionKey nodeVersionKey = node.getNodeVersionKey(); invalidateNodeCaches(nodeVersionKey, true, true, true); } // Finally remove the node reference @@ -1279,7 +1277,8 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO // The new node will have new data not present in the cache, yet // TODO: Look to move the data in a cache-efficient way invalidateNodeCaches(newChildNodeId); - invalidateNodeChildrenCaches(newChildNodeId); + invalidateNodeChildrenCaches(newChildNodeId, true, true); + invalidateNodeChildrenCaches(newChildNodeId, false, true); // Now update the original to be 'deleted' NodeUpdateEntity childNodeUpdate = new NodeUpdateEntity(); childNodeUpdate.setId(childNodeId); @@ -1339,7 +1338,11 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO childAssocRetryingHelper.doWithRetry(callback); // Check for cyclic relationships - cycleCheck(newChildNodeId); + // TODO: This adds a lot of overhead when moving hierarchies. + // While getPaths is faster, it would be better to avoid the parentAssocsCache + // completely. + getPaths(newChildNode.getNodePair(), false); +// cycleCheck(newChildNodeId); // Update ACLs for moved tree Long newParentAclId = newParentNode.getAclId(); @@ -1637,7 +1640,7 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO primaryParentNodeId, optionalOldSharedAlcIdInAdditionToNull, newSharedAclId); - invalidateNodeChildrenCaches(primaryParentNodeId); + invalidateNodeChildrenCaches(primaryParentNodeId, true, false); } public void deleteNode(Long nodeId) @@ -1689,7 +1692,8 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO deleteNodeAssocsToAndFrom(nodeId); // Remove child associations (invalidate children) - invalidateNodeChildrenCaches(nodeId); + invalidateNodeChildrenCaches(nodeId, true, true); + invalidateNodeChildrenCaches(nodeId, false, true); deleteChildAssocsToAndFrom(nodeId); // Remove aspects @@ -2996,7 +3000,6 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO parentNodeId, assocTypeQName, assocQName, - null, maxResults, new ChildAssocRefBatchingQueryCallback(resultsCallback)); } @@ -3902,6 +3905,7 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO protected abstract int updateStore(StoreEntity store); protected abstract Long insertNode(NodeEntity node); protected abstract int updateNode(NodeUpdateEntity nodeUpdate); + protected abstract int updateNodes(Long txnId, List nodeIds); protected abstract void updatePrimaryChildrenSharedAclId( Long txnId, Long primaryParentNodeId, @@ -3947,6 +3951,11 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO protected abstract int updateChildAssocsUniqueName(Long childNodeId, String name); protected abstract int deleteChildAssocsToAndFrom(Long nodeId); protected abstract ChildAssocEntity selectChildAssoc(Long assocId); + protected abstract List selectChildNodeIds( + Long nodeId, + Boolean isPrimary, + Long minAssocIdInclusive, + int maxResults); protected abstract List selectPrimaryChildAcls(Long nodeId); protected abstract List selectChildAssoc( Long parentNodeId, @@ -3968,7 +3977,6 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO Long parentNodeId, QName assocTypeQName, QName assocQName, - Long minAssocIdInclusive, int maxResults, ChildAssocRefQueryCallback resultsCallback); protected abstract void selectChildAssocs( diff --git a/source/java/org/alfresco/repo/domain/node/ibatis/NodeDAOImpl.java b/source/java/org/alfresco/repo/domain/node/ibatis/NodeDAOImpl.java index 8fc300a8ef..0ef4de257c 100644 --- a/source/java/org/alfresco/repo/domain/node/ibatis/NodeDAOImpl.java +++ b/source/java/org/alfresco/repo/domain/node/ibatis/NodeDAOImpl.java @@ -85,6 +85,7 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl private static final String SELECT_STORE_ROOT_NODE_BY_REF = "alfresco.node.select_StoreRootNodeByRef"; private static final String INSERT_NODE = "alfresco.node.insert.insert_Node"; private static final String UPDATE_NODE = "alfresco.node.update_Node"; + private static final String UPDATE_NODE_BULK_TOUCH = "alfresco.node.update_NodeBulkTouch"; private static final String DELETE_NODE_BY_ID = "alfresco.node.delete_NodeById"; private static final String DELETE_NODES_BY_TXN_COMMIT_TIME = "alfresco.node.delete_NodesByTxnCommitTime"; private static final String SELECT_NODE_BY_ID = "alfresco.node.select_NodeById"; @@ -109,6 +110,7 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl private static final String SELECT_NODE_ASSOCS_BY_TARGET = "alfresco.node.select_NodeAssocsByTarget"; private static final String SELECT_NODE_ASSOC_BY_ID = "alfresco.node.select_NodeAssocById"; private static final String SELECT_NODE_ASSOCS_MAX_INDEX = "alfresco.node.select_NodeAssocsMaxId"; + private static final String SELECT_CHILD_NODE_IDS = "alfresco.node.select.children.select_ChildNodeIds_Limited"; private static final String SELECT_NODE_PRIMARY_CHILD_ACLS = "alfresco.node.select_NodePrimaryChildAcls"; private static final String INSERT_CHILD_ASSOC = "alfresco.node.insert.insert_ChildAssoc"; private static final String DELETE_CHILD_ASSOC_BY_ID = "alfresco.node.delete_ChildAssocById"; @@ -317,6 +319,19 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl return template.update(UPDATE_NODE, nodeUpdate); } + @Override + protected int updateNodes(Long txnId, List nodeIds) + { + if (nodeIds.size() == 0) + { + return 0; + } + IdsEntity ids = new IdsEntity(); + ids.setIdOne(txnId); + ids.setIds(nodeIds); + return template.update(UPDATE_NODE_BULK_TOUCH, ids); + } + @Override protected void updatePrimaryChildrenSharedAclId( Long txnId, @@ -880,6 +895,25 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl return (ChildAssocEntity) template.selectOne(SELECT_CHILD_ASSOC_BY_ID, assoc); } + @SuppressWarnings("unchecked") + @Override + protected List selectChildNodeIds( + Long nodeId, + Boolean isPrimary, + Long minAssocIdInclusive, + int maxResults) + { + ChildAssocEntity assoc = new ChildAssocEntity(); + NodeEntity parentNode = new NodeEntity(); + parentNode.setId(nodeId); + assoc.setParentNode(parentNode); + assoc.setPrimary(isPrimary); + assoc.setId(minAssocIdInclusive); + + RowBounds rowBounds = new RowBounds(0, maxResults); + return (List) template.selectList(SELECT_CHILD_NODE_IDS, assoc, rowBounds); + } + @SuppressWarnings("unchecked") @Override public List selectPrimaryChildAcls(Long nodeId) @@ -1053,7 +1087,6 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl Long parentNodeId, QName assocTypeQName, QName assocQName, - Long minAssocIdInclusive, final int maxResults, ChildAssocRefQueryCallback resultsCallback) { @@ -1062,7 +1095,6 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl NodeEntity parentNode = new NodeEntity(); parentNode.setId(parentNodeId); assoc.setParentNode(parentNode); - assoc.setId(minAssocIdInclusive); // Type QName if (assocTypeQName != null)