Bug 9060: TracingBroker with transaction-debug-context-enabled 00/62500/1
authorMichael Vorburger <vorburger@redhat.com>
Thu, 24 Aug 2017 20:37:51 +0000 (22:37 +0200)
committerMichael Vorburger <vorburger@redhat.com>
Thu, 31 Aug 2017 14:10:37 +0000 (16:10 +0200)
This is one of a serious of commits which is part of a solution I'm
proposing in order to be able to detect OOM issues such as Bug 9034,
based on using the mdsal-trace DataBroker.

Change-Id: If62b7f76ea03d8cabe0c5a2088983275cfe50e44
Signed-off-by: Michael Vorburger <vorburger@redhat.com>
12 files changed:
opendaylight/md-sal/mdsal-trace/api/src/main/yang/mdsaltrace.yang
opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/closetracker/impl/AbstractCloseTracked.java [new file with mode: 0644]
opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/closetracker/impl/CloseTracked.java [new file with mode: 0644]
opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/closetracker/impl/CloseTrackedRegistry.java [new file with mode: 0644]
opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/closetracker/impl/CloseTrackedTrait.java [new file with mode: 0644]
opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/closetracker/impl/package-info.java [new file with mode: 0644]
opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/dom/impl/AbstractTracingWriteTransaction.java [new file with mode: 0644]
opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/dom/impl/TracingBroker.java
opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/dom/impl/TracingReadOnlyTransaction.java
opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/dom/impl/TracingReadWriteTransaction.java
opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/dom/impl/TracingTransactionChain.java
opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/dom/impl/TracingWriteTransaction.java

index ce72da750811bea070d51aa69ff7aa3130e2bfe3..5c6b09b5bf9db531d01e3b7ff48964ecc1c42b5e 100644 (file)
@@ -18,11 +18,18 @@ module mdsaltrace {
     }
 
     container config {
+        // TODO leaf enabled ...
         leaf-list registration-watches {
             type string;
         }
         leaf-list write-watches {
             type string;
         }
+        leaf transaction-debug-context-enabled {
+            default false;
+            type boolean;
+            description "Enable or disable transaction context debug. This will preserve the call site trace for
+                         transactions, so that the original caller of un-close'd() transaction can be identified";
+        }
     }
 }
