From f14db280f3e86af052876e2606102c9d90a4ef1b Mon Sep 17 00:00:00 2001 From: Steven Glover Date: Thu, 15 Apr 2010 12:38:19 +0000 Subject: [PATCH] Fix for http://issues.alfresco.com/jira/browse/ALF-713. 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 --- .../alfresco/repo/domain/UsageDeltaDAO.java | 8 ++++ .../hibernate/HibernateUsageDeltaDAO.java | 43 ++++++++++++++++--- .../repo/domain/hibernate/UsageDelta.hbm.xml | 2 +- .../alfresco/repo/usage/ContentUsageImpl.java | 14 ++++-- .../alfresco/repo/usage/UsageServiceImpl.java | 5 +++ .../usage/UserUsageTrackingComponent.java | 8 ++-- .../service/cmr/usage/UsageService.java | 8 +++- 7 files changed, 73 insertions(+), 15 deletions(-) diff --git a/source/java/org/alfresco/repo/domain/UsageDeltaDAO.java b/source/java/org/alfresco/repo/domain/UsageDeltaDAO.java index 5869ffa0e1..2f1db5256b 100644 --- a/source/java/org/alfresco/repo/domain/UsageDeltaDAO.java +++ b/source/java/org/alfresco/repo/domain/UsageDeltaDAO.java @@ -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 getUsageDeltaNodes(); public int deleteDeltas(NodeRef nodeRef); diff --git a/source/java/org/alfresco/repo/domain/hibernate/HibernateUsageDeltaDAO.java b/source/java/org/alfresco/repo/domain/hibernate/HibernateUsageDeltaDAO.java index 8d30cdcedf..691c2a60e6 100644 --- a/source/java/org/alfresco/repo/domain/hibernate/HibernateUsageDeltaDAO.java +++ b/source/java/org/alfresco/repo/domain/hibernate/HibernateUsageDeltaDAO.java @@ -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) diff --git a/source/java/org/alfresco/repo/domain/hibernate/UsageDelta.hbm.xml b/source/java/org/alfresco/repo/domain/hibernate/UsageDelta.hbm.xml index bfebe7bfc2..cdd3493493 100644 --- a/source/java/org/alfresco/repo/domain/hibernate/UsageDelta.hbm.xml +++ b/source/java/org/alfresco/repo/domain/hibernate/UsageDelta.hbm.xml @@ -50,7 +50,7 @@ select - sum(deltaSize) + count(deltaSize), sum(deltaSize) from org.alfresco.repo.domain.hibernate.UsageDeltaImpl as usage_delta where diff --git a/source/java/org/alfresco/repo/usage/ContentUsageImpl.java b/source/java/org/alfresco/repo/usage/ContentUsageImpl.java index f36220ec73..177334f7e8 100644 --- a/source/java/org/alfresco/repo/usage/ContentUsageImpl.java +++ b/source/java/org/alfresco/repo/usage/ContentUsageImpl.java @@ -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()) diff --git a/source/java/org/alfresco/repo/usage/UsageServiceImpl.java b/source/java/org/alfresco/repo/usage/UsageServiceImpl.java index 9652701682..7d0d1c47f5 100644 --- a/source/java/org/alfresco/repo/usage/UsageServiceImpl.java +++ b/source/java/org/alfresco/repo/usage/UsageServiceImpl.java @@ -49,6 +49,11 @@ public class UsageServiceImpl implements UsageService return usageDeltaDAO.getTotalDeltaSize(usageNodeRef); } + public long getAndRemoveTotalDeltaSize(NodeRef usageNodeRef) + { + return usageDeltaDAO.getAndRemoveTotalDeltaSize(usageNodeRef); + } + public Set getUsageDeltaNodes() { return usageDeltaDAO.getUsageDeltaNodes(); diff --git a/source/java/org/alfresco/repo/usage/UserUsageTrackingComponent.java b/source/java/org/alfresco/repo/usage/UserUsageTrackingComponent.java index 480c4d0cf3..e6115f580a 100644 --- a/source/java/org/alfresco/repo/usage/UserUsageTrackingComponent.java +++ b/source/java/org/alfresco/repo/usage/UserUsageTrackingComponent.java @@ -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()) diff --git a/source/java/org/alfresco/service/cmr/usage/UsageService.java b/source/java/org/alfresco/service/cmr/usage/UsageService.java index 856fd3f158..bae60026d2 100644 --- a/source/java/org/alfresco/service/cmr/usage/UsageService.java +++ b/source/java/org/alfresco/service/cmr/usage/UsageService.java @@ -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 */