Transactions can now print their allocation context.
Change-Id: I801a44d1270f8becd2835cc31b514e2e0fdaee33
Signed-off-by: Robert Varga <rovarga@cisco.com>
@Override
public java.lang.AutoCloseable createInstance() {
-
- InMemoryDOMDataStore dataStore = InMemoryDOMDataStoreFactory.create(
- "DOM-CFG", getSchemaServiceDependency(),
+ InMemoryDOMDataStore dataStore = InMemoryDOMDataStoreFactory.create("DOM-CFG", getSchemaServiceDependency(),
+ getDebugTransactions(),
InMemoryDOMDataStoreConfigProperties.create(getMaxDataChangeExecutorPoolSize(),
getMaxDataChangeExecutorQueueSize(), getMaxDataChangeListenerQueueSize(),
getMaxDataStoreExecutorQueueSize()));
@Override
public java.lang.AutoCloseable createInstance() {
InMemoryDOMDataStore dataStore = InMemoryDOMDataStoreFactory.create("DOM-OPER", getSchemaServiceDependency(),
- InMemoryDOMDataStoreConfigProperties.create(getMaxDataChangeExecutorPoolSize(),
+ getDebugTransactions(), InMemoryDOMDataStoreConfigProperties.create(getMaxDataChangeExecutorPoolSize(),
getMaxDataChangeExecutorQueueSize(), getMaxDataChangeListenerQueueSize(),
getMaxDataStoreExecutorQueueSize()));
*/
package org.opendaylight.controller.md.sal.dom.store.impl;
-import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransaction;
-
import com.google.common.base.Objects;
import com.google.common.base.Objects.ToStringHelper;
import com.google.common.base.Preconditions;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransaction;
+import org.slf4j.Logger;
+
/**
* Abstract DOM Store Transaction
*
* Convenience super implementation of DOM Store transaction which provides
* common implementation of {@link #toString()} and {@link #getIdentifier()}.
- *
- *
*/
abstract class AbstractDOMStoreTransaction implements DOMStoreTransaction {
+ private final Throwable debugContext;
private final Object identifier;
- protected AbstractDOMStoreTransaction(final Object identifier) {
- this.identifier = Preconditions.checkNotNull(identifier,"Identifier must not be null.");
+ protected AbstractDOMStoreTransaction(final Object identifier, final boolean debug) {
+ this.identifier = Preconditions.checkNotNull(identifier, "Identifier must not be null.");
+ this.debugContext = debug ? new Throwable().fillInStackTrace() : null;
}
@Override
return identifier;
}
+ protected final void warnDebugContext(final Logger logger) {
+ if (debugContext != null) {
+ logger.warn("Transaction {} has been allocated in the following context", identifier, debugContext);
+ }
+ }
+
@Override
public final String toString() {
return addToStringAttributes(Objects.toStringHelper(this)).toString();
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
-
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
-
import javax.annotation.concurrent.GuardedBy;
-
import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener;
import org.opendaylight.controller.md.sal.common.api.data.OptimisticLockFailedException;
private final ExecutorService dataChangeListenerExecutor;
private final ExecutorService domStoreExecutor;
-
+ private final boolean debugTransactions;
private final String name;
private volatile AutoCloseable closeable;
public InMemoryDOMDataStore(final String name, final ExecutorService domStoreExecutor,
final ExecutorService dataChangeListenerExecutor) {
this(name, domStoreExecutor, dataChangeListenerExecutor,
- InMemoryDOMDataStoreConfigProperties.DEFAULT_MAX_DATA_CHANGE_LISTENER_QUEUE_SIZE);
+ InMemoryDOMDataStoreConfigProperties.DEFAULT_MAX_DATA_CHANGE_LISTENER_QUEUE_SIZE, false);
}
public InMemoryDOMDataStore(final String name, final ExecutorService domStoreExecutor,
- final ExecutorService dataChangeListenerExecutor, final int maxDataChangeListenerQueueSize) {
+ final ExecutorService dataChangeListenerExecutor, final int maxDataChangeListenerQueueSize,
+ final boolean debugTransactions) {
this.name = Preconditions.checkNotNull(name);
this.domStoreExecutor = Preconditions.checkNotNull(domStoreExecutor);
this.listeningExecutor = MoreExecutors.listeningDecorator(this.domStoreExecutor);
this.dataChangeListenerExecutor = Preconditions.checkNotNull(dataChangeListenerExecutor);
+ this.debugTransactions = debugTransactions;
dataChangeListenerNotificationManager =
new QueuedNotificationManager<>(this.dataChangeListenerExecutor,
@Override
public DOMStoreReadTransaction newReadOnlyTransaction() {
- return new SnapshotBackedReadTransaction(nextIdentifier(), dataTree.takeSnapshot());
+ return new SnapshotBackedReadTransaction(nextIdentifier(), debugTransactions, dataTree.takeSnapshot());
}
@Override
public DOMStoreReadWriteTransaction newReadWriteTransaction() {
- return new SnapshotBackedReadWriteTransaction(nextIdentifier(), dataTree.takeSnapshot(), this);
+ return new SnapshotBackedReadWriteTransaction(nextIdentifier(), debugTransactions, dataTree.takeSnapshot(), this);
}
@Override
public DOMStoreWriteTransaction newWriteOnlyTransaction() {
- return new SnapshotBackedWriteTransaction(nextIdentifier(), dataTree.takeSnapshot(), this);
+ return new SnapshotBackedWriteTransaction(nextIdentifier(), debugTransactions, dataTree.takeSnapshot(), this);
}
@Override
}
}
+ boolean getDebugTransactions() {
+ return debugTransactions;
+ }
+
@Override
public <L extends AsyncDataChangeListener<YangInstanceIdentifier, NormalizedNode<?, ?>>> ListenerRegistration<L> registerChangeListener(
final YangInstanceIdentifier path, final L listener, final DataChangeScope scope) {
} else {
snapshot = dataTree.takeSnapshot();
}
- return new SnapshotBackedReadTransaction(nextIdentifier(), snapshot);
+ return new SnapshotBackedReadTransaction(nextIdentifier(), getDebugTransactions(), snapshot);
}
@Override
snapshot = dataTree.takeSnapshot();
}
final SnapshotBackedReadWriteTransaction ret = new SnapshotBackedReadWriteTransaction(nextIdentifier(),
- snapshot, this);
+ getDebugTransactions(), snapshot, this);
latestOutstandingTx = ret;
return ret;
}
} else {
snapshot = dataTree.takeSnapshot();
}
- final SnapshotBackedWriteTransaction ret = new SnapshotBackedWriteTransaction(nextIdentifier(), snapshot,
- this);
+ final SnapshotBackedWriteTransaction ret = new SnapshotBackedWriteTransaction(nextIdentifier(),
+ getDebugTransactions(), snapshot, this);
latestOutstandingTx = ret;
return ret;
}
} catch (ConflictingModificationAppliedException e) {
LOG.warn("Store Tx: {} Conflicting modification for {}.", transaction.getIdentifier(),
e.getPath());
+ transaction.warnDebugContext(LOG);
throw new OptimisticLockFailedException("Optimistic lock failed.",e);
} catch (DataValidationFailedException e) {
LOG.warn("Store Tx: {} Data Precondition failed for {}.", transaction.getIdentifier(),
e.getPath(), e);
+ transaction.warnDebugContext(LOG);
throw new TransactionCommitFailedException("Data did not pass validation.",e);
}
}
* 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.dom.store.impl;
import java.util.concurrent.ExecutorService;
-
import javax.annotation.Nullable;
-
import org.opendaylight.controller.sal.core.api.model.SchemaService;
import org.opendaylight.yangtools.util.concurrent.SpecialExecutors;
public static InMemoryDOMDataStore create(final String name,
@Nullable final SchemaService schemaService,
@Nullable final InMemoryDOMDataStoreConfigProperties properties) {
+ return create(name, schemaService, false, properties);
+ }
+
+ /**
+ * Creates an InMemoryDOMDataStore instance.
+ *
+ * @param name the name of the data store
+ * @param schemaService the SchemaService to which to register the data store.
+ * @param debugTransactions enable transaction debugging
+ * @param properties configuration properties for the InMemoryDOMDataStore instance. If null,
+ * default property values are used.
+ * @return an InMemoryDOMDataStore instance
+ */
+ public static InMemoryDOMDataStore create(final String name,
+ @Nullable final SchemaService schemaService, final boolean debugTransactions,
+ @Nullable final InMemoryDOMDataStoreConfigProperties properties) {
InMemoryDOMDataStoreConfigProperties actualProperties = properties;
if(actualProperties == null) {
InMemoryDOMDataStore dataStore = new InMemoryDOMDataStore(name,
domStoreExecutor, dataChangeListenerExecutor,
- actualProperties.getMaxDataChangeListenerQueueSize());
+ actualProperties.getMaxDataChangeListenerQueueSize(), debugTransactions);
if(schemaService != null) {
schemaService.registerSchemaContextListener(dataStore);
*/
package org.opendaylight.controller.md.sal.dom.store.impl;
+import static com.google.common.base.Preconditions.checkNotNull;
+
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.util.concurrent.CheckedFuture;
import com.google.common.util.concurrent.Futures;
+
import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadTransaction;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import static com.google.common.base.Preconditions.checkNotNull;
-
/**
*
* Implementation of read-only transaction backed by {@link DataTreeSnapshot}
private static final Logger LOG = LoggerFactory.getLogger(SnapshotBackedReadTransaction.class);
private volatile DataTreeSnapshot stableSnapshot;
- public SnapshotBackedReadTransaction(final Object identifier, final DataTreeSnapshot snapshot) {
- super(identifier);
+ public SnapshotBackedReadTransaction(final Object identifier, final boolean debug, final DataTreeSnapshot snapshot) {
+ super(identifier, debug);
this.stableSnapshot = Preconditions.checkNotNull(snapshot);
LOG.debug("ReadOnly Tx: {} allocated with snapshot {}", identifier, snapshot);
}
}
@Override
- public CheckedFuture<Boolean, ReadFailedException> exists(YangInstanceIdentifier path) {
+ public CheckedFuture<Boolean, ReadFailedException> exists(final YangInstanceIdentifier path) {
LOG.debug("Tx: {} Exists: {}", getIdentifier(), path);
checkNotNull(path, "Path must not be null.");
import static com.google.common.base.Preconditions.checkNotNull;
-import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
-import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot;
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.Futures;
+
import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction;
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;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.google.common.base.Optional;
-import com.google.common.util.concurrent.CheckedFuture;
-import com.google.common.util.concurrent.Futures;
-
/**
* Implementation of Read-Write transaction which is backed by {@link DataTreeSnapshot}
* and executed according to {@link TransactionReadyPrototype}.
* @param snapshot Snapshot which will be modified.
* @param readyImpl Implementation of ready method.
*/
- protected SnapshotBackedReadWriteTransaction(final Object identifier, final DataTreeSnapshot snapshot,
- final TransactionReadyPrototype store) {
- super(identifier, snapshot, store);
+ protected SnapshotBackedReadWriteTransaction(final Object identifier, final boolean debug,
+ final DataTreeSnapshot snapshot, final TransactionReadyPrototype store) {
+ super(identifier, debug, snapshot, store);
}
@Override
}
}
- @Override public CheckedFuture<Boolean, ReadFailedException> exists(
- YangInstanceIdentifier path) {
+ @Override
+ public CheckedFuture<Boolean, ReadFailedException> exists(final YangInstanceIdentifier path) {
try {
return Futures.immediateCheckedFuture(
read(path).checkedGet().isPresent());
import static com.google.common.base.Preconditions.checkState;
+import com.google.common.base.Objects.ToStringHelper;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Throwables;
import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
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;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.google.common.base.Objects.ToStringHelper;
-import com.google.common.base.Preconditions;
-import com.google.common.base.Throwables;
-import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot;
-import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
-
/**
* Implementation of Write transaction which is backed by
* {@link DataTreeSnapshot} and executed according to
* @param readyImpl
* Implementation of ready method.
*/
- public SnapshotBackedWriteTransaction(final Object identifier, final DataTreeSnapshot snapshot,
- final TransactionReadyPrototype readyImpl) {
- super(identifier);
+ public SnapshotBackedWriteTransaction(final Object identifier, final boolean debug,
+ final DataTreeSnapshot snapshot, final TransactionReadyPrototype readyImpl) {
+ super(identifier, debug);
mutableTree = snapshot.newModification();
this.readyImpl = Preconditions.checkNotNull(readyImpl, "readyImpl must not be null.");
LOG.debug("Write Tx: {} allocated with snapshot {}", identifier, snapshot);
-
module opendaylight-inmemory-datastore-provider {
yang-version 1;
import config { prefix config; revision-date 2013-04-05; }
import rpc-context { prefix rpcx; revision-date 2013-06-17; }
- import opendaylight-config-dom-datastore {prefix config-dom-store-spi;}
- import opendaylight-operational-dom-datastore {prefix operational-dom-store-spi;}
+ import opendaylight-config-dom-datastore {prefix config-dom-store-spi;}
+ import opendaylight-operational-dom-datastore {prefix operational-dom-store-spi;}
import opendaylight-md-sal-dom {prefix sal;}
description
// This is the definition of the service implementation as a module identity.
- identity inmemory-operational-datastore-provider {
- base config:module-type;
- config:provided-service operational-dom-store-spi:operational-dom-datastore;
- config:java-name-prefix InMemoryOperationalDataStoreProvider;
- }
+ identity inmemory-operational-datastore-provider {
+ base config:module-type;
+ config:provided-service operational-dom-store-spi:operational-dom-datastore;
+ config:java-name-prefix InMemoryOperationalDataStoreProvider;
+ }
grouping datastore-configuration {
leaf max-data-change-executor-queue-size {
type uint16;
description "The maximum queue size for the data change listeners.";
}
-
leaf max-data-store-executor-queue-size {
default 5000;
type uint16;
description "The maximum queue size for the data store executor.";
}
+ leaf debug-transactions {
+ type boolean;
+ default false;
+ description "Enable transaction lifecycle debugging.";
+ }
}
// Augments the 'configuration' choice node under modules/module.
*/
package org.opendaylight.controller.md.sal.dom.store.impl;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
import com.google.common.base.Optional;
import com.google.common.util.concurrent.CheckedFuture;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
+
+import java.util.concurrent.ExecutionException;
+
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import java.util.concurrent.ExecutionException;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
public class InMemoryDataStoreTest {
Mockito.doThrow( new RuntimeException( "mock ex" ) ).when( mockSnapshot )
.readNode( Mockito.any( YangInstanceIdentifier.class ) );
- DOMStoreReadTransaction readTx = new SnapshotBackedReadTransaction( "1", mockSnapshot );
+ DOMStoreReadTransaction readTx = new SnapshotBackedReadTransaction("1", true, mockSnapshot);
doReadAndThrowEx( readTx );
}
.readNode( Mockito.any( YangInstanceIdentifier.class ) );
Mockito.doReturn( mockModification ).when( mockSnapshot ).newModification();
TransactionReadyPrototype mockReady = Mockito.mock( TransactionReadyPrototype.class );
- DOMStoreReadTransaction readTx = new SnapshotBackedReadWriteTransaction( "1", mockSnapshot, mockReady );
+ DOMStoreReadTransaction readTx = new SnapshotBackedReadWriteTransaction("1", false, mockSnapshot, mockReady);
doReadAndThrowEx( readTx );
}
- private void doReadAndThrowEx( DOMStoreReadTransaction readTx ) throws Throwable {
+ private void doReadAndThrowEx( final DOMStoreReadTransaction readTx ) throws Throwable {
try {
readTx.read(TestModel.TEST_PATH).get();