BUG-5280: define RetiredGenerationException 40/39540/47
authorRobert Varga <rovarga@cisco.com>
Fri, 27 May 2016 15:24:20 +0000 (17:24 +0200)
committerRobert Varga <rovarga@cisco.com>
Mon, 20 Jun 2016 10:59:34 +0000 (12:59 +0200)
This defines the error reported by the backend whenever it sees
a request coming from an outdated client generation. The client
behavior handles these failures by invoking an upcall to the subclass
and initiate shutdown.

Change-Id: Iea1e04e3754fdc09fbdd9c6dc0d420a05145791c
Signed-off-by: Robert Varga <rovarga@cisco.com>
opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/RetiredGenerationException.java [new file with mode: 0644]
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/databroker/actors/dds/DistributedDataStoreClientBehavior.java
opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/actors/client/ClientActorBehavior.java

diff --git a/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/RetiredGenerationException.java b/opendaylight/md-sal/cds-access-api/src/main/java/org/opendaylight/controller/cluster/access/concepts/RetiredGenerationException.java
new file mode 100644 (file)
index 0000000..219f281
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.cluster.access.concepts;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * General error raised when the recipient of a {@link Request} determines that the request contains
+ * a {@link ClientIdentifier} which corresponds to an outdated generation.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public final class RetiredGenerationException extends RequestException {
+    private static final long serialVersionUID = 1L;
+
+    public RetiredGenerationException(final long newGeneration) {
+        super("Originating generation was superseded by " + Long.toUnsignedString(newGeneration));
+    }
+
+    @Override
+    public boolean isRetriable() {
+        return false;
+    }
+}
index 2b5e675..917e759 100644 (file)
@@ -64,6 +64,11 @@ final class DistributedDataStoreClientBehavior extends ClientActorBehavior imple
     //
     //
 
+    @Override
+    protected void haltClient(final Throwable cause) {
+        // FIXME: Add state flushing here once we have state
+    }
+
     private void createLocalHistory(final CreateLocalHistoryCommand command) {
         final CompletableFuture<ClientLocalHistory> future = command.future();
         final LocalHistoryIdentifier historyId = new LocalHistoryIdentifier(getIdentifier(), nextHistoryId++);
@@ -80,7 +85,7 @@ final class DistributedDataStoreClientBehavior extends ClientActorBehavior imple
         } else if (command instanceof GetClientRequest) {
             ((GetClientRequest) command).getReplyTo().tell(new Status.Success(this), ActorRef.noSender());
         } else if (SHUTDOWN.equals(command)) {
-            // Add shutdown procedures here
+            // FIXME: Add shutdown procedures here
             return null;
         } else {
             LOG.warn("{}: ignoring unhandled command {}", persistenceId(), command);
index b770b99..1d812b7 100644 (file)
@@ -11,7 +11,12 @@ import com.google.common.annotations.Beta;
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 import org.opendaylight.controller.cluster.access.concepts.ClientIdentifier;
+import org.opendaylight.controller.cluster.access.concepts.RequestException;
+import org.opendaylight.controller.cluster.access.concepts.RequestFailure;
+import org.opendaylight.controller.cluster.access.concepts.RetiredGenerationException;
 import org.opendaylight.yangtools.concepts.Identifiable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * A behavior, which handles messages sent to a {@link AbstractClientActor}.
@@ -23,12 +28,24 @@ import org.opendaylight.yangtools.concepts.Identifiable;
 @Beta
 public abstract class ClientActorBehavior extends RecoveredClientActorBehavior<ClientActorContext>
         implements Identifiable<ClientIdentifier> {
+    private static final Logger LOG = LoggerFactory.getLogger(ClientActorBehavior.class);
+
     protected ClientActorBehavior(final @Nonnull ClientActorContext context) {
         super(context);
     }
 
     @Override
     final ClientActorBehavior onReceiveCommand(final Object command) {
+        if (command instanceof RequestFailure) {
+            final RequestFailure<?, ?> failure = (RequestFailure<?, ?>) command;
+            final RequestException cause = failure.getCause();
+            if (cause instanceof RetiredGenerationException) {
+                LOG.error("{}: current generation {} has been superseded", persistenceId(), getIdentifier(), cause);
+                haltClient(cause);
+                return null;
+            }
+        }
+
         // TODO: any client-common logic (such as validation and common dispatch) needs to go here
         return onCommand(command);
     }
@@ -38,6 +55,16 @@ public abstract class ClientActorBehavior extends RecoveredClientActorBehavior<C
         return context().getIdentifier();
     }
 
+    /**
+     * Halt And Catch Fire.
+     *
+     * Halt processing on this client. Implementations need to ensure they initiate state flush procedures. No attempt
+     * to use this instance should be made after this method returns. Any such use may result in undefined behavior.
+     *
+     * @param cause Failure cause
+     */
+    protected abstract void haltClient(@Nonnull Throwable cause);
+
     /**
      * Override this method to handle any command which is not handled by the base behavior.
      *