diff --git a/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java b/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java index 493f164b63..19d955e1c5 100644 --- a/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java +++ b/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java @@ -153,11 +153,11 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO private EntityLookupCache nodesCache; /** * Cache for the QName values:
- * KEY: ID
+ * KEY: NodeVersionKey
* VALUE: Set<QName>
* VALUE KEY: None
*/ - private EntityLookupCache, Serializable> aspectsCache; + private EntityLookupCache, Serializable> aspectsCache; /** * Cache for the Node properties:
* KEY: NodeVersionKey
@@ -184,7 +184,7 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO // Caches rootNodesCache = new EntityLookupCache(new RootNodesCacheCallbackDAO()); nodesCache = new EntityLookupCache(new NodesCacheCallbackDAO()); - aspectsCache = new EntityLookupCache, Serializable>(new AspectsCallbackDAO()); + aspectsCache = new EntityLookupCache, Serializable>(new AspectsCallbackDAO()); propertiesCache = new EntityLookupCache, Serializable>(new PropertiesCallbackDAO()); parentAssocsCache = new EntityLookupCache(new ParentAssocsCallbackDAO()); } @@ -312,9 +312,9 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO * * @param aspectsCache the cache */ - public void setAspectsCache(SimpleCache> aspectsCache) + public void setAspectsCache(SimpleCache> aspectsCache) { - this.aspectsCache = new EntityLookupCache, Serializable>( + this.aspectsCache = new EntityLookupCache, Serializable>( aspectsCache, CACHE_REGION_ASPECTS, new AspectsCallbackDAO()); @@ -325,7 +325,7 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO * * @param propertiesCache the cache */ - public void setPropertiesCache(SimpleCache> propertiesCache) + public void setPropertiesCache(SimpleCache> propertiesCache) { this.propertiesCache = new EntityLookupCache, Serializable>( propertiesCache, @@ -535,10 +535,11 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO // Properties propertiesCache.removeByKey(nodeVersionKey); // Aspects - aspectsCache.removeByKey(nodeId); + aspectsCache.removeByKey(nodeVersionKey); // Parent Assocs parentAssocsCache.removeByKey(nodeId); } + invalidateCachesByNodeId(nodeId, null, parentAssocsCache); // Finally remove the node reference nodesCache.removeByKey(nodeId); } @@ -893,6 +894,8 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO return nodeTxnId.equals(currentTxnId); } + // TODO: Restore to simple version + // TODO: Add read-through option for caches public Status getNodeRefStatus(NodeRef nodeRef) { Node node = null; @@ -971,7 +974,23 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO Pair pair = nodesCache.getByKey(nodeId); if (pair == null || pair.getSecond().getDeleted()) { - throw new ConcurrencyFailureException("No live node exists for ID " + nodeId); + // Go back to the database and get what is there + NodeEntity dbNode = selectNodeById(nodeId, null); + if (pair == null) + { + throw new ConcurrencyFailureException( + "No node exists: \n" + + " ID: " + nodeId + "\n" + + " DB row: " + dbNode); + } + else + { + throw new ConcurrencyFailureException( + "No live node exists: \n" + + " ID: " + nodeId + "\n" + + " Cache row: " + pair.getSecond() + "\n" + + " DB row: " + dbNode); + } } else { @@ -2111,6 +2130,7 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO Pair> cacheEntry = propertiesCache.getByKey(nodeVersionKey); if (cacheEntry == null) { + invalidateNodeCaches(nodeId); throw new DataIntegrityViolationException("Invalid node ID: " + nodeId); } Map cachedProperties = cacheEntry.getSecond(); @@ -2258,7 +2278,7 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO catch (RuntimeException e) { // This could be because the cache is out of date - aspectsCache.removeByKey(nodeId); + invalidateNodeCaches(nodeId); throw e; } finally @@ -2297,12 +2317,12 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO // Just delete all the node's aspects int deleteCount = deleteNodeAspects(nodeId, null); - // Manually update the cache - aspectsCache.setValue(nodeId, Collections.emptySet()); - // Touch to bring into current txn touchNodeImpl(nodeId); + // Manually update the cache + setNodeAspectsCached(nodeId, Collections.emptySet()); + // Done return deleteCount > 0; } @@ -2315,11 +2335,6 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO Set aspectQNameIdsToRemove = qnameDAO.convertQNamesToIds(aspectQNames, false); int deleteCount = deleteNodeAspects(nodeId, aspectQNameIdsToRemove); - // Manually update the cache - Set newAspectQNames = new HashSet(existingAspectQNames); - newAspectQNames.removeAll(aspectQNames); - aspectsCache.setValue(nodeId, newAspectQNames); - // If we are removing the sys:aspect_root, then the parent assocs cache is unreliable if (aspectQNames.contains(ContentModel.ASPECT_ROOT)) { @@ -2331,6 +2346,11 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO // Touch to bring into current txn touchNodeImpl(nodeId); + // Manually update the cache + Set newAspectQNames = new HashSet(existingAspectQNames); + newAspectQNames.removeAll(aspectQNames); + setNodeAspectsCached(nodeId, newAspectQNames); + // Done return deleteCount > 0; } @@ -2355,9 +2375,11 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO */ private Set getNodeAspectsCached(Long nodeId) { - Pair> cacheEntry = aspectsCache.getByKey(nodeId); + NodeVersionKey nodeVersionKey = getNodeNotNull(nodeId).getNodeVersionKey(); + Pair> cacheEntry = aspectsCache.getByKey(nodeVersionKey); if (cacheEntry == null) { + invalidateNodeCaches(nodeId); throw new DataIntegrityViolationException("Invalid node ID: " + nodeId); } return new HashSet(cacheEntry.getSecond()); @@ -2368,7 +2390,8 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO */ private void setNodeAspectsCached(Long nodeId, Set aspects) { - aspectsCache.setValue(nodeId, Collections.unmodifiableSet(aspects)); + NodeVersionKey nodeVersionKey = getNodeNotNull(nodeId).getNodeVersionKey(); + aspectsCache.setValue(nodeVersionKey, Collections.unmodifiableSet(aspects)); } /** @@ -2377,16 +2400,16 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO * @author Derek Hulley * @since 3.4 */ - private class AspectsCallbackDAO extends EntityLookupCallbackDAOAdaptor, Serializable> + private class AspectsCallbackDAO extends EntityLookupCallbackDAOAdaptor, Serializable> { - public Pair> createValue(Set value) + public Pair> createValue(Set value) { throw new UnsupportedOperationException("A node always has a 'set' of aspects."); } - public Pair> findByKey(Long nodeId) + public Pair> findByKey(NodeVersionKey nodeVersionKey) { - NodeVersionKey nodeVersionKey = getNodeNotNull(nodeId).getNodeVersionKey(); + Long nodeId = nodeVersionKey.getNodeId(); Set nodeIds = Collections.singleton(nodeId); Map> nodeAspectQNameIdsByVersionKey = selectNodeAspects(nodeIds); Set nodeAspectQNames = nodeAspectQNameIdsByVersionKey.get(nodeVersionKey); @@ -2408,7 +2431,7 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO } } // Done - return new Pair>(nodeId, Collections.unmodifiableSet(nodeAspectQNames)); + return new Pair>(nodeVersionKey, Collections.unmodifiableSet(nodeAspectQNames)); } } @@ -3682,7 +3705,7 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO { propertiesNodeIds.add(nodeId); } - if (aspectsCache.getValue(nodeId) == null) + if (aspectsCache.getValue(nodeVersionKey) == null) { aspectNodeIds.add(nodeId); } @@ -3707,7 +3730,7 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO // Cache the absence of aspects too! for (Long nodeId: aspectNodeIds) { - aspectsCache.setValue(nodeId, Collections.emptySet()); + setNodeAspectsCached(nodeId, Collections.emptySet()); } Map> propsByNodeId = selectNodeProperties(propertiesNodeIds); diff --git a/source/java/org/alfresco/repo/domain/node/NodeEntity.java b/source/java/org/alfresco/repo/domain/node/NodeEntity.java index 785fbc7509..03cc48ce31 100644 --- a/source/java/org/alfresco/repo/domain/node/NodeEntity.java +++ b/source/java/org/alfresco/repo/domain/node/NodeEntity.java @@ -85,7 +85,8 @@ public class NodeEntity implements Node, PermissionCheckValue { StringBuilder sb = new StringBuilder(512); sb.append("NodeEntity") - .append("[ ID=").append(id); + .append("[ ID=").append(id) + .append(", version=").append(version); if (store != null) { sb.append(", store=").append(store.getProtocol()).append("://").append(store.getIdentifier()); diff --git a/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java b/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java index dc0b62330e..aa16a5c3c3 100644 --- a/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java +++ b/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java @@ -2326,6 +2326,9 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl } ChildAssociationRef oldParentAssocRef = oldParentAssocPair.getSecond(); + // Get the aspects for later use + Set nodeToMoveAspectQNames = nodeDAO.getNodeAspects(nodeToMoveId); + boolean movingStore = !oldStoreRef.equals(newStoreRef); // Invoke "Before"policy behaviour @@ -2375,7 +2378,6 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl propagateTimeStamps(oldParentAssocRef); propagateTimeStamps(newParentAssocRef); - Set nodeToMoveAspectQNames = nodeDAO.getNodeAspects(nodeToMoveId); // The Node changes NodeRefs, so this is really the deletion of the old node and creation // of a node in a new store as far as the clients are concerned. invokeOnDeleteNode(oldParentAssocRef, nodeToMoveTypeQName, nodeToMoveAspectQNames, true);