mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-08-07 17:49:17 +00:00
Fixed ALF-10964: Add back cache for getChildByName
- Originally removed as part of the 'reverse lookup' of parentAssocsCache - This cache is NOT clustered; the child target version is checked; requery if necessary - NB: Cache misses are NOT cached. Do do so would mean making the cache clustered. It is better to avoid querying for random files that don't exist over and over. Add a higher level cache (as is done in CIFS) for that case. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@31417 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -309,6 +309,39 @@
|
||||
<property name="disableSharedCache" value="${system.cache.disableMutableSharedCaches}" />
|
||||
</bean>
|
||||
|
||||
<!-- ===================================== -->
|
||||
<!-- Child by cm:name lookup for nodes -->
|
||||
<!-- ===================================== -->
|
||||
|
||||
<!-- The cross-transaction shared cache for Child-by-name -->
|
||||
|
||||
<bean name="node.childByNameSharedCache" class="org.alfresco.repo.cache.EhCacheAdapter">
|
||||
<property name="cache">
|
||||
<bean class="org.springframework.cache.ehcache.EhCacheFactoryBean" >
|
||||
<property name="cacheManager">
|
||||
<ref bean="internalEHCacheManager" />
|
||||
</property>
|
||||
<property name="cacheName">
|
||||
<value>org.alfresco.cache.node.childByNameCache</value>
|
||||
</property>
|
||||
</bean>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<!-- The transactional cache for Child-by-name -->
|
||||
|
||||
<bean name="node.childByNameCache" class="org.alfresco.repo.cache.TransactionalCache">
|
||||
<property name="sharedCache">
|
||||
<ref bean="node.parentAssocsSharedCache" />
|
||||
</property>
|
||||
<property name="name">
|
||||
<value>org.alfresco.cache.node.childByNameTransactionalCache</value>
|
||||
</property>
|
||||
<property name="maxCacheSize" value="65000" />
|
||||
<property name="mutable" value="true" />
|
||||
<property name="disableSharedCache" value="${system.cache.disableMutableSharedCaches}" />
|
||||
</bean>
|
||||
|
||||
<!-- ===================================== -->
|
||||
<!-- Rules lookup for nodes -->
|
||||
<!-- ===================================== -->
|
||||
|
@@ -120,6 +120,7 @@
|
||||
<property name="aspectsCache" ref="node.aspectsCache"/>
|
||||
<property name="propertiesCache" ref="node.propertiesCache"/>
|
||||
<property name="parentAssocsCache" ref="node.parentAssocsCache"/>
|
||||
<property name="childByNameCache" ref="node.childByNameCache"/>
|
||||
</bean>
|
||||
<bean id="nodeDAO.org.hibernate.dialect.Dialect" class="org.alfresco.repo.domain.node.ibatis.NodeDAOImpl" parent="nodeDAObase" />
|
||||
<bean id="nodeDAO.org.alfresco.repo.domain.hibernate.dialect.AlfrescoSQLServerDialect" class="org.alfresco.repo.domain.node.ibatis.NodeDAOImpl$MSSQL" parent="nodeDAO.org.hibernate.dialect.Dialect" />
|
||||
|
@@ -35,6 +35,9 @@
|
||||
overflowToDisk="false"
|
||||
statistics="false"
|
||||
/>
|
||||
|
||||
<!-- Node caches -->
|
||||
|
||||
<cache
|
||||
name="org.alfresco.cache.node.rootNodesCache"
|
||||
maxElementsInMemory="500"
|
||||
@@ -77,6 +80,13 @@
|
||||
overflowToDisk="false"
|
||||
statistics="false"
|
||||
/>
|
||||
<cache
|
||||
name="org.alfresco.cache.node.childByNameCache"
|
||||
maxElementsInMemory="130000"
|
||||
eternal="true"
|
||||
overflowToDisk="false"
|
||||
statistics="false"
|
||||
/>
|
||||
|
||||
<!-- AVM caches -->
|
||||
|
||||
|
@@ -197,6 +197,16 @@
|
||||
replicateAsynchronously = false"/>
|
||||
</cache>
|
||||
|
||||
<cache
|
||||
name="org.alfresco.cache.node.childByNameCache"
|
||||
maxElementsInMemory="130000"
|
||||
eternal="true"
|
||||
overflowToDisk="false"
|
||||
statistics="false"
|
||||
>
|
||||
<!-- Not clustered -->
|
||||
</cache>
|
||||
|
||||
<cache
|
||||
name="org.alfresco.cache.avm.avmEntityCache"
|
||||
maxElementsInMemory="10000"
|
||||
|
@@ -42,6 +42,7 @@ import org.alfresco.ibatis.BatchingDAO;
|
||||
import org.alfresco.ibatis.RetryingCallbackHelper;
|
||||
import org.alfresco.ibatis.RetryingCallbackHelper.RetryingCallback;
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.cache.NullCache;
|
||||
import org.alfresco.repo.cache.SimpleCache;
|
||||
import org.alfresco.repo.cache.lookup.EntityLookupCache;
|
||||
import org.alfresco.repo.cache.lookup.EntityLookupCache.EntityLookupCallbackDAOAdaptor;
|
||||
@@ -173,6 +174,11 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
|
||||
*/
|
||||
private EntityLookupCache<NodeVersionKey, ParentAssocsInfo, Serializable> parentAssocsCache;
|
||||
|
||||
/**
|
||||
* Cache for fast lookups of child nodes by <b>cm:name</b>.
|
||||
*/
|
||||
private SimpleCache<ChildByNameKey, ChildAssocEntity> childByNameCache;
|
||||
|
||||
/**
|
||||
* Constructor. Set up various instance-specific members such as caches and locks.
|
||||
*/
|
||||
@@ -187,6 +193,7 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
|
||||
aspectsCache = new EntityLookupCache<NodeVersionKey, Set<QName>, Serializable>(new AspectsCallbackDAO());
|
||||
propertiesCache = new EntityLookupCache<NodeVersionKey, Map<QName, Serializable>, Serializable>(new PropertiesCallbackDAO());
|
||||
parentAssocsCache = new EntityLookupCache<NodeVersionKey, ParentAssocsInfo, Serializable>(new ParentAssocsCallbackDAO());
|
||||
childByNameCache = new NullCache<ChildByNameKey, ChildAssocEntity>();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -346,6 +353,16 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
|
||||
new ParentAssocsCallbackDAO());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the cache that maintains lookups by child <b>cm:name</b>
|
||||
*
|
||||
* @param childByNameCache the cache
|
||||
*/
|
||||
public void setChildByNameCache(SimpleCache<ChildByNameKey, ChildAssocEntity> childByNameCache)
|
||||
{
|
||||
this.childByNameCache = childByNameCache;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize
|
||||
*/
|
||||
@@ -2018,6 +2035,7 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
|
||||
// Touch to bring into current transaction
|
||||
if (updated)
|
||||
{
|
||||
// We have to explicitly update the node (sys:locale or cm:auditable)
|
||||
updateNodeImpl(node, nodeUpdate);
|
||||
}
|
||||
|
||||
@@ -3023,9 +3041,59 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks a cache and then queries.
|
||||
* <p/>
|
||||
* Note: If we were to cach misses, then we would have to ensure that the cache is
|
||||
* kept up to date whenever any affection association is changed. This is actually
|
||||
* not possible without forcing the cache to be fully clustered. So to
|
||||
* avoid clustering the cache, we instead watch the node child version,
|
||||
* which relies on a cache that is already clustered.
|
||||
*/
|
||||
public Pair<Long, ChildAssociationRef> getChildAssoc(Long parentNodeId, QName assocTypeQName, String childName)
|
||||
{
|
||||
ChildAssocEntity assoc = selectChildAssoc(parentNodeId, assocTypeQName, childName);
|
||||
ChildByNameKey key = new ChildByNameKey(parentNodeId, assocTypeQName, childName);
|
||||
ChildAssocEntity assoc = childByNameCache.get(key);
|
||||
boolean query = false;
|
||||
if (assoc == null)
|
||||
{
|
||||
query = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Check that the resultant child node has not moved on
|
||||
Node childNode = assoc.getChildNode();
|
||||
Long childNodeId = childNode.getId();
|
||||
NodeVersionKey childNodeVersionKey = childNode.getNodeVersionKey();
|
||||
Pair<Long, Node> childNodeFromCache = nodesCache.getByKey(childNodeId);
|
||||
if (childNodeFromCache == null)
|
||||
{
|
||||
// Child node no longer exists (or never did)
|
||||
query = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
NodeVersionKey childNodeFromCacheVersionKey = childNodeFromCache.getSecond().getNodeVersionKey();
|
||||
if (!childNodeFromCacheVersionKey.equals(childNodeVersionKey))
|
||||
{
|
||||
// The child node has moved on. We don't know why, but must query again.
|
||||
query = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (query)
|
||||
{
|
||||
assoc = selectChildAssoc(parentNodeId, assocTypeQName, childName);
|
||||
if (assoc != null)
|
||||
{
|
||||
childByNameCache.put(key, assoc);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We do not cache misses. See javadoc.
|
||||
}
|
||||
}
|
||||
// Now return, checking the assoc's ID for null
|
||||
return assoc == null ? null : assoc.getPair(qnameDAO);
|
||||
}
|
||||
|
||||
|
@@ -500,4 +500,39 @@ public class NodeServiceTest extends TestCase
|
||||
List<ChildAssociationRef> parentAssocsPost = nodeService.getParentAssocs(secondaryNodeRef);
|
||||
assertEquals("Incorrect number of parent assocs", 1, parentAssocsPost.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that file renames are handled when getting children
|
||||
*/
|
||||
public void testCaches_RenameNode()
|
||||
{
|
||||
final NodeRef[] nodeRefs = new NodeRef[2];
|
||||
final NodeRef workspaceRootNodeRef = nodeService.getRootNode(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE);
|
||||
buildNodeHierarchy(workspaceRootNodeRef, nodeRefs);
|
||||
|
||||
// What is the name of the first child?
|
||||
String name = (String) nodeService.getProperty(nodeRefs[1], ContentModel.PROP_NAME);
|
||||
// Now query for it
|
||||
NodeRef nodeRefCheck = nodeService.getChildByName(nodeRefs[0], ContentModel.ASSOC_CONTAINS, name);
|
||||
assertNotNull("Did not find node by name", nodeRefCheck);
|
||||
assertEquals("Node found was not correct", nodeRefs[1], nodeRefCheck);
|
||||
|
||||
// Rename the node
|
||||
nodeService.setProperty(nodeRefs[1], ContentModel.PROP_NAME, "New Name");
|
||||
// Should find nothing
|
||||
nodeRefCheck = nodeService.getChildByName(nodeRefs[0], ContentModel.ASSOC_CONTAINS, name);
|
||||
assertNull("Should not have found anything", nodeRefCheck);
|
||||
|
||||
// Add another child with the same original name
|
||||
NodeRef newChildNodeRef = nodeService.createNode(
|
||||
nodeRefs[0],
|
||||
ContentModel.ASSOC_CONTAINS,
|
||||
QName.createQName(NAMESPACE, name),
|
||||
ContentModel.TYPE_FOLDER,
|
||||
Collections.singletonMap(ContentModel.PROP_NAME, (Serializable)name)).getChildRef();
|
||||
// We should find this new node when looking for the name
|
||||
nodeRefCheck = nodeService.getChildByName(nodeRefs[0], ContentModel.ASSOC_CONTAINS, name);
|
||||
assertNotNull("Did not find node by name", nodeRefCheck);
|
||||
assertEquals("Node found was not correct", newChildNodeRef, nodeRefCheck);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user