From 27b168d3ca3807123b4877f1ad0662b2610f393d Mon Sep 17 00:00:00 2001 From: Tom Pantelis Date: Tue, 27 Jun 2017 16:09:32 -0400 Subject: [PATCH] Add MXBean to report shard registered DTCL/DCL info It's useful to see what listeners (DTCL/DCL) are registered for each shard. Added a new message, GetInfo, sent to the DTCL/DCL actors that returns DataTreeListenerInfo, including the stringified user listener instance and the stringified YangInstanceIdentifier path. A new ShardDataTreeListenerInfoMXBean is instantiated for each shard which reports the DTCL and DCL info. Change-Id: I312bc5d03fe836bc208ea442ebc2af0ef103120f Signed-off-by: Tom Pantelis --- .../controller/cluster/raft/RaftActor.java | 4 +- .../client/messages/OnDemandRaftState.java | 55 +++++++------ ...ndingDOMDataTreeChangeListenerAdapter.java | 5 ++ .../cluster/datastore/DataChangeListener.java | 8 ++ .../DataTreeChangeListenerActor.java | 8 ++ .../controller/cluster/datastore/Shard.java | 10 ++- .../mbeans/shard/OnDemandShardStateCache.java | 76 +++++++++++++++++ .../ShardDataTreeListenerInfoMXBean.java | 23 ++++++ .../ShardDataTreeListenerInfoMXBeanImpl.java | 82 +++++++++++++++++++ .../jmx/mbeans/shard/ShardStats.java | 51 +++--------- .../messages/DataTreeListenerInfo.java | 48 +++++++++++ .../cluster/datastore/messages/GetInfo.java | 20 +++++ .../messages/OnDemandShardState.java | 7 +- 13 files changed, 327 insertions(+), 70 deletions(-) create mode 100644 opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shard/OnDemandShardStateCache.java create mode 100644 opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shard/ShardDataTreeListenerInfoMXBean.java create mode 100644 opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shard/ShardDataTreeListenerInfoMXBeanImpl.java create mode 100644 opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/DataTreeListenerInfo.java create mode 100644 opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/GetInfo.java diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/RaftActor.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/RaftActor.java index 6d01752a95..9441f28fbc 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/RaftActor.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/RaftActor.java @@ -451,7 +451,7 @@ public abstract class RaftActor extends AbstractUntypedPersistentActor { } final RaftActorBehavior currentBehavior = context.getCurrentBehavior(); - OnDemandRaftState.AbstractBuilder builder = newOnDemandRaftStateBuilder() + OnDemandRaftState.AbstractBuilder builder = newOnDemandRaftStateBuilder() .commitIndex(context.getCommitIndex()) .currentTerm(context.getTermInformation().getCurrentTerm()) .inMemoryJournalDataSize(replicatedLog().dataSize()) @@ -495,7 +495,7 @@ public abstract class RaftActor extends AbstractUntypedPersistentActor { } - protected OnDemandRaftState.AbstractBuilder newOnDemandRaftStateBuilder() { + protected OnDemandRaftState.AbstractBuilder newOnDemandRaftStateBuilder() { return OnDemandRaftState.builder(); } diff --git a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/client/messages/OnDemandRaftState.java b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/client/messages/OnDemandRaftState.java index 9bbee881ea..d9298f9568 100644 --- a/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/client/messages/OnDemandRaftState.java +++ b/opendaylight/md-sal/sal-akka-raft/src/main/java/org/opendaylight/controller/cluster/raft/client/messages/OnDemandRaftState.java @@ -133,126 +133,127 @@ public class OnDemandRaftState { return customRaftPolicyClassName; } - public abstract static class AbstractBuilder> { + public abstract static class AbstractBuilder, T extends OnDemandRaftState> { @SuppressWarnings("unchecked") - protected T self() { - return (T) this; + protected B self() { + return (B) this; } @Nonnull protected abstract OnDemandRaftState state(); - public T lastLogIndex(long value) { + public B lastLogIndex(long value) { state().lastLogIndex = value; return self(); } - public T lastLogTerm(long value) { + public B lastLogTerm(long value) { state().lastLogTerm = value; return self(); } - public T currentTerm(long value) { + public B currentTerm(long value) { state().currentTerm = value; return self(); } - public T commitIndex(long value) { + public B commitIndex(long value) { state().commitIndex = value; return self(); } - public T lastApplied(long value) { + public B lastApplied(long value) { state().lastApplied = value; return self(); } - public T lastIndex(long value) { + public B lastIndex(long value) { state().lastIndex = value; return self(); } - public T lastTerm(long value) { + public B lastTerm(long value) { state().lastTerm = value; return self(); } - public T snapshotIndex(long value) { + public B snapshotIndex(long value) { state().snapshotIndex = value; return self(); } - public T snapshotTerm(long value) { + public B snapshotTerm(long value) { state().snapshotTerm = value; return self(); } - public T replicatedToAllIndex(long value) { + public B replicatedToAllIndex(long value) { state().replicatedToAllIndex = value; return self(); } - public T inMemoryJournalDataSize(long value) { + public B inMemoryJournalDataSize(long value) { state().inMemoryJournalDataSize = value; return self(); } - public T inMemoryJournalLogSize(long value) { + public B inMemoryJournalLogSize(long value) { state().inMemoryJournalLogSize = value; return self(); } - public T leader(String value) { + public B leader(String value) { state().leader = value; return self(); } - public T raftState(String value) { + public B raftState(String value) { state().raftState = value; return self(); } - public T votedFor(String value) { + public B votedFor(String value) { state().votedFor = value; return self(); } - public T isVoting(boolean isVoting) { + public B isVoting(boolean isVoting) { state().isVoting = isVoting; return self(); } - public T followerInfoList(List followerInfoList) { + public B followerInfoList(List followerInfoList) { state().followerInfoList = followerInfoList; return self(); } - public T peerAddresses(Map peerAddresses) { + public B peerAddresses(Map peerAddresses) { state().peerAddresses = peerAddresses; return self(); } - public T peerVotingStates(Map peerVotingStates) { + public B peerVotingStates(Map peerVotingStates) { state().peerVotingStates = ImmutableMap.copyOf(peerVotingStates); return self(); } - public T isSnapshotCaptureInitiated(boolean value) { + public B isSnapshotCaptureInitiated(boolean value) { state().isSnapshotCaptureInitiated = value; return self(); } - public T customRaftPolicyClassName(String className) { + public B customRaftPolicyClassName(String className) { state().customRaftPolicyClassName = className; return self(); } - public OnDemandRaftState build() { - return state(); + @SuppressWarnings("unchecked") + public T build() { + return (T) state(); } } - public static class Builder extends AbstractBuilder { + public static class Builder extends AbstractBuilder { private final OnDemandRaftState state = new OnDemandRaftState(); @Override diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMDataTreeChangeListenerAdapter.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMDataTreeChangeListenerAdapter.java index ecf5db048c..e0cd69b86f 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMDataTreeChangeListenerAdapter.java +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/BindingDOMDataTreeChangeListenerAdapter.java @@ -40,4 +40,9 @@ class BindingDOMDataTreeChangeListenerAdapter implements D final Collection> bindingChanges = LazyDataTreeModification.from(codec, domChanges, store); listener.onDataTreeChanged(bindingChanges); } + + @Override + public String toString() { + return listener.toString(); + } } diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DataChangeListener.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DataChangeListener.java index dc19aa400f..f0c6595ac4 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DataChangeListener.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DataChangeListener.java @@ -13,7 +13,9 @@ import com.google.common.base.Preconditions; import org.opendaylight.controller.cluster.common.actor.AbstractUntypedActor; import org.opendaylight.controller.cluster.datastore.messages.DataChanged; import org.opendaylight.controller.cluster.datastore.messages.DataChangedReply; +import org.opendaylight.controller.cluster.datastore.messages.DataTreeListenerInfo; import org.opendaylight.controller.cluster.datastore.messages.EnableNotification; +import org.opendaylight.controller.cluster.datastore.messages.GetInfo; import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener; import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent; import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener; @@ -30,6 +32,7 @@ public class DataChangeListener extends AbstractUntypedActor { private final AsyncDataChangeListener> listener; private final YangInstanceIdentifier registeredPath; private boolean notificationsEnabled = false; + private long notificationCount; public DataChangeListener(AsyncDataChangeListener> listener, final YangInstanceIdentifier registeredPath) { @@ -43,6 +46,9 @@ public class DataChangeListener extends AbstractUntypedActor { dataChanged(message); } else if (message instanceof EnableNotification) { enableNotification((EnableNotification) message); + } else if (message instanceof GetInfo) { + getSender().tell(new DataTreeListenerInfo(listener.toString(), registeredPath.toString(), + notificationsEnabled, notificationCount), getSelf()); } else { unknownMessage(message); } @@ -68,6 +74,8 @@ public class DataChangeListener extends AbstractUntypedActor { LOG.debug("Sending change notification {} to listener {}", change, listener); + notificationCount++; + try { this.listener.onDataChanged(change); } catch (RuntimeException e) { diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DataTreeChangeListenerActor.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DataTreeChangeListenerActor.java index 902156f346..e877a4d9e8 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DataTreeChangeListenerActor.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/DataTreeChangeListenerActor.java @@ -12,7 +12,9 @@ import com.google.common.base.Preconditions; import org.opendaylight.controller.cluster.common.actor.AbstractUntypedActor; import org.opendaylight.controller.cluster.datastore.messages.DataTreeChanged; import org.opendaylight.controller.cluster.datastore.messages.DataTreeChangedReply; +import org.opendaylight.controller.cluster.datastore.messages.DataTreeListenerInfo; import org.opendaylight.controller.cluster.datastore.messages.EnableNotification; +import org.opendaylight.controller.cluster.datastore.messages.GetInfo; import org.opendaylight.controller.md.sal.dom.api.DOMDataTreeChangeListener; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; @@ -24,6 +26,7 @@ final class DataTreeChangeListenerActor extends AbstractUntypedActor { private final DOMDataTreeChangeListener listener; private final YangInstanceIdentifier registeredPath; private boolean notificationsEnabled = false; + private long notificationCount; private String logContext = ""; private DataTreeChangeListenerActor(final DOMDataTreeChangeListener listener, @@ -38,6 +41,9 @@ final class DataTreeChangeListenerActor extends AbstractUntypedActor { dataChanged((DataTreeChanged)message); } else if (message instanceof EnableNotification) { enableNotification((EnableNotification) message); + } else if (message instanceof GetInfo) { + getSender().tell(new DataTreeListenerInfo(listener.toString(), registeredPath.toString(), + notificationsEnabled, notificationCount), getSelf()); } else { unknownMessage(message); } @@ -55,6 +61,8 @@ final class DataTreeChangeListenerActor extends AbstractUntypedActor { LOG.debug("{}: Sending {} change notification(s) {} to listener {}", logContext, message.getChanges().size(), message.getChanges(), listener); + notificationCount++; + try { this.listener.onDataTreeChanged(message.getChanges()); } catch (Exception e) { diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/Shard.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/Shard.java index 50d4e0252f..1c85aef978 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/Shard.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/Shard.java @@ -60,6 +60,7 @@ import org.opendaylight.controller.cluster.common.actor.MessageTracker.Error; import org.opendaylight.controller.cluster.common.actor.MeteringBehavior; import org.opendaylight.controller.cluster.datastore.exceptions.NoShardLeaderException; import org.opendaylight.controller.cluster.datastore.identifiers.ShardIdentifier; +import org.opendaylight.controller.cluster.datastore.jmx.mbeans.shard.ShardDataTreeListenerInfoMXBeanImpl; import org.opendaylight.controller.cluster.datastore.jmx.mbeans.shard.ShardMBeanFactory; import org.opendaylight.controller.cluster.datastore.jmx.mbeans.shard.ShardStats; import org.opendaylight.controller.cluster.datastore.messages.AbortTransaction; @@ -163,6 +164,8 @@ public class Shard extends RaftActor { private final ShardStats shardMBean; + private final ShardDataTreeListenerInfoMXBeanImpl listenerInfoMXBean; + private DatastoreContext datastoreContext; private final ShardCommitCoordinator commitCoordinator; @@ -257,6 +260,10 @@ public class Shard extends RaftActor { .fileBackedStreamFactory(getRaftActorContext().getFileBackedOutputStreamFactory()) .assembledMessageCallback((message, sender) -> self().tell(message, sender)) .expireStateAfterInactivity(datastoreContext.getRequestTimeout(), TimeUnit.NANOSECONDS).build(); + + listenerInfoMXBean = new ShardDataTreeListenerInfoMXBeanImpl(name, datastoreContext.getDataStoreMXBeanType(), + self()); + listenerInfoMXBean.register(); } private void setTransactionCommitTimeout() { @@ -285,6 +292,7 @@ public class Shard extends RaftActor { commitCoordinator.abortPendingTransactions("Transaction aborted due to shutdown.", this); shardMBean.unregisterMBean(); + listenerInfoMXBean.unregister(); } @Override @@ -971,7 +979,7 @@ public class Shard extends RaftActor { } @Override - protected OnDemandRaftState.AbstractBuilder newOnDemandRaftStateBuilder() { + protected OnDemandRaftState.AbstractBuilder newOnDemandRaftStateBuilder() { return OnDemandShardState.newBuilder().treeChangeListenerActors(treeChangeSupport.getListenerActors()) .dataChangeListenerActors(changeSupport.getListenerActors()) .commitCohortActors(store.getCohortActors()); diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shard/OnDemandShardStateCache.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shard/OnDemandShardStateCache.java new file mode 100644 index 0000000000..230da28e9a --- /dev/null +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shard/OnDemandShardStateCache.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2017 Inocybe Technologies 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.cluster.datastore.jmx.mbeans.shard; + +import akka.actor.ActorRef; +import akka.pattern.Patterns; +import akka.util.Timeout; +import com.google.common.base.Preconditions; +import com.google.common.base.Stopwatch; +import com.google.common.base.Throwables; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.util.concurrent.ExecutionError; +import com.google.common.util.concurrent.UncheckedExecutionException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import org.opendaylight.controller.cluster.datastore.messages.OnDemandShardState; +import org.opendaylight.controller.cluster.raft.client.messages.GetOnDemandRaftState; +import scala.concurrent.Await; + +/** + * Maintains a short-lived shared cache of OnDemandShardState. + * + * @author Thomas Pantelis + */ +class OnDemandShardStateCache { + private static final Cache ONDEMAND_SHARD_STATE_CACHE = + CacheBuilder.newBuilder().expireAfterWrite(2, TimeUnit.SECONDS).build(); + + private final ActorRef shardActor; + private final String shardName; + private volatile String stateRetrievalTime; + + OnDemandShardStateCache(String shardName, ActorRef shardActor) { + this.shardName = Preconditions.checkNotNull(shardName); + this.shardActor = shardActor; + } + + OnDemandShardState get() throws Exception { + if (shardActor == null) { + return OnDemandShardState.newBuilder().build(); + } + + try { + return ONDEMAND_SHARD_STATE_CACHE.get(shardName, this::retrieveState); + } catch (ExecutionException | UncheckedExecutionException | ExecutionError e) { + if (e.getCause() != null) { + Throwables.propagateIfPossible(e.getCause(), Exception.class); + throw new RuntimeException("unexpected", e.getCause()); + } + + throw e; + } + } + + String getStatRetrievaelTime() { + return stateRetrievalTime; + } + + private OnDemandShardState retrieveState() throws Exception { + stateRetrievalTime = null; + Timeout timeout = new Timeout(10, TimeUnit.SECONDS); + Stopwatch timer = Stopwatch.createStarted(); + + OnDemandShardState state = (OnDemandShardState) Await.result(Patterns.ask(shardActor, + GetOnDemandRaftState.INSTANCE, timeout), timeout.duration()); + + stateRetrievalTime = timer.stop().toString(); + return state; + } +} diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shard/ShardDataTreeListenerInfoMXBean.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shard/ShardDataTreeListenerInfoMXBean.java new file mode 100644 index 0000000000..2629d645d9 --- /dev/null +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shard/ShardDataTreeListenerInfoMXBean.java @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2017 Inocybe Technologies 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.cluster.datastore.jmx.mbeans.shard; + +import java.util.List; +import org.opendaylight.controller.cluster.datastore.messages.DataTreeListenerInfo; + +/** + * MXBean interface for reporting shard data tree change listener information. + * + * @author Thomas Pantelis + */ +public interface ShardDataTreeListenerInfoMXBean { + List getDataTreeChangeListenerInfo(); + + @Deprecated + List getDataChangeListenerInfo(); +} diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shard/ShardDataTreeListenerInfoMXBeanImpl.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shard/ShardDataTreeListenerInfoMXBeanImpl.java new file mode 100644 index 0000000000..54cb49cc17 --- /dev/null +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shard/ShardDataTreeListenerInfoMXBeanImpl.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2017 Inocybe Technologies 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.cluster.datastore.jmx.mbeans.shard; + +import akka.actor.ActorRef; +import akka.actor.ActorSelection; +import akka.dispatch.Futures; +import akka.pattern.Patterns; +import akka.util.Timeout; +import com.google.common.base.Preconditions; +import com.google.common.base.Throwables; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.TimeUnit; +import org.opendaylight.controller.cluster.datastore.messages.DataTreeListenerInfo; +import org.opendaylight.controller.cluster.datastore.messages.GetInfo; +import org.opendaylight.controller.cluster.datastore.messages.OnDemandShardState; +import org.opendaylight.controller.md.sal.common.util.jmx.AbstractMXBean; +import scala.concurrent.Await; +import scala.concurrent.ExecutionContext; +import scala.concurrent.Future; + +/** + * Implementation of ShardDataTreeListenerInfoMXBean. + * + * @author Thomas Pantelis + */ +public class ShardDataTreeListenerInfoMXBeanImpl extends AbstractMXBean implements ShardDataTreeListenerInfoMXBean { + private static final String JMX_CATEGORY = "ShardDataTreeListenerInfo"; + + private final OnDemandShardStateCache stateCache; + + public ShardDataTreeListenerInfoMXBeanImpl(final String shardName, final String mxBeanType, + final ActorRef shardActor) { + super(shardName, mxBeanType, JMX_CATEGORY); + stateCache = new OnDemandShardStateCache(shardName, Preconditions.checkNotNull(shardActor)); + } + + @Override + public List getDataTreeChangeListenerInfo() { + return getListenerActorsInfo(getState().getTreeChangeListenerActors()); + } + + @Override + public List getDataChangeListenerInfo() { + return getListenerActorsInfo(getState().getDataChangeListenerActors()); + } + + @SuppressWarnings("checkstyle:IllegalCatch") + private OnDemandShardState getState() { + try { + return stateCache.get(); + } catch (Exception e) { + Throwables.throwIfUnchecked(e); + throw new RuntimeException(e); + } + } + + @SuppressWarnings("checkstyle:IllegalCatch") + private List getListenerActorsInfo(Collection actors) { + final Timeout timeout = new Timeout(20, TimeUnit.SECONDS); + final List> futureList = new ArrayList<>(actors.size()); + for (ActorSelection actor: actors) { + futureList.add(Patterns.ask(actor, GetInfo.INSTANCE, timeout)); + } + + try { + final List listenerInfoList = new ArrayList<>(); + Await.result(Futures.sequence(futureList, ExecutionContext.Implicits$.MODULE$.global()), + timeout.duration()).forEach(obj -> listenerInfoList.add((DataTreeListenerInfo) obj)); + return listenerInfoList; + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shard/ShardStats.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shard/ShardStats.java index 02c838a3c6..5a4cc98011 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shard/ShardStats.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/jmx/mbeans/shard/ShardStats.java @@ -9,27 +9,19 @@ package org.opendaylight.controller.cluster.datastore.jmx.mbeans.shard; import akka.actor.ActorRef; -import akka.pattern.Patterns; -import akka.util.Timeout; import com.google.common.base.Joiner; import com.google.common.base.Joiner.MapJoiner; -import com.google.common.base.Stopwatch; -import com.google.common.cache.Cache; -import com.google.common.cache.CacheBuilder; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; -import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import javax.annotation.Nullable; import javax.annotation.concurrent.GuardedBy; import org.opendaylight.controller.cluster.datastore.Shard; import org.opendaylight.controller.cluster.raft.base.messages.InitiateCaptureSnapshot; import org.opendaylight.controller.cluster.raft.client.messages.FollowerInfo; -import org.opendaylight.controller.cluster.raft.client.messages.GetOnDemandRaftState; import org.opendaylight.controller.cluster.raft.client.messages.OnDemandRaftState; import org.opendaylight.controller.md.sal.common.util.jmx.AbstractMXBean; -import scala.concurrent.Await; /** * Maintains statistics for a shard. @@ -42,11 +34,12 @@ public class ShardStats extends AbstractMXBean implements ShardStatsMXBean { @GuardedBy("DATE_FORMAT") private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); - private static final Cache ONDEMAND_RAFT_STATE_CACHE = - CacheBuilder.newBuilder().expireAfterWrite(2, TimeUnit.SECONDS).build(); - private static final MapJoiner MAP_JOINER = Joiner.on(", ").withKeyValueSeparator(": "); + private final Shard shard; + + private final OnDemandShardStateCache stateCache; + private long committedTransactionsCount; private long readOnlyTransactionCount; @@ -65,12 +58,8 @@ public class ShardStats extends AbstractMXBean implements ShardStatsMXBean { private boolean followerInitialSyncStatus = false; - private final Shard shard; - private String statRetrievalError; - private String statRetrievalTime; - private long leadershipChangeCount; private long lastLeadershipChangeTime; @@ -78,35 +67,19 @@ public class ShardStats extends AbstractMXBean implements ShardStatsMXBean { public ShardStats(final String shardName, final String mxBeanType, @Nullable final Shard shard) { super(shardName, mxBeanType, JMX_CATEGORY_SHARD); this.shard = shard; + stateCache = new OnDemandShardStateCache(shardName, shard != null ? shard.self() : null); } @SuppressWarnings("checkstyle:IllegalCatch") private OnDemandRaftState getOnDemandRaftState() { - String name = getShardName(); - OnDemandRaftState state = ONDEMAND_RAFT_STATE_CACHE.getIfPresent(name); - if (state == null) { + try { + final OnDemandRaftState state = stateCache.get(); statRetrievalError = null; - statRetrievalTime = null; - - if (shard != null) { - Timeout timeout = new Timeout(10, TimeUnit.SECONDS); - try { - Stopwatch timer = Stopwatch.createStarted(); - - state = (OnDemandRaftState) Await.result(Patterns.ask(shard.getSelf(), - GetOnDemandRaftState.INSTANCE, timeout), timeout.duration()); - - statRetrievalTime = timer.stop().toString(); - ONDEMAND_RAFT_STATE_CACHE.put(name, state); - } catch (Exception e) { - statRetrievalError = e.toString(); - } - } - - state = state != null ? state : OnDemandRaftState.builder().build(); + return state; + } catch (Exception e) { + statRetrievalError = e.getCause().toString(); + return OnDemandRaftState.builder().build(); } - - return state; } private static String formatMillis(final long timeMillis) { @@ -327,7 +300,7 @@ public class ShardStats extends AbstractMXBean implements ShardStatsMXBean { @Override public String getStatRetrievalTime() { getOnDemandRaftState(); - return statRetrievalTime; + return stateCache.getStatRetrievaelTime(); } @Override diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/DataTreeListenerInfo.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/DataTreeListenerInfo.java new file mode 100644 index 0000000000..90db2794d3 --- /dev/null +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/DataTreeListenerInfo.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2017 Inocybe Technologies 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.cluster.datastore.messages; + +import com.google.common.base.Preconditions; +import java.beans.ConstructorProperties; + +/** + * Response to a {@link GetInfo} query from a data tree listener actor. + * + * @author Thomas Pantelis + */ +public class DataTreeListenerInfo { + private final String listener; + private final String registeredPath; + private final boolean isEnabled; + private final long notificationCount; + + @ConstructorProperties({"listener","registeredPath", "isEnabled", "notificationCount"}) + public DataTreeListenerInfo(final String listener, final String registeredPath, final boolean isEnabled, + final long notificationCount) { + this.listener = Preconditions.checkNotNull(listener); + this.registeredPath = Preconditions.checkNotNull(registeredPath); + this.isEnabled = isEnabled; + this.notificationCount = notificationCount; + } + + public String getListener() { + return listener; + } + + public String getRegisteredPath() { + return registeredPath; + } + + public boolean isEnabled() { + return isEnabled; + } + + public long getNotificationCount() { + return notificationCount; + } +} diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/GetInfo.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/GetInfo.java new file mode 100644 index 0000000000..2026bd843f --- /dev/null +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/GetInfo.java @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2017 Inocybe Technologies 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.cluster.datastore.messages; + +/** + * Local message sent to an actor to retrieve internal information for reporting. + * + * @author Thomas Pantelis + */ +public final class GetInfo { + public static final GetInfo INSTANCE = new GetInfo(); + + private GetInfo() { + } +} diff --git a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/OnDemandShardState.java b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/OnDemandShardState.java index ba6f229de7..fd79266b19 100644 --- a/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/OnDemandShardState.java +++ b/opendaylight/md-sal/sal-distributed-datastore/src/main/java/org/opendaylight/controller/cluster/datastore/messages/OnDemandShardState.java @@ -38,7 +38,7 @@ public class OnDemandShardState extends OnDemandRaftState { return new Builder(); } - public static class Builder extends AbstractBuilder { + public static class Builder extends AbstractBuilder { private final OnDemandShardState state = new OnDemandShardState(); @Override @@ -60,5 +60,10 @@ public class OnDemandShardState extends OnDemandRaftState { state.commitCohortActors = actors; return self(); } + + @Override + public OnDemandShardState build() { + return super.build(); + } } } -- 2.36.6