You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by aa...@apache.org on 2013/07/24 15:59:32 UTC

svn commit: r1506556 - in /cayenne/main/trunk/framework/cayenne-core-unpublished/src/main/java/org/apache/cayenne/access: HierarchicalObjectResolverNode.java PrefetchObjectResolver.java PrefetchProcessorTreeBuilder.java

Author: aadamchik
Date: Wed Jul 24 13:59:31 2013
New Revision: 1506556

URL: http://svn.apache.org/r1506556
Log:
CAY-1695  Unexpected null value in bidirectional one-to-one prefetch

redoing the algorithm... tracking snapshot versions is flaky

Modified:
    cayenne/main/trunk/framework/cayenne-core-unpublished/src/main/java/org/apache/cayenne/access/HierarchicalObjectResolverNode.java
    cayenne/main/trunk/framework/cayenne-core-unpublished/src/main/java/org/apache/cayenne/access/PrefetchObjectResolver.java
    cayenne/main/trunk/framework/cayenne-core-unpublished/src/main/java/org/apache/cayenne/access/PrefetchProcessorTreeBuilder.java

Modified: cayenne/main/trunk/framework/cayenne-core-unpublished/src/main/java/org/apache/cayenne/access/HierarchicalObjectResolverNode.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-core-unpublished/src/main/java/org/apache/cayenne/access/HierarchicalObjectResolverNode.java?rev=1506556&r1=1506555&r2=1506556&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-core-unpublished/src/main/java/org/apache/cayenne/access/HierarchicalObjectResolverNode.java (original)
+++ cayenne/main/trunk/framework/cayenne-core-unpublished/src/main/java/org/apache/cayenne/access/HierarchicalObjectResolverNode.java Wed Jul 24 13:59:31 2013
@@ -20,6 +20,7 @@ package org.apache.cayenne.access;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 
 import org.apache.cayenne.CayenneRuntimeException;
 import org.apache.cayenne.DataRow;
@@ -32,8 +33,8 @@ class HierarchicalObjectResolverNode ext
     private PrefetchProcessorNode node;
 
     HierarchicalObjectResolverNode(PrefetchProcessorNode node, DataContext context, ClassDescriptor descriptor,
-            boolean refresh, long txStartRowVersion) {
-        super(context, descriptor, refresh, txStartRowVersion);
+            boolean refresh, Map<ObjectId, Persistent> seen) {
+        super(context, descriptor, refresh, seen);
         this.node = node;
     }
 

Modified: cayenne/main/trunk/framework/cayenne-core-unpublished/src/main/java/org/apache/cayenne/access/PrefetchObjectResolver.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-core-unpublished/src/main/java/org/apache/cayenne/access/PrefetchObjectResolver.java?rev=1506556&r1=1506555&r2=1506556&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-core-unpublished/src/main/java/org/apache/cayenne/access/PrefetchObjectResolver.java (original)
+++ cayenne/main/trunk/framework/cayenne-core-unpublished/src/main/java/org/apache/cayenne/access/PrefetchObjectResolver.java Wed Jul 24 13:59:31 2013
@@ -18,7 +18,8 @@
  ****************************************************************/
 package org.apache.cayenne.access;
 
-import org.apache.cayenne.DataObject;
+import java.util.Map;
+
 import org.apache.cayenne.DataRow;
 import org.apache.cayenne.ObjectId;
 import org.apache.cayenne.Persistent;
