Node cache changes (step): aspects are cached using the node's ID-VERSION key

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@31178 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Derek Hulley
2011-10-12 15:38:04 +00:00
parent 5e9c517438
commit c8bdbb0135
3 changed files with 54 additions and 28 deletions

View File

@@ -153,11 +153,11 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
private EntityLookupCache<Long, Node, NodeRef> nodesCache; private EntityLookupCache<Long, Node, NodeRef> nodesCache;
/** /**
* Cache for the QName values:<br/> * Cache for the QName values:<br/>
* KEY: ID<br/> * KEY: NodeVersionKey<br/>
* VALUE: Set&lt;QName&gt;<br/> * VALUE: Set&lt;QName&gt;<br/>
* VALUE KEY: None<br/> * VALUE KEY: None<br/>
*/ */
private EntityLookupCache<Long, Set<QName>, Serializable> aspectsCache; private EntityLookupCache<NodeVersionKey, Set<QName>, Serializable> aspectsCache;
/** /**
* Cache for the Node properties:<br/> * Cache for the Node properties:<br/>
* KEY: NodeVersionKey<br/> * KEY: NodeVersionKey<br/>
@@ -184,7 +184,7 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
// Caches // Caches
rootNodesCache = new EntityLookupCache<StoreRef, Node, Serializable>(new RootNodesCacheCallbackDAO()); rootNodesCache = new EntityLookupCache<StoreRef, Node, Serializable>(new RootNodesCacheCallbackDAO());
nodesCache = new EntityLookupCache<Long, Node, NodeRef>(new NodesCacheCallbackDAO()); nodesCache = new EntityLookupCache<Long, Node, NodeRef>(new NodesCacheCallbackDAO());
aspectsCache = new EntityLookupCache<Long, Set<QName>, Serializable>(new AspectsCallbackDAO()); aspectsCache = new EntityLookupCache<NodeVersionKey, Set<QName>, Serializable>(new AspectsCallbackDAO());
propertiesCache = new EntityLookupCache<NodeVersionKey, Map<QName, Serializable>, Serializable>(new PropertiesCallbackDAO()); propertiesCache = new EntityLookupCache<NodeVersionKey, Map<QName, Serializable>, Serializable>(new PropertiesCallbackDAO());
parentAssocsCache = new EntityLookupCache<Long, ParentAssocsInfo, ChildByNameKey>(new ParentAssocsCallbackDAO()); parentAssocsCache = new EntityLookupCache<Long, ParentAssocsInfo, ChildByNameKey>(new ParentAssocsCallbackDAO());
} }
@@ -312,9 +312,9 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
* *
* @param aspectsCache the cache * @param aspectsCache the cache
*/ */
public void setAspectsCache(SimpleCache<Long, Set<QName>> aspectsCache) public void setAspectsCache(SimpleCache<NodeVersionKey, Set<QName>> aspectsCache)
{ {
this.aspectsCache = new EntityLookupCache<Long, Set<QName>, Serializable>( this.aspectsCache = new EntityLookupCache<NodeVersionKey, Set<QName>, Serializable>(
aspectsCache, aspectsCache,
CACHE_REGION_ASPECTS, CACHE_REGION_ASPECTS,
new AspectsCallbackDAO()); new AspectsCallbackDAO());
@@ -325,7 +325,7 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
* *
* @param propertiesCache the cache * @param propertiesCache the cache
*/ */
public void setPropertiesCache(SimpleCache<Long, Map<QName, Serializable>> propertiesCache) public void setPropertiesCache(SimpleCache<NodeVersionKey, Map<QName, Serializable>> propertiesCache)
{ {
this.propertiesCache = new EntityLookupCache<NodeVersionKey, Map<QName, Serializable>, Serializable>( this.propertiesCache = new EntityLookupCache<NodeVersionKey, Map<QName, Serializable>, Serializable>(
propertiesCache, propertiesCache,
@@ -535,10 +535,11 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
// Properties // Properties
propertiesCache.removeByKey(nodeVersionKey); propertiesCache.removeByKey(nodeVersionKey);
// Aspects // Aspects
aspectsCache.removeByKey(nodeId); aspectsCache.removeByKey(nodeVersionKey);
// Parent Assocs // Parent Assocs
parentAssocsCache.removeByKey(nodeId); parentAssocsCache.removeByKey(nodeId);
} }
invalidateCachesByNodeId(nodeId, null, parentAssocsCache);
// Finally remove the node reference // Finally remove the node reference
nodesCache.removeByKey(nodeId); nodesCache.removeByKey(nodeId);
} }
@@ -893,6 +894,8 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
return nodeTxnId.equals(currentTxnId); return nodeTxnId.equals(currentTxnId);
} }
// TODO: Restore to simple version
// TODO: Add read-through option for caches
public Status getNodeRefStatus(NodeRef nodeRef) public Status getNodeRefStatus(NodeRef nodeRef)
{ {
Node node = null; Node node = null;
@@ -971,7 +974,23 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
Pair<Long, Node> pair = nodesCache.getByKey(nodeId); Pair<Long, Node> pair = nodesCache.getByKey(nodeId);
if (pair == null || pair.getSecond().getDeleted()) 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 else
{ {
@@ -2111,6 +2130,7 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
Pair<NodeVersionKey, Map<QName, Serializable>> cacheEntry = propertiesCache.getByKey(nodeVersionKey); Pair<NodeVersionKey, Map<QName, Serializable>> cacheEntry = propertiesCache.getByKey(nodeVersionKey);
if (cacheEntry == null) if (cacheEntry == null)
{ {
invalidateNodeCaches(nodeId);
throw new DataIntegrityViolationException("Invalid node ID: " + nodeId); throw new DataIntegrityViolationException("Invalid node ID: " + nodeId);
} }
Map<QName, Serializable> cachedProperties = cacheEntry.getSecond(); Map<QName, Serializable> cachedProperties = cacheEntry.getSecond();
@@ -2258,7 +2278,7 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
catch (RuntimeException e) catch (RuntimeException e)
{ {
// This could be because the cache is out of date // This could be because the cache is out of date
aspectsCache.removeByKey(nodeId); invalidateNodeCaches(nodeId);
throw e; throw e;
} }
finally finally
@@ -2297,12 +2317,12 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
// Just delete all the node's aspects // Just delete all the node's aspects
int deleteCount = deleteNodeAspects(nodeId, null); int deleteCount = deleteNodeAspects(nodeId, null);
// Manually update the cache
aspectsCache.setValue(nodeId, Collections.<QName>emptySet());
// Touch to bring into current txn // Touch to bring into current txn
touchNodeImpl(nodeId); touchNodeImpl(nodeId);
// Manually update the cache
setNodeAspectsCached(nodeId, Collections.<QName>emptySet());
// Done // Done
return deleteCount > 0; return deleteCount > 0;
} }
@@ -2315,11 +2335,6 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
Set<Long> aspectQNameIdsToRemove = qnameDAO.convertQNamesToIds(aspectQNames, false); Set<Long> aspectQNameIdsToRemove = qnameDAO.convertQNamesToIds(aspectQNames, false);
int deleteCount = deleteNodeAspects(nodeId, aspectQNameIdsToRemove); int deleteCount = deleteNodeAspects(nodeId, aspectQNameIdsToRemove);
// Manually update the cache
Set<QName> newAspectQNames = new HashSet<QName>(existingAspectQNames);
newAspectQNames.removeAll(aspectQNames);
aspectsCache.setValue(nodeId, newAspectQNames);
// If we are removing the sys:aspect_root, then the parent assocs cache is unreliable // If we are removing the sys:aspect_root, then the parent assocs cache is unreliable
if (aspectQNames.contains(ContentModel.ASPECT_ROOT)) if (aspectQNames.contains(ContentModel.ASPECT_ROOT))
{ {
@@ -2331,6 +2346,11 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
// Touch to bring into current txn // Touch to bring into current txn
touchNodeImpl(nodeId); touchNodeImpl(nodeId);
// Manually update the cache
Set<QName> newAspectQNames = new HashSet<QName>(existingAspectQNames);
newAspectQNames.removeAll(aspectQNames);
setNodeAspectsCached(nodeId, newAspectQNames);
// Done // Done
return deleteCount > 0; return deleteCount > 0;
} }
@@ -2355,9 +2375,11 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
*/ */
private Set<QName> getNodeAspectsCached(Long nodeId) private Set<QName> getNodeAspectsCached(Long nodeId)
{ {
Pair<Long, Set<QName>> cacheEntry = aspectsCache.getByKey(nodeId); NodeVersionKey nodeVersionKey = getNodeNotNull(nodeId).getNodeVersionKey();
Pair<NodeVersionKey, Set<QName>> cacheEntry = aspectsCache.getByKey(nodeVersionKey);
if (cacheEntry == null) if (cacheEntry == null)
{ {
invalidateNodeCaches(nodeId);
throw new DataIntegrityViolationException("Invalid node ID: " + nodeId); throw new DataIntegrityViolationException("Invalid node ID: " + nodeId);
} }
return new HashSet<QName>(cacheEntry.getSecond()); return new HashSet<QName>(cacheEntry.getSecond());
@@ -2368,7 +2390,8 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
*/ */
private void setNodeAspectsCached(Long nodeId, Set<QName> aspects) private void setNodeAspectsCached(Long nodeId, Set<QName> 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 * @author Derek Hulley
* @since 3.4 * @since 3.4
*/ */
private class AspectsCallbackDAO extends EntityLookupCallbackDAOAdaptor<Long, Set<QName>, Serializable> private class AspectsCallbackDAO extends EntityLookupCallbackDAOAdaptor<NodeVersionKey, Set<QName>, Serializable>
{ {
public Pair<Long, Set<QName>> createValue(Set<QName> value) public Pair<NodeVersionKey, Set<QName>> createValue(Set<QName> value)
{ {
throw new UnsupportedOperationException("A node always has a 'set' of aspects."); throw new UnsupportedOperationException("A node always has a 'set' of aspects.");
} }
public Pair<Long, Set<QName>> findByKey(Long nodeId) public Pair<NodeVersionKey, Set<QName>> findByKey(NodeVersionKey nodeVersionKey)
{ {
NodeVersionKey nodeVersionKey = getNodeNotNull(nodeId).getNodeVersionKey(); Long nodeId = nodeVersionKey.getNodeId();
Set<Long> nodeIds = Collections.singleton(nodeId); Set<Long> nodeIds = Collections.singleton(nodeId);
Map<NodeVersionKey, Set<QName>> nodeAspectQNameIdsByVersionKey = selectNodeAspects(nodeIds); Map<NodeVersionKey, Set<QName>> nodeAspectQNameIdsByVersionKey = selectNodeAspects(nodeIds);
Set<QName> nodeAspectQNames = nodeAspectQNameIdsByVersionKey.get(nodeVersionKey); Set<QName> nodeAspectQNames = nodeAspectQNameIdsByVersionKey.get(nodeVersionKey);
@@ -2408,7 +2431,7 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
} }
} }
// Done // Done
return new Pair<Long, Set<QName>>(nodeId, Collections.unmodifiableSet(nodeAspectQNames)); return new Pair<NodeVersionKey, Set<QName>>(nodeVersionKey, Collections.unmodifiableSet(nodeAspectQNames));
} }
} }
@@ -3682,7 +3705,7 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
{ {
propertiesNodeIds.add(nodeId); propertiesNodeIds.add(nodeId);
} }
if (aspectsCache.getValue(nodeId) == null) if (aspectsCache.getValue(nodeVersionKey) == null)
{ {
aspectNodeIds.add(nodeId); aspectNodeIds.add(nodeId);
} }
@@ -3707,7 +3730,7 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
// Cache the absence of aspects too! // Cache the absence of aspects too!
for (Long nodeId: aspectNodeIds) for (Long nodeId: aspectNodeIds)
{ {
aspectsCache.setValue(nodeId, Collections.<QName>emptySet()); setNodeAspectsCached(nodeId, Collections.<QName>emptySet());
} }
Map<NodeVersionKey, Map<NodePropertyKey, NodePropertyValue>> propsByNodeId = selectNodeProperties(propertiesNodeIds); Map<NodeVersionKey, Map<NodePropertyKey, NodePropertyValue>> propsByNodeId = selectNodeProperties(propertiesNodeIds);

View File

@@ -85,7 +85,8 @@ public class NodeEntity implements Node, PermissionCheckValue
{ {
StringBuilder sb = new StringBuilder(512); StringBuilder sb = new StringBuilder(512);
sb.append("NodeEntity") sb.append("NodeEntity")
.append("[ ID=").append(id); .append("[ ID=").append(id)
.append(", version=").append(version);
if (store != null) if (store != null)
{ {
sb.append(", store=").append(store.getProtocol()).append("://").append(store.getIdentifier()); sb.append(", store=").append(store.getProtocol()).append("://").append(store.getIdentifier());

View File

@@ -2326,6 +2326,9 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
} }
ChildAssociationRef oldParentAssocRef = oldParentAssocPair.getSecond(); ChildAssociationRef oldParentAssocRef = oldParentAssocPair.getSecond();
// Get the aspects for later use
Set<QName> nodeToMoveAspectQNames = nodeDAO.getNodeAspects(nodeToMoveId);
boolean movingStore = !oldStoreRef.equals(newStoreRef); boolean movingStore = !oldStoreRef.equals(newStoreRef);
// Invoke "Before"policy behaviour // Invoke "Before"policy behaviour
@@ -2375,7 +2378,6 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl
propagateTimeStamps(oldParentAssocRef); propagateTimeStamps(oldParentAssocRef);
propagateTimeStamps(newParentAssocRef); propagateTimeStamps(newParentAssocRef);
Set<QName> nodeToMoveAspectQNames = nodeDAO.getNodeAspects(nodeToMoveId);
// The Node changes NodeRefs, so this is really the deletion of the old node and creation // 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. // of a node in a new store as far as the clients are concerned.
invokeOnDeleteNode(oldParentAssocRef, nodeToMoveTypeQName, nodeToMoveAspectQNames, true); invokeOnDeleteNode(oldParentAssocRef, nodeToMoveTypeQName, nodeToMoveAspectQNames, true);