Delay snapshot backed transaction ready error to 3PC canCommit.
Change-Id: Ief659423b401936a286f04c2f6c3732722c5aabf
JIRA: CONTROLLER-1812
Signed-off-by: Jaime Caamaño Ruiz <jcaamano@suse.com>
protected LocalThreePhaseCommitCohort(final ActorContext actorContext, final ActorSelection leader,
final SnapshotBackedWriteTransaction<TransactionIdentifier> transaction,
- final DataTreeModification modification) {
+ final DataTreeModification modification,
+ final Exception operationError) {
this.actorContext = Preconditions.checkNotNull(actorContext);
this.leader = Preconditions.checkNotNull(leader);
this.transaction = Preconditions.checkNotNull(transaction);
this.modification = Preconditions.checkNotNull(modification);
- this.operationError = null;
+ this.operationError = operationError;
}
protected LocalThreePhaseCommitCohort(final ActorContext actorContext, final ActorSelection leader,
@Override
protected DOMStoreThreePhaseCommitCohort createCohort(
final SnapshotBackedWriteTransaction<TransactionIdentifier> transaction,
- final DataTreeModification modification) {
- return new LocalChainThreePhaseCommitCohort(transaction, modification);
+ final DataTreeModification modification,
+ final Exception operationError) {
+ return new LocalChainThreePhaseCommitCohort(transaction, modification, operationError);
}
@Override
private class LocalChainThreePhaseCommitCohort extends LocalThreePhaseCommitCohort {
protected LocalChainThreePhaseCommitCohort(SnapshotBackedWriteTransaction<TransactionIdentifier> transaction,
- DataTreeModification modification) {
- super(parent.getActorContext(), leader, transaction, modification);
+ DataTreeModification modification, Exception operationError) {
+ super(parent.getActorContext(), leader, transaction, modification, operationError);
}
protected LocalChainThreePhaseCommitCohort(SnapshotBackedWriteTransaction<TransactionIdentifier> transaction,
@Override
protected DOMStoreThreePhaseCommitCohort transactionReady(
- final SnapshotBackedWriteTransaction<TransactionIdentifier> tx, final DataTreeModification tree) {
- return new LocalThreePhaseCommitCohort(actorContext, leader, tx, tree);
+ final SnapshotBackedWriteTransaction<TransactionIdentifier> tx,
+ final DataTreeModification tree,
+ final Exception readyError) {
+ return new LocalThreePhaseCommitCohort(actorContext, leader, tx, tree, readyError);
}
@SuppressWarnings({"unchecked", "checkstyle:IllegalCatch"})
(SnapshotBackedWriteTransaction<TransactionIdentifier>)tx, operationError);
}
- try {
- return (LocalThreePhaseCommitCohort) tx.ready();
- } catch (Exception e) {
- // Unfortunately we need to cast to SnapshotBackedWriteTransaction here as it's required by
- // LocalThreePhaseCommitCohort.
- return new LocalThreePhaseCommitCohort(actorContext, leader,
- (SnapshotBackedWriteTransaction<TransactionIdentifier>)tx, e);
- }
+ return (LocalThreePhaseCommitCohort) tx.ready();
}
}
}
@Override
- protected final DOMStoreThreePhaseCommitCohort transactionReady(final SnapshotBackedWriteTransaction<T> tx, final DataTreeModification tree) {
+ protected final DOMStoreThreePhaseCommitCohort transactionReady(
+ final SnapshotBackedWriteTransaction<T> tx,
+ final DataTreeModification tree,
+ final Exception readyError) {
+
final State localState = state;
if (localState instanceof Allocated) {
LOG.debug("Ignoring transaction {} readiness due to state {}", tx, localState);
}
- return createCohort(tx, tree);
+ return createCohort(tx, tree, readyError);
}
@Override
*
* @param transaction Transaction handle
* @param modification {@link DataTreeModification} which needs to be applied to the backend
+ * @param operationError Any previous error that could be reported through three phase commit
* @return A {@link DOMStoreThreePhaseCommitCohort} cohort.
*/
- protected abstract DOMStoreThreePhaseCommitCohort createCohort(final SnapshotBackedWriteTransaction<T> transaction, final DataTreeModification modification);
+ protected abstract DOMStoreThreePhaseCommitCohort createCohort(final SnapshotBackedWriteTransaction<T> transaction,
+ final DataTreeModification modification,
+ final Exception operationError);
}
import com.google.common.base.Preconditions;
import com.google.common.base.Throwables;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+import javax.annotation.Nullable;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
final DataTreeModification tree = mutableTree;
TREE_UPDATER.lazySet(this, null);
- tree.ready();
- return wasReady.transactionReady(this, tree);
+ try {
+ tree.ready();
+ return wasReady.transactionReady(this, tree, null);
+ } catch (RuntimeException e) {
+ LOG.debug("Store transaction: {}: unexpected failure when readying", getIdentifier(), e);
+ return wasReady.transactionReady(this, tree, e);
+ }
}
@Override
* Transaction on which ready was invoked.
* @param tree
* Modified data tree which has been constructed.
+ * @param readyError
+ * Any error that has already happened when readying.
* @return DOMStoreThreePhaseCommitCohort associated with transaction
*/
- protected abstract DOMStoreThreePhaseCommitCohort transactionReady(SnapshotBackedWriteTransaction<T> tx, DataTreeModification tree);
+ protected abstract DOMStoreThreePhaseCommitCohort transactionReady(SnapshotBackedWriteTransaction<T> tx,
+ DataTreeModification tree,
+ @Nullable Exception readyError);
}
}
final class ChainedTransactionCommitImpl extends InMemoryDOMStoreThreePhaseCommitCohort {
private final DOMStoreTransactionChainImpl txChain;
- ChainedTransactionCommitImpl(final InMemoryDOMDataStore store, final SnapshotBackedWriteTransaction<String> transaction,
- final DataTreeModification modification, final DOMStoreTransactionChainImpl txChain) {
- super(store, transaction, modification);
+ ChainedTransactionCommitImpl(final InMemoryDOMDataStore store,
+ final SnapshotBackedWriteTransaction<String> transaction,
+ final DataTreeModification modification,
+ final DOMStoreTransactionChainImpl txChain,
+ final Exception operationError) {
+ super(store, transaction, modification, operationError);
this.txChain = Preconditions.checkNotNull(txChain);
}
}
@Override
- protected DOMStoreThreePhaseCommitCohort createCohort(final SnapshotBackedWriteTransaction<String> tx, final DataTreeModification modification) {
- return new ChainedTransactionCommitImpl(store, tx, modification, this);
+ protected DOMStoreThreePhaseCommitCohort createCohort(final SnapshotBackedWriteTransaction<String> tx,
+ final DataTreeModification modification,
+ final Exception operationError) {
+ return new ChainedTransactionCommitImpl(store, tx, modification, this, operationError);
}
@Override
}
@Override
- protected DOMStoreThreePhaseCommitCohort transactionReady(final SnapshotBackedWriteTransaction<String> tx, final DataTreeModification modification) {
+ protected DOMStoreThreePhaseCommitCohort transactionReady(final SnapshotBackedWriteTransaction<String> tx,
+ final DataTreeModification modification,
+ final Exception readyError) {
LOG.debug("Tx: {} is submitted. Modifications: {}", tx.getIdentifier(), modification);
- return new InMemoryDOMStoreThreePhaseCommitCohort(this, tx, modification);
+ return new InMemoryDOMStoreThreePhaseCommitCohort(this, tx, modification, readyError);
}
String nextIdentifier() {
private final DataTreeModification modification;
private final InMemoryDOMDataStore store;
private DataTreeCandidate candidate;
+ private final Exception operationError;
- public InMemoryDOMStoreThreePhaseCommitCohort(final InMemoryDOMDataStore store, final SnapshotBackedWriteTransaction<String> writeTransaction, final DataTreeModification modification) {
+ public InMemoryDOMStoreThreePhaseCommitCohort(final InMemoryDOMDataStore store,
+ final SnapshotBackedWriteTransaction<String> writeTransaction,
+ final DataTreeModification modification,
+ final Exception operationError) {
this.transaction = Preconditions.checkNotNull(writeTransaction);
this.modification = Preconditions.checkNotNull(modification);
this.store = Preconditions.checkNotNull(store);
+ this.operationError = operationError;
}
private static void warnDebugContext(final AbstractDOMStoreTransaction<?> transaction) {
@Override
public final ListenableFuture<Boolean> canCommit() {
+ if (operationError != null) {
+ return Futures.immediateFailedFuture(operationError);
+ }
+
try {
store.validate(modification);
LOG.debug("Store Transaction: {} can be committed", getTransaction().getIdentifier());
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import java.util.concurrent.ExecutionException;
+import org.junit.Assert;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.mockito.Mockito;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadTransaction;
import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot;
import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
writeTx.ready();
}
+ @Test
+ public void testReadyWithMissingMandatoryData() throws InterruptedException {
+ DOMStoreWriteTransaction writeTx = domStore.newWriteOnlyTransaction();
+ NormalizedNode<?, ?> testNode = ImmutableContainerNodeBuilder.create()
+ .withNodeIdentifier(new NodeIdentifier(TestModel.MANDATORY_DATA_TEST_QNAME))
+ .addChild(ImmutableNodes.leafNode(TestModel.OPTIONAL_QNAME, "data"))
+ .build();
+ writeTx.write(TestModel.MANDATORY_DATA_TEST_PATH, testNode);
+ DOMStoreThreePhaseCommitCohort ready = writeTx.ready();
+ try {
+ ready.canCommit().get();
+ Assert.fail("Expected exception on canCommit");
+ } catch (ExecutionException e) {
+ // nop
+ }
+ }
+
@Test
public void testTransactionAbort() throws InterruptedException, ExecutionException {
public static final YangInstanceIdentifier TEST_PATH = YangInstanceIdentifier.of(TEST_QNAME);
public static final YangInstanceIdentifier OUTER_LIST_PATH =
YangInstanceIdentifier.builder(TEST_PATH).node(OUTER_LIST_QNAME).build();
+
+ public static final QName MANDATORY_DATA_TEST_QNAME =
+ QName.create("urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:store:test",
+ "2014-03-13",
+ "mandatory-data-test");
+ public static final QName OPTIONAL_QNAME = QName.create(MANDATORY_DATA_TEST_QNAME, "optional-data");
+ public static final QName MANDATORY_QNAME = QName.create(MANDATORY_DATA_TEST_QNAME, "mandatory-data");
+ public static final YangInstanceIdentifier MANDATORY_DATA_TEST_PATH =
+ YangInstanceIdentifier.of(MANDATORY_DATA_TEST_QNAME);
+
private static final String DATASTORE_TEST_YANG = "/odl-datastore-test.yang";
public static SchemaContext createTestContext() {
}
}
}
+
+ container mandatory-data-test {
+ presence "needs to be present when empty";
+
+ leaf optional-data {
+ type string;
+ }
+ leaf mandatory-data {
+ type string;
+ mandatory true;
+ }
+ }
}