/*
* 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.internal.util.nio;
import java.io.IOException;
import java.util.ArrayDeque;
import java.util.Deque;
import org.apache.ignite.IgniteException;
import org.apache.ignite.IgniteLogger;
import org.apache.ignite.cluster.ClusterNode;
import org.apache.ignite.internal.util.typedef.internal.S;
import org.apache.ignite.lang.IgniteBiTuple;
import org.apache.ignite.lang.IgniteInClosure;
import org.jetbrains.annotations.Nullable;
/**
* Recovery information for single node.
*/
@Deprecated // To be splitted into separate classes for in/out data when do not need maintain backward compatibility.
public class GridNioRecoveryDescriptor {
/** Number of acknowledged messages. */
private long acked;
/** Unacknowledged messages. */
private final ArrayDeque<SessionWriteRequest> msgReqs;
/** Number of messages to resend. */
private int resendCnt;
/** Number of received messages. */
private long rcvCnt;
/** Number of sent messages. */
private long sentCnt;
/** Reserved flag. */
private boolean reserved;
/** Last acknowledged message. */
private long lastAck;
/** Node left flag. */
private boolean nodeLeft;
/** Target node. */
private final ClusterNode node;
/** Logger. */
private final IgniteLogger log;
/** Incoming connection request from remote node. */
private IgniteBiTuple<Long, IgniteInClosure<Boolean>> handshakeReq;
/** Connected flag. */
private boolean connected;
/** Number of outgoing connect attempts. */
private long connectCnt;
/** Maximum size of unacknowledged messages queue. */
private final int queueLimit;
/** Number of descriptor reservations (for info purposes). */
private int reserveCnt;
/** */
private final boolean pairedConnections;
/**
* @param pairedConnections {@code True} if in/out connections pair is used for communication with node.
* @param queueLimit Maximum size of unacknowledged messages queue.
* @param node Node.
* @param log Logger.
*/
public GridNioRecoveryDescriptor(
boolean pairedConnections,
int queueLimit,
ClusterNode node,
IgniteLogger log
) {
assert !node.isLocal() : node;
assert queueLimit > 0;
msgReqs = new ArrayDeque<>(queueLimit);
this.pairedConnections = pairedConnections;
this.queueLimit = queueLimit;
this.node = node;
this.log = log;
}
/**
* @return {@code True} if in/out connections pair is used for communication with node.
*/
public boolean pairedConnections() {
return pairedConnections;
}
/**
* @return Connect count.
*/
public long incrementConnectCount() {
return connectCnt++;
}
/**
* @return Node.
*/
public ClusterNode node() {
return node;
}
/**
* Increments received messages counter.
*
* @return Number of received messages.
*/
public long onReceived() {
rcvCnt++;
return rcvCnt;
}
/**
* @return Number of received messages.
*/
public long received() {
return rcvCnt;
}
/**
* @return Number of sent messages.
*/
public long sent() {
return sentCnt;
}
/**
* @param lastAck Last acknowledged message.
*/
public void lastAcknowledged(long lastAck) {
this.lastAck = lastAck;
}
/**
* @return Last acknowledged message.
*/
public long lastAcknowledged() {
return lastAck;
}
/**
* @return Maximum size of unacknowledged messages queue.
*/
public int queueLimit() {
return queueLimit;
}
/**
* @param req Write request.
* @return {@code False} if queue limit is exceeded.
*/
public boolean add(SessionWriteRequest req) {
assert req != null;
if (!req.skipRecovery()) {
if (resendCnt == 0) {
msgReqs.addLast(req);
sentCnt++;
return msgReqs.size() < queueLimit;
}
else
resendCnt--;
}
return true;
}
/**
* @param rcvCnt Number of messages received by remote node.
*/
public void ackReceived(long rcvCnt) {
if (log.isDebugEnabled())
log.debug("Handle acknowledgment [acked=" + acked + ", rcvCnt=" + rcvCnt +
", msgReqs=" + msgReqs.size() + ']');
while (acked < rcvCnt) {
SessionWriteRequest req = msgReqs.pollFirst();
assert req != null : "Missed message [rcvCnt=" + rcvCnt +
", acked=" + acked +
", desc=" + this + ']';
if (req.ackClosure() != null)
req.ackClosure().apply(null);
req.onAckReceived();
acked++;
}
}
/**
* @return Last acked message by remote node.
*/
public long acked() {
return acked;
}
/**
* Node left callback.
*
* @return {@code False} if descriptor is reserved.
*/
public boolean onNodeLeft() {
SessionWriteRequest[] reqs = null;
synchronized (this) {
nodeLeft = true;
if (reserved)
return false;
if (!msgReqs.isEmpty()) {
reqs = msgReqs.toArray(new SessionWriteRequest[msgReqs.size()]);
msgReqs.clear();
}
}
if (reqs != null)
notifyOnNodeLeft(reqs);
return true;
}
/**
* @return Requests for unacknowledged messages.
*/
public Deque<SessionWriteRequest> messagesRequests() {
return msgReqs;
}
/**
* @param node Node.
* @return {@code True} if node is not null and has the same order as initial remtoe node.
*/
public boolean nodeAlive(@Nullable ClusterNode node) {
return node != null && node.order() == this.node.order();
}
/**
* @throws InterruptedException If interrupted.
* @return {@code True} if reserved.
*/
public boolean reserve() throws InterruptedException {
synchronized (this) {
while (!connected && reserved)
wait();
if (!connected) {
reserved = true;
reserveCnt++;
}
return !connected;
}
}
/**
* @param rcvCnt Number of messages received by remote node.
*/
public void onHandshake(long rcvCnt) {
synchronized (this) {
if (!nodeLeft)
ackReceived(rcvCnt);
resendCnt = msgReqs.size();
}
}
/**
*
*/
public void onConnected() {
synchronized (this) {
assert reserved : this;
assert !connected : this;
connected = true;
if (handshakeReq != null) {
IgniteInClosure<Boolean> c = handshakeReq.get2();
assert c != null;
c.apply(false);
handshakeReq = null;
}
notifyAll();
}
}
/**
* @return Connected flag.
*/
public boolean connected() {
synchronized (this) {
return connected;
}
}
/**
* @return Reserved flag.
*/
public boolean reserved() {
synchronized (this) {
return reserved;
}
}
/**
* @return Current handshake index.
*/
public Long handshakeIndex() {
synchronized (this) {
return handshakeReq != null ? handshakeReq.get1() : null;
}
}
/**
*
*/
public void release() {
SessionWriteRequest[] futs = null;
synchronized (this) {
connected = false;
if (handshakeReq != null) {
IgniteInClosure<Boolean> c = handshakeReq.get2();
assert c != null;
handshakeReq = null;
c.apply(true);
}
else {
reserved = false;
notifyAll();
}
if (nodeLeft && !msgReqs.isEmpty()) {
futs = msgReqs.toArray(new SessionWriteRequest[msgReqs.size()]);
msgReqs.clear();
}
}
if (futs != null)
notifyOnNodeLeft(futs);
}
/**
* @param id Handshake ID.
* @param c Closure to run on reserve.
* @return {@code True} if reserved.
*/
public boolean tryReserve(long id, IgniteInClosure<Boolean> c) {
synchronized (this) {
if (connected) {
c.apply(false);
return false;
}
if (reserved) {
if (handshakeReq != null) {
assert handshakeReq.get1() != null;
long id0 = handshakeReq.get1();
assert id0 != id : id0;
if (id > id0) {
IgniteInClosure<Boolean> c0 = handshakeReq.get2();
assert c0 != null;
c0.apply(false);
handshakeReq = new IgniteBiTuple<>(id, c);
}
else
c.apply(false);
}
else
handshakeReq = new IgniteBiTuple<>(id, c);
return false;
}
else {
reserved = true;
reserveCnt++;
return true;
}
}
}
/**
* @return Number of descriptor reservations.
*/
public int reserveCount() {
synchronized (this) {
return reserveCnt;
}
}
/**
* @param reqs Requests to notify about error.
*/
private void notifyOnNodeLeft(SessionWriteRequest[] reqs) {
IOException e = new IOException("Failed to send message, node has left: " + node.id());
for (SessionWriteRequest req : reqs) {
req.onError(e);
if (req.ackClosure() != null)
req.ackClosure().apply(new IgniteException(e));
}
}
/** {@inheritDoc} */
@Override public String toString() {
return S.toString(GridNioRecoveryDescriptor.class, this);
}
}