@@ -29,12 +30,12 @@ import org.apache.cayenne.reflect.ClassD
  */
 class PrefetchObjectResolver extends ObjectResolver {
 
-    long txStartRowVersion;
+    private Map<ObjectId, Persistent> seen;
 
-    public PrefetchObjectResolver(DataContext context, ClassDescriptor descriptor, boolean refresh,
-            long txStartRowVersion) {
+    PrefetchObjectResolver(DataContext context, ClassDescriptor descriptor, boolean refresh,
+            Map<ObjectId, Persistent> seen) {
         super(context, descriptor, refresh);
-        this.txStartRowVersion = txStartRowVersion;
+        this.seen = seen;
     }
 
     @Override
@@ -43,15 +44,13 @@ class PrefetchObjectResolver extends Obj
         // transaction, either by this node or by some other node...
         // added per CAY-1695 ..
 
-        // TODO: is it going to have any side effects? It is run from the
-        // synchronized block, so I guess other threads can't stick their
-        // versions of this object in here?
-        Persistent object = (Persistent) context.getGraphManager().getNode(anId);
-        if (object != null && ((DataObject) object).getSnapshotVersion() >= txStartRowVersion) {
-            return object;
+        Persistent object = seen.get(anId);
+        if (object == null) {
+            object = super.objectFromDataRow(row, anId, classDescriptor);
+            seen.put(anId, object);
         }
 
-        return super.objectFromDataRow(row, anId, classDescriptor);
+        return object;
     }
 
 }

Modified: cayenne/main/trunk/framework/cayenne-core-unpublished/src/main/java/org/apache/cayenne/access/PrefetchProcessorTreeBuilder.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-core-unpublished/src/main/java/org/apache/cayenne/access/PrefetchProcessorTreeBuilder.java?rev=1506556&r1=1506555&r2=1506556&view=diff
==============================================================================
--- cayenne/main/trunk/framework/cayenne-core-unpublished/src/main/java/org/apache/cayenne/access/PrefetchProcessorTreeBuilder.java (original)
+++ cayenne/main/trunk/framework/cayenne-core-unpublished/src/main/java/org/apache/cayenne/access/PrefetchProcessorTreeBuilder.java Wed Jul 24 13:59:31 2013
@@ -18,13 +18,14 @@
  ****************************************************************/
 package org.apache.cayenne.access;
 
+import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 
 import org.apache.cayenne.CayenneRuntimeException;
-import org.apache.cayenne.DataObject;
-import org.apache.cayenne.DataRow;
+import org.apache.cayenne.ObjectId;
+import org.apache.cayenne.Persistent;
 import org.apache.cayenne.query.PrefetchProcessor;
 import org.apache.cayenne.query.PrefetchTreeNode;
 import org.apache.cayenne.query.QueryMetadata;
@@ -41,26 +42,35 @@ final class PrefetchProcessorTreeBuilder
     private ClassDescriptor descriptor;
     private List mainResultRows;
     private Map extraResultsByPath;
+    private Map<ObjectId, Persistent> seen;
 
-    PrefetchProcessorTreeBuilder(HierarchicalObjectResolver objectTreeResolver,
-            List mainResultRows, Map extraResultsByPath) {
+    PrefetchProcessorTreeBuilder(HierarchicalObjectResolver objectTreeResolver, List mainResultRows,
+            Map extraResultsByPath) {
         this.context = objectTreeResolver.context;
         this.queryMetadata = objectTreeResolver.queryMetadata;
         this.mainResultRows = mainResultRows;
         this.extraResultsByPath = extraResultsByPath;
-        this.descriptor=objectTreeResolver.descriptor;
+        this.descriptor = objectTreeResolver.descriptor;
     }
 
     PrefetchProcessorNode buildTree(PrefetchTreeNode tree) {
         // reset state
         this.nodeStack = new LinkedList<PrefetchProcessorNode>();
+
+        // 'seen' is used to avoid re-processing objects already processed in a
+        // given prefetch query (see CAY-1695 for why this is bad). It is
+        // essentially a map of all objects fetched in a given transaction
+
+        // TODO: there are other places that are attempting to track objects in
+        // a tx... can we reuse 'seen' map from here?
+        this.seen = new HashMap<ObjectId, Persistent>();
+
         this.root = null;
 
         tree.traverse(this);
 
         if (root == null) {
-            throw new CayenneRuntimeException(
-                    "Failed to create prefetch processing tree.");
+            throw new CayenneRuntimeException("Failed to create prefetch processing tree.");
         }
 
         return root;
@@ -71,10 +81,8 @@ final class PrefetchProcessorTreeBuilder
         // root should be treated as disjoint
         if (getParent() == null) {
             return startDisjointPrefetch(node);
-        }
-        else {
-            PrefetchProcessorNode decorated = new PrefetchProcessorNode(getParent(), node
-                    .getName());
+        } else {
+            PrefetchProcessorNode decorated = new PrefetchProcessorNode(getParent(), node.getName());
 
             decorated.setPhantom(true);
             return addNode(decorated);
@@ -82,21 +90,23 @@ final class PrefetchProcessorTreeBuilder
     }
 
     private PrefetchProcessorNode createDisjointNode(PrefetchTreeNode node) {
-        // TODO, Andrus, 11/16/2005 - minor inefficiency: 'adjacentJointNodes' would
+        // TODO, Andrus, 11/16/2005 - minor inefficiency: 'adjacentJointNodes'
+        // would
         // grab ALL nodes, we just need to find first and stop...
-        PrefetchProcessorNode decorated = !node.adjacentJointNodes().isEmpty()
-                ? new PrefetchProcessorJointNode(getParent(), node.getName())
-                : new PrefetchProcessorNode(getParent(), node.getName());
+        PrefetchProcessorNode decorated = !node.adjacentJointNodes().isEmpty() ? new PrefetchProcessorJointNode(
+                getParent(), node.getName()) : new PrefetchProcessorNode(getParent(), node.getName());
         decorated.setPhantom(false);
         return decorated;
     }
 
     public boolean startDisjointPrefetch(PrefetchTreeNode node) {
-        // look ahead for joint children as joint children will require a different
+        // look ahead for joint children as joint children will require a
+        // different
         // node type.
         PrefetchProcessorNode decorated = createDisjointNode(node);
 
-        // semantics has to be "DISJOINT" even if the node is joint, as semantics
+        // semantics has to be "DISJOINT" even if the node is joint, as
+        // semantics
         // defines relationship with parent..
         decorated.setSemantics(PrefetchTreeNode.DISJOINT_PREFETCH_SEMANTICS);
         return addNode(decorated);
@@ -110,17 +120,14 @@ final class PrefetchProcessorTreeBuilder
     }
 
     public boolean startJointPrefetch(PrefetchTreeNode node) {
-        PrefetchProcessorJointNode decorated = new PrefetchProcessorJointNode(
-                getParent(),
-                node.getName());
+        PrefetchProcessorJointNode decorated = new PrefetchProcessorJointNode(getParent(), node.getName());
         decorated.setPhantom(false);
         decorated.setSemantics(PrefetchTreeNode.JOINT_PREFETCH_SEMANTICS);
         boolean result = addNode(decorated);
 
         // set "jointChildren" flag on all nodes in the same "join group"
         PrefetchProcessorNode groupNode = decorated;
-        while (groupNode.getParent() != null && !groupNode.isDisjointPrefetch()
-                && !groupNode.isDisjointByIdPrefetch()) {
+        while (groupNode.getParent() != null && !groupNode.isDisjointPrefetch() && !groupNode.isDisjointByIdPrefetch()) {
             groupNode = (PrefetchProcessorNode) groupNode.getParent();
             groupNode.setJointChildren(true);
         }
@@ -148,23 +155,19 @@ final class PrefetchProcessorTreeBuilder
 
         if (currentNode != null) {
             rows = (List) extraResultsByPath.get(node.getPath());
-            arc = (ArcProperty) currentNode.getResolver().getDescriptor().getProperty(
-                    node.getName());
+            arc = (ArcProperty) currentNode.getResolver().getDescriptor().getProperty(node.getName());
 
             if (arc == null) {
-                throw new CayenneRuntimeException("No relationship with name '"
-                        + node.getName()
-                        + "' found in entity "
+                throw new CayenneRuntimeException("No relationship with name '" + node.getName() + "' found in entity "
                         + currentNode.getResolver().getEntity().getName());
             }
 
             descriptor = arc.getTargetDescriptor();
-        }
-        else {
+        } else {
             arc = null;
-            if(this.descriptor!=null){
-                descriptor=this.descriptor;
-            }else{
+            if (this.descriptor != null) {
+                descriptor = this.descriptor;
+            } else {
                 descriptor = queryMetadata.getClassDescriptor();
             }
             rows = mainResultRows;
@@ -173,52 +176,22 @@ final class PrefetchProcessorTreeBuilder
         node.setDataRows(rows);
 
         node.setIncoming(arc);
-        
-        // TODO: is txStartRowVersion guessed
-        // correctly? i.e. the main rows are always fetched
-        // first? I guess this has to stay true if prefetching is
-        // involved.
-        long txStartRowVersion;
-        if (mainResultRows.isEmpty()) {
-            txStartRowVersion = DataObject.DEFAULT_VERSION;
+
+        if (node.getParent() != null && !node.isJointPrefetch()) {
+            node.setResolver(new HierarchicalObjectResolverNode(node, context, descriptor, queryMetadata
+                    .isRefreshingObjects(), seen));
         } else {
-            DataRow firstRow = (DataRow) mainResultRows.get(0);
-            txStartRowVersion = firstRow.getVersion();
-        }
-        
-        if (node.getParent() != null && !node.isJointPrefetch()) { 
-            node.setResolver(new HierarchicalObjectResolverNode(
-                    node,
-                    context,
-                    descriptor,
-                    queryMetadata.isRefreshingObjects(), 
-                    txStartRowVersion));
-        }
-        else {
-            node.setResolver(new PrefetchObjectResolver(context, descriptor, queryMetadata
-                    .isRefreshingObjects(), txStartRowVersion));
+            node.setResolver(new PrefetchObjectResolver(context, descriptor, queryMetadata.isRefreshingObjects(), seen));
         }
 
         if (node.getParent() == null || node.getParent().isPhantom()) {
             node.setParentAttachmentStrategy(new NoopParentAttachmentStrategy());
-        }
-        else if (node.isJointPrefetch()) {
-            node
-                    .setParentAttachmentStrategy(new StackLookupParentAttachmentStrategy(
-                            node));
-        }
-        else if (node
-                .getIncoming()
-                .getRelationship()
-                .isSourceIndependentFromTargetChange()) {
-            node.setParentAttachmentStrategy(new JoinedIdParentAttachementStrategy(
-                    context.getGraphManager(),
-                    node));
-        }
-        else {
-            node
-                    .setParentAttachmentStrategy(new ResultScanParentAttachmentStrategy(
-                            node));
+        } else if (node.isJointPrefetch()) {
+            node.setParentAttachmentStrategy(new StackLookupParentAttachmentStrategy(node));
+        } else if (node.getIncoming().getRelationship().isSourceIndependentFromTargetChange()) {
+            node.setParentAttachmentStrategy(new JoinedIdParentAttachementStrategy(context.getGraphManager(), node));
+        } else {
+            node.setParentAttachmentStrategy(new ResultScanParentAttachmentStrategy(node));
         }
 
         if (currentNode != null) {