Remove failed cache entries 09/96409/7
authorTomas Cere <tomas.cere@pantheon.tech>
Wed, 2 Jun 2021 12:08:25 +0000 (14:08 +0200)
committerRobert Varga <robert.varga@pantheon.tech>
Thu, 3 Jun 2021 15:44:35 +0000 (17:44 +0200)
We need to remove these from the cache once their corresponding
future fails, otherwise these would not be recomputed.

JIRA: YANGTOOLS-1293
Change-Id: Ic1c58392e7948f4a5fb144df6e8782ea0ffcab11
Signed-off-by: Tomas Cere <tomas.cere@pantheon.tech>
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
parser/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/SharedEffectiveModelContextFactory.java
parser/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/parser/repo/SharedEffectiveModelContextFactoryTest.java

index 7c7ff2888401f147a802e65cf661fd923433c701..a3f70814c6998aeae6297b0560d6eaa36ddc0a4d 100644 (file)
@@ -218,6 +218,10 @@ final class SharedEffectiveModelContextFactory implements EffectiveModelContextF
             public void onFailure(final Throwable cause) {
                 LOG.debug("Failed assembly of {} in {}", sources, sw, cause);
                 entry.getFuture().setException(cause);
+
+                // remove failed result from the cache so it can be recomputed, as this might have been a transient
+                // problem.
+                cache.remove(sources, entry);
             }
         }, MoreExecutors.directExecutor());
     }
index 6e88c41d858c930fc10d850026f7b3add4d072c1..bcbaa6ee8522d146b93ab7a912790d8d8f7ef57e 100644 (file)
@@ -7,7 +7,12 @@
  */
 package org.opendaylight.yangtools.yang.parser.repo;
 
+import static java.util.Objects.requireNonNull;
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThrows;
+import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateFailedFluentFuture;
 import static org.opendaylight.yangtools.util.concurrent.FluentFutures.immediateFluentFuture;
 
 import com.google.common.util.concurrent.ListenableFuture;
@@ -18,11 +23,13 @@ import org.junit.runner.RunWith;
 import org.mockito.junit.MockitoJUnitRunner;
 import org.opendaylight.yangtools.yang.common.Revision;
 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+import org.opendaylight.yangtools.yang.model.repo.api.MissingSchemaSourceException;
 import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier;
 import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactoryConfiguration;
 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
 import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
 import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceProvider;
 import org.opendaylight.yangtools.yang.parser.rfc7950.ir.IRSchemaSource;
 import org.opendaylight.yangtools.yang.parser.rfc7950.repo.TextToIRTransformer;
 
@@ -85,4 +92,51 @@ public class SharedEffectiveModelContextFactoryTest {
                 sharedSchemaContextFactory.createEffectiveModelContext(sIdWithoutRevision, provider.getId());
         assertNotNull(schemaContext.get());
     }
+
+    @Test
+    public void testTransientFailureWhilreRetrievingSchemaSource() throws Exception {
+        final RevisionSourceIdentifier s3 =
+            RevisionSourceIdentifier.create("network-topology", Revision.of("2013-10-21"));
+
+        repository.registerSchemaSource(new TransientFailureProvider(
+            YangTextSchemaSource.forResource("/ietf/network-topology@2013-10-21.yang")),
+            PotentialSchemaSource.create(s3, YangTextSchemaSource.class, 1));
+
+        final SharedEffectiveModelContextFactory sharedSchemaContextFactory =
+                new SharedEffectiveModelContextFactory(repository, config);
+
+        ListenableFuture<EffectiveModelContext> schemaContext =
+                sharedSchemaContextFactory.createEffectiveModelContext(s1, s3);
+
+        final ExecutionException exception = assertThrows(ExecutionException.class, schemaContext::get);
+        assertThat(exception.getCause(), instanceOf(MissingSchemaSourceException.class));
+
+        // check if future is invalidated and resolution of source is retried after failure
+        schemaContext = sharedSchemaContextFactory.createEffectiveModelContext(s1, s3);
+        assertNotNull(schemaContext.get());
+    }
+
+    /**
+     * Schema source provider that fails on first attempt of getSource() and succeeds on every subsequent call
+     * to simulate transient failures of source retrieval.
+     */
+    private static final class TransientFailureProvider implements SchemaSourceProvider<YangTextSchemaSource> {
+
+        private final YangTextSchemaSource schemaSource;
+        private boolean shouldFail = true;
+
+        private TransientFailureProvider(final YangTextSchemaSource schemaSource) {
+            this.schemaSource = requireNonNull(schemaSource);
+        }
+
+        @Override
+        public ListenableFuture<? extends YangTextSchemaSource> getSource(final SourceIdentifier sourceIdentifier) {
+            if (shouldFail) {
+                shouldFail = false;
+                return immediateFailedFluentFuture(new Exception("Transient test failure."));
+            }
+
+            return immediateFluentFuture(schemaSource);
+        }
+    }
 }