Bug 1262 - Document Transaction Chaining of MD-SAL 33/8733/3
authorRobert Varga <rovarga@cisco.com>
Mon, 7 Jul 2014 12:06:21 +0000 (14:06 +0200)
committertpantelis <tpanteli@brocade.com>
Fri, 22 Aug 2014 14:40:11 +0000 (10:40 -0400)
Improves and clarifies the transaction chain concept and related APIs.

Change-Id: Ib509cdef33f8b51f8bd142b93657eace489e03a1
Signed-off-by: Tony Tkacik <ttkacik@cisco.com>
Signed-off-by: Robert Varga <rovarga@cisco.com>
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingTransactionChain.java
opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/DataBroker.java
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/TransactionChain.java
opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/TransactionChainFactory.java
opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreTransactionChain.java

index 24ec89ebb27da82f92fa569216ee7db5fc07d6cc..c71aa049c0622397773d5547faa773aac45ad7fd 100644 (file)
@@ -1,18 +1,41 @@
+/*
+ * Copyright (c) 2014 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.md.sal.binding.api;
 
 import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 
+/**
+ * A chain of transactions.
+ * <p>
+ * For more information about transaction chaining and transaction chains
+ * see {@link TransactionChain}.
+ *
+ * @see TransactionChain
+ *
+ */
 public interface BindingTransactionChain extends TransactionFactory, TransactionChain<InstanceIdentifier<?>, DataObject> {
-
+    /**
+     * {@inheritDoc}
+     */
     @Override
     ReadOnlyTransaction newReadOnlyTransaction();
 
+    /**
+     * {@inheritDoc}
+     */
     @Override
     ReadWriteTransaction newReadWriteTransaction();
 
+    /**
+     * {@inheritDoc}
+     */
     @Override
     WriteTransaction newWriteOnlyTransaction();
-
 }
index 619a08eac5f30a6f3a7ff7f5f154b1be4c74bdbc..cc8deb81b31777ee6dd4aa3b5bf19a97f3ababb5 100644 (file)
@@ -20,22 +20,39 @@ import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
  * subscribe for changes to data under a given branch of the tree.
  * <p>
  * For more information on usage, please see the documentation in {@link AsyncDataBroker}.
+ *
+ * @see AsyncDataBroker
+ * @see TransactionChainFactory
  */
 public interface DataBroker extends TransactionFactory, AsyncDataBroker<InstanceIdentifier<?>, DataObject, DataChangeListener>, BindingService, TransactionChainFactory<InstanceIdentifier<?>, DataObject> {
-
+    /**
+     * {@inheritDoc}
+     */
     @Override
     ReadOnlyTransaction newReadOnlyTransaction();
 
+    /**
+     * {@inheritDoc}
+     */
     @Override
     ReadWriteTransaction newReadWriteTransaction();
 
+    /**
+     * {@inheritDoc}
+     */
     @Override
     WriteTransaction newWriteOnlyTransaction();
 
+    /**
+     * {@inheritDoc}
+     */
     @Override
     ListenerRegistration<DataChangeListener> registerDataChangeListener(LogicalDatastoreType store,
             InstanceIdentifier<?> path, DataChangeListener listener, DataChangeScope triggeringScope);
 
+    /**
+     * {@inheritDoc}
+     */
     @Override
     BindingTransactionChain createTransactionChain(TransactionChainListener listener);
 }
