/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ignite.spi.discovery.tcp.internal;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicLong;
import org.apache.ignite.internal.util.GridBoundedLinkedHashMap;
import org.apache.ignite.internal.util.tostring.GridToStringInclude;
import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.internal.util.typedef.internal.S;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.lang.IgniteUuid;
import org.apache.ignite.spi.discovery.tcp.messages.TcpDiscoveryAbstractMessage;
import org.apache.ignite.spi.discovery.tcp.messages.TcpDiscoveryCustomEventMessage;
import org.apache.ignite.spi.discovery.tcp.messages.TcpDiscoveryNodeAddFinishedMessage;
import org.apache.ignite.spi.discovery.tcp.messages.TcpDiscoveryNodeAddedMessage;
import org.apache.ignite.spi.discovery.tcp.messages.TcpDiscoveryNodeFailedMessage;
import org.apache.ignite.spi.discovery.tcp.messages.TcpDiscoveryNodeLeftMessage;
/**
* Statistics for {@link org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi}.
*/
public class TcpDiscoveryStatistics {
/** Join started timestamp. */
private long joinStartedTs;
/** Join finished timestamp. */
private long joinFinishedTs;
/** Coordinator since timestamp. */
private final AtomicLong crdSinceTs = new AtomicLong();
/** Joined nodes count. */
private int joinedNodesCnt;
/** Failed nodes count. */
private int failedNodesCnt;
/** Left nodes count. */
private int leftNodesCnt;
/** Ack timeouts count. */
private int ackTimeoutsCnt;
/** Socket timeouts count. */
private int sockTimeoutsCnt;
/** Received messages. */
@GridToStringInclude
private final Map<String, Integer> rcvdMsgs = new HashMap<>();
/** Processed messages. */
@GridToStringInclude
private final Map<String, Integer> procMsgs = new HashMap<>();
/** Average time taken to serialize messages. */
@GridToStringInclude
private final Map<String, Long> avgMsgsSndTimes = new HashMap<>();
/** Average time taken to serialize messages. */
@GridToStringInclude
private final Map<String, Long> maxMsgsSndTimes = new HashMap<>();
/** Sent messages. */
@GridToStringInclude
private final Map<String, Integer> sentMsgs = new HashMap<>();
/** Messages receive timestamps. */
private final Map<IgniteUuid, Long> msgsRcvTs = new GridBoundedLinkedHashMap<>(1024);
/** Messages processing start timestamps. */
private final Map<IgniteUuid, Long> msgsProcStartTs = new GridBoundedLinkedHashMap<>(1024);
/** Ring messages sent timestamps. */
private final Map<IgniteUuid, Long> ringMsgsSndTs = new GridBoundedLinkedHashMap<>(1024);
/** */
@GridToStringInclude
private final Map<String, Long> avgMsgsAckTimes = new HashMap<>();
/** */
@GridToStringInclude
private final Map<String, Long> maxMsgsAckTimes = new HashMap<>();
/** Average time messages is in queue. */
private long avgMsgQueueTime;
/** Max time messages is in queue. */
private long maxMsgQueueTime;
/** Total number of ring messages sent. */
private int ringMsgsSent;
/** Average time it takes for messages to pass the full ring. */
private long avgRingMsgTime;
/** Max time it takes for messages to pass the full ring. */
private long maxRingMsgTime;
/** Class name of ring message that required the biggest time for full ring traverse. */
private String maxRingTimeMsgCls;
/** Average message processing time. */
private long avgMsgProcTime;
/** Max message processing time. */
private long maxMsgProcTime;
/** Class name of the message that required the biggest time to process. */
private String maxProcTimeMsgCls;
/** Socket readers created count. */
private int sockReadersCreated;
/** Socket readers removed count. */
private int sockReadersRmv;
/** Average time it takes to initialize connection from another node. */
private long avgSrvSockInitTime;
/** Max time it takes to initialize connection from another node. */
private long maxSrvSockInitTime;
/** Number of outgoing connections established. */
private int clientSockCreatedCnt;
/** Average time it takes to connect to another node. */
private long avgClientSockInitTime;
/** Max time it takes to connect to another node. */
private long maxClientSockInitTime;
/** Pending messages registered count. */
private int pendingMsgsRegistered;
/** Pending messages discarded count. */
private int pendingMsgsDiscarded;
/**
* Increments joined nodes count.
*/
public synchronized void onNodeJoined() {
joinedNodesCnt++;
}
/**
* Increments left nodes count.
*/
public synchronized void onNodeLeft() {
leftNodesCnt++;
}
/**
* Increments failed nodes count.
*/
public synchronized void onNodeFailed() {
failedNodesCnt++;
}
/**
* Increments ack timeouts count.
*/
public synchronized void onAckTimeout() {
ackTimeoutsCnt++;
}
/**
* Increments socket timeouts count.
*/
public synchronized void onSocketTimeout() {
sockTimeoutsCnt++;
}
/**
* Initializes coordinator since date (if needed).
*/
public void onBecomingCoordinator() {
crdSinceTs.compareAndSet(0, U.currentTimeMillis());
}
/**
* Initializes join started timestamp.
*/
public synchronized void onJoinStarted() {
joinStartedTs = U.currentTimeMillis();
}
/**
* Initializes join finished timestamp.
*/
public synchronized void onJoinFinished() {
joinFinishedTs = U.currentTimeMillis();
}
/**
* @return Join started timestamp.
*/
public synchronized long joinStarted() {
return joinStartedTs;
}
/**
* @return Join finished timestamp.
*/
public synchronized long joinFinished() {
return joinFinishedTs;
}
/**
* Collects necessary stats for message received by SPI.
*
* @param msg Received message.
*/
public synchronized void onMessageReceived(TcpDiscoveryAbstractMessage msg) {
assert msg != null;
Integer cnt = F.addIfAbsent(rcvdMsgs, msg.getClass().getSimpleName(), new Callable<Integer>() {
@Override public Integer call() {
return 0;
}
});
assert cnt != null;
rcvdMsgs.put(msg.getClass().getSimpleName(), ++cnt);
msgsRcvTs.put(msg.id(), U.currentTimeMillis());
}
/**
* Collects necessary stats for message processed by SPI.
*
* @param msg Processed message.
*/
public synchronized void onMessageProcessingStarted(TcpDiscoveryAbstractMessage msg) {
assert msg != null;
Integer cnt = F.addIfAbsent(procMsgs, msg.getClass().getSimpleName(), new Callable<Integer>() {
@Override public Integer call() {
return 0;
}
});
assert cnt != null;
procMsgs.put(msg.getClass().getSimpleName(), ++cnt);
Long rcvdTs = msgsRcvTs.remove(msg.id());
if (rcvdTs != null) {
long duration = U.currentTimeMillis() - rcvdTs;
if (maxMsgQueueTime < duration)
maxMsgQueueTime = duration;
int totalProcMsgs = totalProcessedMessages();
if (totalProcMsgs != 0)
avgMsgQueueTime = (avgMsgQueueTime * (totalProcMsgs - 1)) / totalProcMsgs;
}
msgsProcStartTs.put(msg.id(), U.currentTimeMillis());
}
/**
* Collects necessary stats for message processed by SPI.
*
* @param msg Processed message.
*/
public synchronized void onMessageProcessingFinished(TcpDiscoveryAbstractMessage msg) {
assert msg != null;
Long startTs = msgsProcStartTs.get(msg.id());
if (startTs != null) {
long duration = U.currentTimeMillis() - startTs;
int totalProcMsgs = totalProcessedMessages();
if (totalProcMsgs != 0)
avgMsgProcTime = (avgMsgProcTime * (totalProcMsgs - 1) + duration) / totalProcMsgs;
if (duration > maxMsgProcTime) {
maxMsgProcTime = duration;
maxProcTimeMsgCls = msg.getClass().getSimpleName();
}
msgsProcStartTs.remove(msg.id());
}
}
/**
* Called by coordinator when ring message is sent.
* @param msg Sent message.
* @param time Time taken to serialize message.
*/
public synchronized void onMessageSent(TcpDiscoveryAbstractMessage msg, long time) {
assert msg != null;
assert time >= 0 : time;
if (crdSinceTs.get() > 0 &&
(msg instanceof TcpDiscoveryCustomEventMessage) ||
(msg instanceof TcpDiscoveryNodeAddedMessage) ||
(msg instanceof TcpDiscoveryNodeAddFinishedMessage) ||
(msg instanceof TcpDiscoveryNodeLeftMessage) ||
(msg instanceof TcpDiscoveryNodeFailedMessage)) {
ringMsgsSndTs.put(msg.id(), U.currentTimeMillis());
ringMsgsSent++;
}
Integer cnt = F.addIfAbsent(sentMsgs, msg.getClass().getSimpleName(), new Callable<Integer>() {
@Override public Integer call() {
return 0;
}
});
assert cnt != null;
sentMsgs.put(msg.getClass().getSimpleName(), ++cnt);
addTimeInfo(avgMsgsSndTimes, maxMsgsSndTimes, msg, cnt, time);
addTimeInfo(avgMsgsAckTimes, maxMsgsAckTimes, msg, cnt, time);
}
/**
* @param avgTimes Average times.
* @param maxTimes Max times.
* @param msg Message.
* @param cnt Total message count.
* @param time Time.
*/
private void addTimeInfo(Map<String, Long> avgTimes,
Map<String, Long> maxTimes,
TcpDiscoveryAbstractMessage msg,
int cnt,
long time) {
Long avgTime = F.addIfAbsent(avgTimes, msg.getClass().getSimpleName(), new Callable<Long>() {
@Override public Long call() {
return 0L;
}
});
assert avgTime != null;
avgTime = (avgTime * (cnt - 1) + time) / cnt;
avgTimes.put(msg.getClass().getSimpleName(), avgTime);
Long maxTime = F.addIfAbsent(maxTimes, msg.getClass().getSimpleName(), new Callable<Long>() {
@Override public Long call() {
return 0L;
}
});
assert maxTime != null;
if (time > maxTime)
maxTimes.put(msg.getClass().getSimpleName(), time);
}
/**
* Called by coordinator when ring message makes full pass.
*
* @param msg Message.
*/
public synchronized void onRingMessageReceived(TcpDiscoveryAbstractMessage msg) {
assert msg != null;
Long sentTs = ringMsgsSndTs.get(msg.id());
if (sentTs != null) {
long duration = U.currentTimeMillis() - sentTs;
if (maxRingMsgTime < duration) {
maxRingMsgTime = duration;
maxRingTimeMsgCls = msg.getClass().getSimpleName();
}
if (ringMsgsSent != 0)
avgRingMsgTime = (avgRingMsgTime * (ringMsgsSent - 1) + duration) / ringMsgsSent;
}
}
/**
* Gets max time for ring message to make full pass.
*
* @return Max full pass time.
*/
public synchronized long maxRingMessageTime() {
return maxRingMsgTime;
}
/**
* Gets class name of the message that took max time to make full pass.
*
* @return Message class name.
*/
public synchronized String maxRingDurationMessageClass() {
return maxRingTimeMsgCls;
}
/**
* Gets class name of the message took max time to process.
*
* @return Message class name.
*/
public synchronized String maxProcessingTimeMessageClass() {
return maxProcTimeMsgCls;
}
/**
* @param initTime Time socket was initialized in.
*/
public synchronized void onServerSocketInitialized(long initTime) {
assert initTime >= 0;
if (maxSrvSockInitTime < initTime)
maxSrvSockInitTime = initTime;
avgSrvSockInitTime = (avgSrvSockInitTime * (sockReadersCreated - 1) + initTime) / sockReadersCreated;
}
/**
* @param initTime Time socket was initialized in.
*/
public synchronized void onClientSocketInitialized(long initTime) {
assert initTime >= 0;
clientSockCreatedCnt++;
if (maxClientSockInitTime < initTime)
maxClientSockInitTime = initTime;
avgClientSockInitTime = (avgClientSockInitTime * (clientSockCreatedCnt - 1) + initTime) / clientSockCreatedCnt;
}
/**
* Increments pending messages registered count.
*/
public synchronized void onPendingMessageRegistered() {
pendingMsgsRegistered++;
}
/**
* Increments pending messages discarded count.
*/
public synchronized void onPendingMessageDiscarded() {
pendingMsgsDiscarded++;
}
/**
* Increments socket readers created count.
*/
public synchronized void onSocketReaderCreated() {
sockReadersCreated++;
}
/**
* Increments socket readers removed count.
*/
public synchronized void onSocketReaderRemoved() {
sockReadersRmv++;
}
/**
* Gets processed messages counts (grouped by type).
*
* @return Map containing message types and respective counts.
*/
public synchronized Map<String, Integer> processedMessages() {
return new HashMap<>(procMsgs);
}
/**
* Gets received messages counts (grouped by type).
*
* @return Map containing message types and respective counts.
*/
public synchronized Map<String, Integer> receivedMessages() {
return new HashMap<>(rcvdMsgs);
}
/**
* @return Sent messages counts (grouped by type).
*/
public synchronized Map<String, Integer> sentMessages() {
return new HashMap<>(sentMsgs);
}
/**
* Gets max messages send time (grouped by type).
*
* @return Map containing messages types and max send times.
*/
public synchronized Map<String, Long> maxMessagesSendTimes() {
return new HashMap<>(maxMsgsSndTimes);
}
/**
* Gets average messages send time (grouped by type).
*
* @return Map containing messages types and average send times.
*/
public synchronized Map<String, Long> avgMessagesSendTimes() {
return new HashMap<>(avgMsgsSndTimes);
}
/**
* Gets total received messages count.
*
* @return Total received messages count.
*/
public synchronized int totalReceivedMessages() {
return F.sumInt(receivedMessages().values());
}
/**
* Gets total processed messages count.
*
* @return Total processed messages count.
*/
public synchronized int totalProcessedMessages() {
return F.sumInt(processedMessages().values());
}
/**
* Gets max message processing time.
*
* @return Max message processing time.
*/
public synchronized long maxMessageProcessingTime(){
return maxMsgProcTime;
}
/**
* Gets average message processing time.
*
* @return Average message processing time.
*/
public synchronized long avgMessageProcessingTime() {
return avgMsgProcTime;
}
/**
* Gets pending messages registered count.
*
* @return Pending messages registered count.
*/
public synchronized long pendingMessagesRegistered() {
return pendingMsgsRegistered;
}
/**
* Gets pending messages discarded count.
*
* @return Pending messages registered count.
*/
public synchronized long pendingMessagesDiscarded() {
return pendingMsgsDiscarded;
}
/**
* Gets nodes joined count.
*
* @return Nodes joined count.
*/
public synchronized int joinedNodesCount() {
return joinedNodesCnt;
}
/**
* Gets nodes left count.
*
* @return Nodes left count.
*/
public synchronized int leftNodesCount() {
return leftNodesCnt;
}
/**
* Gets failed nodes count.
*
* @return Failed nodes count.
*/
public synchronized int failedNodesCount() {
return failedNodesCnt;
}
/**
* @return Ack timeouts count.
*/
public synchronized int ackTimeoutsCount() {
return ackTimeoutsCnt;
}
/**
* @return Socket timeouts count.
*/
public synchronized int socketTimeoutsCount() {
return sockTimeoutsCnt;
}
/**
* Gets socket readers created count.
*
* @return Socket readers created count.
*/
public synchronized int socketReadersCreated() {
return sockReadersCreated;
}
/**
* Gets socket readers removed count.
*
* @return Socket readers removed count.
*/
public synchronized int socketReadersRemoved() {
return sockReadersRmv;
}
/**
* Gets time local node has been coordinator since.
*
* @return Coordinator since timestamp.
*/
public long coordinatorSinceTimestamp() {
return crdSinceTs.get();
}
/**
* Clears statistics.
*/
public synchronized void clear() {
ackTimeoutsCnt = 0;
avgClientSockInitTime = 0;
avgMsgProcTime = 0;
avgMsgQueueTime = 0;
avgMsgsAckTimes.clear();
avgMsgsSndTimes.clear();
avgRingMsgTime = 0;
avgSrvSockInitTime = 0;
clientSockCreatedCnt = 0;
crdSinceTs.set(0);
failedNodesCnt = 0;
joinedNodesCnt = 0;
joinFinishedTs = 0;
joinStartedTs = 0;
leftNodesCnt = 0;
maxClientSockInitTime = 0;
maxMsgProcTime = 0;
maxMsgQueueTime = 0;
maxMsgsAckTimes.clear();
maxMsgsSndTimes.clear();
maxProcTimeMsgCls = null;
maxRingMsgTime = 0;
maxRingTimeMsgCls = null;
maxSrvSockInitTime = 0;
pendingMsgsDiscarded = 0;
pendingMsgsRegistered = 0;
procMsgs.clear();
rcvdMsgs.clear();
ringMsgsSent = 0;
sentMsgs.clear();
sockReadersCreated = 0;
sockReadersRmv = 0;
sockTimeoutsCnt = 0;
}
/** {@inheritDoc} */
@Override public synchronized String toString() {
return S.toString(TcpDiscoveryStatistics.class, this);
}
}