diff --git a/opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/closetracker/impl/AbstractCloseTracked.java b/opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/closetracker/impl/AbstractCloseTracked.java
new file mode 100644 (file)
index 0000000..5673c8e
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2017 Red Hat, 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.trace.closetracker.impl;
+
+import java.time.Instant;
+import javax.annotation.Nullable;
+
+/**
+ * Convenience abstract base class for {@link CloseTracked} implementors.
+ *
+ * @author Michael Vorburger.ch
+ */
+public abstract class AbstractCloseTracked<T extends AbstractCloseTracked<T>> implements CloseTracked<T> {
+
+    private final CloseTrackedTrait<T> closeTracker;
+
+    protected AbstractCloseTracked(CloseTrackedRegistry<T> transactionChainRegistry) {
+        this.closeTracker = new CloseTrackedTrait<>(transactionChainRegistry);
+    }
+
+    protected void removeFromTrackedRegistry() {
+        closeTracker.removeFromTrackedRegistry();
+    }
+
+    @Override
+    public final Instant getObjectCreated() {
+        return closeTracker.getObjectCreated();
+    }
+
+    @Override
+    public @Nullable Throwable getAllocationContext() {
+        return closeTracker.getAllocationContext();
+    }
+}
diff --git a/opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/closetracker/impl/CloseTracked.java b/opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/closetracker/impl/CloseTracked.java
new file mode 100644 (file)
index 0000000..6d8524f
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2017 Red Hat, 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.trace.closetracker.impl;
+
+import java.time.Instant;
+import javax.annotation.Nullable;
+
+/**
+ * Object which can track where it has been created, and if it has been correctly closed.
+ *
+ * <p>Includes preserving the context of the call stack which created this object, and the instant it was created.
+ *
+ * @author Michael Vorburger.ch
+ */
+public interface CloseTracked<T extends CloseTracked<T>> {
+
+    Instant getObjectCreated();
+
+    @Nullable Throwable getAllocationContext();
+
+}
diff --git a/opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/closetracker/impl/CloseTrackedRegistry.java b/opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/closetracker/impl/CloseTrackedRegistry.java
new file mode 100644 (file)
index 0000000..9364a57
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2017 Red Hat, 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.trace.closetracker.impl;
+
+import java.util.Set;
+import java.util.concurrent.ConcurrentSkipListSet;
+import javax.annotation.concurrent.ThreadSafe;
+
+/**
+ * Registry of {@link CloseTracked} instances.
+ *
+ * @author Michael Vorburger.ch
+ */
+@ThreadSafe
+public class CloseTrackedRegistry<T extends CloseTracked<T>> {
+
+    // unused OK for now, at least we'll be able to see this in HPROF heap dumps and know what is which
+    private final @SuppressWarnings("unused") Object anchor;
+    private final @SuppressWarnings("unused") String createDescription;
+
+    private final Set<CloseTracked<T>> tracked = new ConcurrentSkipListSet<>(
+        (o1, o2) -> o1.getObjectCreated().compareTo(o2.getObjectCreated()));
+
+    private final boolean isDebugContextEnabled;
+
+    /**
+     * Constructor.
+     *
+     * @param anchor
+     *            object where this registry is stored in, used for human output in
+     *            logging and other output
+     * @param createDescription
+     *            description of creator of instances of this registry, typically
+     *            e.g. name of method in the anchor class
+     * @param isDebugContextEnabled
+     *            whether or not the call stack should be preserved; this is (of
+     *            course) an expensive operation, and should only be used during
+     *            troubleshooting
+     */
+    public CloseTrackedRegistry(Object anchor, String createDescription, boolean isDebugContextEnabled) {
+        this.anchor = anchor;
+        this.createDescription = createDescription;
+        this.isDebugContextEnabled = isDebugContextEnabled;
+    }
+
+    public boolean isDebugContextEnabled() {
+        return isDebugContextEnabled;
+    }
+
+    // package protected, not public; only CloseTrackedTrait invokes this
+    void add(CloseTracked<T> closeTracked) {
+        tracked.add(closeTracked);
+    }
+
+    // package protected, not public; only CloseTrackedTrait invokes this
+    void remove(CloseTracked<T> closeTracked) {
+        tracked.remove(closeTracked);
+    }
+
+    // TODO Later add methods to dump & query what's not closed, by creation time, incl. creation stack trace
+
+    // TODO we could even support add/close of Object instead of CloseTracked, by creating a wrapper?
+
+}
diff --git a/opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/closetracker/impl/CloseTrackedTrait.java b/opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/closetracker/impl/CloseTrackedTrait.java
new file mode 100644 (file)
index 0000000..9f2237d
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2017 Red Hat, 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.trace.closetracker.impl;
+
+import java.time.Instant;
+import java.util.Objects;
+import javax.annotation.Nullable;
+
+/**
+ * Implementation of {@link CloseTracked} which can be used as a field in
+ * another class which implements {@link CloseTracked} and delegates its methods
+ * to this.
+ *
+ * <p>This is useful if that class already has another parent class.
+ * If it does not, then it's typically more convenient to just extend AbstractCloseTracked.
+ *
+ * @author Michael Vorburger.ch
+ */
+public class CloseTrackedTrait<T extends CloseTracked<T>> implements CloseTracked<T> {
+
+    private final Instant created;
+    private final @Nullable Throwable allocationContext;
+    private final CloseTrackedRegistry<T> closeTrackedRegistry;
+
+    public CloseTrackedTrait(CloseTrackedRegistry<T> transactionChainRegistry) {
+        this.created = Instant.now();
+        if (transactionChainRegistry.isDebugContextEnabled()) {
+            this.allocationContext = new Throwable("allocated at");
+        } else {
+            this.allocationContext = null;
+        }
+        this.closeTrackedRegistry = Objects.requireNonNull(transactionChainRegistry, "transactionChainRegistry");
+        this.closeTrackedRegistry.add(this);
+    }
+
+    @Override
+    public Instant getObjectCreated() {
+        return created;
+    }
+
+    @Override
+    public Throwable getAllocationContext() {
+        return allocationContext;
+    }
+
+    public void removeFromTrackedRegistry() {
+        closeTrackedRegistry.remove(this);
+    }
+
+}
diff --git a/opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/closetracker/impl/package-info.java b/opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/closetracker/impl/package-info.java
new file mode 100644 (file)
index 0000000..60d9674
--- /dev/null
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2017 Red Hat, 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
+ */
+
+/**
+ * Utilities to track (non) "closing" of objects.
+ */
+// This generic infra may be moved somewhere else, later
+@org.eclipse.jdt.annotation.NonNullByDefault
+package org.opendaylight.controller.md.sal.trace.closetracker.impl;
diff --git a/opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/dom/impl/AbstractTracingWriteTransaction.java b/opendaylight/md-sal/mdsal-trace/dom-impl/src/main/java/org/opendaylight/controller/md/sal/trace/dom/impl/AbstractTracingWriteTransaction.java
new file mode 100644 (file)
index 0000000..a06ee5e
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2016 Red Hat, 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.trace.dom.impl;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+
+abstract class AbstractTracingWriteTransaction implements DOMDataWriteTransaction {
+
+    private final DOMDataWriteTransaction delegate;
+    private final TracingBroker tracingBroker;
+    private final List<String> logs = new ArrayList<>();
+
+    AbstractTracingWriteTransaction(DOMDataWriteTransaction delegate, TracingBroker tracingBroker) {
+        this.delegate = Objects.requireNonNull(delegate);
+        this.tracingBroker = Objects.requireNonNull(tracingBroker);
+    }
+
+    private void recordOp(LogicalDatastoreType store, YangInstanceIdentifier yiid, String method,
+            NormalizedNode<?, ?> node) {
+        if (!tracingBroker.isWriteWatched(yiid, store)) {
+            return;
+        }
+
+        final Object value = node != null ? node.getValue() : null;
+
+        if (value != null && value instanceof ImmutableSet && ((Set<?>)value).isEmpty()) {
+            if (TracingBroker.LOG.isDebugEnabled()) {
+                TracingBroker.LOG.debug("Empty data set write to {}", tracingBroker.toPathString(yiid));
+            }
+        } else {
+            StringBuilder sb = new StringBuilder();
+            sb.append("Method \"").append(method);
+            sb.append("\" to ").append(store);
+            sb.append(" at ").append(tracingBroker.toPathString(yiid)).append('.');
+            sb.append(" Data: ");
+            if (node != null) {
+                sb.append(node.getValue());
+            } else {
+                sb.append("null");
+            }
+            sb.append(" Stack:").append(tracingBroker.getStackSummary());
+            synchronized (this) {
+                logs.add(sb.toString());
+            }
+        }
+    }
+
+    private synchronized void logOps() {
+        synchronized (this) {
+            logs.forEach(log -> TracingBroker.LOG.warn(log));
+            logs.clear();
+        }
+    }
+
+    @Override
+    public void put(LogicalDatastoreType store, YangInstanceIdentifier yiid, NormalizedNode<?, ?> node) {
+        recordOp(store, yiid, "put", node);
+        delegate.put(store, yiid, node);
+    }
+
+    @Override
+    public void merge(LogicalDatastoreType store, YangInstanceIdentifier yiid, NormalizedNode<?, ?> node) {
+        recordOp(store, yiid, "merge", node);
+        delegate.merge(store, yiid, node);
+    }
+
+    @Override
+    public boolean cancel() {
+        synchronized (this) {
+            logs.clear();
+        }
+        boolean result = delegate.cancel();
+        return result;
+    }
+
+    @Override
+    public void delete(LogicalDatastoreType store, YangInstanceIdentifier yiid) {
+        recordOp(store, yiid, "delete", null);
+        delegate.delete(store, yiid);
+    }
+
+    @Override
+    public CheckedFuture<Void, TransactionCommitFailedException> submit() {
+        logOps();
+        return delegate.submit();
+    }
+
+    @Override
+    public ListenableFuture<RpcResult<TransactionStatus>> commit() {
+        logOps();
+        return delegate.commit();
+    }
+
+    @Override
+    public Object getIdentifier() {
+        return delegate.getIdentifier();
+    }
+}
index 7feb361db24dbaf0cf1d6abd2845275bff243897..2e16b0ca0ff45736155ffad14a5d0e3dd2724fda 100644 (file)
@@ -28,6 +28,7 @@ import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeIdentifier;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
 import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
 import org.opendaylight.controller.md.sal.trace.api.TracingDOMDataBroker;
