Cache leaf lookups 64/93364/1
authorRobert Varga <robert.varga@pantheon.tech>
Mon, 26 Oct 2020 01:28:00 +0000 (02:28 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Mon, 26 Oct 2020 01:29:16 +0000 (02:29 +0100)
When we are binding a method name to its schema, we are performing
the inverse lookup. Instantiate a cache for these, so we end up
searching only once in DefaultQueryFactory's life.

JIRA: MDSAL-611
Change-Id: Idf9f3de22d907036478dc289387746eeb5a719a7
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/query/DefaultDescendantQueryBuilder.java
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/query/DefaultQueryFactory.java
binding/mdsal-binding-dom-adapter/src/main/java/org/opendaylight/mdsal/binding/dom/adapter/query/QueryBuilderState.java

index c99ab47ee5395aed5c52e48109deea8acf2bdf66..9d8929bfdaebf8e7bedbc28e55b8c4e013acd198 100644 (file)
@@ -11,7 +11,6 @@ import org.opendaylight.mdsal.binding.api.query.DescendantQueryBuilder;
 import org.opendaylight.mdsal.binding.api.query.MatchBuilderPath;
 import org.opendaylight.mdsal.binding.api.query.QueryExpression;
 import org.opendaylight.mdsal.binding.api.query.QueryStructureException;
-import org.opendaylight.mdsal.binding.dom.codec.api.BindingCodecTree;
 import org.opendaylight.yangtools.yang.binding.ChildOf;
 import org.opendaylight.yangtools.yang.binding.ChoiceIn;
 import org.opendaylight.yangtools.yang.binding.DataObject;
@@ -23,8 +22,8 @@ final class DefaultDescendantQueryBuilder<R extends DataObject, T extends DataOb
     private final InstanceIdentifierBuilder<T> childPath;
     private final QueryBuilderState builder;
 
-    DefaultDescendantQueryBuilder(final BindingCodecTree codec, final InstanceIdentifier<T> rootPath) {
-        this.builder = new QueryBuilderState(codec, rootPath);
+    DefaultDescendantQueryBuilder(final DefaultQueryFactory factory, final InstanceIdentifier<T> rootPath) {
+        this.builder = new QueryBuilderState(factory, rootPath);
         this.childPath = rootPath.builder();
     }
 
index 29e0ad62f39becd19c9f9e42f3e1f48fb33d4c9e..c254625711fcfb99f831e943b464e0024c6d6f51 100644 (file)
@@ -10,21 +10,75 @@ package org.opendaylight.mdsal.binding.dom.adapter.query;
 import static java.util.Objects.requireNonNull;
 
 import com.google.common.annotations.Beta;
+import com.google.common.base.Throwables;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
 import java.util.ServiceLoader;
+import java.util.concurrent.ExecutionException;
 import javax.inject.Inject;
 import javax.inject.Singleton;
+import org.eclipse.jdt.annotation.NonNull;
 import org.kohsuke.MetaInfServices;
 import org.opendaylight.mdsal.binding.api.query.DescendantQueryBuilder;
 import org.opendaylight.mdsal.binding.api.query.QueryFactory;
+import org.opendaylight.mdsal.binding.api.query.QueryStructureException;
 import org.opendaylight.mdsal.binding.dom.codec.api.BindingCodecTree;
+import org.opendaylight.mdsal.binding.dom.codec.spi.BindingSchemaMapping;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 @Beta
 @MetaInfServices
 @Singleton
 public final class DefaultQueryFactory implements QueryFactory {
-    private final BindingCodecTree codec;
+    private static final class MethodId {
+        final DataNodeContainer parent;
+        final String methodName;
+
+        MethodId(final DataNodeContainer parent, final String methodName) {
+            this.parent = requireNonNull(parent);
+            this.methodName = requireNonNull(methodName);
+        }
+
+        @Override
+        public int hashCode() {
+            return parent.hashCode() * 31 + methodName.hashCode();
+        }
+
+        @Override
+        public boolean equals(final Object obj) {
+            if (!(obj instanceof MethodId)) {
+                return false;
+            }
+            final MethodId other = (MethodId) obj;
+            return methodName.equals(other.methodName) && parent.equals(other.parent);
+        }
+    }
+
+    private static final Logger LOG = LoggerFactory.getLogger(DefaultQueryFactory.class);
+
+    private final LoadingCache<MethodId, NodeIdentifier> knownMethods = CacheBuilder.newBuilder().build(
+        new CacheLoader<MethodId, NodeIdentifier>() {
+            @Override
+            public NodeIdentifier load(final MethodId key) {
+                final DataNodeContainer parent = key.parent;
+                final String methodName = key.methodName;
+
+                for (DataSchemaNode child : parent.getChildNodes()) {
+                    if (methodName.equals(BindingSchemaMapping.getGetterMethodName(child))) {
+                        return NodeIdentifier.create(child.getQName());
+                    }
+                }
+                throw new QueryStructureException("Failed to find schema matching " + methodName + " in " + parent);
+            }
+        });
+    private final @NonNull BindingCodecTree codec;
 
     public DefaultQueryFactory() {
         this(ServiceLoader.load(BindingCodecTree.class).findFirst().orElseThrow());
@@ -37,6 +91,21 @@ public final class DefaultQueryFactory implements QueryFactory {
 
     @Override
     public <T extends DataObject> DescendantQueryBuilder<T> querySubtree(final InstanceIdentifier<T> rootPath) {
-        return new DefaultDescendantQueryBuilder<>(codec, rootPath);
+        return new DefaultDescendantQueryBuilder<>(this, rootPath);
+    }
+
+    @NonNull BindingCodecTree codec() {
+        return codec;
+    }
+
+    @NonNull NodeIdentifier findChild(final DataNodeContainer parent, final String methodName) {
+        try {
+            return knownMethods.get(new MethodId(parent, methodName));
+        } catch (ExecutionException e) {
+            LOG.debug("Failed to find method for {}", methodName, e);
+            final Throwable cause = e.getCause();
+            Throwables.throwIfUnchecked(cause);
+            throw new IllegalStateException("Failed to load cache", e);
+        }
     }
 }
index e6cb12181d79ba597fbd0e4796f349d1420e68e1..c57497c0de47e56b3ce1557c999d62dba086d150 100644 (file)
@@ -16,12 +16,10 @@ import java.util.List;
 import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.mdsal.binding.api.query.MatchBuilderPath.LeafReference;
 import org.opendaylight.mdsal.binding.api.query.QueryExpression;
-import org.opendaylight.mdsal.binding.api.query.QueryStructureException;
 import org.opendaylight.mdsal.binding.dom.adapter.query.LambdaDecoder.LambdaTarget;
 import org.opendaylight.mdsal.binding.dom.codec.api.BindingCodecTree;
 import org.opendaylight.mdsal.binding.dom.codec.api.BindingCodecTreeNode;
 import org.opendaylight.mdsal.binding.dom.codec.api.BindingDataObjectCodecTreeNode;
-import org.opendaylight.mdsal.binding.dom.codec.spi.BindingSchemaMapping;
 import org.opendaylight.mdsal.dom.api.query.DOMQuery;
 import org.opendaylight.mdsal.dom.api.query.DOMQueryPredicate;
 import org.opendaylight.yangtools.concepts.Immutable;
@@ -30,7 +28,6 @@ import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
-import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.DocumentedNode.WithStatus;
 
 final class QueryBuilderState {
@@ -44,15 +41,17 @@ final class QueryBuilderState {
         }
     }
 
-    private final BindingCodecTree codec;
-
     private final List<DOMQueryPredicate> predicates = new ArrayList<>();
+    private final DefaultQueryFactory factory;
     private final YangInstanceIdentifier root;
+    private final BindingCodecTree codec;
+
     private YangInstanceIdentifier absoluteSelect;
     private YangInstanceIdentifier relativeSelect;
 
-    QueryBuilderState(final BindingCodecTree codec, final InstanceIdentifier<?> root) {
-        this.codec = requireNonNull(codec);
+    QueryBuilderState(final DefaultQueryFactory factory, final InstanceIdentifier<?> root) {
+        this.codec = factory.codec();
+        this.factory = factory;
         this.root = fromBinding(root);
     }
 
@@ -77,12 +76,12 @@ final class QueryBuilderState {
         final LambdaTarget targetLeaf = LambdaDecoder.resolveLambda(ref);
         verify(targetLeaf.targetClass.equals(bindingPath.getTargetType().getName()), "Mismatched target %s and path %s",
             targetLeaf, bindingPath);
-        final DataSchemaNode child = findChild((DataNodeContainer) targetSchema, targetLeaf.targetMethod);
+        final NodeIdentifier childId = factory.findChild((DataNodeContainer) targetSchema, targetLeaf.targetMethod);
         final YangInstanceIdentifier absTarget = fromBinding(bindingPath);
         final YangInstanceIdentifier relTarget = absTarget.relativeTo(absoluteSelect)
                 .orElseThrow(() -> new IllegalStateException(absoluteSelect + " is not an ancestor of " + absTarget));
 
-        return new BoundMethod(relTarget, targetCodec.yangPathArgumentChild(NodeIdentifier.create(child.getQName())));
+        return new BoundMethod(relTarget, targetCodec.yangPathArgumentChild(childId));
     }
 
     void addPredicate(final DOMQueryPredicate predicate) {
@@ -96,13 +95,4 @@ final class QueryBuilderState {
     private @NonNull YangInstanceIdentifier fromBinding(final InstanceIdentifier<?> bindingId) {
         return codec.getInstanceIdentifierCodec().fromBinding(bindingId);
     }
-
-    private static DataSchemaNode findChild(final DataNodeContainer parent, final String methodName) {
-        for (DataSchemaNode child : parent.getChildNodes()) {
-            if (methodName.equals(BindingSchemaMapping.getGetterMethodName(child))) {
-                return child;
-            }
-        }
-        throw new QueryStructureException("Failed to find schema matching " + methodName + " in " + parent);
-    }
 }