Deduplicate concurrent SchemaContext loads 90/82490/3
authorRobert Varga <robert.varga@pantheon.tech>
Tue, 11 Jun 2019 16:53:18 +0000 (18:53 +0200)
committerRobert Varga <nite@hq.sk>
Thu, 13 Jun 2019 08:06:44 +0000 (08:06 +0000)
In case we are running concurrent SchemaContext computation, if
the result is not cached we will have multiple results. These
need to be reconciled with the cache, so that those results will
be squashed into a single instance.

JIRA: YANGTOOLS-1004
Change-Id: I82df2b32a534d0acc803548579fb8ad138853c68
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/SharedSchemaContextFactory.java

index 5f2214444da8b52b28e8f66e844089e998dbe601..10a81fa01e32e5f8edaf388e0b66cefccbe26b94 100644 (file)
@@ -24,6 +24,7 @@ import com.google.common.util.concurrent.FutureCallback;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.MoreExecutors;
+import com.google.common.util.concurrent.SettableFuture;
 import java.util.Collection;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
@@ -31,6 +32,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
+import java.util.concurrent.ExecutionException;
 import org.antlr.v4.runtime.ParserRuleContext;
 import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.yangtools.antlrv4.code.gen.YangStatementParser.StatementContext;
@@ -101,20 +103,31 @@ final class SharedSchemaContextFactory implements EffectiveModelContextFactory {
         final ListenableFuture<EffectiveModelContext> cf = Futures.transformAsync(sf, assembleSources,
             MoreExecutors.directExecutor());
 
-        // Populate cache when successful
+        final SettableFuture<EffectiveModelContext> rf = SettableFuture.create();
         Futures.addCallback(cf, new FutureCallback<EffectiveModelContext>() {
             @Override
             public void onSuccess(final EffectiveModelContext result) {
-                cache.put(uniqueSourceIdentifiers, result);
+                // Deduplicate concurrent loads
+                final EffectiveModelContext existing;
+                try {
+                    existing = cache.get(uniqueSourceIdentifiers, () -> result);
+                } catch (ExecutionException e) {
+                    LOG.warn("Failed to recheck result with cache, will use computed value", e);
+                    rf.set(result);
+                    return;
+                }
+
+                rf.set(existing);
             }
 
             @Override
             public void onFailure(final Throwable cause) {
                 LOG.debug("Failed to assemble sources", cause);
+                rf.setException(cause);
             }
         }, MoreExecutors.directExecutor());
 
-        return cf;
+        return rf;
     }
 
     private ListenableFuture<ASTSchemaSource> requestSource(final @NonNull SourceIdentifier identifier) {