+import org.opendaylight.controller.md.sal.trace.closetracker.impl.CloseTrackedRegistry;
 import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsaltrace.rev160908.Config;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
@@ -99,6 +100,11 @@ public class TracingBroker implements TracingDOMDataBroker {
     private final List<Watch> registrationWatches = new ArrayList<>();
     private final List<Watch> writeWatches = new ArrayList<>();
 
+    private final boolean isDebugging;
+    private final CloseTrackedRegistry<TracingTransactionChain> transactionChainsRegistry;
+    private final CloseTrackedRegistry<TracingReadOnlyTransaction> readOnlyTransactionsRegistry;
+    private final CloseTrackedRegistry<TracingWriteTransaction> writeTransactionsRegistry;
+    private final CloseTrackedRegistry<TracingReadWriteTransaction> readWriteTransactionsRegistry;
 
     private class Watch {
         final String iidString;
@@ -161,6 +167,16 @@ public class TracingBroker implements TracingDOMDataBroker {
         this.delegate = Objects.requireNonNull(delegate);
         this.codec = Objects.requireNonNull(codec);
         configure(config);
+
+        if (config.isTransactionDebugContextEnabled() != null) {
+            this.isDebugging = config.isTransactionDebugContextEnabled();
+        } else {
+            this.isDebugging = false;
+        }
+        this.transactionChainsRegistry     = new CloseTrackedRegistry<>(this, "createTransactionChain", isDebugging);
+        this.readOnlyTransactionsRegistry  = new CloseTrackedRegistry<>(this, "newReadOnlyTransaction", isDebugging);
+        this.writeTransactionsRegistry     = new CloseTrackedRegistry<>(this, "newWriteOnlyTransaction", isDebugging);
+        this.readWriteTransactionsRegistry = new CloseTrackedRegistry<>(this, "newReadWriteTransaction", isDebugging);
     }
 
     private void configure(Config config) {
@@ -244,8 +260,8 @@ public class TracingBroker implements TracingDOMDataBroker {
     }
 
 
-    void toPathString(YangInstanceIdentifier yiid, StringBuilder sb) {
-        InstanceIdentifier iid = codec.fromYangInstanceIdentifier(yiid);
+    private void toPathString(YangInstanceIdentifier yiid, StringBuilder sb) {
+        InstanceIdentifier<?> iid = codec.fromYangInstanceIdentifier(yiid);
         if (null == iid) {
             reconstructIidPathString(yiid, sb);
         } else {
@@ -278,12 +294,12 @@ public class TracingBroker implements TracingDOMDataBroker {
 
     @Override
     public DOMDataReadWriteTransaction newReadWriteTransaction() {
-        return new TracingReadWriteTransaction(delegate.newReadWriteTransaction(), this);
+        return new TracingReadWriteTransaction(delegate.newReadWriteTransaction(), this, readWriteTransactionsRegistry);
     }
 
     @Override
     public DOMDataWriteTransaction newWriteOnlyTransaction() {
-        return new TracingWriteTransaction(delegate.newWriteOnlyTransaction(), this);
+        return new TracingWriteTransaction(delegate.newWriteOnlyTransaction(), this, writeTransactionsRegistry);
     }
 
     @Override
@@ -299,12 +315,13 @@ public class TracingBroker implements TracingDOMDataBroker {
 
     @Override
     public DOMTransactionChain createTransactionChain(TransactionChainListener transactionChainListener) {
-        return new TracingTransactionChain(delegate.createTransactionChain(transactionChainListener), this);
+        return new TracingTransactionChain(
+                delegate.createTransactionChain(transactionChainListener), this, transactionChainsRegistry);
     }
 
     @Override
     public DOMDataReadOnlyTransaction newReadOnlyTransaction() {
-        return new TracingReadOnlyTransaction(delegate.newReadOnlyTransaction(), this);
+        return new TracingReadOnlyTransaction(delegate.newReadOnlyTransaction(), this, readOnlyTransactionsRegistry);
     }
 
     @Nonnull
index a93f7f9c43638c43cf17aa344ce5a4b977942652..6bc6d66493c926ca77f3f7486a727cded9b8bef5 100644 (file)
@@ -12,15 +12,21 @@ import com.google.common.util.concurrent.CheckedFuture;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction;
+import org.opendaylight.controller.md.sal.trace.closetracker.impl.AbstractCloseTracked;
+import org.opendaylight.controller.md.sal.trace.closetracker.impl.CloseTrackedRegistry;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 
-class TracingReadOnlyTransaction implements DOMDataReadOnlyTransaction {
+class TracingReadOnlyTransaction
+        extends AbstractCloseTracked<TracingReadOnlyTransaction>
+        implements DOMDataReadOnlyTransaction {
 
     private final DOMDataReadOnlyTransaction delegate;
     private final TracingBroker tracingBroker;
 
-    TracingReadOnlyTransaction(DOMDataReadOnlyTransaction delegate, TracingBroker tracingBroker) {
+    TracingReadOnlyTransaction(DOMDataReadOnlyTransaction delegate, TracingBroker tracingBroker,
+            CloseTrackedRegistry<TracingReadOnlyTransaction> readOnlyTransactionsRegistry) {
+        super(readOnlyTransactionsRegistry);
         this.delegate = delegate;
         this.tracingBroker = tracingBroker;
     }
@@ -44,6 +50,7 @@ class TracingReadOnlyTransaction implements DOMDataReadOnlyTransaction {
     @Override
     public void close() {
         delegate.close();
+        super.removeFromTrackedRegistry();
     }
 
 }
index c906ef933b323605d844af0405aa2164b8586a84..68c4786317abfb32d1a3d21e9dd9f8fb1e80577c 100644 (file)
@@ -7,24 +7,34 @@
  */
 package org.opendaylight.controller.md.sal.trace.dom.impl;
 
-
 import com.google.common.base.Optional;
 import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.time.Instant;
 import java.util.Objects;
-
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
+import org.opendaylight.controller.md.sal.trace.closetracker.impl.CloseTracked;
+import org.opendaylight.controller.md.sal.trace.closetracker.impl.CloseTrackedRegistry;
+import org.opendaylight.controller.md.sal.trace.closetracker.impl.CloseTrackedTrait;
+import org.opendaylight.yangtools.yang.common.RpcResult;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 
+class TracingReadWriteTransaction
+    extends AbstractTracingWriteTransaction
+        implements DOMDataReadWriteTransaction, CloseTracked<TracingReadWriteTransaction> {
 
-class TracingReadWriteTransaction extends TracingWriteTransaction implements DOMDataReadWriteTransaction {
-
+    private final CloseTrackedTrait<TracingReadWriteTransaction> closeTracker;
     private final DOMDataReadWriteTransaction delegate;
 
-    TracingReadWriteTransaction(DOMDataReadWriteTransaction delegate, TracingBroker tracingBroker) {
+    TracingReadWriteTransaction(DOMDataReadWriteTransaction delegate, TracingBroker tracingBroker,
+            CloseTrackedRegistry<TracingReadWriteTransaction> readWriteTransactionsRegistry) {
         super(delegate, tracingBroker);
+        this.closeTracker = new CloseTrackedTrait<>(readWriteTransactionsRegistry);
         this.delegate = Objects.requireNonNull(delegate);
     }
 
@@ -38,4 +48,32 @@ class TracingReadWriteTransaction extends TracingWriteTransaction implements DOM
     public CheckedFuture<Boolean, ReadFailedException> exists(LogicalDatastoreType store, YangInstanceIdentifier yiid) {
         return delegate.exists(store, yiid);
     }
+
+    @Override
+    public CheckedFuture<Void, TransactionCommitFailedException> submit() {
+        closeTracker.removeFromTrackedRegistry();
+        return super.submit();
+    }
+
+    @Override
+    public ListenableFuture<RpcResult<TransactionStatus>> commit() {
+        closeTracker.removeFromTrackedRegistry();
+        return super.commit();
+    }
+
+    @Override
+    public boolean cancel() {
+        closeTracker.removeFromTrackedRegistry();
+        return super.cancel();
+    }
+
+    @Override
+    public Instant getObjectCreated() {
+        return closeTracker.getObjectCreated();
+    }
+
+    @Override
+    public Throwable getAllocationContext() {
+        return closeTracker.getAllocationContext();
+    }
 }
index b158dbcf4a195b49034015019c233afa3bf3070e..c8f4539cc7ba65353946a993f585a276bf53086b 100644 (file)
@@ -12,35 +12,52 @@ import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
 import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain;
+import org.opendaylight.controller.md.sal.trace.closetracker.impl.AbstractCloseTracked;
+import org.opendaylight.controller.md.sal.trace.closetracker.impl.CloseTrackedRegistry;
 
-class TracingTransactionChain implements DOMTransactionChain {
+class TracingTransactionChain extends AbstractCloseTracked<TracingTransactionChain> implements DOMTransactionChain {
 
     private final DOMTransactionChain delegate;
     private final TracingBroker tracingBroker;
+    private final CloseTrackedRegistry<TracingReadOnlyTransaction> readOnlyTransactionsRegistry;
+    private final CloseTrackedRegistry<TracingWriteTransaction> writeTransactionsRegistry;
+    private final CloseTrackedRegistry<TracingReadWriteTransaction> readWriteTransactionsRegistry;
 
-    TracingTransactionChain(DOMTransactionChain delegate, TracingBroker tracingBroker) {
+    TracingTransactionChain(DOMTransactionChain delegate, TracingBroker tracingBroker,
+            CloseTrackedRegistry<TracingTransactionChain> transactionChainsRegistry) {
+        super(transactionChainsRegistry);
         this.delegate = Objects.requireNonNull(delegate);
         this.tracingBroker = Objects.requireNonNull(tracingBroker);
+
+        final boolean isDebugging = transactionChainsRegistry.isDebugContextEnabled();
+        this.readOnlyTransactionsRegistry  = new CloseTrackedRegistry<>(this, "newReadOnlyTransaction", isDebugging);
+        this.writeTransactionsRegistry     = new CloseTrackedRegistry<>(this, "newWriteOnlyTransaction", isDebugging);
+        this.readWriteTransactionsRegistry = new CloseTrackedRegistry<>(this, "newReadWriteTransaction", isDebugging);
     }
 
     @Override
+    @SuppressWarnings("resource")
     public DOMDataReadOnlyTransaction newReadOnlyTransaction() {
-        return new TracingReadOnlyTransaction(delegate.newReadOnlyTransaction(), tracingBroker);
+        final DOMDataReadOnlyTransaction tx = delegate.newReadOnlyTransaction();
+        return new TracingReadOnlyTransaction(tx, tracingBroker, readOnlyTransactionsRegistry);
     }
 
     @Override
     public DOMDataReadWriteTransaction newReadWriteTransaction() {
-        return new TracingReadWriteTransaction(delegate.newReadWriteTransaction(), tracingBroker);
+        return new TracingReadWriteTransaction(delegate.newReadWriteTransaction(), tracingBroker,
+                readWriteTransactionsRegistry);
     }
 
     @Override
     public DOMDataWriteTransaction newWriteOnlyTransaction() {
-        return new TracingWriteTransaction(delegate.newWriteOnlyTransaction(), tracingBroker);
+        final DOMDataWriteTransaction tx = delegate.newWriteOnlyTransaction();
+        return new TracingWriteTransaction(tx, tracingBroker, writeTransactionsRegistry);
     }
 
     @Override
     public void close() {
         delegate.close();
+        super.removeFromTrackedRegistry();
     }
 
 }
index a895e983c26693dbed48619adea0e33f15893131..123129ce156eeb08781987cf5e4a848f1d46cb52 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016 Red Hat, Inc. and others.  All rights reserved.
+ * Copyright (c) 2017 Red Hat, 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,
  */
 package org.opendaylight.controller.md.sal.trace.dom.impl;
 
-import com.google.common.collect.ImmutableSet;
 import com.google.common.util.concurrent.CheckedFuture;
 import com.google.common.util.concurrent.ListenableFuture;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-import java.util.Set;
-
+import java.time.Instant;
 import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
-import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.controller.md.sal.trace.closetracker.impl.CloseTracked;
+import org.opendaylight.controller.md.sal.trace.closetracker.impl.CloseTrackedRegistry;
+import org.opendaylight.controller.md.sal.trace.closetracker.impl.CloseTrackedTrait;
 import org.opendaylight.yangtools.yang.common.RpcResult;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
-
-class TracingWriteTransaction implements DOMDataWriteTransaction {
-
-    private final DOMDataWriteTransaction delegate;
-    private final TracingBroker tracingBroker;
-    private final List<String> logs = new ArrayList<>();
-
-    TracingWriteTransaction(DOMDataWriteTransaction delegate, TracingBroker tracingBroker) {
-        this.delegate = Objects.requireNonNull(delegate);
-        this.tracingBroker = Objects.requireNonNull(tracingBroker);
-    }
-
-    private void recordOp(LogicalDatastoreType store, YangInstanceIdentifier yiid,
-                                                                            String method, NormalizedNode<?,?> node) {
-        if (!tracingBroker.isWriteWatched(yiid, store)) {
-            return;
-        }
 
-        Object value = null;
-        if (node != null) {
-            value = node.getValue();
-        }
+class TracingWriteTransaction extends AbstractTracingWriteTransaction
+        implements CloseTracked<TracingWriteTransaction> {
 
-        if (value != null && value instanceof ImmutableSet && ((Set)value).isEmpty()) {
-            if (tracingBroker.LOG.isDebugEnabled()) {
-                tracingBroker.LOG.debug("Empty data set write to {}", tracingBroker.toPathString(yiid));
-            }
-        } else {
-            StringBuilder sb = new StringBuilder();
-            sb.append("Method \"").append(method);
-            sb.append("\" to ").append(store);
-            sb.append(" at ").append(tracingBroker.toPathString(yiid)).append('.');
-            sb.append(" Data: ");
-            if (node != null) {
-                sb.append(node.getValue());
-            } else {
-                sb.append("null");
-            }
-            sb.append(" Stack:").append(tracingBroker.getStackSummary());
-            synchronized (this) {
-                logs.add(sb.toString());
-            }
-        }
-    }
+    private final CloseTrackedTrait<TracingWriteTransaction> closeTracker;
 
-    private synchronized void logOps() {
-        for (String log : logs) {
-            tracingBroker.LOG.warn(log);
-        }
-        logs.clear();
+    TracingWriteTransaction(DOMDataWriteTransaction delegate, TracingBroker tracingBroker,
+            CloseTrackedRegistry<TracingWriteTransaction> writeTransactionsRegistry) {
+        super(delegate, tracingBroker);
+        this.closeTracker = new CloseTrackedTrait<>(writeTransactionsRegistry);
     }
 
     @Override
-    public void put(LogicalDatastoreType store, YangInstanceIdentifier yiid, NormalizedNode<?, ?> node) {
-        recordOp(store, yiid, "put", node);
-        delegate.put(store, yiid, node);
+    public CheckedFuture<Void, TransactionCommitFailedException> submit() {
+        closeTracker.removeFromTrackedRegistry();
+        return super.submit();
     }
 
     @Override
-    public void merge(LogicalDatastoreType store, YangInstanceIdentifier yiid, NormalizedNode<?, ?> node) {
-        recordOp(store, yiid, "merge", node);
-        delegate.merge(store, yiid, node);
+    public ListenableFuture<RpcResult<TransactionStatus>> commit() {
+        closeTracker.removeFromTrackedRegistry();
+        return super.commit();
     }
 
     @Override
     public boolean cancel() {
-        logs.clear();
-        return delegate.cancel();
-    }
-
-    @Override
-    public void delete(LogicalDatastoreType store, YangInstanceIdentifier yiid) {
-        recordOp(store, yiid, "delete", null);
-        delegate.delete(store, yiid);
+        closeTracker.removeFromTrackedRegistry();
+        return super.cancel();
     }
 
     @Override
-    public CheckedFuture<Void, TransactionCommitFailedException> submit() {
-        logOps();
-        return delegate.submit();
+    public Instant getObjectCreated() {
+        return closeTracker.getObjectCreated();
     }
 
     @Override
-    public ListenableFuture<RpcResult<TransactionStatus>> commit() {
-        logOps();
-        return delegate.commit();
+    public Throwable getAllocationContext() {
+        return closeTracker.getAllocationContext();
     }
 
-    @Override
-    public Object getIdentifier() {
-        return delegate.getIdentifier();
-    }
 }