Add option to enable/disable basic DCL and/or DTCL 72/41472/1
authorRyan Goulding <ryandgoulding@gmail.com>
Fri, 24 Jun 2016 15:50:00 +0000 (11:50 -0400)
committerRyan Goulding <ryandgoulding@gmail.com>
Thu, 7 Jul 2016 11:24:10 +0000 (11:24 +0000)
The cars stress test is a very appropriate place to measure the effects
of DCL and DTCL on a very long list.  This change adds a few RPC
implementations in order to do the following:

1) enable DCL
2) disable DCL
3) enable DTCL
4) disable DTCL

This change includes very basic DCL/DTCL implementations, which just log
a message at trace level (off by default but there for ensuring the
onData*Changed(...) method is actually called.

The existing clustering-test-app behavior doesn't change at all;  these
new RPC(s) do not need to be used, and the added Listener implementations
are not registered listeners by default.

Change-Id: I6fcec6cd8c0a082e815561e88b325a55022ad2af
Signed-off-by: Ryan Goulding <ryandgoulding@gmail.com>
(cherry picked from commit 7a53dd074428ce5c4be767a51c509b1b8cf0f05e)

opendaylight/md-sal/samples/clustering-test-app/model/src/main/yang/car.yang
opendaylight/md-sal/samples/clustering-test-app/provider/src/main/java/org/opendaylight/controller/clustering/it/provider/CarDataChangeListener.java [new file with mode: 0644]
opendaylight/md-sal/samples/clustering-test-app/provider/src/main/java/org/opendaylight/controller/clustering/it/provider/CarDataTreeChangeListener.java [new file with mode: 0644]
opendaylight/md-sal/samples/clustering-test-app/provider/src/main/java/org/opendaylight/controller/clustering/it/provider/CarProvider.java

index f8c2f5b88ccf839a3b38cbcff1637e1fe03e56c2..f279949b5eb3412005775617765ea0a7c662d3b7 100644 (file)
@@ -98,4 +98,22 @@ module car {
               }
         }
     }
+
+    rpc register-logging-dcl {
+        description "Registers a basic logging DCL on the cars container.  This is useful
+                    for analyzing effects of DCL on a long, flat list.";
+    }
+
+    rpc unregister-logging-dcls {
+        description "Unregisters the logging DCL(s) for the cars container.";
+    }
+
+    rpc register-logging-dtcl {
+        description "Registers a basic logging DTCL on the cars container.  This is useful
+                            for analyzing the effects of DTCL on a long, flat list.";
+    }
+
+    rpc unregister-logging-dtcls {
+        description "Unregisters the logging DTCL(s) for the cars container.";
+    }
 }
