diff --git a/config/alfresco/bootstrap/lightWeightVersionStore.xml b/config/alfresco/bootstrap/lightWeightVersionStore.xml index c5f0f0037b..66de187300 100644 --- a/config/alfresco/bootstrap/lightWeightVersionStore.xml +++ b/config/alfresco/bootstrap/lightWeightVersionStore.xml @@ -1,4 +1,5 @@ - + @@ -8,6 +9,7 @@ All + \ No newline at end of file diff --git a/config/alfresco/messages/patch-service.properties b/config/alfresco/messages/patch-service.properties index 91991e3a95..b75b752955 100644 --- a/config/alfresco/messages/patch-service.properties +++ b/config/alfresco/messages/patch-service.properties @@ -112,6 +112,9 @@ patch.systemDescriptorContent.result=Added the version properties content to the patch.systemDescriptorContent.err.no_version_properties=The version.properties resource could not be found. patch.systemDescriptorContent.err.no_descriptor=The system descriptor could not be found. +patch.versionHistoryPerformance.description=Improves the performance of version history lookups. +patch.versionHistoryPerformance.result=Updated {0} version history objects to improve performance. + patch.multilingualBootstrap.description=Bootstraps the node that will hold the multilingual containers. patch.wcmFolders.description=Ensures the existance of the WCM specific 'Web Projects' and 'Web Forms' folders. @@ -131,4 +134,4 @@ patch.userAndPersonUserNamesAsIdentifiers.description=Reindex user:user and cm:p patch.userAndPersonUserNamesAsIdentifiers.result=Reindexed user:user and cm:person uids as identifiers patch.contentFormFolderType.description=Update WCM Content Form folder type. -patch.contentFormFolderType.result=Updated {0} WCM Content Form objects to 'wcm:formfolder' type. \ No newline at end of file +patch.contentFormFolderType.result=Updated {0} WCM Content Form objects to 'wcm:formfolder' type. diff --git a/config/alfresco/patch/patch-services-context.xml b/config/alfresco/patch/patch-services-context.xml index eaa70cbd3a..6a543354f3 100644 --- a/config/alfresco/patch/patch-services-context.xml +++ b/config/alfresco/patch/patch-services-context.xml @@ -517,6 +517,16 @@ + + patch.versionHistoryPerformance + patch.versionHistoryPerformance.description + 0 + 24 + 25 + + + + patch.multilingualBootstrap patch.multilingualBootstrap.description diff --git a/source/java/org/alfresco/repo/admin/patch/impl/VersionHistoryPerformancePatch.java b/source/java/org/alfresco/repo/admin/patch/impl/VersionHistoryPerformancePatch.java new file mode 100644 index 0000000000..de674a4ecd --- /dev/null +++ b/source/java/org/alfresco/repo/admin/patch/impl/VersionHistoryPerformancePatch.java @@ -0,0 +1,62 @@ +package org.alfresco.repo.admin.patch.impl; + +import java.util.List; + +import org.alfresco.i18n.I18NUtil; +import org.alfresco.model.ContentModel; +import org.alfresco.repo.admin.patch.AbstractPatch; +import org.alfresco.repo.version.VersionModel; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.version.VersionService; +import org.alfresco.service.namespace.QName; + +public class VersionHistoryPerformancePatch extends AbstractPatch +{ + private static final String MSG_SUCCESS = "patch.versionHistoryPerformance.result"; + + private VersionService versionService; + + public void setVersionService(VersionService versionService) + { + this.versionService = versionService; + } + + @Override + protected String applyInternal() throws Exception + { + // Set the aspect on the root node of the version store + StoreRef versionStoreRef = this.versionService.getVersionStoreReference(); + NodeRef rootNodeRef = this.nodeService.getRootNode(versionStoreRef); + this.nodeService.addAspect(rootNodeRef, VersionModel.ASPECT_VERSION_STORE_ROOT, null); + + List assocs = this.nodeService.getChildAssocs(rootNodeRef); + int updateCount = 0; + + for(ChildAssociationRef childAssocRef : assocs) + { + NodeRef nodeRef = childAssocRef.getChildRef(); + if (VersionModel.TYPE_QNAME_VERSION_HISTORY.equals(this.nodeService.getType(nodeRef)) == true) + { + // Get the id + String versionedNodeId = (String)this.nodeService.getProperty(nodeRef, VersionModel.PROP_QNAME_VERSIONED_NODE_ID); + + // Set the cm:name + this.nodeService.setProperty(nodeRef, ContentModel.PROP_NAME, versionedNodeId); + + // Move the node + this.nodeService.moveNode( nodeRef, + rootNodeRef, + VersionModel.CHILD_QNAME_VERSION_HISTORIES, + QName.createQName(VersionModel.NAMESPACE_URI, versionedNodeId)); + + updateCount++; + } + } + + // Build the result message + return I18NUtil.getMessage(MSG_SUCCESS, updateCount); + } + +} diff --git a/source/java/org/alfresco/repo/version/VersionModel.java b/source/java/org/alfresco/repo/version/VersionModel.java index aac0f38c1d..f3ebcb7bd9 100644 --- a/source/java/org/alfresco/repo/version/VersionModel.java +++ b/source/java/org/alfresco/repo/version/VersionModel.java @@ -63,6 +63,9 @@ public interface VersionModel public static final String PROP_FROZEN_NODE_STORE_ID = "frozenNodeStoreId"; public static final String PROP_FROZEN_ASPECTS = "frozenAspects"; + /** The version store root aspect */ + public static final QName ASPECT_VERSION_STORE_ROOT = QName.createQName(NAMESPACE_URI, "versionStoreRoot"); + /** * Version history type */ diff --git a/source/java/org/alfresco/repo/version/VersionServiceImpl.java b/source/java/org/alfresco/repo/version/VersionServiceImpl.java index c7bbc082d8..ea8214b7dd 100644 --- a/source/java/org/alfresco/repo/version/VersionServiceImpl.java +++ b/source/java/org/alfresco/repo/version/VersionServiceImpl.java @@ -308,17 +308,8 @@ public class VersionServiceImpl extends AbstractVersionServiceImpl if (versionHistoryRef == null) { - HashMap props = new HashMap(); - props.put(PROP_QNAME_VERSIONED_NODE_ID, nodeRef.getId()); - - // Create a new version history node - ChildAssociationRef childAssocRef = this.dbNodeService.createNode( - getRootNode(), - ContentModel.ASSOC_CHILDREN, - CHILD_QNAME_VERSION_HISTORIES, - TYPE_QNAME_VERSION_HISTORY, - props); - versionHistoryRef = childAssocRef.getChildRef(); + // Create the version history + versionHistoryRef = createVersionHistory(nodeRef); } else { @@ -387,6 +378,28 @@ public class VersionServiceImpl extends AbstractVersionServiceImpl // Return the data object representing the newly created version return version; } + + /** + * Creates a new version history node, applying the root version aspect is required + * + * @param nodeRef the node ref + * @return the version history node reference + */ + private NodeRef createVersionHistory(NodeRef nodeRef) + { + HashMap props = new HashMap(); + props.put(ContentModel.PROP_NAME, nodeRef.getId()); + props.put(PROP_QNAME_VERSIONED_NODE_ID, nodeRef.getId()); + + // Create a new version history node + ChildAssociationRef childAssocRef = this.dbNodeService.createNode( + getRootNode(), + CHILD_QNAME_VERSION_HISTORIES, + QName.createQName(VersionModel.NAMESPACE_URI, nodeRef.getId()), + TYPE_QNAME_VERSION_HISTORY, + props); + return childAssocRef.getChildRef(); + } /** * @see org.alfresco.service.cmr.version.VersionService#getVersionHistory(NodeRef) @@ -771,20 +784,7 @@ public class VersionServiceImpl extends AbstractVersionServiceImpl */ private NodeRef getVersionHistoryNodeRef(NodeRef nodeRef) { - NodeRef result = null; - - Collection versionHistories = this.dbNodeService.getChildAssocs(getRootNode()); - for (ChildAssociationRef versionHistory : versionHistories) - { - String nodeId = (String)this.dbNodeService.getProperty(versionHistory.getChildRef(), VersionModel.PROP_QNAME_VERSIONED_NODE_ID); - if (nodeId != null && nodeId.equals(nodeRef.getId()) == true) - { - result = versionHistory.getChildRef(); - break; - } - } - - return result; + return this.dbNodeService.getChildByName(getRootNode(), CHILD_QNAME_VERSION_HISTORIES, nodeRef.getId()); } /** diff --git a/source/java/org/alfresco/repo/version/VersionServiceImplTest.java b/source/java/org/alfresco/repo/version/VersionServiceImplTest.java index b3a66f4470..37cdb47ca5 100644 --- a/source/java/org/alfresco/repo/version/VersionServiceImplTest.java +++ b/source/java/org/alfresco/repo/version/VersionServiceImplTest.java @@ -27,22 +27,33 @@ package org.alfresco.repo.version; import java.io.Serializable; import java.util.Collection; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Set; import org.alfresco.model.ApplicationModel; import org.alfresco.model.ContentModel; +import org.alfresco.repo.security.authentication.AuthenticationComponent; import org.alfresco.repo.transaction.TransactionUtil; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.model.FileFolderService; +import org.alfresco.service.cmr.model.FileInfo; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.version.Version; import org.alfresco.service.cmr.version.VersionHistory; +import org.alfresco.service.cmr.version.VersionService; import org.alfresco.service.cmr.version.VersionServiceException; import org.alfresco.service.cmr.version.VersionType; import org.alfresco.service.namespace.QName; +import org.alfresco.util.ApplicationContextHelper; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.context.ApplicationContext; /** * versionService test class. @@ -51,7 +62,9 @@ import org.alfresco.service.namespace.QName; */ public class VersionServiceImplTest extends BaseVersionStoreTest { - private static final String UPDATED_VALUE_1 = "updatedValue1"; + private static Log logger = LogFactory.getLog(VersionServiceImplTest.class); + + private static final String UPDATED_VALUE_1 = "updatedValue1"; private static final String UPDATED_VALUE_2 = "updatedValue2"; private static final String UPDATED_VALUE_3 = "updatedValue3"; private static final String UPDATED_CONTENT_1 = "updatedContent1"; @@ -718,4 +731,93 @@ public class VersionServiceImplTest extends BaseVersionStoreTest Object editionCodeArchive = nodeService.getProperty(versionNodeRef, prop); assertEquals(editionCodeArchive.getClass(), Integer.class); } + public static void main(String ... args) + { + try + { + doMain(args); + System.exit(1); + } + catch (Throwable e) + { + logger.error(e); + System.exit(1); + } + } + private static void doMain(String ... args) + { + if (args.length != 1) + { + System.out.println("Usage: VersionServiceImplTest fileCount"); + System.exit(1); + } + int fileCount = Integer.parseInt(args[0]); + + ApplicationContext ctx = ApplicationContextHelper.getApplicationContext(); + final ServiceRegistry serviceRegistry = (ServiceRegistry) ctx.getBean(ServiceRegistry.SERVICE_REGISTRY); + final FileFolderService fileFolderService = serviceRegistry.getFileFolderService(); + final NodeService nodeService = serviceRegistry.getNodeService(); + final VersionService versionService = serviceRegistry.getVersionService(); + final AuthenticationComponent authenticationComponent = (AuthenticationComponent) ctx.getBean("authenticationComponent"); + + authenticationComponent.setSystemUserAsCurrentUser(); + // Create a new store + StoreRef storeRef = new StoreRef("test", "VersionServiceImplTest.main"); + if (!nodeService.exists(storeRef)) + { + nodeService.createStore(storeRef.getProtocol(), storeRef.getIdentifier()); + } + NodeRef rootNodeRef = nodeService.getRootNode(storeRef); + // Create a folder + NodeRef folderNodeRef = nodeService.createNode( + rootNodeRef, + ContentModel.ASSOC_CHILDREN, + QName.createQName("test", "versionMain"), + ContentModel.TYPE_FOLDER).getChildRef(); + // Now load the folder with the prescribed number of documents + int count = 0; + long start = System.currentTimeMillis(); + long lastReport = start; + for (int i = 0; i < fileCount; i++) + { + fileFolderService.create(folderNodeRef, "file-" + i, ContentModel.TYPE_CONTENT); + count++; + // Report every 10s + long now = System.currentTimeMillis(); + if (now - lastReport > 10000L) + { + long delta = (now - start); + double average = (double) delta / (double) count; + System.out.println( + "File Creation: \n" + + " Count: " + count + " of " + fileCount + "\n" + + " Average (ms): " + average); + lastReport = now; + } + } + // Get all the children again + List files = fileFolderService.listFiles(folderNodeRef); + // Version each one + count = 0; + start = System.currentTimeMillis(); + lastReport = start; + for (FileInfo fileInfo : files) + { + NodeRef nodeRef = fileInfo.getNodeRef(); + versionService.createVersion(nodeRef, null); + count++; + // Report every 10s + long now = System.currentTimeMillis(); + if (now - lastReport > 10000L) + { + long delta = (now - start); + double average = (double) delta / (double) count; + System.out.println( + "Version: \n" + + " Count: " + count + " of " + fileCount + "\n" + + " Average (ms): " + average); + lastReport = now; + } + } + } } diff --git a/source/java/org/alfresco/repo/version/version_model.xml b/source/java/org/alfresco/repo/version/version_model.xml index 68d903fb33..de7143bac0 100644 --- a/source/java/org/alfresco/repo/version/version_model.xml +++ b/source/java/org/alfresco/repo/version/version_model.xml @@ -129,7 +129,7 @@ - sys:base + cm:cmobject @@ -153,4 +153,24 @@ + + + Version Store Root + + + + false + true + + + ver:versionHistory + false + true + + false + + + + +