index 940559ef895bf9cec406eee2fbe73a7617da581a..32e32f94eb34e59d5b956095235883a717d3f95b 100644 (file)
@@ -10,39 +10,114 @@ package org.opendaylight.controller.md.sal.common.api.data;
 import org.opendaylight.yangtools.concepts.Path;
 
 /**
- * A chain of transactions. Transactions in a chain need to be committed in sequence and each
- * transaction should see the effects of previous transactions as if they happened. A chain
- * makes no guarantees of atomicity, in fact transactions are committed as soon as possible.
+ * A chain of transactions. Transactions in a chain need to be committed in
+ * sequence and each transaction should see the effects of previous committed transactions
+ * as they occurred. A chain makes no guarantees of atomicity across the chained transactions -
+ * the transactions are committed as soon as possible in the order that they were submitted.
  *
+ * This behaviour is different from the default AsyncDataBroker, where a
+ * transaction is always created from the current global state, not taking into
+ * account any transactions previously committed by the calling thread. Due to
+ * the asynchronous nature of transaction submission this can lead to surprising
+ * results. If a thread executes the following sequence sufficiently quickly:
+ *
+ * AsyncWriteTransaction t1 = broker.newWriteOnlyTransaction();
+ * t1.put(id, data);
+ * t1.submit();
+ *
+ * AsyncReadTransaction t2 = broker.newReadOnlyTransaction();
+ * Optional<?> maybeData = t2.read(id).get();
+ *
+ * it may happen, that it sees maybeData.isPresent() == false, simply because
+ * t1 has not completed the processes of being applied and t2 is actually
+ * allocated from the previous state. This is obviously bad for users who create
+ * incremental state in the datastore and actually read what they write in
+ * subsequent transactions.
+ *
+ * Using a TransactionChain instead of a broker solves this particular problem,
+ * and leads to expected behavior: t2 will always see the data written in t1
+ * present.
  */
