OutOfOrderRequestException is used for two distinct cases, which is
a mixup during refactor.
The first case is when an envelope's sequence does not match the
sequence we are expecting on a connection. This is a retriable
exception and happens due to mailbox queueing during leadership
changes:
- a FE sees us as a leader, sends requests
- we become a follower, we reject a few requests
- we become a leader, at which point we must not process requests
until the FE reconnects, as we would not be processing them in
the correct order.
The second case is when we receive a Request with an unexpected
sequence. This is a hard error, as it indicates that the client
has made a mistake and lost a request (like the case fixed in
fe69101801085580f2fe72762abea5c5fa83d978).
Separate these two cases out by introducing
OutOfSequenceEnvelopeException and handle it by initiating a session
reconnect.
Change-Id: Ifb0bac41ff2efd6385455fd9c77b8b39054dd4a0
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
(cherry picked from commit
d02d60083ee163cf465c265364c21c0df9cdc3c7)
import org.opendaylight.controller.cluster.access.concepts.RequestException;
/**
- * A {@link RequestException} indicating that the backend has received an unexpected request. This would typically
- * mean that messages are not arriving in the sequence they were generated by the frontend.
+ * A {@link RequestException} indicating that the backend has received a Request whose sequence does not match the
+ * next expected sequence for the target. This is a hard error, as it indicates a Request is missing in the stream.
*
* @author Robert Varga
*/
@Override
public boolean isRetriable() {
- return true;
+ return false;
}
}
--- /dev/null
+/*
+ * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.commands;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.controller.cluster.access.concepts.RequestException;
+
+/**
+ * A {@link RequestException} indicating that the backend has received a RequestEnvelope whose sequence does not match
+ * the next expected sequence. This can happen during leader transitions, when a part of the stream is rejected because
+ * the backend is not the leader and it transitions to being a leader with old stream messages still being present.
+ *
+ * @author Robert Varga
+ */
+@Beta
+public final class OutOfSequenceEnvelopeException extends RequestException {
+ private static final long serialVersionUID = 1L;
+
+ public OutOfSequenceEnvelopeException(final long expectedEnvelope) {
+ super("Expecting envelope " + Long.toUnsignedString(expectedEnvelope));
+ }
+
+ @Override
+ public boolean isRetriable() {
+ return true;
+ }
+}
*/
package org.opendaylight.controller.cluster.access.commands;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@Override
protected void isRetriable() {
- assertTrue(OBJECT.isRetriable());
+ assertFalse(OBJECT.isRetriable());
}
@Override
import javax.annotation.Nullable;
import javax.annotation.concurrent.GuardedBy;
import org.opendaylight.controller.cluster.access.commands.NotLeaderException;
+import org.opendaylight.controller.cluster.access.commands.OutOfSequenceEnvelopeException;
import org.opendaylight.controller.cluster.access.concepts.ClientIdentifier;
import org.opendaylight.controller.cluster.access.concepts.FailureEnvelope;
import org.opendaylight.controller.cluster.access.concepts.LocalHistoryIdentifier;
return conn.reconnect(this);
}
}
+ if (cause instanceof OutOfSequenceEnvelopeException) {
+ final AbstractClientConnection<T> conn = getConnection(command);
+ if (conn instanceof ReconnectingClientConnection) {
+ // Already reconnecting, do not churn the logs
+ return this;
+ } else if (conn != null) {
+ LOG.info("{}: connection {} indicated no sequencing mismatch on {} sequence {}, reconnecting it",
+ persistenceId(), conn, failure.getTarget(), failure.getSequence(), cause);
+ return conn.reconnect(this);
+ }
+ }
return onRequestFailure(command);
}
*/
abstract class AbstractFrontendHistory implements Identifiable<LocalHistoryIdentifier> {
private static final Logger LOG = LoggerFactory.getLogger(AbstractFrontendHistory.class);
- private static final OutOfOrderRequestException UNSEQUENCED_START = new OutOfOrderRequestException(0);
private final Map<TransactionIdentifier, FrontendTransaction> transactions = new HashMap<>();
private final RangeSet<UnsignedLong> purgedTransactions;
// The transaction does not exist and we are about to create it, check sequence number
if (request.getSequence() != 0) {
LOG.debug("{}: no transaction state present, unexpected request {}", persistenceId(), request);
- throw UNSEQUENCED_START;
+ throw new OutOfOrderRequestException(0);
}
tx = createTransaction(request, id);
import org.opendaylight.controller.cluster.access.commands.DestroyLocalHistoryRequest;
import org.opendaylight.controller.cluster.access.commands.LocalHistoryRequest;
import org.opendaylight.controller.cluster.access.commands.LocalHistorySuccess;
-import org.opendaylight.controller.cluster.access.commands.OutOfOrderRequestException;
+import org.opendaylight.controller.cluster.access.commands.OutOfSequenceEnvelopeException;
import org.opendaylight.controller.cluster.access.commands.PurgeLocalHistoryRequest;
import org.opendaylight.controller.cluster.access.commands.TransactionRequest;
import org.opendaylight.controller.cluster.access.commands.TransactionSuccess;
return clientId;
}
- private void checkRequestSequence(final RequestEnvelope envelope) throws OutOfOrderRequestException {
+ private void checkRequestSequence(final RequestEnvelope envelope) throws OutOfSequenceEnvelopeException {
if (expectedTxSequence != envelope.getTxSequence()) {
- throw new OutOfOrderRequestException(expectedTxSequence);
+ throw new OutOfSequenceEnvelopeException(expectedTxSequence);
}
}