\ No newline at end of file
diff --git a/opendaylight/md-sal/samples/clustering-test-app/provider/src/main/java/org/opendaylight/controller/clustering/it/provider/CarDataChangeListener.java b/opendaylight/md-sal/samples/clustering-test-app/provider/src/main/java/org/opendaylight/controller/clustering/it/provider/CarDataChangeListener.java
new file mode 100644 (file)
index 0000000..ac5c368
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2016 Brocade Communications 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.clustering.it.provider;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Provides a basic DCL implementation for performance testing reasons.  Emits a summary
+ * of the changes that occurred.
+ *
+ * @author Ryan Goulding (ryandgoulding@gmail.com)
+ */
+public class CarDataChangeListener implements DataChangeListener {
+    private static final Logger LOG = LoggerFactory.getLogger(CarDataChangeListener.class);
+
+    @Override
+    public void onDataChanged(AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
+        if (LOG.isTraceEnabled()) {
+            LOG.trace("onDataChanged invoked");
+            outputChanges(change);
+        }
+    }
+
+    private void outputChanges(final AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
+        final Map<InstanceIdentifier<?>, DataObject> originalData = change.getOriginalData() != null ?
+                change.getOriginalData(): Collections.<InstanceIdentifier<?>, DataObject>emptyMap();
+        final Map<InstanceIdentifier<?>, DataObject> updatedData = change.getUpdatedData() != null ?
+                change.getUpdatedData(): Collections.<InstanceIdentifier<?>, DataObject>emptyMap();
+        final Map<InstanceIdentifier<?>, DataObject> createdData = change.getCreatedData() != null ?
+                change.getCreatedData(): Collections.<InstanceIdentifier<?>, DataObject>emptyMap();
+        final Set<InstanceIdentifier<?>> removedPaths = change.getRemovedPaths() != null ?
+                change.getRemovedPaths(): Collections.<InstanceIdentifier<?>>emptySet();
+        LOG.trace("AsyncDataChangeEvent - originalData={} updatedData={} createdData={} removedPaths={}",
+                originalData, updatedData, createdData, removedPaths);
+    }
+}
diff --git a/opendaylight/md-sal/samples/clustering-test-app/provider/src/main/java/org/opendaylight/controller/clustering/it/provider/CarDataTreeChangeListener.java b/opendaylight/md-sal/samples/clustering-test-app/provider/src/main/java/org/opendaylight/controller/clustering/it/provider/CarDataTreeChangeListener.java
new file mode 100644 (file)
index 0000000..c157b6c
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2016 Brocade Communications 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.clustering.it.provider;
+
+import static org.opendaylight.controller.md.sal.binding.api.DataObjectModification.ModificationType;
+import static org.opendaylight.controller.md.sal.binding.api.DataObjectModification.ModificationType.DELETE;
+import static org.opendaylight.controller.md.sal.binding.api.DataObjectModification.ModificationType.SUBTREE_MODIFIED;
+import static org.opendaylight.controller.md.sal.binding.api.DataObjectModification.ModificationType.WRITE;
+
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.sal.clustering.it.car.rev140818.Cars;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Provides a basic DTCL implementation for performance testing reasons.  Emits a rudimentary
+ * summary of the changes that occurred.
+ *
+ * @author Ryan Goulding (ryandgoulding@gmail.com)
+ */
+public class CarDataTreeChangeListener implements DataTreeChangeListener<Cars> {
+    private static final Logger LOG = LoggerFactory.getLogger(CarDataTreeChangeListener.class);
+
+    @java.lang.Override
+    public void onDataTreeChanged(@Nonnull java.util.Collection<DataTreeModification<Cars>> changes) {
+        if (LOG.isTraceEnabled()) {
+            for (DataTreeModification<Cars> change : changes) {
+                ouputChanges(change);
+            }
+        }
+    }
+
+    private void ouputChanges(final DataTreeModification<Cars> change) {
+        final DataObjectModification<Cars> rootNode = change.getRootNode();
+        final ModificationType modificationType = rootNode.getModificationType();
+        final InstanceIdentifier<Cars> rootIdentifier = change.getRootPath().getRootIdentifier();
+        switch (modificationType) {
+            case WRITE:
+            case SUBTREE_MODIFIED: {
+                final Cars dataBefore = rootNode.getDataBefore();
+                final Cars dataAfter = rootNode.getDataAfter();
+                LOG.trace("onDataTreeChanged - Cars config with path {} was added or changed from {} to {}",
+                        rootIdentifier, dataBefore, dataAfter);
+                break;
+            }
+            case DELETE: {
+                LOG.trace("onDataTreeChanged - Cars config with path {} was deleted", rootIdentifier);
+                break;
+            }
+            default: {
+                LOG.trace("onDataTreeChanged called with unknown modificationType: {}", modificationType);
+                break;
+            }
+        }
+    }
+}
index 1ab7f5e9016c30a29ab493c978824f6e2b85c44a..2dfb32b421f3f6e6c7275316091476bb7c69531c 100644 (file)
@@ -8,19 +8,24 @@
 package org.opendaylight.controller.clustering.it.provider;
 
 import com.google.common.base.Stopwatch;
+import com.google.common.collect.Sets;
 import com.google.common.util.concurrent.Futures;
+import java.util.Collection;
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicLong;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
 import org.opendaylight.controller.md.sal.common.api.clustering.CandidateAlreadyRegisteredException;
 import org.opendaylight.controller.md.sal.common.api.clustering.Entity;
 import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipChange;
 import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipListener;
 import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipService;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.sal.clustering.it.car.rev140818.CarId;