-public interface TransactionChain<P extends Path<P>, D> extends AutoCloseable, AsyncDataTransactionFactory<P, D> {
+public interface TransactionChain<P extends Path<P>, D> extends AutoCloseable,
+        AsyncDataTransactionFactory<P, D> {
 
     /**
      * Create a new read only transaction which will continue the chain.
-     * The previous read-write transaction has to be either COMMITED or CANCELLED.
+     *
+     * <p>
+     * The previous write transaction has to be either SUBMITTED
+     * ({@link AsyncWriteTransaction#submit submit} was invoked) or CANCELLED
+     * ({@link #close close} was invoked).
+     * <p>
+     * The returned read-only transaction presents an isolated view of the data if the previous
+     * write transaction was successful - in other words, this read-only transaction will see the
+     * state changes made by the previous write transaction in the chain. However, state which
+     * was introduced by other transactions outside this transaction chain after creation of
+     * the previous transaction is not visible.
      *
      * @return New transaction in the chain.
-     * @throws IllegalStateException if the previous transaction was not COMMITED
-     *    or CANCELLED.
-     * @throws TransactionChainClosedException if the chain has been closed.
+     * @throws IllegalStateException
+     *             if the previous transaction was not SUBMITTED or CANCELLED.
+     * @throws TransactionChainClosedException
+     *             if the chain has been closed.
      */
     @Override
     public AsyncReadOnlyTransaction<P, D> newReadOnlyTransaction();
 
-
     /**
-     * Create a new read write transaction which will continue the chain.
-     * The previous read-write transaction has to be either COMMITED or CANCELLED.
+     * Create a new read-write transaction which will continue the chain.
+     *
+     * <p>
+     * The previous write transaction has to be either SUBMITTED
+     * ({@link AsyncWriteTransaction#submit submit} was invoked) or CANCELLED
+     * ({@link #close close} was invoked).
+     * <p>
+     * The returned read-write transaction presents an isolated view of the data if the previous
+     * write transaction was successful - in other words, this read-write transaction will see the
+     * state changes made by the previous write transaction in the chain. However, state which
+     * was introduced by other transactions outside this transaction chain after creation of
+     * the previous transaction is not visible.
+     * <p>
+     * Committing this read-write transaction using {@link AsyncWriteTransaction#submit submit}
+     * will submit the state changes in this transaction to be visible to any subsequent
+     * transaction in this chain and also to any transaction outside this chain.
      *
      * @return New transaction in the chain.
-     * @throws IllegalStateException if the previous transaction was not COMMITTED
-     *    or CANCELLED.
-     * @throws TransactionChainClosedException if the chain has been closed.
+     * @throws IllegalStateException
+     *             if the previous transaction was not SUBMITTED or CANCELLED.
+     * @throws TransactionChainClosedException
+     *             if the chain has been closed.
      */
     @Override
     public AsyncReadWriteTransaction<P, D> newReadWriteTransaction();
 
+    /**
+     * Create a new write-only transaction which will continue the chain.
+     *
+     * <p>
+     * The previous write transaction has to be either SUBMITTED
+     * ({@link AsyncWriteTransaction#submit submit} was invoked) or CANCELLED
+     * ({@link #close close} was invoked).
+     * <p>
+     * The returned write-only transaction presents an isolated view of the data if the previous
+     * write transaction was successful - in other words, this write-only transaction will see the
+     * state changes made by the previous write transaction in the chain. However, state which
+     * was introduced by other transactions outside this transaction chain after creation of
+     * the previous transaction is not visible.
+     * <p>
+     * Committing this write-only transaction using {@link AsyncWriteTransaction#submit submit}
+     * will submit the state changes in this transaction to be visible to any subsequent
+     * transaction in this chain and also to any transaction outside this chain.
+     *
+     * @return New transaction in the chain.
+     * @throws IllegalStateException
+     *             if the previous transaction was not SUBMITTED or CANCELLED.
+     * @throws TransactionChainClosedException
+     *             if the chain has been closed.
+     */
+    @Override
+    public AsyncWriteTransaction<P, D> newWriteOnlyTransaction();
+
     @Override
     void close();
 }
-
index 94d21f5fd6bb02736a360592dc6826a3c70e4ae4..470e6110047adb93014455ae679dc8126b7e1dd3 100644 (file)
@@ -13,6 +13,7 @@ import org.opendaylight.yangtools.concepts.Path;
  * Interface for creating transaction chains.
  */
 public interface TransactionChainFactory<P extends Path<P>, D> {
+
     /**
      * Create a new transaction chain. The chain will be initialized to read
      * from its backing datastore, with no outstanding transaction. Listener
index 5876c50d51380a865afead594618627e7347bf3f..b916fddca7796e90dca402f1a8b4ac6a26fe2837 100644 (file)
@@ -12,8 +12,6 @@ package org.opendaylight.controller.sal.core.spi.data;
  * sequence and each transaction must see the effects of previous transactions
  * as if they happened. A chain makes no guarantees of atomicity, in fact
  * transactions are committed as soon as possible.
- *
- *
  */
 public interface DOMStoreTransactionChain extends DOMStoreTransactionFactory, AutoCloseable {
 
@@ -35,7 +33,7 @@ public interface DOMStoreTransactionChain extends DOMStoreTransactionFactory, Au
      *             if the chain has been closed.
      */
     @Override
-    public DOMStoreReadTransaction newReadOnlyTransaction();
+    DOMStoreReadTransaction newReadOnlyTransaction();
 
     /**
      * Create a new read write transaction which will continue the chain. The
@@ -55,10 +53,10 @@ public interface DOMStoreTransactionChain extends DOMStoreTransactionFactory, Au
      *             if the chain has been closed.
      */
     @Override
-    public DOMStoreReadWriteTransaction newReadWriteTransaction();
+    DOMStoreReadWriteTransaction newReadWriteTransaction();
 
     /**
-     * Create a new read write transaction which will continue the chain. The
+     * Create a new write-only transaction which will continue the chain. The
      * previous read-write transaction has to be either READY or CANCELLED.
      *
      *
@@ -68,8 +66,7 @@ public interface DOMStoreTransactionChain extends DOMStoreTransactionFactory, Au
      *             if the chain has been closed.
      */
     @Override
-    public DOMStoreWriteTransaction newWriteOnlyTransaction();
-
+    DOMStoreWriteTransaction newWriteOnlyTransaction();
 
     /**
      * Closes Transaction Chain.
@@ -80,6 +77,5 @@ public interface DOMStoreTransactionChain extends DOMStoreTransactionFactory, Au
      * @throws IllegalStateException If any of the outstanding created transactions was not canceled or ready.
      */
     @Override
-    public void close();
-
+    void close();
 }