Can't reproduce the bug but spotted a race condition in UserUsageTrackingComponent that may cause this in rare circumstances. Deal with this by throwing a ConcurrencyFailureException and let the txn retry handler retry the operation.

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@19862 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Steven Glover
2010-04-15 12:38:19 +00:00
parent 47905d3314
commit f14db280f3
7 changed files with 73 additions and 15 deletions

View File

@@ -43,6 +43,14 @@ public interface UsageDeltaDAO
*/
public long getTotalDeltaSize(NodeRef usageNodeRef);
/**
* Get the total delta size for a node and remove any deltas used in the calculation.
*
* @param nodeRef the node reference
* @return sum of delta sizes (in bytes) - can be +ve or -ve
*/
public long getAndRemoveTotalDeltaSize(NodeRef usageNodeRef);
public Set<NodeRef> getUsageDeltaNodes();
public int deleteDeltas(NodeRef nodeRef);

View File

@@ -31,6 +31,7 @@ import org.alfresco.service.cmr.repository.InvalidNodeRefException;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.util.GUID;
import org.alfresco.util.Pair;
import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.extensions.surf.util.ParameterCheck;
import org.hibernate.Query;
import org.hibernate.Session;
@@ -158,12 +159,12 @@ public class HibernateUsageDeltaDAO extends HibernateDaoSupport implements Usage
// execute
Integer delCount = (Integer) getHibernateTemplate().execute(callback);
return delCount.intValue();
return (delCount == null ? 0 : delCount.intValue());
}
@SuppressWarnings("unchecked")
public long getTotalDeltaSize(NodeRef nodeRef)
private Object[] getTotalDeltaSizeImpl(NodeRef nodeRef)
{
final Long nodeId = getNodeIdNotNull(nodeRef);
HibernateCallback callback = new HibernateCallback()
@@ -176,10 +177,40 @@ public class HibernateUsageDeltaDAO extends HibernateDaoSupport implements Usage
return query.uniqueResult();
}
};
// execute read-only tx
Long queryResult = (Long)getHibernateTemplate().execute(callback);
return (queryResult == null ? 0 : queryResult);
return (Object[])getHibernateTemplate().execute(callback);
}
public long getTotalDeltaSize(NodeRef nodeRef)
{
Object[] result = getTotalDeltaSizeImpl(nodeRef);
Long queryResult = (Long)result[1];
return (queryResult == null ? 0 : queryResult.longValue());
}
/**
* Guard against deleting deltas committed by another transaction after calculating the delta sum above.
* If the expected number of deletes is different from the actual number of deletes then deltas from
* another committed transaction are probably being removed.
*/
@SuppressWarnings("unchecked")
public long getAndRemoveTotalDeltaSize(NodeRef nodeRef)
{
Object[] result = getTotalDeltaSizeImpl(nodeRef);
Long queryResult = (Long)result[1];
Number deltaCount = (Number)result[0];
long expectedNumDeletes = deltaCount == null ? 0 : deltaCount.intValue();
int numDeltasRemoved = deleteDeltas(nodeRef);
if(expectedNumDeletes != numDeltasRemoved) {
throw new ConcurrencyFailureException("More than expected number of usage delta deletes has occurred for node id "
+ nodeRef.getId() + " in store " + nodeRef.getStoreRef());
}
return (queryResult == null ? 0 : queryResult.longValue());
}
public void insertDelta(NodeRef usageNodeRef, long deltaSize)

View File

@@ -50,7 +50,7 @@
<!-- Get total usage delta for a node -->
<query name="usage.GetTotalDeltaSize">
select
sum(deltaSize)
count(deltaSize), sum(deltaSize)
from
org.alfresco.repo.domain.hibernate.UsageDeltaImpl as usage_delta
where

View File

@@ -504,7 +504,11 @@ public class ContentUsageImpl implements ContentUsageService,
return (currentUsage == null ? -1 : currentUsage);
}
public long getUserUsage(String userName)
public long getUserUsage(String userName) {
return getUserUsage(userName, false);
}
public long getUserUsage(String userName, boolean removeDeltas)
{
ParameterCheck.mandatoryString("userName", userName);
@@ -518,9 +522,11 @@ public class ContentUsageImpl implements ContentUsageService,
if (currentUsage != -1)
{
// add any deltas
currentUsage = currentUsage + usageService.getTotalDeltaSize(personNodeRef);
long deltaSize = removeDeltas ? usageService.getAndRemoveTotalDeltaSize(personNodeRef) :
usageService.getTotalDeltaSize(personNodeRef);
// add any deltas to the currentUsage, removing them if required
currentUsage = currentUsage + deltaSize;
if (currentUsage < 0)
{
if (logger.isWarnEnabled())

View File

@@ -49,6 +49,11 @@ public class UsageServiceImpl implements UsageService
return usageDeltaDAO.getTotalDeltaSize(usageNodeRef);
}
public long getAndRemoveTotalDeltaSize(NodeRef usageNodeRef)
{
return usageDeltaDAO.getAndRemoveTotalDeltaSize(usageNodeRef);
}
public Set<NodeRef> getUsageDeltaNodes()
{
return usageDeltaDAO.getUsageDeltaNodes();

View File

@@ -564,9 +564,11 @@ public class UserUsageTrackingComponent extends AbstractLifecycleBean
long currentUsage = contentUsageImpl.getUserStoredUsage(personNodeRef);
if (currentUsage != -1)
{
// collapse the usage deltas
currentUsage = contentUsageImpl.getUserUsage(userName);
usageService.deleteDeltas(personNodeRef);
// Collapse the usage deltas
// Calculate and remove deltas in one go to guard against deletion of
// deltas from another transaction that have not been included in the
// calculation
currentUsage = contentUsageImpl.getUserUsage(userName, true);
contentUsageImpl.setUserStoredUsage(personNodeRef, currentUsage);
if (logger.isTraceEnabled())

View File

@@ -44,7 +44,13 @@ public interface UsageService
*/
@NotAuditable
public long getTotalDeltaSize(NodeRef usageNodeRef);
/**
* Get sum of usage delta sizes and remove affected deltas.
*/
@NotAuditable
public long getAndRemoveTotalDeltaSize(NodeRef usageNodeRef);
/**
* Get distinct set of usage delta nodes
*/