@@ -32,6 +37,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controll
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.sal.clustering.it.car.rev140818.UnregisterOwnershipInput;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.sal.clustering.it.car.rev140818.cars.CarEntry;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.sal.clustering.it.car.rev140818.cars.CarEntryBuilder;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.common.RpcError.ErrorType;
 import org.opendaylight.yangtools.yang.common.RpcResult;
@@ -57,6 +63,15 @@ public class CarProvider implements CarService {
     private volatile Thread testThread;
     private volatile boolean stopThread;
 
+    private static final InstanceIdentifier CARS_IID = InstanceIdentifier.builder(Cars.class).build();
+    private static final DataTreeIdentifier<Cars> CARS_DTID = new DataTreeIdentifier<>(
+            LogicalDatastoreType.CONFIGURATION, CARS_IID);
+
+    private Collection<ListenerRegistration<DataChangeListener>> carsDclRegistrations =
+            Sets.newConcurrentHashSet();
+    private Collection<ListenerRegistration<CarDataTreeChangeListener>> carsDtclRegistrations =
+            Sets.newConcurrentHashSet();
+
     public CarProvider(DataBroker dataProvider, EntityOwnershipService ownershipService) {
         this.dataProvider = dataProvider;
         this.ownershipService = ownershipService;
@@ -183,4 +198,61 @@ public class CarProvider implements CarService {
             LOG.info("ownershipChanged: {}", ownershipChange);
         }
     }
+
+    @Override
+    public Future<RpcResult<java.lang.Void>> registerLoggingDcl() {
+        LOG.info("Registering a new CarDataChangeListener");
+        final ListenerRegistration carsDclRegistration = dataProvider.registerDataChangeListener(
+                LogicalDatastoreType.CONFIGURATION, CARS_IID, new CarDataChangeListener(),
+                AsyncDataBroker.DataChangeScope.SUBTREE);
+
+        if (carsDclRegistration != null) {
+            carsDclRegistrations.add(carsDclRegistration);
+            return RpcResultBuilder.<Void>success().buildFuture();
+        }
+        return RpcResultBuilder.<Void>failed().buildFuture();
+    }
+
+    @Override
+    public Future<RpcResult<java.lang.Void>> registerLoggingDtcl() {
+        LOG.info("Registering a new CarDataTreeChangeListener");
+        final ListenerRegistration<CarDataTreeChangeListener> carsDtclRegistration =
+                dataProvider.registerDataTreeChangeListener(CARS_DTID, new CarDataTreeChangeListener());
+
+        if (carsDtclRegistration != null) {
+            carsDtclRegistrations.add(carsDtclRegistration);
+            return RpcResultBuilder.<Void>success().buildFuture();
+        }
+        return RpcResultBuilder.<Void>failed().buildFuture();
+    }
+
+    @Override
+    public Future<RpcResult<java.lang.Void>> unregisterLoggingDcls() {
+        LOG.info("Unregistering the CarDataChangeListener(s)");
+        synchronized (carsDclRegistrations) {
+            int numListeners = 0;
+            for (ListenerRegistration<DataChangeListener> carsDclRegistration : carsDclRegistrations) {
+                carsDclRegistration.close();
+                numListeners++;
+            }
+            carsDclRegistrations.clear();
+            LOG.info("Unregistered {} CarDataChangeListener(s)", numListeners);
+        }
+        return RpcResultBuilder.<Void>success().buildFuture();
+    }
+
+    @Override
+    public Future<RpcResult<java.lang.Void>> unregisterLoggingDtcls() {
+        LOG.info("Unregistering the CarDataTreeChangeListener(s)");
+        synchronized (carsDtclRegistrations) {
+            int numListeners = 0;
+            for (ListenerRegistration<CarDataTreeChangeListener> carsDtclRegistration : carsDtclRegistrations) {
+                carsDtclRegistration.close();
+                numListeners++;
+            }
+            carsDtclRegistrations.clear();
+            LOG.info("Unregistered {} CaraDataTreeChangeListener(s)", numListeners);
+        }
+        return RpcResultBuilder.<Void>success().buildFuture();
+    }
 }