From d5a72e85c4c651dccf3f3183672a9d003f94bbf3 Mon Sep 17 00:00:00 2001 From: Mikhail Petrov Date: Fri, 21 Aug 2026 17:55:33 +0300 Subject: [PATCH 1/8] IGNITE-29004 Refactored ClientImpl to use single instance of TcpDiscoveryIoSession per connection --- .../ignite/spi/discovery/tcp/ClientImpl.java | 223 +++++++----------- .../discovery/tcp/TcpDiscoveryIoSession.java | 35 ++- 2 files changed, 104 insertions(+), 154 deletions(-) diff --git a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/ClientImpl.java b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/ClientImpl.java index 53b1dbfc93556..c77503aaed087 100644 --- a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/ClientImpl.java +++ b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/ClientImpl.java @@ -535,7 +535,7 @@ else if (state == DISCONNECTED) { * @throws IgniteSpiException If failed. * @see TcpDiscoverySpi#joinTimeout */ - @Nullable private SocketStream joinTopology( + @Nullable private TcpDiscoveryIoSession joinTopology( InetSocketAddress prevAddr, long timeout, @Nullable Runnable beforeEachSleep, @@ -581,12 +581,12 @@ else if (state == DISCONNECTED) { Collection addrs0 = new ArrayList<>(addrs); - T2> waitAndRes = sendJoinRequests(prevAddr != null, addrs); + T2> waitAndRes = sendJoinRequests(prevAddr != null, addrs); addrs.clear(); boolean wait = waitAndRes.get1(); - T2 res = waitAndRes.get2(); + T2 res = waitAndRes.get2(); if (res != null) return res.get1(); @@ -611,7 +611,7 @@ else if (addrs.isEmpty()) { } /** */ - private T2> sendJoinRequests( + private T2> sendJoinRequests( boolean recon, Collection addrs ) throws InterruptedException { @@ -619,21 +619,21 @@ private T2> sendJoinRequests( if (Thread.currentThread().isInterrupted()) throw new InterruptedException(); - T2 sockAndRes = sendJoinRequest(recon, addr); + T2 joinRes = sendJoinRequest(recon, addr); - if (sockAndRes == null) + if (joinRes == null) continue; - assert sockAndRes.get1() != null && sockAndRes.get2() != null : sockAndRes; + assert joinRes.get1() != null && joinRes.get2() != null : joinRes; - Socket sock = sockAndRes.get1().socket(); + Socket sock = joinRes.get1().socket(); if (log.isDebugEnabled()) - log.debug("Received response to join request [addr=" + addr + ", res=" + sockAndRes.get2() + ']'); + log.debug("Received response to join request [addr=" + addr + ", res=" + joinRes.get2() + ']'); - switch (sockAndRes.get2()) { + switch (joinRes.get2()) { case RES_OK: - return new T2<>(false, sockAndRes); + return new T2<>(false, joinRes); case RES_CONTINUE_JOIN: case RES_WAIT: @@ -643,7 +643,7 @@ private T2> sendJoinRequests( default: if (log.isDebugEnabled()) - log.debug("Received unexpected response to join request: " + sockAndRes.get2()); + log.debug("Received unexpected response to join request: " + joinRes.get2()); U.closeQuiet(sock); } @@ -671,7 +671,7 @@ private static void sleepEx(long millis, Runnable before, Runnable after) throws * @param addr Address. * @return Socket, connect response and client acknowledge support flag. */ - @Nullable private T2 sendJoinRequest(boolean recon, + @Nullable private T2 sendJoinRequest(boolean recon, InetSocketAddress addr) throws InterruptedException { assert addr != null; @@ -731,7 +731,7 @@ private static void sleepEx(long millis, Runnable before, Runnable after) throws if (log.isInfoEnabled()) log.info("Reconnecting to the addresses of a proper DC [addrs=" + redirectAddrs + ']'); - T2> redirectedRes = sendJoinRequests(recon, redirectAddrs); + T2> redirectedRes = sendJoinRequests(recon, redirectAddrs); return redirectedRes.get2(); } @@ -783,7 +783,7 @@ private static void sleepEx(long millis, Runnable before, Runnable after) throws log.debug("Message has been sent to address [msg=" + msg + ", addr=" + addr + ", rmtNodeId=" + rmtNodeId + ']'); - return new T2<>(new SocketStream(sock, ses), spi.readReceipt(sock, timeoutHelper.nextTimeoutChunk(ackTimeout0))); + return new T2<>(ses, spi.readReceipt(sock, timeoutHelper.nextTimeoutChunk(ackTimeout0))); } catch (IOException | IgniteCheckedException e) { U.closeQuiet(sock); @@ -968,10 +968,10 @@ private NavigableSet allVisibleNodes() { /** {@inheritDoc} */ @Override public void brakeConnection() { - SocketStream sockStream = msgWorker.currSock; + TcpDiscoveryIoSession ses = msgWorker.currSes; - if (sockStream != null) - U.closeQuiet(sockStream.socket()); + if (ses != null) + U.closeQuiet(ses.socket()); } /** {@inheritDoc} */ @@ -1093,7 +1093,7 @@ private class SocketReader extends IgniteSpiThread { private final Object mux = new Object(); /** */ - private SocketStream sockStream; + private TcpDiscoveryIoSession ses; /** */ private UUID rmtNodeId; @@ -1108,12 +1108,12 @@ private class SocketReader extends IgniteSpiThread { } /** - * @param sockStream Socket. + * @param ses IO session. * @param rmtNodeId Rmt node id. */ - void setSocket(SocketStream sockStream, UUID rmtNodeId) { + void onRemoteSessionReady(TcpDiscoveryIoSession ses, UUID rmtNodeId) { synchronized (mux) { - this.sockStream = sockStream; + this.ses = ses; this.rmtNodeId = rmtNodeId; @@ -1128,16 +1128,14 @@ private void forceStopRead() throws InterruptedException { CountDownLatch stopReadLatch; synchronized (mux) { - SocketStream stream = sockStream; - - if (stream == null) + if (ses == null) return; this.stopReadLatch = stopReadLatch = new CountDownLatch(1); - U.closeQuiet(stream.socket()); + U.closeQuiet(ses.socket()); - this.sockStream = null; + this.ses = null; this.rmtNodeId = null; mux.notifyAll(); @@ -1149,7 +1147,7 @@ private void forceStopRead() throws InterruptedException { /** {@inheritDoc} */ @Override protected void body() throws InterruptedException { while (!isInterrupted()) { - SocketStream sockStream; + TcpDiscoveryIoSession ses; UUID rmtNodeId; // Disconnected from router node. @@ -1162,25 +1160,23 @@ private void forceStopRead() throws InterruptedException { stopReadLatch = null; } - if (this.sockStream == null) { + if (this.ses == null) { mux.wait(); continue; } - sockStream = this.sockStream; + ses = this.ses; rmtNodeId = this.rmtNodeId; } - Socket sock = sockStream.socket(); + Socket sock = ses.socket(); U.enhanceThreadName(U.id8(rmtNodeId) - + ' ' + sockStream.sock.getInetAddress().getHostAddress() - + ":" + sockStream.sock.getPort()); + + ' ' + sock.getInetAddress().getHostAddress() + + ":" + sock.getPort()); try { - TcpDiscoveryIoSession ses = sockStream.session(); - assert sock.getKeepAlive() && sock.getTcpNoDelay() : "Socket wasn't configured properly:" + " KeepAlive " + sock.getKeepAlive() + " TcpNoDelay " + sock.getTcpNoDelay(); @@ -1237,7 +1233,7 @@ private void forceStopRead() throws InterruptedException { } } catch (IOException e) { - msgWorker.addMessage(new SocketClosedMessage(sockStream)); + msgWorker.addMessage(new SessionClosedMessage(ses)); if (log.isDebugEnabled()) U.error(log, "Connection failed [sock=" + sock + ", locNodeId=" + getLocalNodeId() + ']', e); @@ -1246,8 +1242,8 @@ private void forceStopRead() throws InterruptedException { U.closeQuiet(sock); synchronized (mux) { - if (this.sockStream == sockStream) { - this.sockStream = null; + if (this.ses == ses) { + this.ses = null; this.rmtNodeId = null; } } @@ -1263,9 +1259,6 @@ private class SocketWriter extends IgniteSpiThread { /** */ private final Object mux = new Object(); - /** */ - private Socket sock; - /** */ private TcpDiscoveryIoSession ses; @@ -1314,7 +1307,7 @@ private void forceLeave() throws InterruptedException { synchronized (mux) { // If writer was stopped. - if (sock == null) + if (ses == null) return; this.forceLeaveLatch = forceLeaveLatch = new CountDownLatch(1); @@ -1327,14 +1320,10 @@ private void forceLeave() throws InterruptedException { forceLeaveLatch.await(); } - /** - * @param sock Socket. - */ - private void setSocket(Socket sock) { + /** */ + private void onRemoteSessionReady(TcpDiscoveryIoSession ses) { synchronized (mux) { - this.sock = sock; - - ses = createSession(sock); + this.ses = ses; unackedMsg = null; @@ -1347,7 +1336,7 @@ private void setSocket(Socket sock) { */ public boolean isOnline() { synchronized (mux) { - return sock != null; + return ses != null; } } @@ -1371,12 +1360,12 @@ void ackReceived(TcpDiscoveryClientAckResponse res) { TcpDiscoveryAbstractMessage msg = null; while (!Thread.currentThread().isInterrupted()) { - Socket sock; + TcpDiscoveryIoSession ses; synchronized (mux) { - sock = this.sock; + ses = this.ses; - if (sock == null) { + if (ses == null) { mux.wait(); continue; @@ -1397,9 +1386,9 @@ void ackReceived(TcpDiscoveryClientAckResponse res) { } } - U.closeQuiet(sock); + U.closeQuiet(ses.socket()); - this.sock = null; + this.ses = null; clear(); @@ -1490,11 +1479,11 @@ void ackReceived(TcpDiscoveryClientAckResponse res) { else U.error(log, "Failed to send message: " + msg, e); - U.closeQuiet(sock); + U.closeQuiet(ses.socket()); synchronized (mux) { - if (sock == this.sock) - this.sock = null; // Connection has dead. + if (ses == this.ses) + this.ses = null; // Connection has dead. clear(); } @@ -1526,7 +1515,7 @@ private void clear() { */ private class Reconnector extends IgniteSpiThread { /** */ - private volatile SocketStream sockStream; + private volatile TcpDiscoveryIoSession ses; /** */ private final boolean join; @@ -1551,10 +1540,10 @@ protected Reconnector(boolean join, InetSocketAddress prevAddr) { public void cancel() { interrupt(); - SocketStream sockStream = this.sockStream; + TcpDiscoveryIoSession ses = this.ses; - if (sockStream != null) - U.closeQuiet(sockStream.socket()); + if (ses != null) + U.closeQuiet(ses.socket()); } /** {@inheritDoc} */ @@ -1572,9 +1561,9 @@ public void cancel() { try { while (true) { - SocketStream joinRes = joinTopology(prevAddr, timeout, null, null); + TcpDiscoveryIoSession ses = joinTopology(prevAddr, timeout, null, null); - if (joinRes == null) { + if (ses == null) { if (join) { joinError(new IgniteSpiException("Join process timed out, connection failed and " + "failed to reconnect (consider increasing 'joinTimeout' configuration property) " + @@ -1587,10 +1576,9 @@ public void cancel() { return; } - sockStream = joinRes; + this.ses = ses; - Socket sock = sockStream.socket(); - TcpDiscoveryIoSession ses = sockStream.session(); + Socket sock = ses.socket(); if (isInterrupted()) throw new InterruptedException(); @@ -1677,10 +1665,10 @@ else if (spi.ensured(msg)) { } finally { if (!success) { - SocketStream sockStream = this.sockStream; + TcpDiscoveryIoSession ses = this.ses; - if (sockStream != null) - U.closeQuiet(sockStream.socket()); + if (ses != null) + U.closeQuiet(ses.socket()); if (join) joinError(new IgniteSpiException("Failed to connect to cluster, connection failed and failed " + @@ -1700,7 +1688,7 @@ protected class MessageWorker extends GridWorker { private final BlockingDeque queue = new LinkedBlockingDeque<>(); /** */ - private SocketStream currSock; + private TcpDiscoveryIoSession currSes; /** */ private Reconnector reconnector; @@ -1751,7 +1739,7 @@ private MessageWorker(IgniteLogger log) { if (state == STARTING) { joinError(new IgniteSpiException("Join process timed out, did not receive response for " + "join request (consider increasing 'joinTimeout' configuration property) " + - "[joinTimeout=" + spi.joinTimeout + ", sock=" + currSock + ']')); + "[joinTimeout=" + spi.joinTimeout + ", ses=" + currSes + ']')); break; } @@ -1774,7 +1762,7 @@ else if (msg == SPI_STOP) { assert spi.getSpiContext().isStopping(); - if (connected && currSock != null) { + if (connected && currSes != null) { TcpDiscoveryNodeLeftMessage leftMsg = new TcpDiscoveryNodeLeftMessage(getLocalNodeId()); leftMsg.client(true); @@ -1796,7 +1784,7 @@ else if (msg == SPI_RECONNECT) { sockWriter.forceLeave(); sockReader.forceStopRead(); - currSock = null; + currSes = null; queue.clear(); @@ -1824,13 +1812,13 @@ else if (msg instanceof TcpDiscoveryNodeFailedMessage && forceFailMsg = msg0; } - else if (msg instanceof SocketClosedMessage) { - if (((SocketClosedMessage)msg).sock == currSock) { - Socket sock = currSock.sock; + else if (msg instanceof SessionClosedMessage sesClosedMsg) { + if (sesClosedMsg.ses == currSes) { + Socket sock = currSes.socket(); InetSocketAddress prevAddr = new InetSocketAddress(sock.getInetAddress(), sock.getPort()); - currSock = null; + currSes = null; boolean join = joinLatch.getCount() > 0; @@ -1990,10 +1978,10 @@ else if (discoMsg instanceof TcpDiscoveryCheckFailedMessage) ((IgniteEx)spi.ignite()).context().failure().process(new FailureContext(CRITICAL_ERROR, t)); } finally { - SocketStream currSock = this.currSock; + TcpDiscoveryIoSession ses = this.currSes; - if (currSock != null) - U.closeQuiet(currSock.socket()); + if (ses != null) + U.closeQuiet(ses.socket()); if (joinLatch.getCount() > 0) joinError(new IgniteSpiException("Some error in join process.")); // This should not occur. @@ -2072,10 +2060,10 @@ private void tryJoin() throws InterruptedException { joinCnt++; - SocketStream joinRes; + TcpDiscoveryIoSession ses; try { - joinRes = joinTopology(null, spi.joinTimeout, + ses = joinTopology(null, spi.joinTimeout, new Runnable() { @Override public void run() { blockingSectionBegin(); @@ -2093,7 +2081,7 @@ private void tryJoin() throws InterruptedException { return; } - if (joinRes == null) { + if (ses == null) { if (join) joinError(new IgniteSpiException("Join process timed out (timeout = " + spi.joinTimeout + ").")); else { @@ -2105,9 +2093,9 @@ private void tryJoin() throws InterruptedException { return; } - currSock = joinRes; + currSes = ses; - sockWriter.setSocket(joinRes.socket()); + sockWriter.onRemoteSessionReady(ses); if (spi.joinTimeout > 0) { final int joinCnt0 = joinCnt; @@ -2117,7 +2105,7 @@ private void tryJoin() throws InterruptedException { }, spi.joinTimeout, MILLISECONDS); } - sockReader.setSocket(joinRes, locNode.clientRouterNodeId()); + sockReader.onRemoteSessionReady(ses, locNode.clientRouterNodeId()); } /** */ @@ -2519,11 +2507,12 @@ private void processClientReconnectMessage(TcpDiscoveryClientReconnectMessage ms if (getLocalNodeId().equals(msg.creatorNodeId())) { if (reconnector != null) { assert msg.success() : msg; + assert reconnector.ses != null : msg; - currSock = reconnector.sockStream; + currSes = reconnector.ses; - sockWriter.setSocket(currSock.socket()); - sockReader.setSocket(currSock, locNode.clientRouterNodeId()); + sockWriter.onRemoteSessionReady(currSes); + sockReader.onRemoteSessionReady(currSes, locNode.clientRouterNodeId()); reconnector = null; @@ -2707,62 +2696,14 @@ private JoinTimeout(int joinCnt) { /** * */ - private static class SocketClosedMessage { + private static class SessionClosedMessage { /** */ - private final SocketStream sock; - - /** - * @param sock Socket. - */ - private SocketClosedMessage(SocketStream sock) { - this.sock = sock; - } - } - - /** - * - */ - private static class SocketStream { - /** */ - private final Socket sock; - - /** - * The only session ever used to read messages from the socket. Shared by all socket users - * ({@link Reconnector}, {@link SocketReader}), otherwise messages buffered by one session's - * read-ahead would be lost when another session takes the socket over. - */ private final TcpDiscoveryIoSession ses; - /** - * @param sock Socket. - * @param ses Session bound to the socket. - */ - SocketStream(Socket sock, TcpDiscoveryIoSession ses) { - assert sock != null; - assert ses != null; - - this.sock = sock; + /** */ + private SessionClosedMessage(TcpDiscoveryIoSession ses) { this.ses = ses; } - - /** - * @return Socket. - */ - Socket socket() { - return sock; - } - - /** - * @return Session bound to the socket. - */ - TcpDiscoveryIoSession session() { - return ses; - } - - /** {@inheritDoc} */ - @Override public String toString() { - return sock.toString(); - } } /** diff --git a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoveryIoSession.java b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoveryIoSession.java index 3749f3b4234fe..8c34284e87fb7 100644 --- a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoveryIoSession.java +++ b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoveryIoSession.java @@ -85,8 +85,11 @@ public class TcpDiscoveryIoSession { /** Buffered socket input stream. */ private final CompositeInputStream in; - /** Intermediate buffer for serializing discovery messages. */ - private final ByteBuffer msgBuf; + /** */ + private final ByteBuffer readBuf; + + /** */ + private final ByteBuffer writeBuf; /** * Creates a new discovery I/O session bound to the given socket. @@ -99,7 +102,8 @@ public class TcpDiscoveryIoSession { this.sock = sock; this.spi = spi; - msgBuf = ByteBuffer.allocate(MSG_BUFFER_SIZE); + readBuf = ByteBuffer.allocate(MSG_BUFFER_SIZE); + writeBuf = ByteBuffer.allocate(MSG_BUFFER_SIZE); msgWriter = new DirectMessageWriter(spi.messageFactory()); msgReader = new DirectMessageReader(spi.messageFactory(), null); @@ -168,19 +172,19 @@ T readMessage() throws IgniteCheckedException, IOException { } msgReader.reset(); - msgReader.setBuffer(msgBuf); + msgReader.setBuffer(readBuf); boolean finished; do { - msgBuf.clear(); + readBuf.clear(); - int read = in.read(msgBuf.array(), msgBuf.position(), msgBuf.remaining()); + int read = in.read(readBuf.array(), readBuf.position(), readBuf.remaining()); if (read == -1) throw new EOFException("Connection closed before message was fully read."); - msgBuf.limit(read); + readBuf.limit(read); finished = MessageSerialization.readFrom(spi.messageFactory(), msg, msgReader); @@ -188,10 +192,10 @@ T readMessage() throws IgniteCheckedException, IOException { // This behaviour guarantees that we never read a next message from the buffer right after the end of // the previous message. But it is not guaranteed with Client Discovery where messages aren't acknowledged. // Thus, we have to keep the uprocessed bytes read from the socket. It won't return them again. - if (msgBuf.hasRemaining()) { - byte[] unprocessedReadTail = new byte[msgBuf.remaining()]; + if (readBuf.hasRemaining()) { + byte[] unprocessedReadTail = new byte[readBuf.remaining()]; - msgBuf.get(unprocessedReadTail, 0, msgBuf.remaining()); + readBuf.get(unprocessedReadTail, 0, readBuf.remaining()); in.attachByteArray(unprocessedReadTail); } @@ -249,17 +253,17 @@ void serializeMessage(Message m, OutputStream out) throws IOException, IgniteChe DiscoveryMarshalling.marshal(m, kctx, null); msgWriter.reset(); - msgWriter.setBuffer(msgBuf); + msgWriter.setBuffer(writeBuf); boolean finished; do { // Should be cleared before first operation. - msgBuf.clear(); + writeBuf.clear(); finished = MessageSerialization.writeTo(spi.messageFactory(), m, msgWriter); - out.write(msgBuf.array(), 0, msgBuf.position()); + out.write(writeBuf.array(), 0, writeBuf.position()); } while (!finished); } @@ -284,6 +288,11 @@ private void detectSslAlert(byte b0, byte b1, InputStream in) throws IOException throw new StreamCorruptedException("invalid stream header: " + hex); } + /** {@inheritDoc} */ + @Override public String toString() { + return "TcpDiscoveryIoSession [sock=" + sock + ']'; + } + /** * Input stream implementation that combines a byte array and a regular InputStream allowing to read bytes * from the array first and then proceed with reading from InputStream. From f6752b0727dadafc8869d3bf20104c2d7c26f7d0 Mon Sep 17 00:00:00 2001 From: Mikhail Petrov Date: Sun, 23 Aug 2026 23:01:25 +0300 Subject: [PATCH 2/8] IGNITE-29005 Replaced raw socket usage with session for Discovery network operations --- .../ignite/spi/discovery/tcp/ClientImpl.java | 95 ++++---- .../ignite/spi/discovery/tcp/ServerImpl.java | 197 ++++++---------- .../discovery/tcp/TcpDiscoveryIoSession.java | 142 +++++++++++- .../spi/discovery/tcp/TcpDiscoverySpi.java | 210 +++++++++--------- .../internal/IgniteClientRejoinTest.java | 22 +- .../IgniteDiscoveryMassiveNodeFailTest.java | 16 +- .../IgniteCacheTopologySplitAbstractTest.java | 20 +- .../rest/RestProcessorHangTest.java | 8 +- ...niteTcpCommunicationConnectOnInitTest.java | 28 +-- .../discovery/tcp/BlockTcpDiscoverySpi.java | 7 +- .../tcp/IgniteMetricsOverflowTest.java | 7 +- .../tcp/MultiDataCenterSplitTest.java | 6 +- .../tcp/ReceivedMessagesTracker.java | 9 +- ...entDiscoverySpiFailureTimeoutSelfTest.java | 17 +- .../tcp/TcpClientDiscoverySpiSelfTest.java | 27 ++- .../TcpClientDiscoveryUnresolvedHostTest.java | 23 +- .../TcpDiscoveryCoordinatorFailureTest.java | 18 +- .../tcp/TcpDiscoveryFailedJoinTest.java | 33 +-- .../tcp/TcpDiscoveryNetworkIssuesTest.java | 27 +-- ...cpDiscoveryPendingMessageDeliveryTest.java | 13 +- ...TcpDiscoverySpiFailureTimeoutSelfTest.java | 6 +- .../TcpDiscoverySpiReconnectDelayTest.java | 8 +- 22 files changed, 465 insertions(+), 474 deletions(-) diff --git a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/ClientImpl.java b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/ClientImpl.java index c77503aaed087..7416ab6a67b01 100644 --- a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/ClientImpl.java +++ b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/ClientImpl.java @@ -21,7 +21,6 @@ import java.io.InterruptedIOException; import java.io.StreamCorruptedException; import java.net.InetSocketAddress; -import java.net.Socket; import java.net.SocketTimeoutException; import java.util.ArrayDeque; import java.util.ArrayList; @@ -626,7 +625,7 @@ private T2> sendJoinRequests( assert joinRes.get1() != null && joinRes.get2() != null : joinRes; - Socket sock = joinRes.get1().socket(); + TcpDiscoveryIoSession ses = joinRes.get1(); if (log.isDebugEnabled()) log.debug("Received response to join request [addr=" + addr + ", res=" + joinRes.get2() + ']'); @@ -637,7 +636,7 @@ private T2> sendJoinRequests( case RES_CONTINUE_JOIN: case RES_WAIT: - U.closeQuiet(sock); + U.closeQuiet(ses); return new T2<>(true, null); @@ -645,7 +644,7 @@ private T2> sendJoinRequests( if (log.isDebugEnabled()) log.debug("Received unexpected response to join request: " + joinRes.get2()); - U.closeQuiet(sock); + U.closeQuiet(ses); } } @@ -696,18 +695,16 @@ private static void sleepEx(long millis, Runnable before, Runnable after) throws DiscoveryDataPacket discoveryData = null; while (true) { - boolean openSock = false; + boolean openSes = false; - Socket sock = null; + TcpDiscoveryIoSession ses = null; try { long tsNanos = System.nanoTime(); - sock = spi.openSocket(addr, timeoutHelper); + ses = spi.openSession(addr, timeoutHelper); - openSock = true; - - TcpDiscoveryIoSession ses = createSession(sock); + openSes = true; TcpDiscoveryHandshakeRequest req = new TcpDiscoveryHandshakeRequest(locNodeId, locNode.features()); @@ -724,9 +721,9 @@ private static void sleepEx(long millis, Runnable before, Runnable after) throws Collection redirectAddrs = res.redirectAddresses(); if (redirectAddrs != null) { - U.closeQuiet(sock); + U.closeQuiet(ses); - openSock = false; + openSes = false; if (log.isInfoEnabled()) log.info("Reconnecting to the addresses of a proper DC [addrs=" + redirectAddrs + ']'); @@ -783,10 +780,10 @@ private static void sleepEx(long millis, Runnable before, Runnable after) throws log.debug("Message has been sent to address [msg=" + msg + ", addr=" + addr + ", rmtNodeId=" + rmtNodeId + ']'); - return new T2<>(ses, spi.readReceipt(sock, timeoutHelper.nextTimeoutChunk(ackTimeout0))); + return new T2<>(ses, spi.readReceipt(ses, timeoutHelper.nextTimeoutChunk(ackTimeout0))); } catch (IOException | IgniteCheckedException e) { - U.closeQuiet(sock); + U.closeQuiet(ses); if (log.isDebugEnabled()) log.error("Exception on joining: " + e.getMessage(), e); @@ -836,7 +833,7 @@ private static void sleepEx(long millis, Runnable before, Runnable after) throws if (!spi.failureDetectionTimeoutEnabled() && ++reconCnt == spi.getReconnectCount()) break; - if (!openSock) { + if (!openSes) { // Reconnect for the second time, if connection is not established. if (connectAttempts < 2) { connectAttempts++; @@ -971,7 +968,7 @@ private NavigableSet allVisibleNodes() { TcpDiscoveryIoSession ses = msgWorker.currSes; if (ses != null) - U.closeQuiet(ses.socket()); + U.closeQuiet(ses); } /** {@inheritDoc} */ @@ -1133,7 +1130,7 @@ private void forceStopRead() throws InterruptedException { this.stopReadLatch = stopReadLatch = new CountDownLatch(1); - U.closeQuiet(ses.socket()); + U.closeQuiet(ses); this.ses = null; this.rmtNodeId = null; @@ -1170,26 +1167,25 @@ private void forceStopRead() throws InterruptedException { rmtNodeId = this.rmtNodeId; } - Socket sock = ses.socket(); - U.enhanceThreadName(U.id8(rmtNodeId) - + ' ' + sock.getInetAddress().getHostAddress() - + ":" + sock.getPort()); + + ' ' + ses.socket().getInetAddress().getHostAddress() + + ":" + ses.socket().getPort()); try { - assert sock.getKeepAlive() && sock.getTcpNoDelay() : "Socket wasn't configured properly:" + - " KeepAlive " + sock.getKeepAlive() + - " TcpNoDelay " + sock.getTcpNoDelay(); + assert ses.socket().getKeepAlive() && ses.socket().getTcpNoDelay() : + "Socket wasn't configured properly:" + + " KeepAlive " + ses.socket().getKeepAlive() + + " TcpNoDelay " + ses.socket().getTcpNoDelay(); while (!isInterrupted()) { TcpDiscoveryAbstractMessage msg; try { - msg = spi.readMessage(ses, sock.getSoTimeout()); + msg = spi.readMessage(ses, ses.socket().getSoTimeout()); } catch (IgniteCheckedException e) { if (log.isDebugEnabled()) - U.error(log, "Failed to read message [sock=" + sock + ", " + + U.error(log, "Failed to read message [ses=" + ses + ", " + "locNodeId=" + getLocalNodeId() + ", rmtNodeId=" + rmtNodeId + ']', e); // Exists possibility that exception raised on interruption. @@ -1209,7 +1205,7 @@ private void forceStopRead() throws InterruptedException { "(make sure same versions of all classes are available on all nodes) " + "[rmtNodeId=" + rmtNodeId + ", err=" + clsNotFoundEx.getMessage() + ']'); else - LT.error(log, e, "Failed to read message [sock=" + sock + ", locNodeId=" + + LT.error(log, e, "Failed to read message [ses=" + ses + ", locNodeId=" + getLocalNodeId() + ", rmtNodeId=" + rmtNodeId + ']'); continue; @@ -1236,10 +1232,10 @@ private void forceStopRead() throws InterruptedException { msgWorker.addMessage(new SessionClosedMessage(ses)); if (log.isDebugEnabled()) - U.error(log, "Connection failed [sock=" + sock + ", locNodeId=" + getLocalNodeId() + ']', e); + U.error(log, "Connection failed [ses=" + ses + ", locNodeId=" + getLocalNodeId() + ']', e); } finally { - U.closeQuiet(sock); + U.closeQuiet(ses); synchronized (mux) { if (this.ses == ses) { @@ -1386,7 +1382,7 @@ void ackReceived(TcpDiscoveryClientAckResponse res) { } } - U.closeQuiet(ses.socket()); + U.closeQuiet(ses); this.ses = null; @@ -1479,7 +1475,7 @@ void ackReceived(TcpDiscoveryClientAckResponse res) { else U.error(log, "Failed to send message: " + msg, e); - U.closeQuiet(ses.socket()); + U.closeQuiet(ses); synchronized (mux) { if (ses == this.ses) @@ -1542,8 +1538,7 @@ public void cancel() { TcpDiscoveryIoSession ses = this.ses; - if (ses != null) - U.closeQuiet(ses.socket()); + U.closeQuiet(ses); } /** {@inheritDoc} */ @@ -1578,26 +1573,19 @@ public void cancel() { this.ses = ses; - Socket sock = ses.socket(); - if (isInterrupted()) throw new InterruptedException(); - int oldTimeout = 0; - try { - oldTimeout = sock.getSoTimeout(); - - sock.setSoTimeout((int)spi.netTimeout); - - assert sock.getKeepAlive() && sock.getTcpNoDelay() : "Socket wasn't configured properly:" + - " KeepAlive " + sock.getKeepAlive() + - " TcpNoDelay " + sock.getTcpNoDelay(); + assert ses.socket().getKeepAlive() && ses.socket().getTcpNoDelay() : + "Socket wasn't configured properly:" + + " KeepAlive " + ses.socket().getKeepAlive() + + " TcpNoDelay " + ses.socket().getTcpNoDelay(); List msgs = null; while (!isInterrupted()) { - TcpDiscoveryAbstractMessage msg = spi.readMessage(ses, sock.getSoTimeout()); + TcpDiscoveryAbstractMessage msg = spi.readMessage(ses, spi.netTimeout); if (msg instanceof TcpDiscoveryClientReconnectMessage) { TcpDiscoveryClientReconnectMessage res = (TcpDiscoveryClientReconnectMessage)msg; @@ -1632,7 +1620,7 @@ else if (spi.ensured(msg)) { } } catch (IOException | IgniteCheckedException e) { - U.closeQuiet(sock); + U.closeQuiet(ses); if (log.isDebugEnabled()) log.error("Reconnect error [join=" + join + ", timeout=" + timeout + ']', e); @@ -1650,10 +1638,6 @@ else if (spi.ensured(msg)) { else U.warn(log, "Failed to reconnect to cluster (will retry): " + e); } - finally { - if (success) - sock.setSoTimeout(oldTimeout); - } } } catch (IOException | IgniteCheckedException e) { @@ -1667,8 +1651,7 @@ else if (spi.ensured(msg)) { if (!success) { TcpDiscoveryIoSession ses = this.ses; - if (ses != null) - U.closeQuiet(ses.socket()); + U.closeQuiet(ses); if (join) joinError(new IgniteSpiException("Failed to connect to cluster, connection failed and failed " + @@ -1814,9 +1797,9 @@ else if (msg instanceof TcpDiscoveryNodeFailedMessage && } else if (msg instanceof SessionClosedMessage sesClosedMsg) { if (sesClosedMsg.ses == currSes) { - Socket sock = currSes.socket(); - - InetSocketAddress prevAddr = new InetSocketAddress(sock.getInetAddress(), sock.getPort()); + InetSocketAddress prevAddr = new InetSocketAddress( + currSes.socket().getInetAddress(), + currSes.socket().getPort()); currSes = null; @@ -1981,7 +1964,7 @@ else if (discoMsg instanceof TcpDiscoveryCheckFailedMessage) TcpDiscoveryIoSession ses = this.currSes; if (ses != null) - U.closeQuiet(ses.socket()); + U.closeQuiet(ses); if (joinLatch.getCount() > 0) joinError(new IgniteSpiException("Some error in join process.")); // This should not occur. diff --git a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/ServerImpl.java b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/ServerImpl.java index fe86a1c789b42..045a68ebd8e9b 100644 --- a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/ServerImpl.java +++ b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/ServerImpl.java @@ -950,9 +950,7 @@ private boolean pingNode(TcpDiscoveryNode node) { fut.sock = sock; - sock = spi.openSocket(sock, addr, timeoutHelper); - - TcpDiscoveryIoSession ses = createSession(sock); + TcpDiscoveryIoSession ses = spi.openSession(sock, addr, timeoutHelper); spi.writeMessage(ses, new TcpDiscoveryPingRequest(locNodeId, clientNodeId), timeoutHelper.nextTimeoutChunk(spi.getSocketTimeout())); @@ -1497,13 +1495,12 @@ else if (U.millisSinceNanos(joinStartNanos) > spi.joinTimeout) boolean openSock = false; - Socket sock = null; + TcpDiscoveryIoSession ses = null; try { long tsNanos = System.nanoTime(); - sock = spi.openSocket(addr, timeoutHelper); - TcpDiscoveryIoSession ses = createSession(sock); + ses = spi.openSession(addr, timeoutHelper); openSock = true; @@ -1564,7 +1561,7 @@ else if (U.millisSinceNanos(joinStartNanos) > spi.joinTimeout) // E.g. due to class not found issue. joinReqSent = msg instanceof TcpDiscoveryJoinRequestMessage; - int receipt = spi.readReceipt(sock, timeoutHelper.nextTimeoutChunk(ackTimeout0)); + int receipt = spi.readReceipt(ses, timeoutHelper.nextTimeoutChunk(ackTimeout0)); spi.stats.onMessageSent(msg, U.nanosToMillis(tsNanos0 - tsNanos)); @@ -1652,7 +1649,7 @@ else if (U.millisSinceNanos(joinStartNanos) > spi.joinTimeout) } } finally { - U.closeQuiet(sock); + U.closeQuiet(ses); } } @@ -1993,10 +1990,7 @@ private void clearNodeAddedMessage(TcpDiscoveryAbstractMessage msg) { /** {@inheritDoc} */ @Override public void brakeConnection() { - Socket sock = msgWorker.sock; - - if (sock != null) - U.closeQuiet(sock); + U.closeQuiet(msgWorker.ses); } /** {@inheritDoc} */ @@ -2864,9 +2858,6 @@ protected class RingMessageWorker extends MessageWorker connLsnr : spi.incomeConnLsnrs) connLsnr.apply(sock); - byte[] buf = new byte[4]; - int read = 0; - - while (read < buf.length) { - int r = sock.getInputStream().read(buf, read, buf.length - read); - - if (r >= 0) - read += r; - else { - if (log.isDebugEnabled()) - log.debug("Failed to read magic header (too few bytes received) " + - "[rmtAddr=" + rmtAddr + - ", locAddr=" + sock.getLocalSocketAddress() + ']'); - - LT.warn(log, "Failed to read magic header (too few bytes received) [rmtAddr=" + - rmtAddr + ", locAddr=" + sock.getLocalSocketAddress() + ']'); - - return; - } - } - - if (!Arrays.equals(buf, U.IGNITE_HEADER)) { - if (log.isDebugEnabled()) - log.debug("Unknown connection detected (possible reasons: an incompatible Ignite node or " + - "other software connecting to this Ignite port" + - (!spi.isSslEnabled() ? ", or missing SSL configuration on remote node" : "") + - ") [rmtAddr=" + rmtAddr + - ", locAddr=" + sock.getLocalSocketAddress() + - ", rcvdHdr=" + U.byteArray2HexString(buf) + ']'); - - LT.warn(log, "Unknown connection detected (possible reasons: an incompatible Ignite node or " + - "other software connecting to this Ignite port" + - (!spi.isSslEnabled() ? ", or missing SSL configuration on remote node" : "") + - ") [rmtAddr=" + sock.getInetAddress() + ", rcvdHdr=" + U.byteArray2HexString(buf) + ']', true); - + if (!spi.readMagicHeader(ses, spi.netTimeout)) return; - } - - // Restore timeout. - sock.setSoTimeout(timeout); TcpDiscoveryAbstractMessage msg = spi.readMessage(ses, spi.netTimeout); @@ -6731,7 +6678,7 @@ else if (log.isInfoEnabled()) { } if (req.client()) { - ClientMessageWorker clientMsgWrk0 = new ClientMessageWorker(sock, nodeId, log); + ClientMessageWorker clientMsgWrk0 = new ClientMessageWorker(ses, nodeId, log); while (true) { ClientMessageWorker old = clientMsgWorkers.putIfAbsent(nodeId, clientMsgWrk0); @@ -6757,7 +6704,7 @@ else if (log.isInfoEnabled()) { log.debug("Already have client message worker, closing connection " + "[locNodeId=" + locNodeId + ", rmtNodeId=" + nodeId + - ", workerSock=" + old.sock + + ", workerSes=" + old.ses + ", sock=" + sock + ']'); return; @@ -6861,7 +6808,7 @@ else if (e.hasCause(ObjectStreamException.class) || (!sock.isClosed() && !e.hasC if (msg instanceof TcpDiscoveryConnectionCheckMessage) { ringMessageReceived(); - spi.writeToSocket(sock, RES_OK, sockTimeout); + spi.writeReceipt(ses, RES_OK, sockTimeout); continue; } @@ -6887,7 +6834,7 @@ else if (msg instanceof TcpDiscoveryClientReconnectMessage) { TcpDiscoverySpiState state = spiStateCopy(); if (state == CONNECTED) { - spi.writeToSocket(sock, RES_OK, sockTimeout); + spi.writeReceipt(ses, RES_OK, sockTimeout); if (clientMsgWrk != null && clientMsgWrk.runner() == null && !clientMsgWrk.isDone()) new MessageWorkerThreadWithCleanup<>(clientMsgWrk, log).start(); @@ -6901,21 +6848,21 @@ else if (msg instanceof TcpDiscoveryClientReconnectMessage) { // If message is received from previous node and node is connecting forward to next node. if (!getLocalNodeId().equals(msg0.routerNodeId()) && state == CONNECTING) { - spi.writeToSocket(sock, RES_OK, sockTimeout); + spi.writeReceipt(ses, RES_OK, sockTimeout); msgWorker.addMessage(msg); continue; } - spi.writeToSocket(sock, RES_CONTINUE_JOIN, sockTimeout); + spi.writeReceipt(ses, RES_CONTINUE_JOIN, sockTimeout); break; } } else if (msg instanceof TcpDiscoveryDuplicateIdMessage) { // Send receipt back. - spi.writeToSocket(sock, RES_OK, sockTimeout); + spi.writeReceipt(ses, RES_OK, sockTimeout); boolean ignored = false; @@ -6944,7 +6891,7 @@ else if (msg instanceof TcpDiscoveryDuplicateIdMessage) { } else if (msg instanceof TcpDiscoveryAuthFailedMessage) { // Send receipt back. - spi.writeToSocket(sock, RES_OK, sockTimeout); + spi.writeReceipt(ses, RES_OK, sockTimeout); synchronized (mux) { if (spiState == CONNECTING) { @@ -6972,7 +6919,7 @@ else if (msg instanceof TcpDiscoveryAuthFailedMessage) { } else if (msg instanceof TcpDiscoveryCheckFailedMessage) { // Send receipt back. - spi.writeToSocket(sock, RES_OK, sockTimeout); + spi.writeReceipt(ses, RES_OK, sockTimeout); boolean ignored = false; @@ -7015,7 +6962,7 @@ else if (msg instanceof TcpDiscoveryCheckFailedMessage) { } else if (msg instanceof TcpDiscoveryLoopbackProblemMessage) { // Send receipt back. - spi.writeToSocket(sock, RES_OK, sockTimeout); + spi.writeReceipt(ses, RES_OK, sockTimeout); boolean ignored = false; @@ -7080,7 +7027,7 @@ else if (msg instanceof TcpDiscoveryRingLatencyCheckMessage) { clientMsgWrk.addMessage(ack); } else - spi.writeToSocket(sock, RES_OK, sockTimeout); + spi.writeReceipt(ses, RES_OK, sockTimeout); if (metricsUpdateMsg != null) processClientMetricsUpdateMessage(metricsUpdateMsg); @@ -7155,7 +7102,7 @@ else if (msg instanceof TcpDiscoveryRingLatencyCheckMessage) { U.interrupt(clientMsgWrk.runner()); } - U.close(sock, log); + ses.close(log); if (log.isInfoEnabled()) { log.info("Finished serving remote node connection [rmtAddr=" + rmtAddr + @@ -7398,13 +7345,13 @@ private boolean processJoinRequestMessage( // Check that joining node can accept incoming connections. if (node.clientRouterNodeId() == null) { if (!pingJoiningNode(node)) { - spi.writeToSocket(sock, RES_JOIN_IMPOSSIBLE, sockTimeout); + spi.writeReceipt(ses, RES_JOIN_IMPOSSIBLE, sockTimeout); return false; } } - spi.writeToSocket(sock, RES_OK, sockTimeout); + spi.writeReceipt(ses, RES_OK, sockTimeout); if (log.isDebugEnabled()) log.debug("Responded to join request message [msg=" + msg + ", res=" + RES_OK + ']'); @@ -7441,7 +7388,7 @@ private boolean processJoinRequestMessage( // Local node is stopping. Remote node should try next one. res = RES_CONTINUE_JOIN; - spi.writeToSocket(sock, res, sockTimeout); + spi.writeReceipt(ses, res, sockTimeout); if (log.isDebugEnabled()) log.debug("Responded to join request message [msg=" + msg + ", res=" + res + ']'); @@ -7486,14 +7433,14 @@ private boolean pingJoiningNode(TcpDiscoveryNode node) { @Override public void interrupt() { super.interrupt(); - U.closeQuiet(sock); + U.closeQuiet(ses); } /** {@inheritDoc} */ @Override protected void cleanup() { super.cleanup(); - U.closeQuiet(sock); + U.closeQuiet(ses); synchronized (mux) { readers.remove(this); @@ -7553,8 +7500,8 @@ private class ClientMessageWorker extends MessageWorker() ); - this.sock = sock; + this.ses = ses; this.clientNodeId = clientNodeId; clientMsgSer = new TcpDiscoveryMessageSerializer(spi); @@ -7659,14 +7606,14 @@ void addMessage(TcpDiscoveryAbstractMessage msg, @Nullable byte[] msgBytes) { clientVer = IgniteUtils.productVersion(node); else if (msgLog.isDebugEnabled()) { msgLog.debug("Skip sending message ack to client, fail to get client node " + - "[sock=" + sock + ", locNodeId=" + getLocalNodeId() + + "[ses=" + ses + ", locNodeId=" + getLocalNodeId() + ", rmtNodeId=" + clientNodeId + ", msg=" + msg + ']'); } } if (clientVer != null) { if (msgLog.isDebugEnabled()) { - msgLog.debug("Sending message ack to client [sock=" + sock + ", locNodeId=" + msgLog.debug("Sending message ack to client [ses=" + ses + ", locNodeId=" + getLocalNodeId() + ", rmtNodeId=" + clientNodeId + ", msg=" + msg + ']'); } @@ -7676,7 +7623,7 @@ else if (msgLog.isDebugEnabled()) { } else { if (msgLog.isDebugEnabled()) { - msgLog.debug("Redirecting message to client [sock=" + sock + ", locNodeId=" + msgLog.debug("Redirecting message to client [ses=" + ses + ", locNodeId=" + getLocalNodeId() + ", rmtNodeId=" + clientNodeId + ", msg=" + msg + ']'); } @@ -7694,10 +7641,10 @@ else if (msgLog.isDebugEnabled()) { } catch (IgniteCheckedException | IOException e) { if (log.isDebugEnabled()) - U.error(log, "Client connection failed [sock=" + sock + ", locNodeId=" + U.error(log, "Client connection failed [ses=" + ses + ", locNodeId=" + getLocalNodeId() + ", rmtNodeId=" + clientNodeId + ", msg=" + msg + ']', e); - onException("Client connection failed [sock=" + sock + ", locNodeId=" + onException("Client connection failed [ses=" + ses + ", locNodeId=" + getLocalNodeId() + ", rmtNodeId=" + clientNodeId + ", msg=" + msg + ']', e); } finally { @@ -7706,7 +7653,7 @@ else if (msgLog.isDebugEnabled()) { U.interrupt(runner()); - U.close(sock, log); + ses.close(log); } } } @@ -7719,7 +7666,7 @@ private void writeToSocket(T2 msgT, long ti throws IgniteCheckedException, IOException { byte[] msgBytes = msgT.get2() == null ? clientMsgSer.serializeMessage(msgT.get1()) : msgT.get2(); - spi.writeToSocket(sock, msgBytes, timeout); + spi.write(ses, msgBytes, timeout); } /** @@ -7799,7 +7746,7 @@ public boolean ping(IgniteSpiOperationTimeoutHelper timeoutHelper) throws Interr @Override protected void tearDown() { pingResult(false); - U.closeQuiet(sock); + U.closeQuiet(ses); } /** {@inheritDoc} */ @@ -8007,24 +7954,6 @@ private static class GridPingFutureAdapter extends GridFutureAdapter { GridPingFutureAdapter(@Nullable UUID nodeId) { this.nodeId = nodeId; } - - /** - * Returns socket associated with this ping future. - * - * @return Socket or {@code null} if no socket associated. - */ - public Socket sock() { - return sock; - } - - /** - * Associates socket with this ping future. - * - * @param sock Socket. - */ - public void sock(Socket sock) { - this.sock = sock; - } } /** diff --git a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoveryIoSession.java b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoveryIoSession.java index 8c34284e87fb7..74397569340e6 100644 --- a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoveryIoSession.java +++ b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoveryIoSession.java @@ -26,12 +26,14 @@ import java.io.OutputStream; import java.io.StreamCorruptedException; import java.net.Socket; +import java.net.SocketException; import java.nio.ByteBuffer; import java.security.cert.Certificate; import javax.net.ssl.SSLPeerUnverifiedException; import javax.net.ssl.SSLSocket; import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.IgniteException; +import org.apache.ignite.IgniteLogger; import org.apache.ignite.internal.GridKernalContext; import org.apache.ignite.internal.IgniteEx; import org.apache.ignite.internal.direct.DirectMessageReader; @@ -60,7 +62,7 @@ * * A leading byte is used to distinguish between the modes. The byte will be removed in future. */ -public class TcpDiscoveryIoSession { +public class TcpDiscoveryIoSession implements AutoCloseable { /** Default size of buffer used for buffering socket in/out. */ private static final int DFLT_SOCK_BUFFER_SIZE = 8192; @@ -68,7 +70,7 @@ public class TcpDiscoveryIoSession { private static final int MSG_BUFFER_SIZE = 100; /** */ - final TcpDiscoverySpi spi; + private final TcpDiscoverySpi spi; /** */ private final Socket sock; @@ -145,6 +147,20 @@ void writeMessage(TcpDiscoveryAbstractMessage msg) throws IgniteCheckedException } } + /** + * Reads the next discovery message from the socket input stream limiting read time. + * + * @param timeout Socket read timeout for this operation, {@code 0} means infinite. + * @param Type of the expected message. + * @return Deserialized message instance. + * @throws IgniteCheckedException If deserialization fails. + */ + T readMessage(long timeout) throws IgniteCheckedException, IOException { + try (SocketTimeoutScope ignored = withTimeout(timeout)) { + return readMessage(); + } + } + /** * Reads the next discovery message from the socket input stream. * @@ -165,7 +181,7 @@ T readMessage() throws IgniteCheckedException, IOException { msg = spi.messageFactory().create(msgType); } catch (IgniteException e) { - detectSslAlert(b0, b1, in); + detectSslAlert(b0, b1); // 'Invalid message type' should not be lost. throw e; @@ -269,11 +285,94 @@ void serializeMessage(Message m, OutputStream out) throws IOException, IgniteChe } /** - * Checks wheter input stream contains SSL alert. + * Writes raw data to the underlying socket output stream. + * + * @param data Raw data to write. + * @throws IOException If failed. + */ + void write(byte[] data) throws IOException { + out.write(data); + + out.flush(); + } + + /** + * Writes a single byte response to the underlying socket output stream. + * + * @param b Integer response. + * @throws IOException If failed. + */ + void write(int b) throws IOException { + out.write(b); + + out.flush(); + } + + /** + * Reads a single byte from the underlying socket input stream limiting read time. + * + * @param timeout Socket read timeout for this operation, {@code 0} means infinite. + * @return Receipt. + * @throws IOException If failed. + * @throws EOFException If the connection has been closed. + */ + int read(long timeout) throws IOException { + try (SocketTimeoutScope ignored = withTimeout(timeout)) { + int res = in.read(); + + if (res == -1) + throw new EOFException(); + + return res; + } + } + + /** + * Reads {@code data.length} bytes from the underlying socket stream into the given array limiting + * read time. + * + * @param data Array to read the data into. + * @param timeout Socket read timeout for this operation, {@code 0} means infinite. + * @return Number of bytes read, less than {@code data.length} only if the connection has been closed. + * @throws IOException If failed. + */ + int read(byte[] data, long timeout) throws IOException { + try (SocketTimeoutScope ignored = withTimeout(timeout)) { + return in.readNBytes(data, 0, data.length); + } + } + + /** + * Applies the given read timeout to the session socket until the returned scope is closed. + * + * @param timeout Socket read timeout, {@code 0} means infinite. + * @return Scope restoring the previous socket read timeout when closed. + * @throws SocketException If the timeout can not be applied. + */ + private SocketTimeoutScope withTimeout(long timeout) throws SocketException { + SocketTimeoutScope scope = new SocketTimeoutScope(sock.getSoTimeout()); + + sock.setSoTimeout((int)timeout); + + return scope; + } + + /** {@inheritDoc} */ + @Override public void close() { + U.closeQuiet(sock); + } + + /** */ + void close(IgniteLogger log) { + U.close(sock, log); + } + + /** + * Checks whether input stream contains SSL alert. * See handling {@code StreamCorruptedException} in {@link #readMessage()}. * Keeps logic similar to {@link java.io.ObjectInputStream#readStreamHeader}. */ - private void detectSslAlert(byte b0, byte b1, InputStream in) throws IOException { + private void detectSslAlert(byte b0, byte b1) throws IOException { byte[] hdr = new byte[4]; hdr[0] = b0; hdr[1] = b1; @@ -293,6 +392,27 @@ private void detectSslAlert(byte b0, byte b1, InputStream in) throws IOException return "TcpDiscoveryIoSession [sock=" + sock + ']'; } + /** Restores the socket read timeout changed for the duration of a single operation. */ + private final class SocketTimeoutScope implements AutoCloseable { + /** */ + private final int oldTimeout; + + /** */ + private SocketTimeoutScope(int oldTimeout) { + this.oldTimeout = oldTimeout; + } + + /** {@inheritDoc} */ + @Override public void close() { + try { + sock.setSoTimeout(oldTimeout); + } + catch (SocketException ignored) { + // No-op. + } + } + } + /** * Input stream implementation that combines a byte array and a regular InputStream allowing to read bytes * from the array first and then proceed with reading from InputStream. @@ -336,7 +456,13 @@ private void attachByteArray(byte[] prefixData) { if (len0 == len) return len0; - return len0 + super.read(b, off + len0, len - len0); + int read = super.read(b, off + len0, len - len0); + + // Do not report the bytes already taken from the prefix buffer as an end of the stream. + if (read < 0) + return len0 > 0 ? len0 : read; + + return len0 + read; } /** {@inheritDoc} */ @@ -348,7 +474,9 @@ private void attachByteArray(byte[] prefixData) { @Override public int readNBytes(byte[] b, int off, int len) throws IOException { int len0 = readPrefixBuffer(b, off, len); - return super.readNBytes(b, off + len0, len - len0); + assert len0 <= len; + + return len0 + super.readNBytes(b, off + len0, len - len0); } /** {@inheritDoc} */ diff --git a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoverySpi.java b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoverySpi.java index a20ac2d7f01b1..f3efcf3e9a334 100644 --- a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoverySpi.java +++ b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoverySpi.java @@ -17,9 +17,7 @@ package org.apache.ignite.spi.discovery.tcp; -import java.io.EOFException; import java.io.IOException; -import java.io.OutputStream; import java.io.Serializable; import java.io.StreamCorruptedException; import java.net.InetAddress; @@ -1575,58 +1573,62 @@ public long getCoordinatorSinceTimestamp() { } /** - * @param sockAddr Remote address. + * @param rmtAddr Remote address. * @param timeoutHelper Timeout helper. - * @return Opened socket. + * @return Session bound to the connected socket. * @throws IOException If failed. * @throws IgniteSpiOperationTimeoutException In case of timeout. * @throws IgniteCheckedException If node is not yet initialized or is stopping. */ - protected Socket openSocket( - InetSocketAddress sockAddr, + protected TcpDiscoveryIoSession openSession( + InetSocketAddress rmtAddr, IgniteSpiOperationTimeoutHelper timeoutHelper ) throws IOException, IgniteSpiOperationTimeoutException, IgniteCheckedException { - return openSocket(createSocket(), sockAddr, timeoutHelper); + Socket sock = createSocket(); + + try { + return openSession(sock, rmtAddr, timeoutHelper); + } + catch (IOException | IgniteCheckedException | IgniteException e) { + U.closeQuiet(sock); + + throw e; + } } /** * Connects to remote address sending {@code U.IGNITE_HEADER} when connection is established. * - * @param sock Socket bound to a local host address. + * @param sock Socket bound to a local host address, not connected yet. * @param remAddr Remote address. * @param timeoutHelper Timeout helper. - * @return Connected socket. + * @return Session bound to the connected socket. * @throws IOException If failed. * @throws IgniteSpiOperationTimeoutException In case of timeout. * @throws IgniteCheckedException If node is not yet initialized or is stopping. */ - protected Socket openSocket( + protected TcpDiscoveryIoSession openSession( Socket sock, InetSocketAddress remAddr, IgniteSpiOperationTimeoutHelper timeoutHelper ) throws IOException, IgniteSpiOperationTimeoutException, IgniteCheckedException { + assert sock != null; assert remAddr != null; - try { - InetSocketAddress resolved = remAddr.isUnresolved() ? - new InetSocketAddress(InetAddress.getByName(remAddr.getHostName()), remAddr.getPort()) : remAddr; + InetSocketAddress resolved = remAddr.isUnresolved() ? + new InetSocketAddress(InetAddress.getByName(remAddr.getHostName()), remAddr.getPort()) : remAddr; - InetAddress addr = resolved.getAddress(); + InetAddress addr = resolved.getAddress(); - assert addr != null; + assert addr != null; - sock.connect(resolved, (int)timeoutHelper.nextTimeoutChunk(sockTimeout)); + sock.connect(resolved, (int)timeoutHelper.nextTimeoutChunk(sockTimeout)); - writeToSocket(sock, U.IGNITE_HEADER, timeoutHelper.nextTimeoutChunk(sockTimeout)); + TcpDiscoveryIoSession ses = new TcpDiscoveryIoSession(sock, this); - return sock; - } - catch (IOException | IgniteCheckedException e) { - if (sock != null) - U.closeQuiet(sock); + write(ses, U.IGNITE_HEADER, timeoutHelper.nextTimeoutChunk(sockTimeout)); - throw e; - } + return ses; } /** @@ -1634,11 +1636,11 @@ protected Socket openSocket( * before, on SSL handshake, and doesn't accept new messages. In a such case it's possible to check the original error * by reading the socket input stream. * - * @param sock Socket to check. + * @param ses Session to check. * @param writeErr Error on writing a message to the socket. * @return {@code SSLException} in case of SSL error, or {@code null} otherwise. */ - private @Nullable SSLException checkSslException(Socket sock, Exception writeErr) { + private @Nullable SSLException checkSslException(TcpDiscoveryIoSession ses, Exception writeErr) { if (!sslEnable) return null; @@ -1650,7 +1652,7 @@ protected Socket openSocket( try { // Set timeout to 1ms, in this case of closed socket it should return fast. if (X.hasCause(writeErr, SocketException.class)) - readReceipt(sock, 1); + readReceipt(ses, 1); } catch (SSLException sslErr) { return sslErr; @@ -1722,31 +1724,26 @@ void validateRemoteFeatures(IgniteNodeFeatureSet rmtFeatures) throws IgniteCheck } /** - * Writes raw data to the socket. + * Writes raw data to the session socket. * - * @param sock Socket. + * @param ses IO session. * @param data Raw data to write. * @param timeout Socket write timeout. * @throws IOException If IO failed or write timed out. * @throws IgniteCheckedException If node is not yet initialized or is stopping. */ - protected void writeToSocket( - Socket sock, + protected void write( + TcpDiscoveryIoSession ses, byte[] data, long timeout ) throws IOException, IgniteCheckedException { - assert sock != null; assert data != null; - try (SocketTimeoutObject ignored = startTimer(sock, timeout)) { - OutputStream out = sock.getOutputStream(); - - out.write(data); - - out.flush(); + try (SocketTimeoutObject ignored = startTimer(ses, timeout)) { + ses.write(data); } catch (IOException e) { - SSLException sslEx = checkSslException(sock, e); + SSLException sslEx = checkSslException(ses, e); throw sslEx == null ? e : sslEx; } @@ -1788,46 +1785,37 @@ protected void writeMessage( TcpDiscoveryAbstractMessage msg, long timeout ) throws IOException, IgniteCheckedException { - Socket sock = ses.socket(); - - assert sock != null; assert msg != null; - try (SocketTimeoutObject ignored = startTimer(sock, timeout)) { + try (SocketTimeoutObject ignored = startTimer(ses, timeout)) { ses.writeMessage(msg); } catch (IgniteCheckedException e) { - SSLException sslEx = checkSslException(sock, e); + SSLException sslEx = checkSslException(ses, e); throw sslEx == null ? e : new IgniteCheckedException(sslEx); } } /** - * Writes response to the socket. + * Writes response to the session socket. * - * @param sock Socket. + * @param ses IO session. * @param res Integer response. * @param timeout Socket timeout. * @throws IOException If IO failed or write timed out. * @throws IgniteCheckedException If node is not yet initialized or is stopping. */ - protected void writeToSocket( - Socket sock, + protected void writeReceipt( + TcpDiscoveryIoSession ses, int res, long timeout ) throws IOException, IgniteCheckedException { - assert sock != null; - - try (SocketTimeoutObject ignored = startTimer(sock, timeout)) { - OutputStream out = sock.getOutputStream(); - - out.write(res); - - out.flush(); + try (SocketTimeoutObject ignored = startTimer(ses, timeout)) { + ses.write(res); } catch (IOException e) { - SSLException sslEx = checkSslException(sock, e); + SSLException sslEx = checkSslException(ses, e); throw (sslEx == null) ? e : sslEx; } @@ -1843,22 +1831,15 @@ protected void writeToSocket( * @throws IgniteCheckedException If unmarshalling failed. */ protected T readMessage(TcpDiscoveryIoSession ses, long timeout) throws IOException, IgniteCheckedException { - Socket sock = ses.socket(); - - assert sock != null; - - int oldTimeout = sock.getSoTimeout(); - try { - sock.setSoTimeout((int)timeout); - - return ses.readMessage(); + return ses.readMessage(timeout); } catch (IOException | IgniteCheckedException e) { if (X.hasCause(e, SocketTimeoutException.class)) LT.warn(log, "Timed out waiting for message to be read (most probably, the reason is " + "long GC pauses on remote node) [curTimeout=" + timeout + - ", rmtAddr=" + sock.getRemoteSocketAddress() + ", rmtPort=" + sock.getPort() + ']'); + ", rmtAddr=" + ses.socket().getRemoteSocketAddress() + + ", rmtPort=" + ses.socket().getPort() + ']'); StreamCorruptedException streamCorruptedCause = X.cause(e, StreamCorruptedException.class); @@ -1880,7 +1861,7 @@ protected T readMessage(TcpDiscoveryIoSession ses, long time if (X.hasCause(e, ClassNotFoundException.class)) { LT.error(log, e, "Failed to read message due to an unknown class to unmarshal received. Unable to " + "process the Discovery protocol. Stopping the Discovery SPI and invoking the failure handler. " + - "RmtAddr=" + sock.getRemoteSocketAddress() + ", rmtPort=" + sock.getPort() + ']'); + "RmtAddr=" + ses.socket().getRemoteSocketAddress() + ", rmtPort=" + ses.socket().getPort() + ']'); ignite.context().failure().process(new FailureContext(FailureType.CRITICAL_ERROR, e)); @@ -1890,58 +1871,64 @@ protected T readMessage(TcpDiscoveryIoSession ses, long time throw e; } - finally { - // Quietly restore timeout. - try { - sock.setSoTimeout(oldTimeout); - } - catch (SocketException ignored) { - // No-op. - } - } } /** - * Reads message delivery receipt from the socket. + * Reads and verifies the {@code U.IGNITE_HEADER} an incoming connection is expected to start with. + * See {@link #openSession(Socket, InetSocketAddress, IgniteSpiOperationTimeoutHelper)} writing this prefix. * - * @param sock Socket. - * @param timeout Socket timeout for this operation. - * @return Receipt. + * @param ses IO session. + * @param timeout Operation timeout. + * @return {@code true} if the Ignite header was successfully read during the specified timeout, + * {@code false} otherwise. * @throws IOException If IO failed or read timed out. */ - protected int readReceipt(Socket sock, long timeout) throws IOException { - assert sock != null; + protected boolean readMagicHeader(TcpDiscoveryIoSession ses, long timeout) throws IOException { + byte[] buf = new byte[U.IGNITE_HEADER.length]; - int oldTimeout = sock.getSoTimeout(); + if (ses.read(buf, timeout) < buf.length) { + LT.warn(log, "Failed to read magic header (too few bytes received) " + + "[rmtAddr=" + ses.socket().getRemoteSocketAddress() + + ", locAddr=" + ses.socket().getLocalSocketAddress() + ']'); - try { - sock.setSoTimeout((int)timeout); + return false; + } - int res = sock.getInputStream().read(); + if (!Arrays.equals(buf, U.IGNITE_HEADER)) { + LT.warn(log, "Unknown connection detected (possible reasons: an incompatible Ignite node or " + + "other software connecting to this Ignite port" + + (!isSslEnabled() ? ", or missing SSL configuration on remote node" : "") + + ") [rmtAddr=" + ses.socket().getRemoteSocketAddress() + + ", locAddr=" + ses.socket().getLocalSocketAddress() + + ", rcvdHdr=" + U.byteArray2HexString(buf) + ']', true); - if (res == -1) - throw new EOFException(); + return false; + } + + return true; + } - return res; + /** + * Reads message delivery receipt from the session socket. + * + * @param ses IO session. + * @param timeout Socket timeout for this operation. + * @return Receipt. + * @throws IOException If IO failed or read timed out. + */ + protected int readReceipt(TcpDiscoveryIoSession ses, long timeout) throws IOException { + try { + return ses.read(timeout); } catch (SocketTimeoutException e) { LT.warn(log, "Timed out waiting for message delivery receipt (most probably, the reason is " + "in long GC pauses on remote node; consider tuning GC and increasing 'ackTimeout' " + "configuration property). Will retry to send message with increased timeout " + - "[currentTimeout=" + timeout + ", rmtAddr=" + sock.getRemoteSocketAddress() + - ", rmtPort=" + sock.getPort() + ']'); + "[currentTimeout=" + timeout + ", rmtAddr=" + ses.socket().getRemoteSocketAddress() + + ", rmtPort=" + ses.socket().getPort() + ']'); throw e; } - finally { - // Quietly restore timeout. - try { - sock.setSoTimeout(oldTimeout); - } - catch (SocketException ignored) { - // No-op. - } - } } /** @@ -2503,9 +2490,9 @@ TcpDiscoveryHandshakeResponse readHandshakeResponse( } /** Starts a timer for a socket operation. */ - private SocketTimeoutObject startTimer(Socket sock, long timeout) throws IgniteCheckedException { + private SocketTimeoutObject startTimer(TcpDiscoveryIoSession ses, long timeout) throws IgniteCheckedException { try { - SocketTimeoutObject obj = new SocketTimeoutObject(sock, U.currentTimeMillis() + timeout); + SocketTimeoutObject obj = new SocketTimeoutObject(ses, U.currentTimeMillis() + timeout); addTimeoutObject(obj); @@ -2533,7 +2520,7 @@ private class SocketTimeoutObject implements IgniteSpiTimeoutObject, AutoCloseab private final IgniteUuid id = IgniteUuid.randomUuid(); /** */ - private final Socket sock; + private final TcpDiscoveryIoSession ses; /** */ private final long endTime; @@ -2542,14 +2529,14 @@ private class SocketTimeoutObject implements IgniteSpiTimeoutObject, AutoCloseab private final AtomicBoolean done = new AtomicBoolean(); /** - * @param sock Socket. + * @param ses IO session. * @param endTime End time. */ - SocketTimeoutObject(Socket sock, long endTime) { - assert sock != null; + SocketTimeoutObject(TcpDiscoveryIoSession ses, long endTime) { + assert ses != null; assert endTime > 0; - this.sock = sock; + this.ses = ses; this.endTime = endTime; } @@ -2563,15 +2550,16 @@ boolean cancel() { /** {@inheritDoc} */ @Override public void onTimeout() { if (done.compareAndSet(false, true)) { - // Close socket - timeout occurred. - U.closeQuiet(sock); + // Close session - timeout occurred. + ses.close(); LT.warn(log, "Socket write has timed out (consider increasing " + (failureDetectionTimeoutEnabled() ? "'IgniteConfiguration.failureDetectionTimeout' configuration property) [" + "failureDetectionTimeout=" + failureDetectionTimeout() : "'sockTimeout' configuration property) [sockTimeout=" + sockTimeout) + - ", rmtAddr=" + sock.getRemoteSocketAddress() + ", rmtPort=" + sock.getPort() + + ", rmtAddr=" + ses.socket().getRemoteSocketAddress() + + ", rmtPort=" + ses.socket().getPort() + ", sockTimeout=" + sockTimeout + ']'); } } diff --git a/modules/core/src/test/java/org/apache/ignite/internal/IgniteClientRejoinTest.java b/modules/core/src/test/java/org/apache/ignite/internal/IgniteClientRejoinTest.java index 23be4f0f8b0a7..e059f8e87a446 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/IgniteClientRejoinTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/IgniteClientRejoinTest.java @@ -353,15 +353,15 @@ private class TcpCommunicationSpi extends org.apache.ignite.spi.communication.tc */ private class DiscoverySpi extends TcpDiscoverySpi { /** {@inheritDoc} */ - @Override protected void writeToSocket( - Socket sock, + @Override protected void write( + TcpDiscoveryIoSession ses, byte[] data, long timeout ) throws IOException, IgniteCheckedException { - if (blockAll || block && sock.getPort() == 47500) + if (blockAll || block && ses.socket().getPort() == 47500) throw new SocketException("Test discovery exception"); - super.writeToSocket(sock, data, timeout); + super.write(ses, data, timeout); } /** {@inheritDoc} */ @@ -377,27 +377,27 @@ private class DiscoverySpi extends TcpDiscoverySpi { } /** {@inheritDoc} */ - @Override protected void writeToSocket( - Socket sock, + @Override protected void writeReceipt( + TcpDiscoveryIoSession ses, int res, long timeout ) throws IOException, IgniteCheckedException { - if (blockAll || block && sock.getPort() == 47500) + if (blockAll || block && ses.socket().getPort() == 47500) throw new SocketException("Test discovery exception"); - super.writeToSocket(sock, res, timeout); + super.writeReceipt(ses, res, timeout); } /** {@inheritDoc} */ - @Override protected Socket openSocket( + @Override protected TcpDiscoveryIoSession openSession( Socket sock, InetSocketAddress remAddr, IgniteSpiOperationTimeoutHelper timeoutHelper ) throws IOException, IgniteCheckedException { - if (blockAll || block && sock.getPort() == 47500) + if (blockAll || block && remAddr.getPort() == 47500) throw new SocketException("Test discovery exception"); - return super.openSocket(sock, remAddr, timeoutHelper); + return super.openSession(sock, remAddr, timeoutHelper); } } } diff --git a/modules/core/src/test/java/org/apache/ignite/internal/IgniteDiscoveryMassiveNodeFailTest.java b/modules/core/src/test/java/org/apache/ignite/internal/IgniteDiscoveryMassiveNodeFailTest.java index 2ddece5dc923c..a12b701809d5d 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/IgniteDiscoveryMassiveNodeFailTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/IgniteDiscoveryMassiveNodeFailTest.java @@ -302,17 +302,17 @@ public void testRecoveryOnDisconnect() throws Exception { */ private class FailDiscoverySpi extends TcpDiscoverySpi { /** {@inheritDoc} */ - @Override protected void writeToSocket( - Socket sock, + @Override protected void write( + TcpDiscoveryIoSession ses, byte[] data, long timeout ) throws IOException, IgniteCheckedException { - assertNotFailedNode(sock); + assertNotFailedNode(ses.socket()); if (isDrop()) return; - super.writeToSocket(sock, data, timeout); + super.write(ses, data, timeout); } /** {@inheritDoc} */ @@ -335,17 +335,17 @@ private boolean isDrop() { } /** {@inheritDoc} */ - @Override protected void writeToSocket( - Socket sock, + @Override protected void writeReceipt( + TcpDiscoveryIoSession ses, int res, long timeout ) throws IOException, IgniteCheckedException { - assertNotFailedNode(sock); + assertNotFailedNode(ses.socket()); if (isDrop()) return; - super.writeToSocket(sock, res, timeout); + super.writeReceipt(ses, res, timeout); } /** diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/dht/IgniteCacheTopologySplitAbstractTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/dht/IgniteCacheTopologySplitAbstractTest.java index f85acbd4a42da..a29986bfa2674 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/dht/IgniteCacheTopologySplitAbstractTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/dht/IgniteCacheTopologySplitAbstractTest.java @@ -207,25 +207,25 @@ protected void checkSegmented(InetSocketAddress sockAddr, long timeout) throws S } /** {@inheritDoc} */ - @Override protected Socket openSocket( + @Override protected TcpDiscoveryIoSession openSession( Socket sock, InetSocketAddress remAddr, IgniteSpiOperationTimeoutHelper timeoutHelper ) throws IOException, IgniteCheckedException { checkSegmented(remAddr, timeoutHelper.nextTimeoutChunk(getSocketTimeout())); - return super.openSocket(sock, remAddr, timeoutHelper); + return super.openSession(sock, remAddr, timeoutHelper); } /** {@inheritDoc} */ - @Override protected void writeToSocket( - Socket sock, + @Override protected void write( + TcpDiscoveryIoSession ses, byte[] data, long timeout ) throws IOException, IgniteCheckedException { - checkSegmented((InetSocketAddress)sock.getRemoteSocketAddress(), timeout); + checkSegmented((InetSocketAddress)ses.socket().getRemoteSocketAddress(), timeout); - super.writeToSocket(sock, data, timeout); + super.write(ses, data, timeout); } /** {@inheritDoc} */ @@ -238,14 +238,14 @@ protected void checkSegmented(InetSocketAddress sockAddr, long timeout) throws S } /** {@inheritDoc} */ - @Override protected void writeToSocket( - Socket sock, + @Override protected void writeReceipt( + TcpDiscoveryIoSession ses, int res, long timeout ) throws IOException, IgniteCheckedException { - checkSegmented((InetSocketAddress)sock.getRemoteSocketAddress(), timeout); + checkSegmented((InetSocketAddress)ses.socket().getRemoteSocketAddress(), timeout); - super.writeToSocket(sock, res, timeout); + super.writeReceipt(ses, res, timeout); } } diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/rest/RestProcessorHangTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/rest/RestProcessorHangTest.java index b473603babcac..04e228110470c 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/rest/RestProcessorHangTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/rest/RestProcessorHangTest.java @@ -18,7 +18,6 @@ package org.apache.ignite.internal.processors.rest; import java.io.IOException; -import java.net.Socket; import java.util.List; import java.util.Objects; import java.util.concurrent.CountDownLatch; @@ -30,6 +29,7 @@ import org.apache.ignite.internal.IgniteKernal; import org.apache.ignite.internal.IgnitionEx; import org.apache.ignite.internal.processors.rest.request.GridRestCacheRequest; +import org.apache.ignite.spi.discovery.tcp.TcpDiscoveryIoSession; import org.apache.ignite.spi.discovery.tcp.TestTcpDiscoverySpi; import org.apache.ignite.testframework.GridTestUtils; import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; @@ -71,8 +71,8 @@ public void testNodeStopOnDiscoverySpiFailTest() throws Exception { // Discovery spi that never allows connecting. TestTcpDiscoverySpi discoSpi = new TestTcpDiscoverySpi() { - @Override protected void writeToSocket( - Socket sock, + @Override protected void writeReceipt( + TcpDiscoveryIoSession ses, int res, long timeout ) throws IOException, IgniteCheckedException { @@ -84,7 +84,7 @@ public void testNodeStopOnDiscoverySpiFailTest() throws Exception { // No-op. } - super.writeToSocket(sock, 255, timeout); + super.writeReceipt(ses, 255, timeout); } }; diff --git a/modules/core/src/test/java/org/apache/ignite/spi/communication/tcp/IgniteTcpCommunicationConnectOnInitTest.java b/modules/core/src/test/java/org/apache/ignite/spi/communication/tcp/IgniteTcpCommunicationConnectOnInitTest.java index ba7507bfb64d5..ff83530400195 100644 --- a/modules/core/src/test/java/org/apache/ignite/spi/communication/tcp/IgniteTcpCommunicationConnectOnInitTest.java +++ b/modules/core/src/test/java/org/apache/ignite/spi/communication/tcp/IgniteTcpCommunicationConnectOnInitTest.java @@ -179,35 +179,25 @@ private GridNioServer startServer() throws Exception { */ private class TestDiscoverySpi extends TcpDiscoverySpi { /** {@inheritDoc} */ - @Override protected Socket openSocket( - InetSocketAddress sockAddr, - IgniteSpiOperationTimeoutHelper timeoutHelper - ) throws IOException, IgniteCheckedException { - awaitLatch(); - - return super.openSocket(sockAddr, timeoutHelper); - } - - /** {@inheritDoc} */ - @Override protected Socket openSocket( + @Override protected TcpDiscoveryIoSession openSession( Socket sock, - InetSocketAddress remAddr, + InetSocketAddress sockAddr, IgniteSpiOperationTimeoutHelper timeoutHelper ) throws IOException, IgniteCheckedException { awaitLatch(); - return super.openSocket(sock, remAddr, timeoutHelper); + return super.openSession(sock, sockAddr, timeoutHelper); } /** {@inheritDoc} */ - @Override protected void writeToSocket( - Socket sock, + @Override protected void write( + TcpDiscoveryIoSession ses, byte[] data, long timeout ) throws IOException, IgniteCheckedException { awaitLatch(); - super.writeToSocket(sock, data, timeout); + super.write(ses, data, timeout); } /** {@inheritDoc} */ @@ -222,14 +212,14 @@ private class TestDiscoverySpi extends TcpDiscoverySpi { } /** {@inheritDoc} */ - @Override protected void writeToSocket( - Socket sock, + @Override protected void writeReceipt( + TcpDiscoveryIoSession ses, int res, long timeout ) throws IOException, IgniteCheckedException { awaitLatch(); - super.writeToSocket(sock, res, timeout); + super.writeReceipt(ses, res, timeout); } /** diff --git a/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/BlockTcpDiscoverySpi.java b/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/BlockTcpDiscoverySpi.java index 5c6060fa60f5d..52e81b05df960 100644 --- a/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/BlockTcpDiscoverySpi.java +++ b/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/BlockTcpDiscoverySpi.java @@ -18,7 +18,6 @@ package org.apache.ignite.spi.discovery.tcp; import java.io.IOException; -import java.net.Socket; import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.cluster.ClusterNode; import org.apache.ignite.internal.managers.discovery.DiscoveryCustomMessage; @@ -65,8 +64,8 @@ private synchronized void apply(ClusterNode addr, TcpDiscoveryAbstractMessage ms } /** {@inheritDoc} */ - @Override protected void writeToSocket( - Socket sock, + @Override protected void write( + TcpDiscoveryIoSession ses, byte[] data, long timeout ) throws IOException, IgniteCheckedException { @@ -77,7 +76,7 @@ private synchronized void apply(ClusterNode addr, TcpDiscoveryAbstractMessage ms apply(spiCtx.localNode(), msg); } - super.writeToSocket(sock, data, timeout); + super.write(ses, data, timeout); } /** {@inheritDoc} */ diff --git a/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/IgniteMetricsOverflowTest.java b/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/IgniteMetricsOverflowTest.java index b6c7d58225217..c041a9bc6085f 100644 --- a/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/IgniteMetricsOverflowTest.java +++ b/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/IgniteMetricsOverflowTest.java @@ -18,7 +18,6 @@ package org.apache.ignite.spi.discovery.tcp; import java.io.IOException; -import java.net.Socket; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.apache.ignite.Ignite; @@ -47,7 +46,7 @@ public class IgniteMetricsOverflowTest extends GridCommonAbstractTest { private CountDownLatch slowDownLatch; /** - * Period of time, for which {@link TcpDiscoverySpi#readReceipt(Socket, long)} execution is delayed on the node + * Period of time, for which {@link TcpDiscoverySpi#readReceipt(TcpDiscoveryIoSession, long)} execution is delayed on the node * with a slow {@link DiscoverySpi}. */ private volatile int readReceiptDelay; @@ -153,7 +152,7 @@ public void testMetricOverflow() throws Exception { /** */ private class TestTcpDiscoverySpi extends TcpDiscoverySpi { /** {@inheritDoc} */ - @Override protected int readReceipt(Socket sock, long timeout) throws IOException { + @Override protected int readReceipt(TcpDiscoveryIoSession ses, long timeout) throws IOException { if (readReceiptDelay > 0) { slowDownLatch.countDown(); @@ -165,7 +164,7 @@ private class TestTcpDiscoverySpi extends TcpDiscoverySpi { } } - return super.readReceipt(sock, timeout); + return super.readReceipt(ses, timeout); } } } diff --git a/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/MultiDataCenterSplitTest.java b/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/MultiDataCenterSplitTest.java index 96bff0e22b004..b429c35ba17c2 100644 --- a/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/MultiDataCenterSplitTest.java +++ b/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/MultiDataCenterSplitTest.java @@ -351,10 +351,10 @@ private TestTcpDiscoverySpi( } /** {@inheritDoc} */ - @Override protected void writeToSocket(Socket sock, byte[] data, long timeout) throws IOException, IgniteCheckedException { - tryToBlock(sock, data, timeout); + @Override protected void write(TcpDiscoveryIoSession ses, byte[] data, long timeout) throws IOException, IgniteCheckedException { + tryToBlock(ses.socket(), data, timeout); - super.writeToSocket(sock, data, timeout); + super.write(ses, data, timeout); } /** */ diff --git a/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/ReceivedMessagesTracker.java b/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/ReceivedMessagesTracker.java index f7b93eba19f03..fea9c7528023d 100644 --- a/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/ReceivedMessagesTracker.java +++ b/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/ReceivedMessagesTracker.java @@ -17,7 +17,6 @@ package org.apache.ignite.spi.discovery.tcp; -import java.net.Socket; import java.util.Collections; import java.util.Map; import java.util.WeakHashMap; @@ -28,18 +27,18 @@ /** */ public class ReceivedMessagesTracker { /** */ - private final Map msgs = Collections.synchronizedMap(new WeakHashMap<>()); + private final Map msgs = Collections.synchronizedMap(new WeakHashMap<>()); /** */ public T track(TcpDiscoveryIoSession ses, T msg) { if (msg instanceof TcpDiscoveryAbstractMessage) - msgs.put(ses.socket(), (TcpDiscoveryAbstractMessage)msg); + msgs.put(ses, (TcpDiscoveryAbstractMessage)msg); return msg; } /** */ - public @Nullable TcpDiscoveryAbstractMessage lastFor(Socket sock) { - return msgs.get(sock); + public @Nullable TcpDiscoveryAbstractMessage lastFor(TcpDiscoveryIoSession ses) { + return msgs.get(ses); } } diff --git a/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/TcpClientDiscoverySpiFailureTimeoutSelfTest.java b/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/TcpClientDiscoverySpiFailureTimeoutSelfTest.java index 3fd9b589bc919..9efb3b52809dc 100644 --- a/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/TcpClientDiscoverySpiFailureTimeoutSelfTest.java +++ b/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/TcpClientDiscoverySpiFailureTimeoutSelfTest.java @@ -18,7 +18,6 @@ package org.apache.ignite.spi.discovery.tcp; import java.io.IOException; -import java.net.Socket; import java.net.SocketTimeoutException; import java.util.ArrayList; import java.util.Collections; @@ -445,8 +444,8 @@ private static class TestTcpDiscoverySpi2 extends TcpDiscoverySpi { private Exception err; /** */ - @Override protected void writeToSocket( - Socket sock, + @Override protected void write( + TcpDiscoveryIoSession ses, byte[] data, long timeout ) throws IOException, IgniteCheckedException { @@ -461,8 +460,8 @@ private static class TestTcpDiscoverySpi2 extends TcpDiscoverySpi { } } - if (sock.getSoTimeout() >= writeToSocketDelay) - super.writeToSocket(sock, data, timeout); + if (ses.socket().getSoTimeout() >= writeToSocketDelay) + super.write(ses, data, timeout); else throw new SocketTimeoutException("Write to socket delay timeout exception."); } @@ -491,8 +490,8 @@ private static class TestTcpDiscoverySpi2 extends TcpDiscoverySpi { } /** */ - @Override protected void writeToSocket( - Socket sock, + @Override protected void writeReceipt( + TcpDiscoveryIoSession ses, int res, long timeout ) throws IOException, IgniteCheckedException { @@ -507,8 +506,8 @@ private static class TestTcpDiscoverySpi2 extends TcpDiscoverySpi { } } - if (sock.getSoTimeout() >= writeToSocketDelay) - super.writeToSocket(sock, res, timeout); + if (ses.socket().getSoTimeout() >= writeToSocketDelay) + super.writeReceipt(ses, res, timeout); else throw new SocketTimeoutException("Write to socket delay timeout exception."); } diff --git a/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/TcpClientDiscoverySpiSelfTest.java b/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/TcpClientDiscoverySpiSelfTest.java index f8a0410bcfa61..ca96f33f159cf 100644 --- a/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/TcpClientDiscoverySpiSelfTest.java +++ b/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/TcpClientDiscoverySpiSelfTest.java @@ -2591,11 +2591,13 @@ private void pauseResumeOperation(boolean isPause, AtomicBoolean... locks) { } /** {@inheritDoc} */ - @Override protected void writeToSocket( - Socket sock, + @Override protected void write( + TcpDiscoveryIoSession ses, byte[] data, long timeout ) throws IOException, IgniteCheckedException { + Socket sock = ses.socket(); + waitFor(writeLock); TcpDiscoveryAbstractMessage msg = decodeMessage(this, data); @@ -2603,7 +2605,7 @@ private void pauseResumeOperation(boolean isPause, AtomicBoolean... locks) { if (msg != null && !onMessage(sock, msg)) return; - super.writeToSocket(sock, data, timeout); + super.write(ses, data, timeout); if (msg != null && afterWrite != null) afterWrite.apply(msg, sock); @@ -2642,13 +2644,14 @@ else if (msg instanceof TcpDiscoveryClientReconnectMessage) } /** {@inheritDoc} */ - @Override protected Socket openSocket( + @Override protected TcpDiscoveryIoSession openSession( + Socket sock, InetSocketAddress sockAddr, IgniteSpiOperationTimeoutHelper timeoutHelper ) throws IOException, IgniteCheckedException { waitFor(openSockLock); - return super.openSocket(sockAddr, timeoutHelper); + return super.openSession(sock, sockAddr, timeoutHelper); } /** @@ -2689,12 +2692,14 @@ public void resumeAll() { } /** {@inheritDoc} */ - @Override protected void writeToSocket( - Socket sock, + @Override protected void writeReceipt( + TcpDiscoveryIoSession ses, int res, long timeout ) throws IOException, IgniteCheckedException { - TcpDiscoveryAbstractMessage msg = msgTracker.lastFor(sock); + Socket sock = ses.socket(); + + TcpDiscoveryAbstractMessage msg = msgTracker.lastFor(ses); if (delayJoinAckFor != null && msg instanceof TcpDiscoveryJoinRequestMessage) { TcpDiscoveryJoinRequestMessage msg0 = (TcpDiscoveryJoinRequestMessage)msg; @@ -2713,12 +2718,12 @@ public void resumeAll() { } } - super.writeToSocket(sock, res, timeout); + super.writeReceipt(ses, res, timeout); } /** {@inheritDoc} */ - @Override protected int readReceipt(Socket sock, long timeout) throws IOException { - int res = super.readReceipt(sock, timeout); + @Override protected int readReceipt(TcpDiscoveryIoSession ses, long timeout) throws IOException { + int res = super.readReceipt(ses, timeout); if (res != TcpDiscoveryImpl.RES_OK) { invalidRes = true; diff --git a/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/TcpClientDiscoveryUnresolvedHostTest.java b/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/TcpClientDiscoveryUnresolvedHostTest.java index de4dd6f7b0a71..a613b758997ea 100644 --- a/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/TcpClientDiscoveryUnresolvedHostTest.java +++ b/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/TcpClientDiscoveryUnresolvedHostTest.java @@ -66,7 +66,10 @@ public void test() throws Exception { //Ignore. } - assertEquals(0, spi.getSockets().size()); + assertFalse(spi.getSockets().isEmpty()); + + for (Socket sock : spi.getSockets()) + assertTrue(sock.isClosed()); } /** @@ -77,33 +80,23 @@ private static class TestTcpDiscoverySpi extends TcpDiscoverySpi { Set sockets = new HashSet<>(); /** {@inheritDoc} */ - @Override Socket createSocket() throws IOException { - Socket sock = super.createSocket(); - - sockets.add(sock); - - return sock; - } - - /** {@inheritDoc} */ - @Override protected Socket openSocket( + @Override protected TcpDiscoveryIoSession openSession( Socket sock, InetSocketAddress remAddr, IgniteSpiOperationTimeoutHelper timeoutHelper ) throws IOException, IgniteCheckedException { try { - return super.openSocket(sock, remAddr, timeoutHelper); + return super.openSession(sock, remAddr, timeoutHelper); } catch (IgniteSpiOperationTimeoutException | IOException e) { - if (sock.isClosed()) - sockets.remove(sock); + sockets.add(sock); throw e; } } /** - * Gets list of sockets opened by this discovery spi. + * Gets sockets of the failed connection attempts. * * @return List of sockets. */ diff --git a/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoveryCoordinatorFailureTest.java b/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoveryCoordinatorFailureTest.java index 43633ef547436..dad5c4c8fd891 100644 --- a/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoveryCoordinatorFailureTest.java +++ b/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoveryCoordinatorFailureTest.java @@ -148,7 +148,7 @@ public void testClusterFailedNewCoordinatorInitialized() throws Exception { stallSpi.startStall(); - // At this point startGrid(3) cannot proceed as well because openSocket() is blocked. + // At this point startGrid(3) cannot proceed as well because openSession() is blocked. assertFalse(fut3.isDone()); fut4.get(); @@ -205,24 +205,14 @@ private static class StallingJoinDiscoverySpi extends TcpDiscoverySpi { private volatile CountDownLatch stallLatch; /** {@inheritDoc} */ - @Override protected Socket openSocket( - InetSocketAddress sockAddr, - IgniteSpiOperationTimeoutHelper timeoutHelper - ) throws IOException, IgniteCheckedException { - checkStall(); - - return super.openSocket(sockAddr, timeoutHelper); - } - - /** {@inheritDoc} */ - @Override protected Socket openSocket( + @Override protected TcpDiscoveryIoSession openSession( Socket sock, - InetSocketAddress remAddr, + InetSocketAddress sockAddr, IgniteSpiOperationTimeoutHelper timeoutHelper ) throws IOException, IgniteCheckedException { checkStall(); - return super.openSocket(sock, remAddr, timeoutHelper); + return super.openSession(sock, sockAddr, timeoutHelper); } /** diff --git a/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoveryFailedJoinTest.java b/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoveryFailedJoinTest.java index 0b5eb6891e794..a53a814b6da3f 100644 --- a/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoveryFailedJoinTest.java +++ b/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoveryFailedJoinTest.java @@ -167,26 +167,15 @@ private void assertStartFailed(final String name) { */ private static class FailTcpDiscoverySpi extends TcpDiscoverySpi { /** {@inheritDoc} */ - @Override protected Socket openSocket( + @Override protected TcpDiscoveryIoSession openSession( + Socket sock, InetSocketAddress sockAddr, IgniteSpiOperationTimeoutHelper timeoutHelper ) throws IOException, IgniteCheckedException { if (sockAddr.getPort() == FAIL_PORT) throw new SocketException("Connection refused"); - return super.openSocket(sockAddr, timeoutHelper); - } - - /** {@inheritDoc} */ - @Override protected Socket openSocket( - Socket sock, - InetSocketAddress remAddr, - IgniteSpiOperationTimeoutHelper timeoutHelper - ) throws IOException, IgniteCheckedException { - if (remAddr.getPort() == FAIL_PORT) - throw new SocketException("Connection refused"); - - return super.openSocket(sock, remAddr, timeoutHelper); + return super.openSession(sock, sockAddr, timeoutHelper); } } @@ -195,13 +184,13 @@ private static class FailTcpDiscoverySpi extends TcpDiscoverySpi { */ private static class DropTcpDiscoverySpi extends TcpDiscoverySpi { /** {@inheritDoc} */ - @Override protected void writeToSocket( - Socket sock, + @Override protected void write( + TcpDiscoveryIoSession ses, byte[] data, long timeout ) throws IOException, IgniteCheckedException { - if (sock.getPort() != FAIL_PORT) - super.writeToSocket(sock, data, timeout); + if (ses.socket().getPort() != FAIL_PORT) + super.write(ses, data, timeout); } /** {@inheritDoc} */ @@ -212,13 +201,13 @@ private static class DropTcpDiscoverySpi extends TcpDiscoverySpi { } /** {@inheritDoc} */ - @Override protected void writeToSocket( - Socket sock, + @Override protected void writeReceipt( + TcpDiscoveryIoSession ses, int res, long timeout ) throws IOException, IgniteCheckedException { - if (sock.getPort() != FAIL_PORT) - super.writeToSocket(sock, res, timeout); + if (ses.socket().getPort() != FAIL_PORT) + super.writeReceipt(ses, res, timeout); } } diff --git a/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoveryNetworkIssuesTest.java b/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoveryNetworkIssuesTest.java index 37496f5d3b209..43996bbd6c438 100644 --- a/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoveryNetworkIssuesTest.java +++ b/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoveryNetworkIssuesTest.java @@ -191,21 +191,22 @@ public void testServerGetsSegmentedOnBecomeDangling() throws Exception { IgniteEx ig1 = startGrid(NODE_1_NAME); specialSpi = new TcpDiscoverySpi() { - @Override protected int readReceipt(Socket sock, long timeout) throws IOException { - if (netBroken.get() && sock.getPort() == NODE_3_PORT) + @Override protected int readReceipt(TcpDiscoveryIoSession ses, long timeout) throws IOException { + if (netBroken.get() && ses.socket().getPort() == NODE_3_PORT) throw new SocketTimeoutException("Read timed out"); - return super.readReceipt(sock, timeout); + return super.readReceipt(ses, timeout); } - @Override protected Socket openSocket( + @Override protected TcpDiscoveryIoSession openSession( + Socket sock, InetSocketAddress sockAddr, IgniteSpiOperationTimeoutHelper timeoutHelper ) throws IOException, IgniteCheckedException { if (netBroken.get() && sockAddr.getPort() == NODE_4_PORT) throw new SocketTimeoutException("connect timed out"); - return super.openSocket(sockAddr, timeoutHelper); + return super.openSession(sock, sockAddr, timeoutHelper); } }; @@ -615,15 +616,15 @@ private boolean dropMsg(Socket sock) { } /** {@inheritDoc} */ - @Override protected void writeToSocket( - Socket sock, + @Override protected void writeReceipt( + TcpDiscoveryIoSession ses, int res, long timeout ) throws IOException, IgniteCheckedException { - if (dropMsg(sock)) + if (dropMsg(ses.socket())) return; - super.writeToSocket(sock, res, timeout); + super.writeReceipt(ses, res, timeout); } /** {@inheritDoc} */ @@ -645,15 +646,15 @@ private boolean dropMsg(Socket sock) { } /** {@inheritDoc} */ - @Override protected void writeToSocket( - Socket sock, + @Override protected void write( + TcpDiscoveryIoSession ses, byte[] data, long timeout ) throws IOException, IgniteCheckedException { - if (dropMsg(sock)) + if (dropMsg(ses.socket())) return; - super.writeToSocket(sock, data, timeout); + super.write(ses, data, timeout); } /** diff --git a/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoveryPendingMessageDeliveryTest.java b/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoveryPendingMessageDeliveryTest.java index ec2f0c30ab2c1..4fabd12223492 100644 --- a/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoveryPendingMessageDeliveryTest.java +++ b/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoveryPendingMessageDeliveryTest.java @@ -18,7 +18,6 @@ package org.apache.ignite.spi.discovery.tcp; import java.io.IOException; -import java.net.Socket; import java.util.Set; import org.apache.ignite.Ignite; import org.apache.ignite.IgniteCheckedException; @@ -268,13 +267,13 @@ private class DyingThreadDiscoverySpi extends NoRingClosingTcpDiscoverySpi { */ private class DyingDiscoverySpi extends NoRingClosingTcpDiscoverySpi { /** {@inheritDoc} */ - @Override protected void writeToSocket( - Socket sock, + @Override protected void write( + TcpDiscoveryIoSession ses, byte[] data, long timeout ) throws IOException, IgniteCheckedException { if (!blockMsgs) - super.writeToSocket(sock, data, timeout); + super.write(ses, data, timeout); } /** {@inheritDoc} */ @@ -285,13 +284,13 @@ private class DyingDiscoverySpi extends NoRingClosingTcpDiscoverySpi { } /** {@inheritDoc} */ - @Override protected void writeToSocket( - Socket sock, + @Override protected void writeReceipt( + TcpDiscoveryIoSession ses, int res, long timeout ) throws IOException, IgniteCheckedException { if (!blockMsgs) - super.writeToSocket(sock, res, timeout); + super.writeReceipt(ses, res, timeout); } } diff --git a/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoverySpiFailureTimeoutSelfTest.java b/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoverySpiFailureTimeoutSelfTest.java index c71c26b054063..e7fccc4e86347 100644 --- a/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoverySpiFailureTimeoutSelfTest.java +++ b/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoverySpiFailureTimeoutSelfTest.java @@ -263,7 +263,7 @@ private static class TestTcpDiscoverySpi extends TcpDiscoverySpi { private volatile IgniteSpiOperationTimeoutException err; /** {@inheritDoc} */ - @Override protected Socket openSocket( + @Override protected TcpDiscoveryIoSession openSession( Socket sock, InetSocketAddress sockAddr, IgniteSpiOperationTimeoutHelper timeoutHelper @@ -291,7 +291,7 @@ else if (openSockTimeoutWait) { } } - super.openSocket(sock, sockAddr, timeoutHelper); + TcpDiscoveryIoSession ses = super.openSession(sock, sockAddr, timeoutHelper); try { Thread.sleep(1500); @@ -300,7 +300,7 @@ else if (openSockTimeoutWait) { // No-op. } - return sock; + return ses; } /** {@inheritDoc} */ diff --git a/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoverySpiReconnectDelayTest.java b/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoverySpiReconnectDelayTest.java index 21c7207c6b04e..0b3f253bc4cc3 100644 --- a/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoverySpiReconnectDelayTest.java +++ b/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoverySpiReconnectDelayTest.java @@ -421,15 +421,15 @@ private static class FailingTcpDiscoverySpi extends TcpDiscoverySpi { } /** {@inheritDoc} */ - @Override protected void writeToSocket( - Socket sock, + @Override protected void writeReceipt( + TcpDiscoveryIoSession ses, int res, long timeout ) throws IOException, IgniteCheckedException { - if (msgTracker.lastFor(sock) instanceof TcpDiscoveryJoinRequestMessage && failJoinReqRes.getAndDecrement() > 0) + if (msgTracker.lastFor(ses) instanceof TcpDiscoveryJoinRequestMessage && failJoinReqRes.getAndDecrement() > 0) res = RES_WAIT; - super.writeToSocket(sock, res, timeout); + super.writeReceipt(ses, res, timeout); } /** From a9a34a113ac1441994e109602cfa3e72cc954018 Mon Sep 17 00:00:00 2001 From: Mikhail Petrov Date: Mon, 24 Aug 2026 11:14:30 +0300 Subject: [PATCH 3/8] IGNITE-29006 Refactored TcpDiscoveryMessageSerializer to handle serialization only --- .../ignite/spi/discovery/tcp/ServerImpl.java | 119 ++++++++---------- .../discovery/tcp/TcpDiscoveryIoSession.java | 81 ++++++------ .../tcp/TcpDiscoveryMessageSerializer.java | 67 ---------- .../tcp/internal/ClientMessageHolder.java | 59 +++++++++ .../TcpDiscoveryMessageSerializer.java | 97 ++++++++++++++ 5 files changed, 244 insertions(+), 179 deletions(-) delete mode 100644 modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoveryMessageSerializer.java create mode 100644 modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/internal/ClientMessageHolder.java create mode 100644 modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/internal/TcpDiscoveryMessageSerializer.java diff --git a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/ServerImpl.java b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/ServerImpl.java index 045a68ebd8e9b..309b54161e9ef 100644 --- a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/ServerImpl.java +++ b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/ServerImpl.java @@ -100,7 +100,6 @@ import org.apache.ignite.internal.util.typedef.C1; import org.apache.ignite.internal.util.typedef.F; import org.apache.ignite.internal.util.typedef.P1; -import org.apache.ignite.internal.util.typedef.T2; import org.apache.ignite.internal.util.typedef.X; import org.apache.ignite.internal.util.typedef.internal.LT; import org.apache.ignite.internal.util.typedef.internal.S; @@ -124,8 +123,10 @@ import org.apache.ignite.spi.discovery.DiscoverySpiCustomMessage; import org.apache.ignite.spi.discovery.DiscoverySpiListener; import org.apache.ignite.spi.discovery.IgniteDiscoveryThread; +import org.apache.ignite.spi.discovery.tcp.internal.ClientMessageHolder; import org.apache.ignite.spi.discovery.tcp.internal.DiscoveryDataPacket; import org.apache.ignite.spi.discovery.tcp.internal.FutureTask; +import org.apache.ignite.spi.discovery.tcp.internal.TcpDiscoveryMessageSerializer; import org.apache.ignite.spi.discovery.tcp.internal.TcpDiscoveryNode; import org.apache.ignite.spi.discovery.tcp.internal.TcpDiscoveryNodesRing; import org.apache.ignite.spi.discovery.tcp.internal.TcpDiscoverySpiState; @@ -2866,7 +2867,7 @@ protected class RingMessageWorker extends MessageWorker> { + private class ClientMessageWorker extends MessageWorker { /** Node ID. */ private final UUID clientNodeId; - // The code responsible for sending and receiving messages to and from client nodes represents a special case in ServerImpl, - // as it is split into two separate components. - // One part, ClientMessageWorker, handles only message sending to clients and does not process responses. - // The other part, which reads messages from clients, is implemented in SocketReader. - // Due to this separation, we don't require a full TcpDiscoveryIoSession here - // and can instead extract just the message-writing functionality. - // At the same time, we aim to keep both reading and writing logic encapsulated within TcpDiscoveryIoSession. - // As a result, we need to copy some code from TcpDiscoveryIoSession into the new class, TcpDiscoveryMessageSerializer. - /** */ - private final TcpDiscoveryMessageSerializer clientMsgSer; - /** Session shared with the socket reader serving the same client connection. */ private final TcpDiscoveryIoSession ses; @@ -7534,8 +7523,6 @@ private ClientMessageWorker(TcpDiscoveryIoSession ses, UUID clientNodeId, Ignite this.ses = ses; this.clientNodeId = clientNodeId; - clientMsgSer = new TcpDiscoveryMessageSerializer(spi); - lastMetricsUpdateMsgTimeNanos = System.nanoTime(); } @@ -7562,24 +7549,19 @@ void metrics(ClusterMetrics metrics) { this.metrics = metrics; } - /** - * @param msg Message. - */ + /** @param msg Discovery Message. */ void addMessage(TcpDiscoveryAbstractMessage msg) { - addMessage(msg, null); + addMessage(new ClientMessageHolder(msg)); } - /** - * @param msg Message. - * @param msgBytes Optional message bytes. - */ - void addMessage(TcpDiscoveryAbstractMessage msg, @Nullable byte[] msgBytes) { - T2 t = new T2<>(msg, msgBytes); + /** @param msgHolder Holder of a Discovery Message to send to the client. */ + void addMessage(ClientMessageHolder msgHolder) { + TcpDiscoveryAbstractMessage msg = msgHolder.message(); if (msg.highPriority()) - queue.addFirst(t); + queue.addFirst(msgHolder); else - queue.add(t); + queue.add(msgHolder); DebugLogger log = messageLogger(msg); @@ -7588,10 +7570,10 @@ void addMessage(TcpDiscoveryAbstractMessage msg, @Nullable byte[] msgBytes) { } /** {@inheritDoc} */ - @Override protected void processMessage(T2 msgT) { + @Override protected void processMessage(ClientMessageHolder msgHolder) { boolean success = false; - TcpDiscoveryAbstractMessage msg = msgT.get1(); + TcpDiscoveryAbstractMessage msg = msgHolder.message(); try { assert msg.verified() : msg; @@ -7617,8 +7599,9 @@ else if (msgLog.isDebugEnabled()) { + getLocalNodeId() + ", rmtNodeId=" + clientNodeId + ", msg=" + msg + ']'); } - writeToSocket(msgT, spi.failureDetectionTimeoutEnabled() ? spi.clientFailureDetectionTimeout() : - spi.getSocketTimeout()); + long timeout = spi.failureDetectionTimeoutEnabled() ? spi.clientFailureDetectionTimeout() : spi.getSocketTimeout(); + + writeMessage(msgHolder, timeout); } } else { @@ -7629,7 +7612,7 @@ else if (msgLog.isDebugEnabled()) { assert topologyInitialized(msg) : msg; - writeToSocket(msgT, spi.getEffectiveSocketTimeout(false)); + writeMessage(msgHolder, spi.getEffectiveSocketTimeout(false)); } boolean clientFailed = msg instanceof TcpDiscoveryNodeFailedMessage && @@ -7659,14 +7642,16 @@ else if (msgLog.isDebugEnabled()) { } /** - * @param msgT Message tuple. + * @param msgHolder Message holder. * @param timeout Timeout. */ - private void writeToSocket(T2 msgT, long timeout) - throws IgniteCheckedException, IOException { - byte[] msgBytes = msgT.get2() == null ? clientMsgSer.serializeMessage(msgT.get1()) : msgT.get2(); + private void writeMessage(ClientMessageHolder msgHolder, long timeout) throws IgniteCheckedException, IOException { + byte[] msgBytes = msgHolder.messageBytes(); - spi.write(ses, msgBytes, timeout); + if (msgBytes != null) + spi.write(ses, msgBytes, timeout); + else + spi.writeMessage(ses, msgHolder.message(), timeout); } /** diff --git a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoveryIoSession.java b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoveryIoSession.java index 74397569340e6..04453b9dd0467 100644 --- a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoveryIoSession.java +++ b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoveryIoSession.java @@ -29,6 +29,7 @@ import java.net.SocketException; import java.nio.ByteBuffer; import java.security.cert.Certificate; +import java.util.concurrent.locks.ReentrantLock; import javax.net.ssl.SSLPeerUnverifiedException; import javax.net.ssl.SSLSocket; import org.apache.ignite.IgniteCheckedException; @@ -37,7 +38,6 @@ import org.apache.ignite.internal.GridKernalContext; import org.apache.ignite.internal.IgniteEx; import org.apache.ignite.internal.direct.DirectMessageReader; -import org.apache.ignite.internal.direct.DirectMessageWriter; import org.apache.ignite.internal.managers.communication.DiscoveryMarshalling; import org.apache.ignite.internal.managers.communication.UnknownMessageException; import org.apache.ignite.internal.util.CommonUtils; @@ -47,6 +47,7 @@ import org.apache.ignite.marshaller.jdk.JdkMarshaller; import org.apache.ignite.plugin.extensions.communication.Message; import org.apache.ignite.plugin.extensions.communication.MessageSerializer; +import org.apache.ignite.spi.discovery.tcp.internal.TcpDiscoveryMessageSerializer; import org.apache.ignite.spi.discovery.tcp.messages.TcpDiscoveryAbstractMessage; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -66,8 +67,8 @@ public class TcpDiscoveryIoSession implements AutoCloseable { /** Default size of buffer used for buffering socket in/out. */ private static final int DFLT_SOCK_BUFFER_SIZE = 8192; - /** Size for an intermediate buffer for serializing discovery messages. */ - private static final int MSG_BUFFER_SIZE = 100; + /** Size of the intermediate buffer a message is deserialized through. */ + private static final int READ_BUFFER_SIZE = 100; /** */ private final TcpDiscoverySpi spi; @@ -76,11 +77,14 @@ public class TcpDiscoveryIoSession implements AutoCloseable { private final Socket sock; /** */ - private final DirectMessageWriter msgWriter; + private final TcpDiscoveryMessageSerializer msgSer; /** */ private final DirectMessageReader msgReader; + /** */ + private final ByteBuffer readBuf; + /** Buffered socket output stream. */ private final OutputStream out; @@ -88,10 +92,7 @@ public class TcpDiscoveryIoSession implements AutoCloseable { private final CompositeInputStream in; /** */ - private final ByteBuffer readBuf; - - /** */ - private final ByteBuffer writeBuf; + private final ReentrantLock sesWriteLock = new ReentrantLock(); /** * Creates a new discovery I/O session bound to the given socket. @@ -104,12 +105,11 @@ public class TcpDiscoveryIoSession implements AutoCloseable { this.sock = sock; this.spi = spi; - readBuf = ByteBuffer.allocate(MSG_BUFFER_SIZE); - writeBuf = ByteBuffer.allocate(MSG_BUFFER_SIZE); - - msgWriter = new DirectMessageWriter(spi.messageFactory()); + readBuf = ByteBuffer.allocate(READ_BUFFER_SIZE); msgReader = new DirectMessageReader(spi.messageFactory(), null); + this.msgSer = new TcpDiscoveryMessageSerializer(spi); + try { int sendBufSize = sock.getSendBufferSize() > 0 ? sock.getSendBufferSize() : DFLT_SOCK_BUFFER_SIZE; int rcvBufSize = sock.getReceiveBufferSize() > 0 ? sock.getReceiveBufferSize() : DFLT_SOCK_BUFFER_SIZE; @@ -129,8 +129,10 @@ public class TcpDiscoveryIoSession implements AutoCloseable { * @throws IgniteCheckedException If serialization fails. */ void writeMessage(TcpDiscoveryAbstractMessage msg) throws IgniteCheckedException, IOException { + sesWriteLock.lock(); + try { - serializeMessage((Message)msg, out); + msgSer.writeTo(msg, out); out.flush(); } @@ -145,6 +147,9 @@ void writeMessage(TcpDiscoveryAbstractMessage msg) throws IgniteCheckedException throw new IgniteCheckedException(e); } + finally { + sesWriteLock.unlock(); + } } /** @@ -256,34 +261,6 @@ public Socket socket() { return sock; } - /** - * Serializes a discovery message into given output stream. - * - * @param m Discovery message to serialize. - * @param out Output stream to write serialized message. - * @throws IOException If serialization fails. - */ - void serializeMessage(Message m, OutputStream out) throws IOException, IgniteCheckedException { - GridKernalContext kctx = ((IgniteEx)spi.ignite()).context(); - - DiscoveryMarshalling.marshal(m, kctx, null); - - msgWriter.reset(); - msgWriter.setBuffer(writeBuf); - - boolean finished; - - do { - // Should be cleared before first operation. - writeBuf.clear(); - - finished = MessageSerialization.writeTo(spi.messageFactory(), m, msgWriter); - - out.write(writeBuf.array(), 0, writeBuf.position()); - } - while (!finished); - } - /** * Writes raw data to the underlying socket output stream. * @@ -291,9 +268,16 @@ void serializeMessage(Message m, OutputStream out) throws IOException, IgniteChe * @throws IOException If failed. */ void write(byte[] data) throws IOException { - out.write(data); + sesWriteLock.lock(); + + try { + out.write(data); - out.flush(); + out.flush(); + } + finally { + sesWriteLock.unlock(); + } } /** @@ -303,9 +287,16 @@ void write(byte[] data) throws IOException { * @throws IOException If failed. */ void write(int b) throws IOException { - out.write(b); + sesWriteLock.lock(); + + try { + out.write(b); - out.flush(); + out.flush(); + } + finally { + sesWriteLock.unlock(); + } } /** diff --git a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoveryMessageSerializer.java b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoveryMessageSerializer.java deleted file mode 100644 index 8c871f9e2eb46..0000000000000 --- a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoveryMessageSerializer.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * 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; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.Socket; -import org.apache.ignite.IgniteCheckedException; -import org.apache.ignite.plugin.extensions.communication.Message; -import org.apache.ignite.plugin.extensions.communication.MessageSerializer; -import org.apache.ignite.spi.discovery.tcp.messages.TcpDiscoveryAbstractMessage; - -/** - * Class is responsible for serializing discovery messages using RU-ready {@link MessageSerializer} mechanism. - *

- * It is used in a special case: when server wants to send discovery messages to clients, it may not have a {@link TcpDiscoveryIoSession} - * to serialize the messages. - * This class enables server to serialize discovery messages anyway, duplicating serialization code from {@link TcpDiscoveryIoSession}. - */ -class TcpDiscoveryMessageSerializer extends TcpDiscoveryIoSession { - /** - * @param spi Discovery SPI instance. - */ - public TcpDiscoveryMessageSerializer(TcpDiscoverySpi spi) { - super(new Socket() { - @Override public OutputStream getOutputStream() throws IOException { - return null; - } - - @Override public InputStream getInputStream() throws IOException { - return null; - } - }, spi); - } - - /** - * Serializes a discovery message into a byte array. - * - * @param msg Discovery message to serialize. - * @return Serialized byte array containing the message data. - * @throws IgniteCheckedException If serialization fails. - * @throws IOException If serialization fails. - */ - byte[] serializeMessage(TcpDiscoveryAbstractMessage msg) throws IgniteCheckedException, IOException { - try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { - serializeMessage((Message)msg, out); - - return out.toByteArray(); - } - } -} diff --git a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/internal/ClientMessageHolder.java b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/internal/ClientMessageHolder.java new file mode 100644 index 0000000000000..8e2f50152b57f --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/internal/ClientMessageHolder.java @@ -0,0 +1,59 @@ +/* + * 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 org.apache.ignite.IgniteCheckedException; +import org.apache.ignite.spi.discovery.tcp.messages.TcpDiscoveryAbstractMessage; +import org.jetbrains.annotations.Nullable; + +/** */ +public class ClientMessageHolder { + /** */ + private final TcpDiscoveryAbstractMessage msg; + + /** */ + private byte[] msgBytes; + + /** */ + public ClientMessageHolder(TcpDiscoveryAbstractMessage msg) { + assert msg != null; + + this.msg = msg; + } + + /** */ + public TcpDiscoveryAbstractMessage message() { + return msg; + } + + /** */ + public synchronized byte @Nullable [] messageBytes() { + return msgBytes; + } + + /** */ + public synchronized void serialize(TcpDiscoveryMessageSerializer ser) throws IgniteCheckedException { + if (msgBytes == null) + msgBytes = ser.serialize(msg); + } + + /** {@inheritDoc} */ + @Override public String toString() { + return msg.toString(); + } +} diff --git a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/internal/TcpDiscoveryMessageSerializer.java b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/internal/TcpDiscoveryMessageSerializer.java new file mode 100644 index 0000000000000..02dfe60eb28cb --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/internal/TcpDiscoveryMessageSerializer.java @@ -0,0 +1,97 @@ +/* + * 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.io.IOException; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import org.apache.ignite.IgniteCheckedException; +import org.apache.ignite.internal.IgniteEx; +import org.apache.ignite.internal.direct.DirectMessageWriter; +import org.apache.ignite.internal.managers.communication.DiscoveryMarshalling; +import org.apache.ignite.internal.util.io.GridByteArrayOutputStream; +import org.apache.ignite.internal.util.nio.MessageSerialization; +import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi; +import org.apache.ignite.spi.discovery.tcp.messages.TcpDiscoveryAbstractMessage; + +/** */ +public class TcpDiscoveryMessageSerializer { + /** Size of the intermediate buffer a message is serialized through. */ + private static final int BUFFER_SIZE = 100; + + /** */ + private final TcpDiscoverySpi spi; + + /** */ + private final DirectMessageWriter writer; + + /** */ + private final ByteBuffer buf = ByteBuffer.allocate(BUFFER_SIZE); + + /** @param spi Discovery SPI whose messages this serializer writes. */ + public TcpDiscoveryMessageSerializer(TcpDiscoverySpi spi) { + this.spi = spi; + + writer = new DirectMessageWriter(spi.messageFactory()); + } + + /** + * Serializes a discovery message into given output stream. + * + * @param msg Discovery message to serialize. + * @param out Output stream to write serialized message. + * @throws IgniteCheckedException If serialization fails. + * @throws IOException If serialization fails. + */ + public void writeTo(TcpDiscoveryAbstractMessage msg, OutputStream out) throws IgniteCheckedException, IOException { + DiscoveryMarshalling.marshal(msg, ((IgniteEx)spi.ignite()).context(), null); + + writer.reset(); + writer.setBuffer(buf); + + boolean finished; + + do { + // Should be cleared before first operation. + buf.clear(); + + finished = MessageSerialization.writeTo(spi.messageFactory(), msg, writer); + + out.write(buf.array(), 0, buf.position()); + } + while (!finished); + } + + /** + * Serializes a discovery message into a byte array. + * + * @param msg Discovery message to serialize. + * @return Serialized byte array containing the message data. + * @throws IgniteCheckedException If serialization fails. + */ + public byte[] serialize(TcpDiscoveryAbstractMessage msg) throws IgniteCheckedException { + try (GridByteArrayOutputStream out = new GridByteArrayOutputStream()) { + writeTo(msg, out); + + return out.toByteArray(); + } + catch (IOException e) { + throw new IgniteCheckedException("Failed to serialize a discovery message: " + msg, e); + } + } +} From d1d77e7162c29cae0ce6d36f32f86b63081a8abb Mon Sep 17 00:00:00 2001 From: Mikhail Petrov Date: Fri, 28 Aug 2026 10:25:27 +0300 Subject: [PATCH 4/8] IGNITE-28851 --- .../JmhDirectMessageReaderBenchmark.java | 5 +- .../apache/ignite/internal/DeprecatedBy.java | 46 ++ .../apache/ignite/internal/IntroducedBy.java | 46 ++ .../ignite/internal/MessageProcessor.java | 85 +++ .../internal/MessageSerializerGenerator.java | 153 ++++- .../internal/MessageSerializationContext.java | 93 +++ .../rollingupgrade/feature/IgniteFeature.java | 0 .../internal/direct/DirectMessageReader.java | 31 +- .../internal/direct/DirectMessageWriter.java | 37 +- .../IgniteMessageSerializationContext.java | 252 ++++++++ .../direct/stream/DirectByteBufferStream.java | 80 ++- .../CompressedMessageSerializer.java | 5 +- .../feature/IgniteComponentFeatureSet.java | 5 + .../tcp/internal/ClusterStateProvider.java | 5 + .../tcp/internal/GridNioServerWrapper.java | 9 +- .../internal/InboundConnectionHandler.java | 20 + .../tcp/internal/TcpHandshakeExecutor.java | 7 +- .../ignite/spi/discovery/tcp/ClientImpl.java | 2 +- .../ignite/spi/discovery/tcp/ServerImpl.java | 15 +- .../discovery/tcp/TcpDiscoveryIoSession.java | 21 +- .../spi/discovery/tcp/TcpDiscoverySpi.java | 33 - .../tcp/internal/ClientMessageHolder.java | 15 +- .../TcpDiscoveryMessageSerializer.java | 15 +- .../codegen/MessageProcessorTest.java | 48 ++ .../direct/DirectMarshallingMessagesTest.java | 5 +- .../AbstractMessageSerializationTest.java | 22 +- .../communication/CompressedMessageTest.java | 15 +- ...dIoManagerOrderedUnmarshalFailureTest.java | 5 +- .../cache/CacheMetricsCacheSizeTest.java | 6 +- ...acheContinuousQueryImmutableEntryTest.java | 5 +- .../QueryEntityMessageSerializationTest.java | 5 +- ...ollingUpgradeMessageSerializationTest.java | 592 ++++++++++++++++++ .../message/TestCoreMessage.java | 114 ++++ .../message/TestDefaultRegistryMessage.java | 86 +++ .../rollingupgrade/message/TestMessage.java | 57 ++ .../message/TestPluginMessage.java | 109 ++++ ...ConfigurationMessageSerializationTest.java | 5 +- ...stributedProcessResultMarshallingTest.java | 9 +- .../IgniteExceptionInNioWorkerSelfTest.java | 5 +- .../communication/MessageMarshalOnceTest.java | 9 +- ...tyBasicPermissionSetSerializationTest.java | 5 +- .../tcp/TestDelayMessageSerializer.java | 5 +- .../DiscoveryUnmarshalVulnerabilityTest.java | 12 +- .../ignite/testframework/GridTestNode.java | 33 +- .../junits/common/GridCommonAbstractTest.java | 4 +- .../testsuites/IgniteBasicTestSuite.java | 2 + .../codegen/ChildMessageSerializer.java | 5 +- .../CorrectEmptyMessageSerializer.java | 5 +- ...stomMapperEnumFieldsMessageSerializer.java | 9 +- ...aultMapperEnumFieldsMessageSerializer.java | 13 +- .../TestCollectionsMessageSerializer.java | 109 ++-- .../codegen/TestEnumSetMessageSerializer.java | 17 +- .../codegen/TestFeatureConflictMessage.java | 29 + .../codegen/TestFeatureRegistry.java | 27 + .../codegen/TestInvalidFeatureMessage.java | 28 + .../codegen/TestInvalidFeatureRegistry.java | 24 + .../codegen/TestMapMessageSerializer.java | 109 ++-- .../TestMarshallableMessageSerializer.java | 5 +- ...stMarshalledArrayMapMessageSerializer.java | 21 +- ...MarshalledCollectionMessageSerializer.java | 9 +- .../TestMarshalledMapMessageSerializer.java | 13 +- .../TestMarshalledMessageSerializer.java | 5 +- ...estMarshalledObjectsMessageSerializer.java | 9 +- .../codegen/TestMessageSerializer.java | 21 +- .../codegen/TestOrderlessFeatureMessage.java | 31 + .../TestRollingUpgradeAwareMessage.java | 48 ++ ...tRollingUpgradeAwareMessageSerializer.java | 128 ++++ .../codegen/TestUnknownFeatureMessage.java | 28 + .../internal/util/nio/GridDirectParser.java | 3 +- .../internal/util/nio/GridNioServer.java | 23 +- .../util/nio/GridNioSessionMetaKey.java | 5 +- .../util/nio/MessageSerialization.java | 32 +- .../communication/MessageReader.java | 23 +- .../communication/MessageSerializer.java | 8 +- .../communication/MessageWriter.java | 24 +- .../zk/internal/DiscoveryMessageParser.java | 6 +- 76 files changed, 2556 insertions(+), 399 deletions(-) create mode 100644 modules/codegen/src/main/java/org/apache/ignite/internal/DeprecatedBy.java create mode 100644 modules/codegen/src/main/java/org/apache/ignite/internal/IntroducedBy.java create mode 100644 modules/commons/src/main/java/org/apache/ignite/internal/MessageSerializationContext.java rename modules/{core => commons}/src/main/java/org/apache/ignite/internal/processors/rollingupgrade/feature/IgniteFeature.java (100%) create mode 100644 modules/core/src/main/java/org/apache/ignite/internal/direct/IgniteMessageSerializationContext.java create mode 100644 modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/message/RollingUpgradeMessageSerializationTest.java create mode 100644 modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/message/TestCoreMessage.java create mode 100644 modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/message/TestDefaultRegistryMessage.java create mode 100644 modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/message/TestMessage.java create mode 100644 modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/message/TestPluginMessage.java create mode 100644 modules/core/src/test/resources/codegen/TestFeatureConflictMessage.java create mode 100644 modules/core/src/test/resources/codegen/TestFeatureRegistry.java create mode 100644 modules/core/src/test/resources/codegen/TestInvalidFeatureMessage.java create mode 100644 modules/core/src/test/resources/codegen/TestInvalidFeatureRegistry.java create mode 100644 modules/core/src/test/resources/codegen/TestOrderlessFeatureMessage.java create mode 100644 modules/core/src/test/resources/codegen/TestRollingUpgradeAwareMessage.java create mode 100644 modules/core/src/test/resources/codegen/TestRollingUpgradeAwareMessageSerializer.java create mode 100644 modules/core/src/test/resources/codegen/TestUnknownFeatureMessage.java diff --git a/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/direct/JmhDirectMessageReaderBenchmark.java b/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/direct/JmhDirectMessageReaderBenchmark.java index 7c93e2be90b60..6735e9514b407 100644 --- a/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/direct/JmhDirectMessageReaderBenchmark.java +++ b/modules/benchmarks/src/main/java/org/apache/ignite/internal/benchmarks/jmh/direct/JmhDirectMessageReaderBenchmark.java @@ -44,6 +44,7 @@ import org.openjdk.jmh.annotations.Warmup; import static java.util.concurrent.TimeUnit.SECONDS; +import static org.apache.ignite.internal.MessageSerializationContext.IGNORED; import static org.openjdk.jmh.annotations.Mode.Throughput; /** Benchmarks the {@link DirectMessageReader} compressed-field hot path. */ @@ -96,7 +97,7 @@ public void setup() { writer.setBuffer(buf); - boolean finished = writer.writeMessage(msg, true); + boolean finished = writer.writeMessage(msg, true, IGNORED); if (!finished) throw new IllegalStateException("Message does not fit into the buffer."); @@ -111,7 +112,7 @@ public Message compressedMessage() { reader.setBuffer(buf); - Message msg = reader.readMessage(true); + Message msg = reader.readMessage(true, IGNORED); reader.reset(); diff --git a/modules/codegen/src/main/java/org/apache/ignite/internal/DeprecatedBy.java b/modules/codegen/src/main/java/org/apache/ignite/internal/DeprecatedBy.java new file mode 100644 index 0000000000000..df8fb672984ff --- /dev/null +++ b/modules/codegen/src/main/java/org/apache/ignite/internal/DeprecatedBy.java @@ -0,0 +1,46 @@ +/* + * 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; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.apache.ignite.internal.processors.rollingupgrade.feature.IgniteFeature; + +/** + * Marks a message field that is planned for removal in a future release. Removing the field requires introducing a new + * {@link IgniteFeature} to which this annotation must be linked. + * + *

A field annotated with this annotation is excluded from message serialization when doing so does not break + * backward compatibility during a Rolling Upgrade.

+ * + * @see IgniteFeature + */ +@Retention(RetentionPolicy.SOURCE) +@Target(ElementType.FIELD) +public @interface DeprecatedBy { + /** @return Name of the Ignite feature that deprecated this field. */ + String value(); + + /** + * @return Class of the registry containing the Ignite feature with the specified name. + * By default, the Ignite Core feature registry is used. + */ + Class registry() default Void.class; +} diff --git a/modules/codegen/src/main/java/org/apache/ignite/internal/IntroducedBy.java b/modules/codegen/src/main/java/org/apache/ignite/internal/IntroducedBy.java new file mode 100644 index 0000000000000..610a19aec4d2f --- /dev/null +++ b/modules/codegen/src/main/java/org/apache/ignite/internal/IntroducedBy.java @@ -0,0 +1,46 @@ +/* + * 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; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.apache.ignite.internal.processors.rollingupgrade.feature.IgniteFeature; + +/** + * Marks a newly introduced message field. Introducing a new field requires introducing a new + * {@link IgniteFeature} to which this annotation must be linked. + * + *

A field annotated with this annotation is included in message serialization only when doing so does not break + * backward compatibility during a Rolling Upgrade.

+ * + * @see IgniteFeature + */ +@Retention(RetentionPolicy.SOURCE) +@Target(ElementType.FIELD) +public @interface IntroducedBy { + /** @return Name of the Ignite feature that introduced this field. */ + String value(); + + /** + * @return Class of the registry containing the Ignite feature with the specified name. + * By default, the Ignite Core feature registry is used. + */ + Class registry() default Void.class; +} diff --git a/modules/codegen/src/main/java/org/apache/ignite/internal/MessageProcessor.java b/modules/codegen/src/main/java/org/apache/ignite/internal/MessageProcessor.java index 8b4ead738ee22..25c4b71aca9ab 100644 --- a/modules/codegen/src/main/java/org/apache/ignite/internal/MessageProcessor.java +++ b/modules/codegen/src/main/java/org/apache/ignite/internal/MessageProcessor.java @@ -48,9 +48,11 @@ import org.apache.ignite.internal.systemview.SystemViewRowAttributeWalkerProcessor; import org.apache.ignite.internal.util.typedef.F; import org.apache.ignite.lang.IgniteBiTuple; +import org.jetbrains.annotations.Nullable; import static org.apache.ignite.internal.MessageSerializerGenerator.DLFT_ENUM_MAPPER_CLS; import static org.apache.ignite.internal.MessageSerializerGenerator.enumType; +import static org.apache.ignite.internal.MessageSerializerGenerator.resolveFeatureRegistry; /** * Annotation processor that generates serialization and deserialization code for classes implementing the {@code Message} interface. @@ -105,6 +107,9 @@ public class MessageProcessor extends AbstractProcessor { /** */ public static final Set NO_PUBLIC_CTOR_MSGS = Set.of(GRID_H2_NULL, ZK_NO_SERVERS_MESSAGE); + /** */ + private static final String IGNITE_FEATURE_CLS = "org.apache.ignite.internal.processors.rollingupgrade.feature.IgniteFeature"; + /** Messages with no fields. A serializer generation intentionally skipped. */ static final String[] SKIP_MESSAGES = { "org.apache.ignite.internal.processors.odbc.ClientMessage", @@ -274,6 +279,14 @@ private List> hierarchicalOrderedFields(TypeElement type) } validateEnumFieldMapping(type, el); + validateRollingUpgradeAnnotations(el); + } + else if (el.getAnnotation(IntroducedBy.class) != null || el.getAnnotation(DeprecatedBy.class) != null) { + processingEnv.getMessager().printMessage( + Diagnostic.Kind.ERROR, + "Annotations @IntroducedBy and @DeprecatedBy guard a serialized field, so the field they are " + + "applied to must also be annotated with @Order.", + el); } } @@ -282,6 +295,78 @@ private List> hierarchicalOrderedFields(TypeElement type) return hierList; } + /** */ + private void validateRollingUpgradeAnnotations(Element el) { + IntroducedBy introducedAnn = el.getAnnotation(IntroducedBy.class); + DeprecatedBy deprecatedAnn = el.getAnnotation(DeprecatedBy.class); + + if (introducedAnn == null && deprecatedAnn == null) + return; + + String introducedFeature = introducedAnn == null + ? null + : resolveFeatureClassName(el, introducedAnn.value(), resolveFeatureRegistry(introducedAnn::registry)); + + String deprecatedFeature = deprecatedAnn == null + ? null + : resolveFeatureClassName(el, deprecatedAnn.value(), resolveFeatureRegistry(deprecatedAnn::registry)); + + if (introducedFeature == null || deprecatedFeature == null) + return; + + if (introducedFeature.equals(deprecatedFeature)) { + processingEnv.getMessager().printMessage( + Diagnostic.Kind.ERROR, + "@IntroducedBy and @DeprecatedBy must not reference the same feature when used on the same element.", + el); + } + } + + /** */ + @Nullable private String resolveFeatureClassName(Element el, String featureName, String regCls) { + TypeElement registryType = processingEnv.getElementUtils().getTypeElement(regCls); + + if (registryType == null) { + processingEnv.getMessager().printMessage( + Diagnostic.Kind.ERROR, "Cannot resolve the feature registry class [registry=" + regCls + ']', el); + + return null; + } + + for (Element regTypeElement : registryType.getEnclosedElements()) { + if (regTypeElement.getKind() != ElementKind.FIELD || !regTypeElement.getSimpleName().contentEquals(featureName)) + continue; + + Set mods = regTypeElement.getModifiers(); + + if (!mods.contains(Modifier.PUBLIC) || !mods.contains(Modifier.STATIC) || !mods.contains(Modifier.FINAL)) { + processingEnv.getMessager().printMessage( + Diagnostic.Kind.ERROR, + "Feature constant must be public static final [registry=" + regCls + ", feature=" + featureName + ']', el); + + return null; + } + + TypeElement featureElement = processingEnv.getElementUtils().getTypeElement(IGNITE_FEATURE_CLS); + + if (featureElement != null && !processingEnv.getTypeUtils().isAssignable(regTypeElement.asType(), featureElement.asType())) { + processingEnv.getMessager().printMessage( + Diagnostic.Kind.ERROR, + "Feature constant must be of type IgniteFeature [registry=" + regCls + ", feature=" + featureName + ']', el); + + return null; + } + + return regCls + '.' + featureName; + } + + processingEnv.getMessager().printMessage( + Diagnostic.Kind.ERROR, "No such feature constant in the registry [feature=" + featureName + + ", registry=" + regCls + ']', el); + + return null; + } + /** * Validates consistency of enum field mappers configuration: the same mapper is used for the same enum across different messages, * CustomMapper annotation is used only for enum fields. diff --git a/modules/codegen/src/main/java/org/apache/ignite/internal/MessageSerializerGenerator.java b/modules/codegen/src/main/java/org/apache/ignite/internal/MessageSerializerGenerator.java index 81680f1b1088c..33f642cac734e 100644 --- a/modules/codegen/src/main/java/org/apache/ignite/internal/MessageSerializerGenerator.java +++ b/modules/codegen/src/main/java/org/apache/ignite/internal/MessageSerializerGenerator.java @@ -30,6 +30,7 @@ import java.util.Set; import java.util.TreeSet; import java.util.UUID; +import java.util.function.Supplier; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; @@ -38,6 +39,7 @@ import javax.lang.model.element.VariableElement; import javax.lang.model.type.ArrayType; import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.MirroredTypeException; import javax.lang.model.type.PrimitiveType; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; @@ -68,6 +70,13 @@ public class MessageSerializerGenerator extends MessageCompanionGenerator { /** */ private static final String MESSAGE_READER_CLS = "org.apache.ignite.plugin.extensions.communication.MessageReader"; + /** */ + private static final String MESSAGE_SER_CTX_CLS = "org.apache.ignite.internal.MessageSerializationContext"; + + /** */ + private static final String DFLT_FEATURE_REG_CLS = + "org.apache.ignite.internal.processors.rollingupgrade.feature.SupportedFeatureRegistry"; + /** */ private static final String ENUM_MAPPER_CLS = "org.apache.ignite.plugin.extensions.communication.mappers.EnumMapper"; @@ -145,6 +154,7 @@ public class MessageSerializerGenerator extends MessageCompanionGenerator { imports.add(MESSAGE_SERIALIZER_CLS); imports.add(MESSAGE_WRITER_CLS); imports.add(MESSAGE_READER_CLS); + imports.add(MESSAGE_SER_CTX_CLS); writeClassHeader(writer, "MessageSerializer", serClsName); @@ -195,8 +205,10 @@ private void generateMethods(List fields) throws Exception { private void generateMethod(List code, List fields, boolean write) throws Exception { code.add(indentedLine(METHOD_JAVADOC)); - code.add(indentedLine("@Override public final boolean %s(" + simpleNameWithGeneric(type) + " msg, %s) {", - write ? "writeTo" : "readFrom", write ? "MessageWriter writer" : "MessageReader reader")); + code.add(indentedLine( + "@Override public final boolean %s(" + simpleNameWithGeneric(type) + " msg, %s, MessageSerializationContext ctx) {", + write ? "writeTo" : "readFrom", + write ? "MessageWriter writer" : "MessageReader reader")); indent++; @@ -261,7 +273,7 @@ private void processField(VariableElement field, int opt, boolean write) throws throw new UnsupportedOperationException("You should use ErrorMessage for serialization of throwables."); if (write) - writeField(opt, callExpr(field, true)); + writeField(field, opt, callExpr(field, true)); else readField(field, opt, callExpr(field, false)); } @@ -288,6 +300,55 @@ private String callExpr(VariableElement field, boolean write) throws Exception { return write ? call.expr("writer.write", fieldRef(field)) : call.expr("reader.read", null); } + /** */ + @Nullable private String buildRollingUpgradeFeatureGuard(VariableElement field) { + List conditions = new ArrayList<>(); + + IntroducedBy introducedAnn = field.getAnnotation(IntroducedBy.class); + + if (introducedAnn != null) { + conditions.add("ctx.includeFieldIntroducedBy(" + buildFeatureArgument( + introducedAnn.value(), + resolveFeatureRegistry(introducedAnn::registry) + ) + ")"); + } + + DeprecatedBy deprecatedAnn = field.getAnnotation(DeprecatedBy.class); + + if (deprecatedAnn != null) { + conditions.add("ctx.includeFieldDeprecatedBy(" + buildFeatureArgument( + deprecatedAnn.value(), + resolveFeatureRegistry(deprecatedAnn::registry) + ) + ")"); + } + + if (conditions.isEmpty()) + return null; + + return String.join(" && ", conditions); + } + + /** */ + private String buildFeatureArgument(String featureName, String registryCls) { + imports.add(registryCls); + + return registryCls.substring(registryCls.lastIndexOf('.') + 1) + "." + featureName; + } + + /** */ + static String resolveFeatureRegistry(Supplier> registryClsAccessor) { + String name; + + try { + name = registryClsAccessor.get().getName(); + } + catch (MirroredTypeException e) { + name = qualifiedClassName(e.getTypeMirror()); + } + + return Void.class.getName().equals(name) ? DFLT_FEATURE_REG_CLS : name; + } + /** * Generate code for processing write of single field: *
@@ -301,13 +362,27 @@ private String callExpr(VariableElement field, boolean write) throws Exception {
      * @param opt Case option.
      * @param writeExpr Writer call expression.
      */
-    private void writeField(int opt, String writeExpr) {
+    private void writeField(VariableElement field, int opt, String writeExpr) {
         write.add(indentedLine("case %d:", opt));
 
         indent++;
 
+        String guard = buildRollingUpgradeFeatureGuard(field);
+
+        if (guard != null) {
+            write.add(indentedLine("if (%s) {", guard));
+
+            indent++;
+        }
+
         returnFalseIf(write, "!" + writeExpr);
 
+        if (guard != null) {
+            indent--;
+
+            write.add(indentedLine("}"));
+        }
+
         write.add(EMPTY);
         write.add(indentedLine("writer.incrementState();"));
         write.add(EMPTY);
@@ -335,11 +410,25 @@ private void readField(VariableElement field, int opt, String readExpr) {
 
         indent++;
 
+        String guard = buildRollingUpgradeFeatureGuard(field);
+
+        if (guard != null) {
+            read.add(indentedLine("if (%s) {", guard));
+
+            indent++;
+        }
+
         read.add(indentedLine("%s = %s;", fieldRef(field), readExpr));
         read.add(EMPTY);
 
         returnFalseIf(read, "!reader.isLastRead()");
 
+        if (guard != null) {
+            indent--;
+
+            read.add(indentedLine("}"));
+        }
+
         read.add(EMPTY);
         read.add(indentedLine("reader.incrementState();"));
         read.add(EMPTY);
@@ -360,13 +449,13 @@ private FieldCall fieldCall(VariableElement field) throws Exception {
             checkTypeForCompress(type);
 
         if (type.getKind().isPrimitive())
-            return new FieldCall(capitalizeOnlyFirst(type.getKind().name()), null, false);
+            return FieldCall.scalar(capitalizeOnlyFirst(type.getKind().name()));
 
         if (type.getKind() == TypeKind.ARRAY) {
             TypeMirror compType = ((ArrayType)type).getComponentType();
 
             if (compType.getKind().isPrimitive())
-                return new FieldCall(capitalizeOnlyFirst(compType.getKind().name()) + "Array", null, false);
+                return FieldCall.scalar(capitalizeOnlyFirst(compType.getKind().name()) + "Array");
 
             if (compType.getKind() == TypeKind.DECLARED) {
                 Element compElem = ((DeclaredType)compType).asElement();
@@ -375,52 +464,52 @@ private FieldCall fieldCall(VariableElement field) throws Exception {
                     imports.add(((QualifiedNameable)compElem).getQualifiedName().toString());
             }
 
-            return new FieldCall("ObjectArray", messageCollectionItemTypes(field, type), false);
+            return FieldCall.collection("ObjectArray", messageCollectionItemTypes(field, type), false);
         }
 
         if (type.getKind() == TypeKind.DECLARED) {
             if (sameType(type, String.class))
-                return new FieldCall("String", null, false);
+                return FieldCall.scalar("String");
 
             if (sameType(type, BitSet.class))
-                return new FieldCall("BitSet", null, false);
+                return FieldCall.scalar("BitSet");
 
             if (sameType(type, UUID.class))
-                return new FieldCall("Uuid", null, false);
+                return FieldCall.scalar("Uuid");
 
             if (sameType(type, IGNITE_UUID_CLS))
-                return new FieldCall("IgniteUuid", null, false);
+                return FieldCall.scalar("IgniteUuid");
 
             if (sameType(type, AFFINITY_TOPOLOGY_VERSION_CLS))
-                return new FieldCall("AffinityTopologyVersion", null, false);
+                return FieldCall.scalar("AffinityTopologyVersion");
 
             if (assignableFrom(erasedType(type), type(Map.class.getName())))
-                return new FieldCall("Map", messageCollectionItemTypes(field, type), compress);
+                return FieldCall.collection("Map", messageCollectionItemTypes(field, type), compress);
 
             if (assignableFrom(type, type(KEY_CACHE_OBJECT_CLS)))
-                return new FieldCall("KeyCacheObject", null, false);
+                return FieldCall.scalar("KeyCacheObject");
 
             if (assignableFrom(type, type(CACHE_OBJECT_CLS)))
-                return new FieldCall("CacheObject", null, false);
+                return FieldCall.scalar("CacheObject");
 
             if (assignableFrom(type, type(GRID_LONG_LIST_CLS)))
-                return new FieldCall("GridLongList", null, false);
+                return FieldCall.scalar("GridLongList");
 
             if (assignableFrom(type, type(IGNITE_PRODUCT_VERSION_CLS)))
-                return new FieldCall("IgniteProductVersion", null, false);
+                return FieldCall.scalar("IgniteProductVersion");
 
             if (assignableFrom(type, type(GRID_CACHE_VERSION_CLS)))
-                return new FieldCall("GridCacheVersion", null, false);
+                return FieldCall.scalar("GridCacheVersion");
 
             if (assignableFrom(type, type(MESSAGE_INTERFACE))) {
                 if (sameType(type, COMPRESSED_MESSAGE_CLASS))
                     throw new IllegalArgumentException(COMPRESSED_MSG_ERROR);
 
-                return new FieldCall("Message", null, compress);
+                return FieldCall.message(compress);
             }
 
             if (assignableFrom(erasedType(type), type(Collection.class.getName())))
-                return new FieldCall("Collection", messageCollectionItemTypes(field, type), false);
+                return FieldCall.collection("Collection", messageCollectionItemTypes(field, type), false);
 
             throw new IllegalArgumentException("Unsupported declared type: " + type);
         }
@@ -756,10 +845,14 @@ private static final class FieldCall {
         private final boolean compress;
 
         /** */
-        private FieldCall(String mtd, @Nullable String collDesc, boolean compress) {
+        private final boolean requiresCtx;
+
+        /** */
+        private FieldCall(String mtd, @Nullable String collDesc, boolean compress, boolean requiresCtx) {
             this.mtd = mtd;
             this.collDesc = collDesc;
             this.compress = compress;
+            this.requiresCtx = requiresCtx;
         }
 
         /** @return Full call expression; {@code valArg}, when given, is passed as the first argument (write side). */
@@ -775,8 +868,26 @@ private String expr(String mtdPrefix, @Nullable String valArg) {
             if (compress)
                 args.add("true");
 
+            if (requiresCtx)
+                args.add("ctx");
+
             return mtdPrefix + mtd + "(" + String.join(", ", args) + ")";
         }
+
+        /** */
+        private static FieldCall scalar(String mtd) {
+            return new FieldCall(mtd, null, false, false);
+        }
+
+        /** */
+        private static FieldCall collection(String mtd, String collDesc, boolean compress) {
+            return new FieldCall(mtd, collDesc, compress, true);
+        }
+
+        /** */
+        private static FieldCall message(boolean compress) {
+            return new FieldCall("Message", null, compress, true);
+        }
     }
 
     /** */
diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/MessageSerializationContext.java b/modules/commons/src/main/java/org/apache/ignite/internal/MessageSerializationContext.java
new file mode 100644
index 0000000000000..5fe1f2373d495
--- /dev/null
+++ b/modules/commons/src/main/java/org/apache/ignite/internal/MessageSerializationContext.java
@@ -0,0 +1,93 @@
+/*
+ * 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;
+
+import org.apache.ignite.internal.processors.rollingupgrade.feature.IgniteFeature;
+import org.apache.ignite.lang.IgniteFuture;
+
+/**
+ * Represents a provider of data required to determine how data transmitted between nodes is serialized and deserialized.
+ */
+public interface MessageSerializationContext {
+    /**
+     * @param feature Feature that deprecated the field.
+     * @return {@code true} if the message field should be included during serialization or deserialization.
+     */
+    boolean includeFieldDeprecatedBy(IgniteFeature feature);
+
+    /**
+     * @param feature Feature that introduced the field.
+     * @return {@code true} if the message field should be included during serialization or deserialization.
+     */
+    boolean includeFieldIntroducedBy(IgniteFeature feature);
+
+    /**
+     * {@link MessageSerializationContext} implementation that ignores Rolling Upgrade compatibility during
+     * message serialization.
+     *
+     * 

Using this context instructs the serialization framework to always serialize the actual message state: all + * newly introduced fields are included, and all deprecated fields are excluded.

+ */ + MessageSerializationContext IGNORED = new MessageSerializationContext() { + /** {@inheritDoc} */ + @Override public boolean includeFieldDeprecatedBy(IgniteFeature feature) { + return false; + } + + /** {@inheritDoc} */ + @Override public boolean includeFieldIntroducedBy(IgniteFeature feature) { + return true; + } + + /** {@inheritDoc} */ + @Override public String toString() { + return "MessageSerializationContext [IGNORED]"; + } + }; + + /** + * Stub {@link MessageSerializationContext} implementation used when the serialization context has not yet been determined. + * + *

The serialization context is unavailable between connection establishment and serialization protocol negotiation. + * Messages sent during this period cannot rely on the {@link IgniteFuture} mechanism to adjust the message serialization + * in an RU-compatible way.

+ */ + MessageSerializationContext UNNEGOTIATED = new MessageSerializationContext() { + /** {@inheritDoc} */ + @Override public boolean includeFieldDeprecatedBy(IgniteFeature feature) { + throw buildError(feature); + } + + /** {@inheritDoc} */ + @Override public boolean includeFieldIntroducedBy(IgniteFeature feature) { + throw buildError(feature); + } + + /** {@inheritDoc} */ + @Override public String toString() { + return "MessageSerializationContext [UNNEGOTIATED]"; + } + + /** */ + private IllegalStateException buildError(IgniteFeature feature) { + return new IllegalStateException( + "A feature-guarded field was serialized before the peer's features were negotiated [feature=" + feature + ']' + ); + } + }; +} diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/rollingupgrade/feature/IgniteFeature.java b/modules/commons/src/main/java/org/apache/ignite/internal/processors/rollingupgrade/feature/IgniteFeature.java similarity index 100% rename from modules/core/src/main/java/org/apache/ignite/internal/processors/rollingupgrade/feature/IgniteFeature.java rename to modules/commons/src/main/java/org/apache/ignite/internal/processors/rollingupgrade/feature/IgniteFeature.java diff --git a/modules/core/src/main/java/org/apache/ignite/internal/direct/DirectMessageReader.java b/modules/core/src/main/java/org/apache/ignite/internal/direct/DirectMessageReader.java index 9ec9b68cc189e..b070629a38ba3 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/direct/DirectMessageReader.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/direct/DirectMessageReader.java @@ -24,6 +24,7 @@ import java.util.UUID; import java.util.function.Function; import org.apache.ignite.IgniteException; +import org.apache.ignite.internal.MessageSerializationContext; import org.apache.ignite.internal.direct.state.DirectMessageState; import org.apache.ignite.internal.direct.state.DirectMessageStateItem; import org.apache.ignite.internal.direct.stream.DirectByteBufferStream; @@ -344,7 +345,7 @@ public ByteBuffer getBuffer() { } /** {@inheritDoc} */ - @Nullable @Override public T readMessage(boolean compress) { + @Nullable @Override public T readMessage(boolean compress, MessageSerializationContext ctx) { DirectByteBufferStream stream = curStream; T msg; @@ -352,10 +353,11 @@ public ByteBuffer getBuffer() { if (compress) msg = readCompressedMessageAndDeserialize( stream, - r -> r.state.item().stream.readMessage(r) + r -> r.state.item().stream.readMessage(r, ctx), + ctx ); else { - msg = stream.readMessage(this); + msg = stream.readMessage(this, ctx); lastRead = stream.lastFinished(); } @@ -397,10 +399,10 @@ public ByteBuffer getBuffer() { } /** {@inheritDoc} */ - @Override public T[] readObjectArray(MessageArrayType type) { + @Override public T[] readObjectArray(MessageArrayType type, MessageSerializationContext ctx) { DirectByteBufferStream stream = curStream; - T[] msg = stream.readObjectArray(type, this); + T[] msg = stream.readObjectArray(type, this, ctx); lastRead = stream.lastFinished(); @@ -408,10 +410,10 @@ public ByteBuffer getBuffer() { } /** {@inheritDoc} */ - @Override public > C readCollection(MessageCollectionType type) { + @Override public > C readCollection(MessageCollectionType type, MessageSerializationContext ctx) { DirectByteBufferStream stream = curStream; - C col = stream.readCollection(type, this); + C col = stream.readCollection(type, this, ctx); lastRead = stream.lastFinished(); @@ -419,7 +421,7 @@ public ByteBuffer getBuffer() { } /** {@inheritDoc} */ - @Override public > M readMap(MessageMapType type, boolean compress) { + @Override public > M readMap(MessageMapType type, boolean compress, MessageSerializationContext ctx) { DirectByteBufferStream stream = curStream; M map; @@ -427,10 +429,11 @@ public ByteBuffer getBuffer() { if (compress) map = readCompressedMessageAndDeserialize( stream, - r -> r.state.item().stream.readMap(type, r) + r -> r.state.item().stream.readMap(type, r, ctx), + ctx ); else { - map = stream.readMap(type, this); + map = stream.readMap(type, this, ctx); lastRead = stream.lastFinished(); } @@ -509,8 +512,12 @@ public ByteBuffer getBuffer() { } /** @return Deserialized object. */ - private T readCompressedMessageAndDeserialize(DirectByteBufferStream stream, Function fun) { - Message msg = stream.readMessage(this); + private T readCompressedMessageAndDeserialize( + DirectByteBufferStream stream, + Function fun, + MessageSerializationContext ctx + ) { + Message msg = stream.readMessage(this, ctx); lastRead = stream.lastFinished(); diff --git a/modules/core/src/main/java/org/apache/ignite/internal/direct/DirectMessageWriter.java b/modules/core/src/main/java/org/apache/ignite/internal/direct/DirectMessageWriter.java index 319aae7b7e947..f09632cfbac4e 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/direct/DirectMessageWriter.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/direct/DirectMessageWriter.java @@ -23,6 +23,7 @@ import java.util.Map; import java.util.UUID; import java.util.function.Consumer; +import org.apache.ignite.internal.MessageSerializationContext; import org.apache.ignite.internal.direct.state.DirectMessageState; import org.apache.ignite.internal.direct.state.DirectMessageStateItem; import org.apache.ignite.internal.direct.stream.DirectByteBufferStream; @@ -334,17 +335,18 @@ public ByteBuffer getBuffer() { } /** {@inheritDoc} */ - @Override public boolean writeMessage(@Nullable Message msg, boolean compress) { + @Override public boolean writeMessage(@Nullable Message msg, boolean compress, MessageSerializationContext ctx) { DirectByteBufferStream stream = curStream; if (compress) writeCompressedMessage( - w -> w.state.item().stream.writeMessage(msg, w), + w -> w.state.item().stream.writeMessage(msg, w, ctx), msg == null, - stream + stream, + ctx ); else - stream.writeMessage(msg, this); + stream.writeMessage(msg, this, ctx); return stream.lastFinished(); } @@ -377,35 +379,36 @@ public ByteBuffer getBuffer() { } /** {@inheritDoc} */ - @Override public boolean writeObjectArray(T[] arr, MessageArrayType type) { + @Override public boolean writeObjectArray(T[] arr, MessageArrayType type, MessageSerializationContext ctx) { DirectByteBufferStream stream = curStream; - stream.writeObjectArray(arr, type, this); + stream.writeObjectArray(arr, type, this, ctx); return stream.lastFinished(); } /** {@inheritDoc} */ - @Override public boolean writeCollection(Collection col, MessageCollectionType type) { + @Override public boolean writeCollection(Collection col, MessageCollectionType type, MessageSerializationContext ctx) { DirectByteBufferStream stream = curStream; - stream.writeCollection(col, type, this); + stream.writeCollection(col, type, this, ctx); return stream.lastFinished(); } /** {@inheritDoc} */ - @Override public boolean writeMap(Map map, MessageMapType type, boolean compress) { + @Override public boolean writeMap(Map map, MessageMapType type, boolean compress, MessageSerializationContext ctx) { DirectByteBufferStream stream = curStream; if (compress) writeCompressedMessage( - w -> w.state.item().stream.writeMap(map, type, w), + w -> w.state.item().stream.writeMap(map, type, w, ctx), map == null, - stream + stream, + ctx ); else - stream.writeMap(map, type, this); + stream.writeMap(map, type, this, ctx); return stream.lastFinished(); } @@ -485,8 +488,14 @@ public ByteBuffer getBuffer() { * @param consumer Consumer. * @param isNull {@code True} if message is null. * @param stream Byte buffer stream. + * @param ctx Serialization context. */ - private void writeCompressedMessage(Consumer consumer, boolean isNull, DirectByteBufferStream stream) { + private void writeCompressedMessage( + Consumer consumer, + boolean isNull, + DirectByteBufferStream stream, + MessageSerializationContext ctx + ) { if (isNull) { stream.writeShort(Short.MIN_VALUE); @@ -536,7 +545,7 @@ private void writeCompressedMessage(Consumer consumer, bool stream.serializeFinished(true); } - stream.writeMessage(stream.compressedMessage(), this); + stream.writeMessage(stream.compressedMessage(), this, ctx); if (stream.lastFinished()) { stream.compressedMessage(null); diff --git a/modules/core/src/main/java/org/apache/ignite/internal/direct/IgniteMessageSerializationContext.java b/modules/core/src/main/java/org/apache/ignite/internal/direct/IgniteMessageSerializationContext.java new file mode 100644 index 0000000000000..816f0344c6326 --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/direct/IgniteMessageSerializationContext.java @@ -0,0 +1,252 @@ +/* + * 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.direct; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import org.apache.ignite.Ignite; +import org.apache.ignite.cluster.ClusterNode; +import org.apache.ignite.internal.IgniteEx; +import org.apache.ignite.internal.MessageSerializationContext; +import org.apache.ignite.internal.cluster.ClusterTopologyCheckedException; +import org.apache.ignite.internal.managers.discovery.IgniteClusterNode; +import org.apache.ignite.internal.processors.rollingupgrade.feature.IgniteComponentFeatureSet; +import org.apache.ignite.internal.processors.rollingupgrade.feature.IgniteFeature; +import org.apache.ignite.internal.processors.rollingupgrade.feature.IgniteFeatureSet; +import org.apache.ignite.internal.processors.rollingupgrade.feature.IgniteNodeFeatureSet; +import org.apache.ignite.spi.discovery.tcp.internal.UnsupportedNodeVersionException; +import org.jetbrains.annotations.Nullable; + +/** */ +public class IgniteMessageSerializationContext implements MessageSerializationContext { + /** */ + private final Map ctxByComponent; + + /** */ + private IgniteMessageSerializationContext(Map ctxByComponent) { + this.ctxByComponent = ctxByComponent; + } + + /** {@inheritDoc} */ + @Override public boolean includeFieldIntroducedBy(IgniteFeature feature) { + return componentContext(feature).includeFieldIntroducedBy(feature.id()); + } + + /** {@inheritDoc} */ + @Override public boolean includeFieldDeprecatedBy(IgniteFeature feature) { + return componentContext(feature).includeFieldDeprecatedBy(feature.id()); + } + + /** */ + private ComponentMessageSerializationContext componentContext(IgniteFeature feature) { + ComponentMessageSerializationContext cmpCtx = ctxByComponent.get(feature.componentName()); + + if (cmpCtx == null) { + throw new IllegalStateException( + "A field is guarded by a feature of an undeclared component. The component must register its messages " + + "and feature set [feature=" + feature + ", component=" + feature.componentName() + + ", declaredComponents=" + ctxByComponent.keySet() + ']' + ); + } + + return cmpCtx; + } + + /** */ + public static IgniteMessageSerializationContext buildForPeers( + Ignite loc, + ClusterNode rmt + ) throws UnsupportedNodeVersionException, ClusterTopologyCheckedException { + assert rmt instanceof IgniteClusterNode : rmt; + + return buildForPeers(loc, resolveRemoteFeatures((IgniteEx)loc, (IgniteClusterNode)rmt)); + } + + /** */ + public static IgniteMessageSerializationContext buildForPeers( + Ignite loc, + IgniteNodeFeatureSet rmt + ) throws UnsupportedNodeVersionException { + return buildForPeers(((IgniteEx)loc).context().localNodeFeatures(), rmt); + } + + /** */ + public static IgniteMessageSerializationContext buildForPeers( + IgniteNodeFeatureSet loc, + IgniteNodeFeatureSet rmt + ) throws UnsupportedNodeVersionException { + assert loc != null; + + if (rmt == null) { + throw new UnsupportedNodeVersionException("Failed to build the message serialization context for the remote node." + + " The remote node's feature set is unavailable."); + } + + Set components = new HashSet<>(loc.components()); + + components.addAll(rmt.components()); + + Map ctxByComponent = new HashMap<>(); + + for (String cmp : components) { + ComponentMessageSerializationContext ctx = resolveComponentSerializationContext( + cmp, + loc.componentFeatures(cmp), + rmt.componentFeatures(cmp) + ); + + ctxByComponent.put(cmp, ctx); + } + + return new IgniteMessageSerializationContext(ctxByComponent); + } + + /** */ + private static @Nullable IgniteNodeFeatureSet resolveRemoteFeatures( + IgniteEx loc, + IgniteClusterNode rmt + ) throws ClusterTopologyCheckedException { + IgniteNodeFeatureSet features = rmt.features(); + + if (features != null) + return features; + + // Node features are unavailable when the node instance was created by Java deserialization + // (see {@link GridAffinityAssignment}). In this case, resolve the node again from the + // local discovery cache by its ID. + ClusterNode cachedNode = loc.context().discovery().node(rmt.id()); + + if (cachedNode == null) { + throw new ClusterTopologyCheckedException( + "Failed to resolve the remote node by ID because it has left the cluster [nodeId=" + rmt.id() + ']'); + } + + assert cachedNode instanceof IgniteClusterNode : cachedNode; + + return ((IgniteClusterNode)cachedNode).features(); + } + + /** */ + private static ComponentMessageSerializationContext resolveComponentSerializationContext( + String cmpName, + @Nullable IgniteComponentFeatureSet locCmpFeatures, + @Nullable IgniteComponentFeatureSet rmtCmpFeatures + ) throws UnsupportedNodeVersionException { + assert locCmpFeatures != null || rmtCmpFeatures != null; + + if (locCmpFeatures == null || rmtCmpFeatures == null) + return new ComponentMessageSerializationContext(null, null); + + int c = locCmpFeatures.version().compareTo(rmtCmpFeatures.version()); + + if (c == 0) { + assert locCmpFeatures.features().equals(rmtCmpFeatures.features()); + + return new ComponentMessageSerializationContext(null, rmtCmpFeatures.features()); + } + else { + IgniteComponentFeatureSet src = c < 0 ? locCmpFeatures : rmtCmpFeatures; + IgniteComponentFeatureSet target = c < 0 ? rmtCmpFeatures : locCmpFeatures; + + if (!src.isUpgradableTo(target)) { + throw new UnsupportedNodeVersionException("Remote node component versions are not supported" + + " [component=" + cmpName + + ", locComponent=" + locCmpFeatures + + ", rmtComponent=" + rmtCmpFeatures + ']'); + } + + return new ComponentMessageSerializationContext(src.features(), src.features()); + } + } + + /** {@inheritDoc} */ + @Override public String toString() { + return "IgniteMessageSerializationContext " + ctxByComponent; + } + + /** {@inheritDoc} */ + @Override public boolean equals(Object o) { + if (this == o) + return true; + + if (o == null || getClass() != o.getClass()) + return false; + + return Objects.equals(ctxByComponent, ((IgniteMessageSerializationContext)o).ctxByComponent); + } + + /** {@inheritDoc} */ + @Override public int hashCode() { + return Objects.hashCode(ctxByComponent); + } + + /** */ + private static final class ComponentMessageSerializationContext { + /** */ + @Nullable private final IgniteFeatureSet excludedDeprecatedFields; + + /** */ + @Nullable private final IgniteFeatureSet includedIntroducedFields; + + /** */ + private ComponentMessageSerializationContext( + @Nullable IgniteFeatureSet excludedDeprecatedFields, + @Nullable IgniteFeatureSet includedIntroducedFields + ) { + this.excludedDeprecatedFields = excludedDeprecatedFields; + this.includedIntroducedFields = includedIntroducedFields; + } + + /** */ + boolean includeFieldIntroducedBy(int featureId) { + return includedIntroducedFields != null && includedIntroducedFields.contains(featureId); + } + + /** */ + boolean includeFieldDeprecatedBy(int featureId) { + return excludedDeprecatedFields == null || !excludedDeprecatedFields.contains(featureId); + } + + /** {@inheritDoc} */ + @Override public boolean equals(Object o) { + if (this == o) + return true; + + if (o == null || getClass() != o.getClass()) + return false; + + ComponentMessageSerializationContext other = (ComponentMessageSerializationContext)o; + + return Objects.equals(excludedDeprecatedFields, other.excludedDeprecatedFields) + && Objects.equals(includedIntroducedFields, other.includedIntroducedFields); + } + + /** {@inheritDoc} */ + @Override public int hashCode() { + return Objects.hash(includedIntroducedFields, excludedDeprecatedFields); + } + + /** {@inheritDoc} */ + @Override public String toString() { + return "ComponentContext [introduced=" + includedIntroducedFields + ", deprecated=" + excludedDeprecatedFields + ']'; + } + } +} diff --git a/modules/core/src/main/java/org/apache/ignite/internal/direct/stream/DirectByteBufferStream.java b/modules/core/src/main/java/org/apache/ignite/internal/direct/stream/DirectByteBufferStream.java index c0752343b5441..965bf5d930f04 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/direct/stream/DirectByteBufferStream.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/direct/stream/DirectByteBufferStream.java @@ -33,6 +33,7 @@ import java.util.function.Supplier; import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.IgniteException; +import org.apache.ignite.internal.MessageSerializationContext; import org.apache.ignite.internal.managers.communication.CompressedMessage; import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion; import org.apache.ignite.internal.processors.cache.CacheObject; @@ -926,11 +927,12 @@ public void writeGridLongList(@Nullable GridLongList val) { /** * @param msg Message. * @param writer Writer. + * @param ctx Serialization context. */ - public void writeMessage(Message msg, MessageWriter writer) { + public void writeMessage(Message msg, MessageWriter writer, MessageSerializationContext ctx) { if (msg != null) { if (buf.hasRemaining()) - nestedWrite(writer, () -> MessageSerialization.writeTo(msgFactory, msg, writer)); + nestedWrite(writer, () -> MessageSerialization.writeTo(msgFactory, msg, writer, ctx)); else lastFinished = false; } @@ -942,8 +944,10 @@ public void writeMessage(Message msg, MessageWriter writer) { * @param arr Array. * @param type Type. * @param writer Writer. + * @param ctx Serialization context. */ - public void writeObjectArray(T[] arr, MessageArrayType type, MessageWriter writer) { + public void writeObjectArray(T[] arr, MessageArrayType type, MessageWriter writer, + MessageSerializationContext ctx) { if (arr != null) { int len = arr.length; @@ -960,7 +964,7 @@ public void writeObjectArray(T[] arr, MessageArrayType type, MessageWriter w if (arrCur == NULL) arrCur = arr[arrPos++]; - write(type.valueType(), arrCur, writer); + write(type.valueType(), arrCur, writer, ctx); if (!lastFinished) return; @@ -978,11 +982,13 @@ public void writeObjectArray(T[] arr, MessageArrayType type, MessageWriter w * @param col Collection. * @param type Type. * @param writer Writer. + * @param ctx Serialization context. */ - public void writeCollection(Collection col, MessageCollectionType type, MessageWriter writer) { + public void writeCollection(Collection col, MessageCollectionType type, MessageWriter writer, + MessageSerializationContext ctx) { if (col != null) { if (col instanceof List && col instanceof RandomAccess) - writeRandomAccessList((List)col, type, writer); + writeRandomAccessList((List)col, type, writer, ctx); else { if (it == null) { writeInt(col.size()); @@ -997,7 +1003,7 @@ public void writeCollection(Collection col, MessageCollectionType type, M if (cur == NULL) cur = it.next(); - write(type.valueType(), cur, writer); + write(type.valueType(), cur, writer, ctx); if (!lastFinished) return; @@ -1016,8 +1022,10 @@ public void writeCollection(Collection col, MessageCollectionType type, M * @param list List. * @param type Type. * @param writer Writer. + * @param ctx Serialization context. */ - private void writeRandomAccessList(List list, MessageCollectionType type, MessageWriter writer) { + private void writeRandomAccessList(List list, MessageCollectionType type, MessageWriter writer, + MessageSerializationContext ctx) { assert list instanceof RandomAccess; int size = list.size(); @@ -1035,7 +1043,7 @@ private void writeRandomAccessList(List list, MessageCollectionType type, if (arrCur == NULL) arrCur = list.get(arrPos++); - write(type.valueType(), arrCur, writer); + write(type.valueType(), arrCur, writer, ctx); if (!lastFinished) return; @@ -1050,8 +1058,10 @@ private void writeRandomAccessList(List list, MessageCollectionType type, * @param map Map. * @param type Type. * @param writer Writer. + * @param ctx Serialization context. */ - public void writeMap(Map map, MessageMapType type, MessageWriter writer) { + public void writeMap(Map map, MessageMapType type, MessageWriter writer, + MessageSerializationContext ctx) { if (map != null) { if (mapIt == null) { writeInt(map.size()); @@ -1071,7 +1081,7 @@ public void writeMap(Map map, MessageMapType type, MessageWriter wr e = (Map.Entry)mapCur; if (!keyDone) { - write(type.keyType(), e.getKey(), writer); + write(type.keyType(), e.getKey(), writer, ctx); if (!lastFinished) return; @@ -1079,7 +1089,7 @@ public void writeMap(Map map, MessageMapType type, MessageWriter wr keyDone = true; } - write(type.valueType(), e.getValue(), writer); + write(type.valueType(), e.getValue(), writer, ctx); if (!lastFinished) return; @@ -1555,9 +1565,10 @@ public GridLongList readGridLongList() { /** * @param reader Reader. + * @param ctx Serialization context. * @return Message. */ - public T readMessage(MessageReader reader) { + public T readMessage(MessageReader reader, MessageSerializationContext ctx) { if (!msgTypeDone) { if (buf.remaining() < Message.DIRECT_TYPE_SIZE) { lastFinished = false; @@ -1576,7 +1587,7 @@ public T readMessage(MessageReader reader) { try { reader.beforeNestedRead(); - lastFinished = MessageSerialization.readFrom(msgFactory, msg, reader); + lastFinished = MessageSerialization.readFrom(msgFactory, msg, reader, ctx); } finally { reader.afterNestedRead(lastFinished); @@ -1600,9 +1611,10 @@ public T readMessage(MessageReader reader) { /** * @param type Item type. * @param reader Reader. + * @param ctx Serialization context. * @return Array. */ - public T[] readObjectArray(MessageArrayType type, MessageReader reader) { + public T[] readObjectArray(MessageArrayType type, MessageReader reader, MessageSerializationContext ctx) { if (readSize == -1) { int size = readInt(); @@ -1617,7 +1629,7 @@ public T[] readObjectArray(MessageArrayType type, MessageReader reader) { objArr = type.clazz() != null ? (Object[])Array.newInstance(type.clazz(), readSize) : new Object[readSize]; for (int i = readItems; i < readSize; i++) { - Object item = read(type.valueType(), reader); + Object item = read(type.valueType(), reader, ctx); if (!lastFinished) return null; @@ -1644,9 +1656,11 @@ public T[] readObjectArray(MessageArrayType type, MessageReader reader) { * * @param type Item type. * @param reader Reader. + * @param ctx Serialization context. * @return {@link ArrayList}, {@link HashSet} or {@link EnumSet}. */ - public > C readCollection(MessageCollectionType type, MessageReader reader) { + public > C readCollection(MessageCollectionType type, MessageReader reader, + MessageSerializationContext ctx) { if (readSize == -1) { int size = readInt(); @@ -1661,7 +1675,7 @@ public > C readCollection(MessageCollectionType type, Me col = newCollection(type); for (int i = readItems; i < readSize; i++) { - Object item = read(type.valueType(), reader); + Object item = read(type.valueType(), reader, ctx); if (!lastFinished) return null; @@ -1696,9 +1710,11 @@ private Collection newCollection(MessageCollectionType type) { /** * @param type Value type. * @param reader Reader. + * @param ctx Serialization context. * @return Map. */ - public > M readMap(MessageMapType type, MessageReader reader) { + public > M readMap(MessageMapType type, MessageReader reader, + MessageSerializationContext ctx) { if (readSize == -1) { int size = readInt(); @@ -1714,7 +1730,7 @@ private Collection newCollection(MessageCollectionType type) { for (int i = readItems; i < readSize; i++) { if (!keyDone) { - Object key = read(type.keyType(), reader); + Object key = read(type.keyType(), reader, ctx); if (!lastFinished) return null; @@ -1723,7 +1739,7 @@ private Collection newCollection(MessageCollectionType type) { keyDone = true; } - Object val = read(type.valueType(), reader); + Object val = read(type.valueType(), reader, ctx); if (!lastFinished) return null; @@ -2003,8 +2019,9 @@ T readArrayLE(ArrayCreator creator, int typeSize, int lenShift, long off) * @param type Type. * @param val Value. * @param writer Writer. + * @param ctx Serialization context. */ - protected void write(MessageType type, Object val, MessageWriter writer) { + protected void write(MessageType type, Object val, MessageWriter writer, MessageSerializationContext ctx) { switch (type.type()) { case BYTE: writeByte((Byte)val); @@ -2132,17 +2149,17 @@ protected void write(MessageType type, Object val, MessageWriter writer) break; case MAP: - nestedWrite(writer, () -> writer.writeMap((Map)val, (MessageMapType)type)); + nestedWrite(writer, () -> writer.writeMap((Map)val, (MessageMapType)type, ctx)); break; case COLLECTION: - nestedWrite(writer, () -> writer.writeCollection((Collection)val, (MessageCollectionType)type)); + nestedWrite(writer, () -> writer.writeCollection((Collection)val, (MessageCollectionType)type, ctx)); break; case ARRAY: - nestedWrite(writer, () -> writer.writeObjectArray((V[])val, (MessageArrayType)type)); + nestedWrite(writer, () -> writer.writeObjectArray((V[])val, (MessageArrayType)type, ctx)); break; @@ -2152,7 +2169,7 @@ protected void write(MessageType type, Object val, MessageWriter writer) break; case MSG: - writeMessage((Message)val, writer); + writeMessage((Message)val, writer, ctx); break; @@ -2176,9 +2193,10 @@ private void nestedWrite(MessageWriter writer, BooleanSupplier s) { /** * @param type Type. * @param reader Reader. + * @param ctx Serialization context. * @return Value. */ - protected Object read(MessageType type, MessageReader reader) { + protected Object read(MessageType type, MessageReader reader, MessageSerializationContext ctx) { switch (type.type()) { case BYTE: return readByte(); @@ -2256,19 +2274,19 @@ protected Object read(MessageType type, MessageReader reader) { return readGridLongList(); case MAP: - return nestedRead(reader, () -> reader.readMap((MessageMapType)type)); + return nestedRead(reader, () -> reader.readMap((MessageMapType)type, ctx)); case COLLECTION: - return nestedRead(reader, () -> reader.readCollection((MessageCollectionType)type)); + return nestedRead(reader, () -> reader.readCollection((MessageCollectionType)type, ctx)); case ARRAY: - return nestedRead(reader, () -> reader.readObjectArray((MessageArrayType)type)); + return nestedRead(reader, () -> reader.readObjectArray((MessageArrayType)type, ctx)); case ENUM: return ((MessageEnumType)type).decode(readByte()); case MSG: - return readMessage(reader); + return readMessage(reader, ctx); default: throw new IllegalArgumentException("Unknown type: " + type); diff --git a/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/CompressedMessageSerializer.java b/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/CompressedMessageSerializer.java index 7661a11421972..4f96b37f88e63 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/CompressedMessageSerializer.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/managers/communication/CompressedMessageSerializer.java @@ -19,6 +19,7 @@ import java.util.ArrayList; import org.apache.ignite.IgniteException; +import org.apache.ignite.internal.MessageSerializationContext; import org.apache.ignite.plugin.extensions.communication.MessageReader; import org.apache.ignite.plugin.extensions.communication.MessageSerializer; import org.apache.ignite.plugin.extensions.communication.MessageWriter; @@ -28,7 +29,7 @@ /** Message serializer for compressed message. */ public class CompressedMessageSerializer implements MessageSerializer { /** {@inheritDoc} */ - @Override public boolean writeTo(CompressedMessage msg, MessageWriter writer) { + @Override public boolean writeTo(CompressedMessage msg, MessageWriter writer, MessageSerializationContext ctx) { if (!writer.isHeaderWritten()) { if (!writer.writeHeader(msg.directType())) return false; @@ -74,7 +75,7 @@ public class CompressedMessageSerializer implements MessageSerializer T readMessage() throws IgniteCheckedException, IOException { readBuf.limit(read); - finished = MessageSerialization.readFrom(spi.messageFactory(), msg, msgReader); + finished = MessageSerialization.readFrom(spi.messageFactory(), msg, msgReader, serCtx); // Server Discovery only sends next message to next Server upon receiving a receipt for the previous one. // This behaviour guarantees that we never read a next message from the buffer right after the end of diff --git a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoverySpi.java b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoverySpi.java index f3efcf3e9a334..ade01b61e6f0b 100644 --- a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoverySpi.java +++ b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoverySpi.java @@ -59,8 +59,6 @@ import org.apache.ignite.internal.managers.discovery.IgniteDiscoverySpi; import org.apache.ignite.internal.processors.failure.FailureProcessor; import org.apache.ignite.internal.processors.metric.MetricRegistryImpl; -import org.apache.ignite.internal.processors.rollingupgrade.feature.IgniteComponentFeatureSet; -import org.apache.ignite.internal.processors.rollingupgrade.feature.IgniteNodeFeatureSet; import org.apache.ignite.internal.util.tostring.GridToStringExclude; import org.apache.ignite.internal.util.typedef.F; import org.apache.ignite.internal.util.typedef.X; @@ -100,7 +98,6 @@ import org.apache.ignite.spi.discovery.tcp.internal.DiscoveryDataPacket; import org.apache.ignite.spi.discovery.tcp.internal.TcpDiscoveryNode; import org.apache.ignite.spi.discovery.tcp.internal.TcpDiscoveryStatistics; -import org.apache.ignite.spi.discovery.tcp.internal.UnsupportedNodeVersionException; import org.apache.ignite.spi.discovery.tcp.ipfinder.TcpDiscoveryIpFinder; import org.apache.ignite.spi.discovery.tcp.ipfinder.jdbc.TcpDiscoveryJdbcIpFinder; import org.apache.ignite.spi.discovery.tcp.ipfinder.multicast.TcpDiscoveryMulticastIpFinder; @@ -1693,36 +1690,6 @@ Socket createSocket() throws IOException { } } - /** */ - void validateRemoteFeatures(IgniteNodeFeatureSet rmtFeatures) throws IgniteCheckedException { - if (rmtFeatures == null) { - throw new UnsupportedNodeVersionException( - "Failed to obtain remote node features. The remote node may be running an unsupported Ignite version," + - " which may result in unexpected handshake message serialization"); - } - - for (IgniteComponentFeatureSet rmtCmpFeatures : rmtFeatures.values()) { - IgniteComponentFeatureSet locCmpFeatures = locNode.features().componentFeatures(rmtCmpFeatures.componentName()); - - if (locCmpFeatures == null) - continue; - - int c = locCmpFeatures.version().compareTo(rmtCmpFeatures.version()); - - if (c == 0) - continue; - - IgniteComponentFeatureSet src = c > 0 ? rmtCmpFeatures : locCmpFeatures; - IgniteComponentFeatureSet target = c > 0 ? locCmpFeatures : rmtCmpFeatures; - - if (!src.isUpgradableTo(target)) { - throw new UnsupportedNodeVersionException("Remote node component versions are not supported" + - " [locComponents=" + locNode.features() + - ", rmtComponents=" + rmtFeatures + ']'); - } - } - } - /** * Writes raw data to the session socket. * diff --git a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/internal/ClientMessageHolder.java b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/internal/ClientMessageHolder.java index 8e2f50152b57f..e663ab47795fc 100644 --- a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/internal/ClientMessageHolder.java +++ b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/internal/ClientMessageHolder.java @@ -17,7 +17,10 @@ package org.apache.ignite.spi.discovery.tcp.internal; +import java.util.HashMap; +import java.util.Map; import org.apache.ignite.IgniteCheckedException; +import org.apache.ignite.internal.MessageSerializationContext; import org.apache.ignite.spi.discovery.tcp.messages.TcpDiscoveryAbstractMessage; import org.jetbrains.annotations.Nullable; @@ -27,7 +30,7 @@ public class ClientMessageHolder { private final TcpDiscoveryAbstractMessage msg; /** */ - private byte[] msgBytes; + private final Map bytesByCtx = new HashMap<>(1); /** */ public ClientMessageHolder(TcpDiscoveryAbstractMessage msg) { @@ -42,14 +45,14 @@ public TcpDiscoveryAbstractMessage message() { } /** */ - public synchronized byte @Nullable [] messageBytes() { - return msgBytes; + public synchronized byte @Nullable [] messageBytes(MessageSerializationContext ctx) { + return bytesByCtx.get(ctx); } /** */ - public synchronized void serialize(TcpDiscoveryMessageSerializer ser) throws IgniteCheckedException { - if (msgBytes == null) - msgBytes = ser.serialize(msg); + public synchronized void serialize(TcpDiscoveryMessageSerializer ser, MessageSerializationContext ctx) throws IgniteCheckedException { + if (!bytesByCtx.containsKey(ctx)) + bytesByCtx.put(ctx, ser.serialize(msg, ctx)); } /** {@inheritDoc} */ diff --git a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/internal/TcpDiscoveryMessageSerializer.java b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/internal/TcpDiscoveryMessageSerializer.java index 02dfe60eb28cb..2e83c7618c851 100644 --- a/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/internal/TcpDiscoveryMessageSerializer.java +++ b/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/internal/TcpDiscoveryMessageSerializer.java @@ -22,6 +22,7 @@ import java.nio.ByteBuffer; import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.internal.IgniteEx; +import org.apache.ignite.internal.MessageSerializationContext; import org.apache.ignite.internal.direct.DirectMessageWriter; import org.apache.ignite.internal.managers.communication.DiscoveryMarshalling; import org.apache.ignite.internal.util.io.GridByteArrayOutputStream; @@ -55,10 +56,15 @@ public TcpDiscoveryMessageSerializer(TcpDiscoverySpi spi) { * * @param msg Discovery message to serialize. * @param out Output stream to write serialized message. + * @param ctx Serialization context the recipient agreed on. * @throws IgniteCheckedException If serialization fails. * @throws IOException If serialization fails. */ - public void writeTo(TcpDiscoveryAbstractMessage msg, OutputStream out) throws IgniteCheckedException, IOException { + public void writeTo( + TcpDiscoveryAbstractMessage msg, + OutputStream out, + MessageSerializationContext ctx + ) throws IgniteCheckedException, IOException { DiscoveryMarshalling.marshal(msg, ((IgniteEx)spi.ignite()).context(), null); writer.reset(); @@ -70,7 +76,7 @@ public void writeTo(TcpDiscoveryAbstractMessage msg, OutputStream out) throws Ig // Should be cleared before first operation. buf.clear(); - finished = MessageSerialization.writeTo(spi.messageFactory(), msg, writer); + finished = MessageSerialization.writeTo(spi.messageFactory(), msg, writer, ctx); out.write(buf.array(), 0, buf.position()); } @@ -81,12 +87,13 @@ public void writeTo(TcpDiscoveryAbstractMessage msg, OutputStream out) throws Ig * Serializes a discovery message into a byte array. * * @param msg Discovery message to serialize. + * @param ctx Serialization context the recipient agreed on. * @return Serialized byte array containing the message data. * @throws IgniteCheckedException If serialization fails. */ - public byte[] serialize(TcpDiscoveryAbstractMessage msg) throws IgniteCheckedException { + public byte[] serialize(TcpDiscoveryAbstractMessage msg, MessageSerializationContext ctx) throws IgniteCheckedException { try (GridByteArrayOutputStream out = new GridByteArrayOutputStream()) { - writeTo(msg, out); + writeTo(msg, out, ctx); return out.toByteArray(); } diff --git a/modules/core/src/test/java/org/apache/ignite/internal/codegen/MessageProcessorTest.java b/modules/core/src/test/java/org/apache/ignite/internal/codegen/MessageProcessorTest.java index 1105f85cf3fd9..29e51cf4922a2 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/codegen/MessageProcessorTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/codegen/MessageProcessorTest.java @@ -93,6 +93,54 @@ public void testProcessorGeneratesSerializer() { .hasSourceEquivalentTo(javaFile("TestMessageMarshaller.java")); } + /** */ + @Test + public void testRollingUpgradeAwareMessage() { + Compilation compilation = compile("TestFeatureRegistry.java", "TestRollingUpgradeAwareMessage.java"); + + assertThat(compilation).succeeded(); + + assertThat(compilation) + .generatedSourceFile("org.apache.ignite.internal.TestRollingUpgradeAwareMessageSerializer") + .hasSourceEquivalentTo(javaFile("TestRollingUpgradeAwareMessageSerializer.java")); + } + + /** */ + @Test + public void testUnknownFeatureConstantRejected() { + Compilation compilation = compile("TestUnknownFeatureMessage.java"); + + assertThat(compilation).failed(); + assertThat(compilation).hadErrorContaining("No such feature constant in the registry [feature=NO_SUCH_FEATURE"); + } + + /** */ + @Test + public void testGuardWithoutOrderRejected() { + Compilation compilation = compile("TestOrderlessFeatureMessage.java"); + + assertThat(compilation).failed(); + assertThat(compilation).hadErrorContaining("must also be annotated with @Order"); + } + + /** */ + @Test + public void testSameFeatureInBothAnnotationsRejected() { + Compilation compilation = compile("TestFeatureConflictMessage.java"); + + assertThat(compilation).failed(); + assertThat(compilation).hadErrorContaining("must not reference the same feature when used on the same element"); + } + + /** */ + @Test + public void testFeatureConstantOfWrongTypeRejected() { + Compilation compilation = compile("TestInvalidFeatureRegistry.java", "TestInvalidFeatureMessage.java"); + + assertThat(compilation).failed(); + assertThat(compilation).hadErrorContaining("must be of type IgniteFeature [registry="); + } + /** */ @Test public void testCollectionsMessage() { diff --git a/modules/core/src/test/java/org/apache/ignite/internal/direct/DirectMarshallingMessagesTest.java b/modules/core/src/test/java/org/apache/ignite/internal/direct/DirectMarshallingMessagesTest.java index e9874e03055fc..a3e66c91819bd 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/direct/DirectMarshallingMessagesTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/direct/DirectMarshallingMessagesTest.java @@ -31,6 +31,7 @@ import org.apache.ignite.transactions.TransactionIsolation; import org.junit.Test; +import static org.apache.ignite.internal.MessageSerializationContext.IGNORED; import static org.apache.ignite.transactions.TransactionIsolation.READ_COMMITTED; import static org.apache.ignite.transactions.TransactionIsolation.REPEATABLE_READ; import static org.apache.ignite.transactions.TransactionIsolation.SERIALIZABLE; @@ -139,7 +140,7 @@ private T doMarshalUnmarshalChunked(T srcMsg) { writer.setBuffer(chunk); - fullyWritten = writer.writeMessage(srcMsg, false); + fullyWritten = writer.writeMessage(srcMsg, false, IGNORED); chunk.flip(); @@ -168,7 +169,7 @@ private T doMarshalUnmarshalChunked(T srcMsg) { reader.setBuffer(chunk); - resMsg = reader.readMessage(false); + resMsg = reader.readMessage(false, IGNORED); pos += chunk.position(); } diff --git a/modules/core/src/test/java/org/apache/ignite/internal/managers/AbstractMessageSerializationTest.java b/modules/core/src/test/java/org/apache/ignite/internal/managers/AbstractMessageSerializationTest.java index bfa3c8e314204..48e861cc6c98a 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/managers/AbstractMessageSerializationTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/managers/AbstractMessageSerializationTest.java @@ -25,6 +25,7 @@ import java.util.Map; import java.util.Set; import java.util.UUID; +import org.apache.ignite.internal.MessageSerializationContext; import org.apache.ignite.internal.managers.communication.IgniteMessageFactoryImpl; import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion; import org.apache.ignite.internal.processors.cache.CacheObject; @@ -47,6 +48,7 @@ import org.junit.Test; import static java.lang.Integer.MAX_VALUE; +import static org.apache.ignite.internal.MessageSerializationContext.IGNORED; import static org.apache.ignite.plugin.extensions.communication.CollectionImplementationType.HASH_SET; import static org.junit.Assert.assertEquals; @@ -106,13 +108,13 @@ private void checkSerializationAndDeserializationConsistency( initializeMessage(msg); - while (!MessageSerialization.writeTo(msgFactory, msg, writer)) { + while (!MessageSerialization.writeTo(msgFactory, msg, writer, IGNORED)) { // No-op. } msg = msgFactory.create(msgType); - while (!MessageSerialization.readFrom(msgFactory, msg, reader)) { + while (!MessageSerialization.readFrom(msgFactory, msg, reader, IGNORED)) { // No-op. } @@ -295,22 +297,22 @@ private boolean writeField(Class type) { } /** {@inheritDoc} */ - @Override public boolean writeMessage(Message val, boolean compress) { + @Override public boolean writeMessage(Message val, boolean compress, MessageSerializationContext ctx) { return writeField(Message.class); } /** {@inheritDoc} */ - @Override public boolean writeObjectArray(T[] arr, MessageArrayType type) { + @Override public boolean writeObjectArray(T[] arr, MessageArrayType type, MessageSerializationContext ctx) { return writeField(Object[].class); } /** {@inheritDoc} */ - @Override public boolean writeCollection(Collection col, MessageCollectionType type) { + @Override public boolean writeCollection(Collection col, MessageCollectionType type, MessageSerializationContext ctx) { return writeField(type.collectionImplementationType() == HASH_SET ? Set.class : Collection.class); } /** {@inheritDoc} */ - @Override public boolean writeMap(Map map, MessageMapType type, boolean compress) { + @Override public boolean writeMap(Map map, MessageMapType type, boolean compress, MessageSerializationContext ctx) { return writeField(type.linked() ? LinkedHashMap.class : HashMap.class); } @@ -537,7 +539,7 @@ private void readField(Class type) { } /** {@inheritDoc} */ - @Override public T readMessage(boolean compress) { + @Override public T readMessage(boolean compress, MessageSerializationContext ctx) { readField(Message.class); return null; @@ -565,21 +567,21 @@ private void readField(Class type) { } /** {@inheritDoc} */ - @Override public T[] readObjectArray(MessageArrayType type) { + @Override public T[] readObjectArray(MessageArrayType type, MessageSerializationContext ctx) { readField(Object[].class); return null; } /** {@inheritDoc} */ - @Override public > C readCollection(MessageCollectionType type) { + @Override public > C readCollection(MessageCollectionType type, MessageSerializationContext ctx) { readField(type.collectionImplementationType() == HASH_SET ? Set.class : Collection.class); return null; } /** {@inheritDoc} */ - @Override public > M readMap(MessageMapType type, boolean compress) { + @Override public > M readMap(MessageMapType type, boolean compress, MessageSerializationContext ctx) { readField(type.linked() ? LinkedHashMap.class : HashMap.class); return null; diff --git a/modules/core/src/test/java/org/apache/ignite/internal/managers/communication/CompressedMessageTest.java b/modules/core/src/test/java/org/apache/ignite/internal/managers/communication/CompressedMessageTest.java index 046dbbb6f55b0..28c5b23e347be 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/managers/communication/CompressedMessageTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/managers/communication/CompressedMessageTest.java @@ -42,6 +42,7 @@ import org.apache.ignite.testframework.GridTestUtils; import org.junit.Test; +import static org.apache.ignite.internal.MessageSerializationContext.IGNORED; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -70,7 +71,7 @@ public void testWriteReadHugeMessage() { ByteBuffer msgBuf = ByteBuffer.allocate(40_960); while (!finished) { - finished = writer.writeMessage(fullMsg, true); + finished = writer.writeMessage(fullMsg, true, IGNORED); if (checkChunkCnt) { DirectMessageState state = U.field(writer, "state"); @@ -104,7 +105,7 @@ public void testWriteReadHugeMessage() { reader.setBuffer(msgBuf); - Message readMsg = reader.readMessage(true); + Message readMsg = reader.readMessage(true, IGNORED); assertTrue(readMsg instanceof GridDhtPartitionsFullMessage); @@ -133,7 +134,7 @@ public void testReadFailsOnNullChunk() { reader.setBuffer(buf); GridTestUtils.assertThrows(null, - () -> MessageSerialization.readFrom(MSG_FACTORY, new CompressedMessage(), reader), + () -> MessageSerialization.readFrom(MSG_FACTORY, new CompressedMessage(), reader, IGNORED), IgniteException.class, "unexpected null chunk"); } @@ -156,7 +157,7 @@ public void testReadFailsOnNegativeDataSize() { reader.setBuffer(buf); GridTestUtils.assertThrows(null, - () -> MessageSerialization.readFrom(MSG_FACTORY, new CompressedMessage(), reader), + () -> MessageSerialization.readFrom(MSG_FACTORY, new CompressedMessage(), reader, IGNORED), IgniteException.class, "Invalid compressed message data size"); } @@ -217,7 +218,7 @@ public void testReadFailsOnTruncatedPayload() { writer.setBuffer(tmpBuf); - assertTrue(writer.writeMessage(fullMessage(), false)); + assertTrue(writer.writeMessage(fullMessage(), false, IGNORED)); tmpBuf.flip(); @@ -231,7 +232,7 @@ public void testReadFailsOnTruncatedPayload() { wireWriter.setBuffer(wire); - assertTrue(wireWriter.writeMessage(compressedMsg, false)); + assertTrue(wireWriter.writeMessage(compressedMsg, false, IGNORED)); wire.flip(); @@ -239,7 +240,7 @@ public void testReadFailsOnTruncatedPayload() { reader.setBuffer(wire); - GridTestUtils.assertThrows(null, () -> reader.readMessage(true), IgniteException.class, "ended unexpectedly"); + GridTestUtils.assertThrows(null, () -> reader.readMessage(true, IGNORED), IgniteException.class, "ended unexpectedly"); } /** */ diff --git a/modules/core/src/test/java/org/apache/ignite/internal/managers/communication/GridIoManagerOrderedUnmarshalFailureTest.java b/modules/core/src/test/java/org/apache/ignite/internal/managers/communication/GridIoManagerOrderedUnmarshalFailureTest.java index 4b51337eac099..2a5ce91b96302 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/managers/communication/GridIoManagerOrderedUnmarshalFailureTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/managers/communication/GridIoManagerOrderedUnmarshalFailureTest.java @@ -28,6 +28,7 @@ import org.apache.ignite.internal.CoreMessagesProvider; import org.apache.ignite.internal.GridKernalContext; import org.apache.ignite.internal.IgniteEx; +import org.apache.ignite.internal.MessageSerializationContext; import org.apache.ignite.internal.processors.cache.CacheObjectContext; import org.apache.ignite.internal.util.typedef.internal.U; import org.apache.ignite.marshaller.Marshaller; @@ -186,7 +187,7 @@ private static class FailingUnmarshalMessage implements Message { /** Writes the two fields behind the header. */ private static class Serializer implements MessageSerializer { /** {@inheritDoc} */ - @Override public boolean writeTo(FailingUnmarshalMessage msg, MessageWriter writer) { + @Override public boolean writeTo(FailingUnmarshalMessage msg, MessageWriter writer, MessageSerializationContext ctx) { if (!writer.isHeaderWritten()) { if (!writer.writeHeader(msg.directType())) return false; @@ -212,7 +213,7 @@ private static class Serializer implements MessageSerializer @@ -122,7 +124,7 @@ public void testCacheSize() throws Exception { // 2kb should be enough for an empty message even if it is a relatively large metrics message. msgWritter.setBuffer(ByteBuffer.allocate(2048)); - assertTrue(MessageSerialization.writeTo(msgFactory, msg, msgWritter)); + assertTrue(MessageSerialization.writeTo(msgFactory, msg, msgWritter, IGNORED)); assertTrue(msgWritter.getBuffer().hasRemaining()); @@ -134,7 +136,7 @@ public void testCacheSize() throws Exception { TcpDiscoveryMetricsUpdateMessage msg2 = new TcpDiscoveryMetricsUpdateMessage(); - assertTrue(MessageSerialization.readFrom(msgFactory, msg2, msgReader)); + assertTrue(MessageSerialization.readFrom(msgFactory, msg2, msgReader, IGNORED)); Map cacheMetrics2 = msg2.serversFullMetricsMessages().values().iterator().next() .cachesMetricsMessages(); diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/query/continuous/IgniteCacheContinuousQueryImmutableEntryTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/query/continuous/IgniteCacheContinuousQueryImmutableEntryTest.java index 45a5fb3381fd5..4f6851c31a6e6 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/query/continuous/IgniteCacheContinuousQueryImmutableEntryTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/query/continuous/IgniteCacheContinuousQueryImmutableEntryTest.java @@ -45,6 +45,7 @@ import static org.apache.ignite.cache.CacheAtomicityMode.ATOMIC; import static org.apache.ignite.cache.CacheMode.PARTITIONED; import static org.apache.ignite.cache.CacheWriteSynchronizationMode.FULL_SYNC; +import static org.apache.ignite.internal.MessageSerializationContext.IGNORED; /** * @@ -204,7 +205,7 @@ private CacheContinuousQueryEntry roundTrip(CacheContinuousQueryEntry e) throws // Skip write class header. writer.onHeaderWritten(); - MessageSerialization.writeTo(msgFactory, e, writer); + MessageSerialization.writeTo(msgFactory, e, writer, IGNORED); CacheContinuousQueryEntry res = new CacheContinuousQueryEntry(); @@ -212,7 +213,7 @@ private CacheContinuousQueryEntry roundTrip(CacheContinuousQueryEntry e) throws reader.setBuffer(ByteBuffer.wrap(buf.array())); - MessageSerialization.readFrom(msgFactory, res, reader); + MessageSerialization.readFrom(msgFactory, res, reader, IGNORED); return res; } diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/query/schema/message/QueryEntityMessageSerializationTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/query/schema/message/QueryEntityMessageSerializationTest.java index f7fea391425dd..60d05ca3feddf 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/query/schema/message/QueryEntityMessageSerializationTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/query/schema/message/QueryEntityMessageSerializationTest.java @@ -48,6 +48,7 @@ import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; import org.junit.Test; +import static org.apache.ignite.internal.MessageSerializationContext.IGNORED; import static org.apache.ignite.internal.util.CommonUtils.makeMessageType; /** Test for serialization round-trip of {@link QueryEntityMessage} and {@link QueryEntityExMessage}. */ @@ -173,7 +174,7 @@ private T writeAndReadBack(T msg, long expReadsWritesCnt) th DirectMessageWriter writer = new DirectMessageWriter(msgFactory); writer.setBuffer(buf); - assertTrue(MessageSerialization.writeTo(msgFactory, msg, writer)); + assertTrue(MessageSerialization.writeTo(msgFactory, msg, writer, IGNORED)); assertEquals("Writes" + ERROR_SUFFIX, expReadsWritesCnt, writer.state()); @@ -184,7 +185,7 @@ private T writeAndReadBack(T msg, long expReadsWritesCnt) th T res = (T)msgFactory.create(makeMessageType(buf.get(), buf.get())); - assertTrue(MessageSerialization.readFrom(msgFactory, res, reader)); + assertTrue(MessageSerialization.readFrom(msgFactory, res, reader, IGNORED)); assertEquals("Reads" + ERROR_SUFFIX, expReadsWritesCnt, reader.state()); diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/message/RollingUpgradeMessageSerializationTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/message/RollingUpgradeMessageSerializationTest.java new file mode 100644 index 0000000000000..4258e93cc6a8a --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/message/RollingUpgradeMessageSerializationTest.java @@ -0,0 +1,592 @@ +/* + * 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.processors.rollingupgrade.message; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; +import org.apache.ignite.Ignite; +import org.apache.ignite.Ignition; +import org.apache.ignite.cluster.ClusterNode; +import org.apache.ignite.configuration.IgniteConfiguration; +import org.apache.ignite.internal.IgniteEx; +import org.apache.ignite.internal.managers.communication.GridIoPolicy; +import org.apache.ignite.internal.processors.rollingupgrade.AbstractRollingUpgradeTest; +import org.apache.ignite.plugin.extensions.communication.Message; +import org.apache.ignite.spi.MessagesPluginProvider; +import org.junit.Test; + +import static org.apache.ignite.internal.processors.rollingupgrade.message.TestMessage.A; +import static org.apache.ignite.internal.processors.rollingupgrade.message.TestMessage.B; +import static org.apache.ignite.internal.processors.rollingupgrade.message.TestMessage.C; +import static org.apache.ignite.internal.processors.rollingupgrade.message.TestMessage.D; +import static org.apache.ignite.internal.processors.rollingupgrade.message.TestMessage.E; +import static org.apache.ignite.internal.processors.rollingupgrade.message.TestMessage.F; + +/** */ +public class RollingUpgradeMessageSerializationTest extends AbstractRollingUpgradeTest { + /** {@inheritDoc} */ + @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName, String ver) throws Exception { + IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName, ver); + + cfg.setPluginProviders(org.apache.ignite.internal.util.typedef.F.concat( + cfg.getPluginProviders(), + new MessagesPluginProvider( + TestCoreMessage.class, + TestPluginMessage.class, + TestDefaultRegistryMessage.class)) + ); + + return cfg; + } + + /** */ + @Test + public void testDiscoveryNewerClient() throws Exception { + IgniteEx srv = startGrid(0, "2.19.0"); + + ru(srv).enableVersionUpgrade(); + + IgniteEx client = startClientGrid(1, "2.20.0"); + + TestCoreMessage receivedMsg = sendOverDiscovery(srv, TestCoreMessage.build()).get(client.name()); + + assertFields(B, null, null, null, receivedMsg); + } + + /** */ + @Test + public void testDiscoveryClientOriginated() throws Exception { + IgniteEx srv = startGrid(0, "2.19.0"); + + ru(srv).enableVersionUpgrade(); + + IgniteEx cli1 = startClientGrid(1, "2.20.0"); + IgniteEx cli2 = startClientGrid(2, "2.19.0"); + + Map receivedMsgs = sendOverDiscovery(cli1, TestCoreMessage.build()); + + assertFields(B, null, null, null, receivedMsgs.get(cli2.name())); + assertFields(B, null, null, null, receivedMsgs.get(srv.name())); + } + + /** */ + @Test + public void testDiscoveryClientsOnDifferentVersions() throws Exception { + startGrid(0, "2.19.0"); + startGrid(1, "2.19.0"); + + ru(1).enableVersionUpgrade(); + + upgradeNodeVersion(0, "2.20.0"); + upgradeNodeVersion(1, "2.20.0"); + + IgniteEx newVerCli = startClientGrid(2, "2.20.0"); + IgniteEx oldVerCli = startClientGrid(3, "2.19.0"); + + Map receivedMsgs = sendOverDiscovery(grid(1), TestCoreMessage.build()); + + assertFields(B, D, E, null, receivedMsgs.get(newVerCli.name())); + assertFields(B, null, null, null, receivedMsgs.get(oldVerCli.name())); + } + + /** */ + @Test + public void testDiscoveryClientsOnOneVersion() throws Exception { + IgniteEx srv = startGrid(0, "2.20.0"); + + startClientGrid(1, "2.20.0"); + startClientGrid(2, "2.20.0"); + + Map receivedMsgs = sendOverDiscovery(srv, TestCoreMessage.build()); + + for (TestCoreMessage msg : receivedMsgs.values()) + assertFields(B, D, E, null, msg); + } + + /** */ + @Test + public void testCommunicationWithClient() throws Exception { + IgniteEx srv = startGrid(0, "2.19.0"); + + ru(srv).enableVersionUpgrade(); + + IgniteEx client = startClientGrid(1, "2.20.0"); + + TestCoreMessage receivedMsg = send(srv, client, TestCoreMessage.build()); + + assertFields(B, null, null, null, receivedMsg); + + receivedMsg = send(client, srv, TestCoreMessage.build()); + + assertFields(B, null, null, null, receivedMsg); + } + + /** */ + @Test + public void testDefaultRegistrySameOldVersion() throws Exception { + startPair("2.19.0", "2.19.0"); + + assertFields(B, D, null, null, send(grid(0), grid(1), TestDefaultRegistryMessage.build())); + } + + /** */ + @Test + public void testDefaultRegistryMixedPair() throws Exception { + startPair("2.19.0", "2.20.0"); + + assertFields(null, D, null, null, send(grid(1), grid(0), TestDefaultRegistryMessage.build())); + + assertFields(null, D, null, null, send(grid(0), grid(1), TestDefaultRegistryMessage.build())); + } + + /** */ + @Test + public void testDefaultRegistrySameNewVersion() throws Exception { + startPair("2.20.0", "2.20.0"); + + assertFields(B, D, null, null, send(grid(0), grid(1), TestDefaultRegistryMessage.build())); + } + + /** */ + @Test + public void testCommunicationSameOldVersion() throws Exception { + startPair("2.19.0", "2.19.0"); + + TestCoreMessage receivedMsg = send(grid(0), grid(1), TestCoreMessage.build()); + + assertFields(B, null, null, null, receivedMsg); + } + + /** */ + @Test + public void testCommunicationNewerSender() throws Exception { + startPair("2.19.0", "2.20.0"); + + TestCoreMessage receivedMsg = send(grid(1), grid(0), TestCoreMessage.build()); + + assertFields(B, null, null, null, receivedMsg); + } + + /** */ + @Test + public void testCommunicationOlderSender() throws Exception { + startPair("2.19.0", "2.20.0"); + + TestCoreMessage receivedMsg = send(grid(0), grid(1), TestCoreMessage.build()); + + assertFields(B, null, null, null, receivedMsg); + } + + /** */ + @Test + public void testCommunicationSameNewVersion() throws Exception { + startPair("2.20.0", "2.20.0"); + + TestCoreMessage receivedMsg = send(grid(0), grid(1), TestCoreMessage.build()); + + assertFields(B, D, E, null, receivedMsg); + } + + /** */ + @Test + public void testDiscoverySameOldVersion() throws Exception { + startPair("2.19.0", "2.19.0"); + + for (TestCoreMessage msg : sendOverDiscovery(grid(1), TestCoreMessage.build()).values()) + assertFields(B, null, null, null, msg); + } + + /** */ + @Test + public void testDiscoveryMixedPair() throws Exception { + startPair("2.19.0", "2.20.0"); + + for (TestCoreMessage msg : sendOverDiscovery(grid(1), TestCoreMessage.build()).values()) + assertFields(B, null, null, null, msg); + } + + /** */ + @Test + public void testDiscoveryUniformRing() throws Exception { + startGrid(0, "2.20.0"); + startGrid(1, "2.20.0"); + startGrid(2, "2.20.0"); + + Map receivedMsgs = sendOverDiscovery(grid(1), TestCoreMessage.build()); + + for (TestCoreMessage msg : receivedMsgs.values()) + assertFields(B, D, E, null, msg); + } + + /** */ + @Test + public void testDiscoveryMixedRing() throws Exception { + startGrid(0, "2.19.0"); + + ru(grid(0)).enableVersionUpgrade(); + + startGrid(1, "2.20.0"); + startGrid(2, "2.20.0"); + + Map receivedMsgs = sendOverDiscovery(grid(1), TestCoreMessage.build()); + + for (TestCoreMessage msg : receivedMsgs.values()) + assertFields(B, null, null, null, msg); + } + + + /** */ + @Test + public void testCommunicationWindowOpenSameVersion() throws Exception { + startPair("2.19.2", "2.19.2"); + + TestCoreMessage receivedMsg = send(grid(0), grid(1), TestCoreMessage.build()); + + assertFields(B, D, null, null, receivedMsg); + } + + /** */ + @Test + public void testCommunicationWindowOpenNewerSender() throws Exception { + startPair("2.19.2", "2.20.0"); + + TestCoreMessage receivedMsg = send(grid(1), grid(0), TestCoreMessage.build()); + + assertFields(B, D, null, null, receivedMsg); + } + + /** */ + @Test + public void testCommunicationWindowOpenOlderSender() throws Exception { + startPair("2.19.2", "2.20.0"); + + TestCoreMessage receivedMsg = send(grid(0), grid(1), TestCoreMessage.build()); + + assertFields(B, D, null, null, receivedMsg); + } + + + /** */ + @Test + public void testCommunicationWindowClosed() throws Exception { + startPair("2.20.0", "2.20.1"); + + TestCoreMessage receivedMsg = send(grid(1), grid(0), TestCoreMessage.build()); + + assertFields(null, null, E, null, receivedMsg); + } + + /** */ + @Test + public void testCommunicationUpgradeOpensWindow() throws Exception { + startGrid(0, "2.19.0"); + startGrid(1, "2.19.0"); + + TestCoreMessage receivedMsg = send(grid(0), grid(1), TestCoreMessage.build()); + + assertFields(B, null, null, null, receivedMsg); + + ru(1).enableVersionUpgrade(); + + upgradeNodeVersion(0, "2.19.2"); + upgradeNodeVersion(1, "2.19.2"); + + receivedMsg = send(grid(0), grid(1), TestCoreMessage.build()); + + assertFields(B, D, null, null, receivedMsg); + } + + /** */ + @Test + public void testCommunicationUpgradeAgreesNewFeature() throws Exception { + startGrid(0, "2.19.2"); + startGrid(1, "2.19.2"); + + TestCoreMessage receivedMsg = send(grid(0), grid(1), TestCoreMessage.build()); + + assertFields(B, D, null, null, receivedMsg); + + ru(1).enableVersionUpgrade(); + + upgradeNodeVersion(0, "2.19.2", "2.20.0"); + + receivedMsg = send(grid(0), grid(1), TestCoreMessage.build()); + + assertFields(B, D, null, null, receivedMsg); + + upgradeNodeVersion(1, "2.19.2", "2.20.0"); + + receivedMsg = send(grid(0), grid(1), TestCoreMessage.build()); + + assertFields(B, D, E, null, receivedMsg); + } + + /** */ + @Test + public void testPluginDiffersCoreMatches() throws Exception { + startPair("2.20.0 | 1.0.0", "2.20.0 | 2.0.0"); + + checkPluginDiffersCoreMatches(grid(1), grid(0)); + + checkPluginDiffersCoreMatches(grid(1), grid(0)); + } + + /** */ + @Test + public void testPluginSameVersion() throws Exception { + startPair("2.20.0 | 2.0.0", "2.20.0 | 2.0.0"); + + TestCoreMessage receivedCoreMsg = send(grid(0), grid(1), TestCoreMessage.build()); + + assertFields(B, D, E, null, receivedCoreMsg); + + TestPluginMessage receivedPluginMsg = send(grid(0), grid(1), TestPluginMessage.build()); + + assertFields(B, D, E, null, receivedPluginMsg); + } + + /** */ + @Test + public void testPluginMissingOnClient() throws Exception { + IgniteEx srv = startGrid(0, "2.20.0 | 2.0.0"); + + ru(srv).enableVersionUpgrade(); + + IgniteEx cli = startClientGrid(1, "2.20.0"); + + checkPluginMissingOnClient(srv, cli); + + checkPluginMissingOnClient(cli, srv); + } + + /** */ + @Test + public void testWholeUpgradeProcess() throws Exception { + startGrid(0, "2.19.0"); + startGrid(1, "2.19.0"); + startClientGrid(2, "2.19.0"); + + checkMessagesTransmissionBetweenAllNodes(B, null, null, null); + + ru(1).enableVersionUpgrade(); + + checkMessagesTransmissionBetweenAllNodes(B, null, null, null); + + upgradeNodeVersion(0, "2.19.0", "2.19.2"); + + checkMessagesTransmissionBetweenAllNodes(B, null, null, null); + + upgradeNodeVersion(1, "2.19.0", "2.19.2"); + + checkMutualMessageTransmission(grid(0), grid(1), B, D, null, null); + checkMutualMessageTransmission(grid(0), grid(2), B, null, null, null); + checkMutualMessageTransmission(grid(1), grid(2), B, null, null, null); + + upgradeNodeVersion(2, "2.19.0", "2.19.2"); + + checkMessagesTransmissionBetweenAllNodes(B, D, null, null); + + finalizeClusterVersion(0, "2.19.2"); + + checkMessagesTransmissionBetweenAllNodes(B, D, null, null); + + ru(1).enableVersionUpgrade(); + + upgradeNodeVersion(0, "2.19.2", "2.20.0"); + + checkMessagesTransmissionBetweenAllNodes(B, D, null, null); + + upgradeNodeVersion(1, "2.19.2", "2.20.0"); + + checkMutualMessageTransmission(grid(0), grid(1), B, D, E, null); + checkMutualMessageTransmission(grid(0), grid(2), B, D, null, null); + checkMutualMessageTransmission(grid(1), grid(2), B, D, null, null); + + upgradeNodeVersion(2, "2.19.2", "2.20.0"); + + checkMessagesTransmissionBetweenAllNodes(B, D, E, null); + + finalizeClusterVersion(0, "2.20.0"); + + checkMessagesTransmissionBetweenAllNodes(B, D, E, null); + + ru(1).enableVersionUpgrade(); + + upgradeNodeVersion(0, "2.20.0", "2.20.1"); + + checkMutualMessageTransmission(grid(0), grid(1), null, null, E, null); + checkMutualMessageTransmission(grid(0), grid(2), null, null, E, null); + checkMutualMessageTransmission(grid(1), grid(2), B, D, E, null); + + upgradeNodeVersion(1, "2.20.0", "2.20.1"); + + checkMutualMessageTransmission(grid(0), grid(1), B, D, E, F); + checkMutualMessageTransmission(grid(0), grid(2), null, null, E, null); + checkMutualMessageTransmission(grid(1), grid(2), null, null, E, null); + + upgradeNodeVersion(2, "2.20.0", "2.20.1"); + + checkMessagesTransmissionBetweenAllNodes(B, D, E, F); + + finalizeClusterVersion(0, "2.20.1"); + + checkMessagesTransmissionBetweenAllNodes(B, D, E, F); + } + + /** */ + private void checkMessagesTransmissionBetweenAllNodes(String expB, String expD, String expE, String expF) throws Exception { + List clusterNodes = Ignition.allGrids(); + + for (int i = 0; i < clusterNodes.size(); i++) { + for (int j = i + 1; j < clusterNodes.size(); j++) { + checkMutualMessageTransmission( + (IgniteEx)clusterNodes.get(i), (IgniteEx)clusterNodes.get(j), expB, expD, expE, expF); + } + } + } + + /** */ + private void checkMutualMessageTransmission( + IgniteEx first, + IgniteEx second, + String expB, + String expD, + String expE, + String expF + ) throws Exception { + checkMessageTransmission(first, second, expB, expD, expE, expF); + checkMessageTransmission(second, first, expB, expD, expE, expF); + } + + /** */ + private void checkMessageTransmission( + IgniteEx from, + IgniteEx to, + String expB, + String expD, + String expE, + String expF + ) throws Exception { + TestCoreMessage receivedCommunicationMsg = send(from, to, TestCoreMessage.build()); + + assertFields(expB, expD, expE, expF, receivedCommunicationMsg); + + TestCoreMessage receivedDiscoveryMsg = sendOverDiscovery(from, TestCoreMessage.build()).get(to.name()); + + assertFields(expB, expD, expE, expF, receivedDiscoveryMsg); + } + + /** */ + private void checkPluginDiffersCoreMatches(IgniteEx snd, IgniteEx rcv) throws Exception { + TestCoreMessage receivedCoreMsg = send(snd, rcv, TestCoreMessage.build()); + + assertFields(B, D, E, null, receivedCoreMsg); + + TestPluginMessage receivedPluginMsg = send(snd, rcv, TestPluginMessage.build()); + + assertFields(B, D, null, null, receivedPluginMsg); + } + + /** */ + private void checkPluginMissingOnClient(IgniteEx snd, IgniteEx rcv) throws Exception { + TestPluginMessage receivedPluginMsg = send(snd, rcv, TestPluginMessage.build()); + + assertFields(B, null, null, null, receivedPluginMsg); + + TestCoreMessage receivedCoreMsg = send(snd, rcv, TestCoreMessage.build()); + + assertFields(B, D, E, null, receivedCoreMsg); + } + + /** */ + private T send(IgniteEx from, IgniteEx to, T msg) throws Exception { + AtomicReference got = new AtomicReference<>(); + CountDownLatch latch = new CountDownLatch(1); + + String topic = msg.getClass().getName(); + + to.context().io().addMessageListener(topic, (nodeId, rcvd, plc) -> { + got.set((T)rcvd); + + latch.countDown(); + }); + + ClusterNode rcvNode = from.context().discovery().node(to.localNode().id()); + + from.context().io().sendToCustomTopic(rcvNode, topic, msg, GridIoPolicy.PUBLIC_POOL); + + assertTrue(latch.await(getTestTimeout(), TimeUnit.MILLISECONDS)); + + return got.get(); + } + + /** */ + private Map sendOverDiscovery( + IgniteEx from, + TestCoreMessage msg + ) throws Exception { + List clusterNodes = Ignition.allGrids(); + + Map receivedMsgs = new ConcurrentHashMap<>(); + + CountDownLatch latch = new CountDownLatch(clusterNodes.size()); + + for (Ignite rcv : clusterNodes) { + String name = rcv.name(); + + ((IgniteEx)rcv).context().discovery().setCustomEventListener(TestCoreMessage.class, + (v, n, m) -> { + receivedMsgs.put(name, m); + + latch.countDown(); + }); + } + + from.context().discovery().sendCustomEvent(msg); + + assertTrue(latch.await(getTestTimeout(), TimeUnit.MILLISECONDS)); + + receivedMsgs.remove(from.name()); + + return receivedMsgs; + } + + /** */ + private void startPair(String firstVer, String secondVer) throws Exception { + IgniteEx first = startGrid(0, firstVer); + + if (!firstVer.equals(secondVer)) + ru(first).enableVersionUpgrade(); + + startGrid(1, secondVer); + } + + /** */ + private static void assertFields(String expB, String expD, String expE, String expF, TestMessage msg) { + assertEquals(A, msg.fldA()); + assertEquals(C, msg.fldC()); + assertEquals(expB, msg.fldB()); + assertEquals(expD, msg.fldD()); + assertEquals(expE, msg.fldE()); + assertEquals(expF, msg.fldF()); + } +} diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/message/TestCoreMessage.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/message/TestCoreMessage.java new file mode 100644 index 0000000000000..b22c083e22faa --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/message/TestCoreMessage.java @@ -0,0 +1,114 @@ +/* + * 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.processors.rollingupgrade.message; + +import org.apache.ignite.internal.DeprecatedBy; +import org.apache.ignite.internal.IntroducedBy; +import org.apache.ignite.internal.Order; +import org.apache.ignite.internal.managers.discovery.DiscoveryCustomMessage; +import org.apache.ignite.internal.processors.rollingupgrade.feature.TestIgniteReleaseFeatures_2_19_2; +import org.apache.ignite.internal.processors.rollingupgrade.feature.TestIgniteReleaseFeatures_2_20_0; +import org.apache.ignite.internal.processors.rollingupgrade.feature.TestIgniteReleaseFeatures_2_20_1; +import org.apache.ignite.lang.IgniteUuid; +import org.jetbrains.annotations.Nullable; + +/** */ +public class TestCoreMessage extends DiscoveryCustomMessage implements TestMessage { + /** */ + @Order(0) + String fldA; + + /** */ + @Order(1) + @DeprecatedBy(value = "VER_2_20_0_ID_3_FEATURE", registry = TestIgniteReleaseFeatures_2_20_0.class) + String fldB; + + /** */ + @Order(2) + String fldC; + + /** */ + @Order(3) + @IntroducedBy(value = "VER_2_19_2_ID_1_FEATURE", registry = TestIgniteReleaseFeatures_2_19_2.class) + @DeprecatedBy(value = "VER_2_20_0_ID_3_FEATURE", registry = TestIgniteReleaseFeatures_2_20_0.class) + String fldD; + + /** */ + @Order(4) + @IntroducedBy(value = "VER_2_20_0_ID_3_FEATURE", registry = TestIgniteReleaseFeatures_2_20_0.class) + String fldE; + + /** */ + @Order(5) + @IntroducedBy(value = "VER_2_20_1_ID_6_FEATURE", registry = TestIgniteReleaseFeatures_2_20_1.class) + String fldF; + + /** */ + public TestCoreMessage() { + super(IgniteUuid.randomUuid()); + } + + /** {@inheritDoc} */ + @Nullable @Override public DiscoveryCustomMessage ackMessage() { + return null; + } + + /** */ + public static TestCoreMessage build() { + TestCoreMessage msg = new TestCoreMessage(); + + msg.fldA = A; + msg.fldB = B; + msg.fldC = C; + msg.fldD = D; + msg.fldE = E; + msg.fldF = F; + + return msg; + } + + /** {@inheritDoc} */ + @Override public String fldA() { + return fldA; + } + + /** {@inheritDoc} */ + @Override public String fldB() { + return fldB; + } + + /** {@inheritDoc} */ + @Override public String fldC() { + return fldC; + } + + /** {@inheritDoc} */ + @Override public String fldD() { + return fldD; + } + + /** {@inheritDoc} */ + @Override public String fldE() { + return fldE; + } + + /** {@inheritDoc} */ + @Override public String fldF() { + return fldF; + } +} diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/message/TestDefaultRegistryMessage.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/message/TestDefaultRegistryMessage.java new file mode 100644 index 0000000000000..5dd169c633526 --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/message/TestDefaultRegistryMessage.java @@ -0,0 +1,86 @@ +/* + * 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.processors.rollingupgrade.message; + +import org.apache.ignite.internal.DeprecatedBy; +import org.apache.ignite.internal.IntroducedBy; +import org.apache.ignite.internal.Order; +import org.apache.ignite.plugin.extensions.communication.Message; + +/** */ +public class TestDefaultRegistryMessage implements Message, TestMessage { + /** */ + @Order(0) + String fldA; + + /** */ + @Order(1) + @DeprecatedBy("ROLLING_UPGRADE_FEATURE") + String fldB; + + /** */ + @Order(2) + String fldC; + + /** */ + @Order(3) + @IntroducedBy("ROLLING_UPGRADE_FEATURE") + String fldD; + + /** */ + public static TestDefaultRegistryMessage build() { + TestDefaultRegistryMessage msg = new TestDefaultRegistryMessage(); + + msg.fldA = A; + msg.fldB = B; + msg.fldC = C; + msg.fldD = D; + + return msg; + } + + /** {@inheritDoc} */ + @Override public String fldA() { + return fldA; + } + + /** {@inheritDoc} */ + @Override public String fldB() { + return fldB; + } + + /** {@inheritDoc} */ + @Override public String fldC() { + return fldC; + } + + /** {@inheritDoc} */ + @Override public String fldD() { + return fldD; + } + + /** {@inheritDoc} */ + @Override public String fldE() { + return null; + } + + /** {@inheritDoc} */ + @Override public String fldF() { + return null; + } +} diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/message/TestMessage.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/message/TestMessage.java new file mode 100644 index 0000000000000..15c639bf6fdaa --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/message/TestMessage.java @@ -0,0 +1,57 @@ +/* + * 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.processors.rollingupgrade.message; + +/** */ +public interface TestMessage { + /** */ + public static final String A = "A"; + + /** */ + public static final String B = "B"; + + /** */ + public static final String C = "C"; + + /** */ + public static final String D = "D"; + + /** */ + public static final String E = "E"; + + /** */ + public static final String F = "F"; + + /** */ + public String fldA(); + + /** */ + public String fldB(); + + /** */ + public String fldC(); + + /** */ + public String fldD(); + + /** */ + public String fldE(); + + /** */ + public String fldF(); +} diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/message/TestPluginMessage.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/message/TestPluginMessage.java new file mode 100644 index 0000000000000..78a56a8e31d9c --- /dev/null +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/message/TestPluginMessage.java @@ -0,0 +1,109 @@ +/* + * 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.processors.rollingupgrade.message; + +import org.apache.ignite.internal.DeprecatedBy; +import org.apache.ignite.internal.IntroducedBy; +import org.apache.ignite.internal.Order; +import org.apache.ignite.internal.processors.rollingupgrade.feature.TestPluginReleaseFeatures_1_0_0; +import org.apache.ignite.internal.processors.rollingupgrade.feature.TestPluginReleaseFeatures_2_0_0; +import org.apache.ignite.internal.processors.rollingupgrade.feature.TestPluginReleaseFeatures_2_1_0; +import org.apache.ignite.plugin.extensions.communication.Message; + +/** + * Carries fields guarded by features of two components at once: the core one and the test plugin's. + *

+ * Only exchanged between nodes that declare the plugin component. A field guarded by a feature of a + * component neither node declares has no answer - see {@code IgniteMessageSerializationContext#component} - + * so this message and the core-only {@link TestCoreMessage} are kept apart rather than + * folded into one that every test would have to declare the plugin for. + */ +public class TestPluginMessage implements Message, TestMessage { + /** */ + @Order(0) + String fldA; + + /** */ + @Order(1) + @DeprecatedBy(value = "VER_2_0_0_ID_1_FEATURE", registry = TestPluginReleaseFeatures_2_0_0.class) + String fldB; + + /** */ + @Order(2) + String fldC; + + /** */ + @Order(3) + @IntroducedBy(value = "VER_1_0_0_ID_0_FEATURE", registry = TestPluginReleaseFeatures_1_0_0.class) + @DeprecatedBy(value = "VER_2_0_0_ID_1_FEATURE", registry = TestPluginReleaseFeatures_2_0_0.class) + String fldD; + + /** */ + @Order(4) + @IntroducedBy(value = "VER_2_0_0_ID_1_FEATURE", registry = TestPluginReleaseFeatures_2_0_0.class) + String fldE; + + /** */ + @Order(5) + @IntroducedBy(value = "VER_2_1_0_ID_2_FEATURE", registry = TestPluginReleaseFeatures_2_1_0.class) + String fldF; + + /** */ + public static TestPluginMessage build() { + TestPluginMessage msg = new TestPluginMessage(); + + msg.fldA = A; + msg.fldB = B; + msg.fldC = C; + msg.fldD = D; + msg.fldE = E; + msg.fldF = F; + + return msg; + } + + /** {@inheritDoc} */ + @Override public String fldA() { + return fldA; + } + + /** {@inheritDoc} */ + @Override public String fldB() { + return fldB; + } + + /** {@inheritDoc} */ + @Override public String fldC() { + return fldC; + } + + /** {@inheritDoc} */ + @Override public String fldD() { + return fldD; + } + + /** {@inheritDoc} */ + @Override public String fldE() { + return fldE; + } + + /** {@inheritDoc} */ + @Override public String fldF() { + return fldF; + } +} diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/service/LazyServiceConfigurationMessageSerializationTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/service/LazyServiceConfigurationMessageSerializationTest.java index 1536b53e9926d..a978a506abb89 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/service/LazyServiceConfigurationMessageSerializationTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/service/LazyServiceConfigurationMessageSerializationTest.java @@ -37,6 +37,7 @@ import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; import org.junit.Test; +import static org.apache.ignite.internal.MessageSerializationContext.IGNORED; import static org.apache.ignite.internal.util.CommonUtils.makeMessageType; import static org.junit.Assert.assertArrayEquals; @@ -126,7 +127,7 @@ private T writeAndReadBack(T msg, long expReadsWritesCnt) th DirectMessageWriter writer = new DirectMessageWriter(msgFactory); writer.setBuffer(buf); - assertTrue(MessageSerialization.writeTo(msgFactory, msg, writer)); + assertTrue(MessageSerialization.writeTo(msgFactory, msg, writer, IGNORED)); assertEquals("Writes" + ERROR_SUFFIX, expReadsWritesCnt, writer.state()); buf.flip(); @@ -136,7 +137,7 @@ private T writeAndReadBack(T msg, long expReadsWritesCnt) th T res = (T)msgFactory.create(makeMessageType(buf.get(), buf.get())); - assertTrue(MessageSerialization.readFrom(msgFactory, res, reader)); + assertTrue(MessageSerialization.readFrom(msgFactory, res, reader, IGNORED)); assertEquals("Reads" + ERROR_SUFFIX, expReadsWritesCnt, reader.state()); DiscoveryMarshalling.unmarshal(res, kctx); diff --git a/modules/core/src/test/java/org/apache/ignite/internal/util/distributed/DistributedProcessResultMarshallingTest.java b/modules/core/src/test/java/org/apache/ignite/internal/util/distributed/DistributedProcessResultMarshallingTest.java index 0286297db11e4..8a8f0521739c4 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/util/distributed/DistributedProcessResultMarshallingTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/util/distributed/DistributedProcessResultMarshallingTest.java @@ -29,6 +29,7 @@ import org.apache.ignite.internal.CoreMessagesProvider; import org.apache.ignite.internal.GridKernalContext; import org.apache.ignite.internal.IgniteEx; +import org.apache.ignite.internal.MessageSerializationContext; import org.apache.ignite.internal.TestRecordingCommunicationSpi; import org.apache.ignite.internal.managers.communication.CommunicationMarshalling; import org.apache.ignite.internal.managers.communication.GridIoMessage; @@ -239,7 +240,7 @@ public PayloadMessage() { /** */ private static class PayloadSerializer implements MessageSerializer { /** {@inheritDoc} */ - @Override public boolean writeTo(PayloadMessage msg, MessageWriter writer) { + @Override public boolean writeTo(PayloadMessage msg, MessageWriter writer, MessageSerializationContext ctx) { if (!writer.isHeaderWritten()) { if (!writer.writeHeader(msg.directType())) return false; @@ -255,7 +256,7 @@ private static class PayloadSerializer implements MessageSerializer { /** {@inheritDoc} */ - @Override public boolean writeTo(MarshalOnceCheckMessage msg, MessageWriter writer) { + @Override public boolean writeTo(MarshalOnceCheckMessage msg, MessageWriter writer, MessageSerializationContext ctx) { if (!writer.isHeaderWritten()) { if (!writer.writeHeader(msg.directType())) return false; @@ -169,7 +170,7 @@ private static class Serializer implements MessageSerializer { /** {@inheritDoc} */ - @Override public boolean writeTo(RetryCheckMessage msg, MessageWriter writer) { + @Override public boolean writeTo(RetryCheckMessage msg, MessageWriter writer, MessageSerializationContext ctx) { if (!writer.isHeaderWritten()) { if (!writer.writeHeader(msg.directType())) return false; @@ -221,7 +222,7 @@ private static class RetrySerializer implements MessageSerializer T writeAndReadBack(T msg) throws IgniteCheckedExcept DirectMessageWriter writer = new DirectMessageWriter(msgFactory); writer.setBuffer(buf); - assertTrue(MessageSerialization.writeTo(msgFactory, msg, writer)); + assertTrue(MessageSerialization.writeTo(msgFactory, msg, writer, IGNORED)); buf.flip(); @@ -126,7 +127,7 @@ private T writeAndReadBack(T msg) throws IgniteCheckedExcept T res = (T)msgFactory.create(makeMessageType(buf.get(), buf.get())); - assertTrue(MessageSerialization.readFrom(msgFactory, res, reader)); + assertTrue(MessageSerialization.readFrom(msgFactory, res, reader, IGNORED)); return res; } diff --git a/modules/core/src/test/java/org/apache/ignite/spi/communication/tcp/TestDelayMessageSerializer.java b/modules/core/src/test/java/org/apache/ignite/spi/communication/tcp/TestDelayMessageSerializer.java index ce905b9806f70..1d61f44f57fa7 100644 --- a/modules/core/src/test/java/org/apache/ignite/spi/communication/tcp/TestDelayMessageSerializer.java +++ b/modules/core/src/test/java/org/apache/ignite/spi/communication/tcp/TestDelayMessageSerializer.java @@ -18,6 +18,7 @@ package org.apache.ignite.spi.communication.tcp; import org.apache.ignite.internal.IgniteInterruptedCheckedException; +import org.apache.ignite.internal.MessageSerializationContext; import org.apache.ignite.internal.util.typedef.internal.U; import org.apache.ignite.plugin.extensions.communication.MessageReader; import org.apache.ignite.plugin.extensions.communication.MessageSerializer; @@ -26,7 +27,7 @@ /** Serializer for {@link TestDelayMessage} that injects an optional write delay for testing. */ public class TestDelayMessageSerializer implements MessageSerializer { /** {@inheritDoc} */ - @Override public boolean writeTo(TestDelayMessage msg, MessageWriter writer) { + @Override public boolean writeTo(TestDelayMessage msg, MessageWriter writer, MessageSerializationContext ctx) { if (!writer.isHeaderWritten()) { if (!writer.writeHeader(msg.directType())) return false; @@ -47,7 +48,7 @@ public class TestDelayMessageSerializer implements MessageSerializer serde; /** {@inheritDoc} */ - @Override public boolean writeTo(ExploitMessage msg, MessageWriter writer) { + @Override public boolean writeTo(ExploitMessage msg, MessageWriter writer, MessageSerializationContext ctx) { initIfNecessary(); - return serde.writeTo(msg, writer); + return serde.writeTo(msg, writer, ctx); } /** {@inheritDoc} */ - @Override public boolean readFrom(ExploitMessage msg, MessageReader reader) { + @Override public boolean readFrom(ExploitMessage msg, MessageReader reader, MessageSerializationContext ctx) { initIfNecessary(); - return serde.readFrom(msg, reader); + return serde.readFrom(msg, reader, ctx); } /** {@inheritDoc} */ diff --git a/modules/core/src/test/java/org/apache/ignite/testframework/GridTestNode.java b/modules/core/src/test/java/org/apache/ignite/testframework/GridTestNode.java index cd76954a1a274..e90cc86847564 100644 --- a/modules/core/src/test/java/org/apache/ignite/testframework/GridTestNode.java +++ b/modules/core/src/test/java/org/apache/ignite/testframework/GridTestNode.java @@ -17,15 +17,19 @@ package org.apache.ignite.testframework; +import java.io.Serializable; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.UUID; import java.util.concurrent.atomic.AtomicInteger; +import org.apache.ignite.cache.CacheMetrics; import org.apache.ignite.cluster.ClusterMetrics; import org.apache.ignite.cluster.ClusterNode; import org.apache.ignite.internal.IgniteNodeAttributes; +import org.apache.ignite.internal.managers.discovery.IgniteClusterNode; +import org.apache.ignite.internal.processors.rollingupgrade.feature.IgniteNodeFeatureSet; import org.apache.ignite.internal.util.lang.GridMetadataAwareAdapter; import org.apache.ignite.lang.IgniteProductVersion; @@ -34,7 +38,7 @@ /** * Test node. */ -public class GridTestNode extends GridMetadataAwareAdapter implements ClusterNode { +public class GridTestNode extends GridMetadataAwareAdapter implements IgniteClusterNode { /** */ private static final IgniteProductVersion VERSION = fromString("99.99.99"); @@ -103,13 +107,6 @@ public GridTestNode(UUID id, ClusterMetrics metrics) { return id; } - /** - * @param consistentId Consistent ID. - */ - public void consistentId(Object consistentId) { - this.consistentId = consistentId; - } - /** {@inheritDoc} */ @Override public Object consistentId() { return consistentId; @@ -213,6 +210,16 @@ public void order(long order) { return VERSION; } + /** {@inheritDoc} */ + @Override public IgniteNodeFeatureSet features() { + return IgniteNodeFeatureSet.LOCAL_CORE_FEATURES; + } + + /** {@inheritDoc} */ + @Override public void setConsistentId(Serializable consistentId) { + this.consistentId = consistentId; + } + /** * Sets node metrics. * @@ -222,6 +229,16 @@ public void setMetrics(ClusterMetrics metrics) { this.metrics = metrics; } + /** {@inheritDoc} */ + @Override public Map cacheMetrics() { + return Collections.emptyMap(); + } + + /** {@inheritDoc} */ + @Override public void setCacheMetrics(Map cacheMetrics) { + // No-op. + } + /** {@inheritDoc} */ @Override public boolean isLocal() { return false; diff --git a/modules/core/src/test/java/org/apache/ignite/testframework/junits/common/GridCommonAbstractTest.java b/modules/core/src/test/java/org/apache/ignite/testframework/junits/common/GridCommonAbstractTest.java index 55e53c7d44cb1..4767e5fdc587d 100755 --- a/modules/core/src/test/java/org/apache/ignite/testframework/junits/common/GridCommonAbstractTest.java +++ b/modules/core/src/test/java/org/apache/ignite/testframework/junits/common/GridCommonAbstractTest.java @@ -1414,7 +1414,7 @@ protected final List movingKeysAfterJoin(Ignite ign, String cacheName, if (nodeInitializer != null) nodeInitializer.apply(fakeNode); - fakeNode.consistentId(joiningNodeConsistentId == null ? getTestIgniteInstanceName(nodes.size()) : + fakeNode.setConsistentId(joiningNodeConsistentId == null ? getTestIgniteInstanceName(nodes.size()) : joiningNodeConsistentId); nodes.add(fakeNode); @@ -1465,7 +1465,7 @@ protected List evictingPartitionsAfterJoin(Ignite ign, IgniteCache { /** */ - @Override public final boolean writeTo(ChildMessage msg, MessageWriter writer) { + @Override public final boolean writeTo(ChildMessage msg, MessageWriter writer, MessageSerializationContext ctx) { if (!writer.isHeaderWritten()) { if (!writer.writeHeader(msg.directType())) return false; @@ -68,7 +69,7 @@ public final class ChildMessageSerializer implements MessageSerializer { /** */ - @Override public final boolean writeTo(CorrectEmptyMessage msg, MessageWriter writer) { + @Override public final boolean writeTo(CorrectEmptyMessage msg, MessageWriter writer, MessageSerializationContext ctx) { if (!writer.isHeaderWritten()) { if (!writer.writeHeader(msg.directType())) return false; @@ -44,7 +45,7 @@ public final class CorrectEmptyMessageSerializer implements MessageSerializer(TransactionIsolation.class, transactionIsolationMapper::encode, transactionIsolationMapper::decode), CollectionImplementationType.ARRAY_LIST), CollectionImplementationType.ARRAY_LIST); /** */ - @Override public final boolean writeTo(CustomMapperEnumFieldsMessage msg, MessageWriter writer) { + @Override public final boolean writeTo(CustomMapperEnumFieldsMessage msg, MessageWriter writer, MessageSerializationContext ctx) { if (!writer.isHeaderWritten()) { if (!writer.writeHeader(msg.directType())) return false; @@ -57,7 +58,7 @@ public final class CustomMapperEnumFieldsMessageSerializer implements MessageSer writer.incrementState(); case 1: - if (!writer.writeCollection(msg.isolations, isolationsCollDesc)) + if (!writer.writeCollection(msg.isolations, isolationsCollDesc, ctx)) return false; writer.incrementState(); @@ -67,7 +68,7 @@ public final class CustomMapperEnumFieldsMessageSerializer implements MessageSer } /** */ - @Override public final boolean readFrom(CustomMapperEnumFieldsMessage msg, MessageReader reader) { + @Override public final boolean readFrom(CustomMapperEnumFieldsMessage msg, MessageReader reader, MessageSerializationContext ctx) { switch (reader.state()) { case 0: msg.txMode = transactionIsolationMapper.decode(reader.readByte()); @@ -78,7 +79,7 @@ public final class CustomMapperEnumFieldsMessageSerializer implements MessageSer reader.incrementState(); case 1: - msg.isolations = reader.readCollection(isolationsCollDesc); + msg.isolations = reader.readCollection(isolationsCollDesc, ctx); if (!reader.isLastRead()) return false; diff --git a/modules/core/src/test/resources/codegen/DefaultMapperEnumFieldsMessageSerializer.java b/modules/core/src/test/resources/codegen/DefaultMapperEnumFieldsMessageSerializer.java index dba15be284691..9e707da48c770 100644 --- a/modules/core/src/test/resources/codegen/DefaultMapperEnumFieldsMessageSerializer.java +++ b/modules/core/src/test/resources/codegen/DefaultMapperEnumFieldsMessageSerializer.java @@ -18,6 +18,7 @@ package org.apache.ignite.internal; import org.apache.ignite.internal.DefaultMapperEnumFieldsMessage; +import org.apache.ignite.internal.MessageSerializationContext; import org.apache.ignite.internal.processors.cache.GridCacheOperation; import org.apache.ignite.internal.processors.cache.verify.PartitionHashRecord.PartitionState; import org.apache.ignite.plugin.extensions.communication.CollectionImplementationType; @@ -50,7 +51,7 @@ public final class DefaultMapperEnumFieldsMessageSerializer implements MessageSe private static final MessageMapType isolationStringMapCollDesc = new MessageMapType(new MessageCollectionType(new MessageEnumType<>(TransactionIsolation.class, DefaultEnumMapper.INSTANCE::encode, b -> DefaultEnumMapper.INSTANCE.decode(transactionIsolationVals, b)), CollectionImplementationType.ARRAY_LIST), new MessageItemType(MessageCollectionItemType.STRING), false); /** */ - @Override public final boolean writeTo(DefaultMapperEnumFieldsMessage msg, MessageWriter writer) { + @Override public final boolean writeTo(DefaultMapperEnumFieldsMessage msg, MessageWriter writer, MessageSerializationContext ctx) { if (!writer.isHeaderWritten()) { if (!writer.writeHeader(msg.directType())) return false; @@ -72,13 +73,13 @@ public final class DefaultMapperEnumFieldsMessageSerializer implements MessageSe writer.incrementState(); case 2: - if (!writer.writeMap(msg.isolationStringMap, isolationStringMapCollDesc)) + if (!writer.writeMap(msg.isolationStringMap, isolationStringMapCollDesc, ctx)) return false; writer.incrementState(); case 3: - if (!writer.writeCollection(msg.partStates, partStatesCollDesc)) + if (!writer.writeCollection(msg.partStates, partStatesCollDesc, ctx)) return false; writer.incrementState(); @@ -88,7 +89,7 @@ public final class DefaultMapperEnumFieldsMessageSerializer implements MessageSe } /** */ - @Override public final boolean readFrom(DefaultMapperEnumFieldsMessage msg, MessageReader reader) { + @Override public final boolean readFrom(DefaultMapperEnumFieldsMessage msg, MessageReader reader, MessageSerializationContext ctx) { switch (reader.state()) { case 0: msg.publicEnum = DefaultEnumMapper.INSTANCE.decode(transactionIsolationVals, reader.readByte()); @@ -107,7 +108,7 @@ public final class DefaultMapperEnumFieldsMessageSerializer implements MessageSe reader.incrementState(); case 2: - msg.isolationStringMap = reader.readMap(isolationStringMapCollDesc); + msg.isolationStringMap = reader.readMap(isolationStringMapCollDesc, ctx); if (!reader.isLastRead()) return false; @@ -115,7 +116,7 @@ public final class DefaultMapperEnumFieldsMessageSerializer implements MessageSe reader.incrementState(); case 3: - msg.partStates = reader.readCollection(partStatesCollDesc); + msg.partStates = reader.readCollection(partStatesCollDesc, ctx); if (!reader.isLastRead()) return false; diff --git a/modules/core/src/test/resources/codegen/TestCollectionsMessageSerializer.java b/modules/core/src/test/resources/codegen/TestCollectionsMessageSerializer.java index bd79c20a48c26..d1814946151ce 100644 --- a/modules/core/src/test/resources/codegen/TestCollectionsMessageSerializer.java +++ b/modules/core/src/test/resources/codegen/TestCollectionsMessageSerializer.java @@ -17,6 +17,7 @@ package org.apache.ignite.internal; +import org.apache.ignite.internal.MessageSerializationContext; import org.apache.ignite.internal.TestCollectionsMessage; import org.apache.ignite.plugin.extensions.communication.CollectionImplementationType; import org.apache.ignite.plugin.extensions.communication.MessageCollectionItemType; @@ -86,7 +87,7 @@ public final class TestCollectionsMessageSerializer implements MessageSerializer private static final MessageCollectionType uuidListCollDesc = new MessageCollectionType(new MessageItemType(MessageCollectionItemType.UUID), CollectionImplementationType.ARRAY_LIST); /** */ - @Override public final boolean writeTo(TestCollectionsMessage msg, MessageWriter writer) { + @Override public final boolean writeTo(TestCollectionsMessage msg, MessageWriter writer, MessageSerializationContext ctx) { if (!writer.isHeaderWritten()) { if (!writer.writeHeader(msg.directType())) return false; @@ -96,157 +97,157 @@ public final class TestCollectionsMessageSerializer implements MessageSerializer switch (writer.state()) { case 0: - if (!writer.writeCollection(msg.booleanArrayList, booleanArrayListCollDesc)) + if (!writer.writeCollection(msg.booleanArrayList, booleanArrayListCollDesc, ctx)) return false; writer.incrementState(); case 1: - if (!writer.writeCollection(msg.byteArrayList, byteArrayListCollDesc)) + if (!writer.writeCollection(msg.byteArrayList, byteArrayListCollDesc, ctx)) return false; writer.incrementState(); case 2: - if (!writer.writeCollection(msg.shortArrayList, shortArrayListCollDesc)) + if (!writer.writeCollection(msg.shortArrayList, shortArrayListCollDesc, ctx)) return false; writer.incrementState(); case 3: - if (!writer.writeCollection(msg.intArrayList, intArrayListCollDesc)) + if (!writer.writeCollection(msg.intArrayList, intArrayListCollDesc, ctx)) return false; writer.incrementState(); case 4: - if (!writer.writeCollection(msg.longArrayList, longArrayListCollDesc)) + if (!writer.writeCollection(msg.longArrayList, longArrayListCollDesc, ctx)) return false; writer.incrementState(); case 5: - if (!writer.writeCollection(msg.charArrayList, charArrayListCollDesc)) + if (!writer.writeCollection(msg.charArrayList, charArrayListCollDesc, ctx)) return false; writer.incrementState(); case 6: - if (!writer.writeCollection(msg.floatArrayList, floatArrayListCollDesc)) + if (!writer.writeCollection(msg.floatArrayList, floatArrayListCollDesc, ctx)) return false; writer.incrementState(); case 7: - if (!writer.writeCollection(msg.doubleArrayList, doubleArrayListCollDesc)) + if (!writer.writeCollection(msg.doubleArrayList, doubleArrayListCollDesc, ctx)) return false; writer.incrementState(); case 8: - if (!writer.writeCollection(msg.stringList, stringListCollDesc)) + if (!writer.writeCollection(msg.stringList, stringListCollDesc, ctx)) return false; writer.incrementState(); case 9: - if (!writer.writeCollection(msg.uuidList, uuidListCollDesc)) + if (!writer.writeCollection(msg.uuidList, uuidListCollDesc, ctx)) return false; writer.incrementState(); case 10: - if (!writer.writeCollection(msg.bitSetList, bitSetListCollDesc)) + if (!writer.writeCollection(msg.bitSetList, bitSetListCollDesc, ctx)) return false; writer.incrementState(); case 11: - if (!writer.writeCollection(msg.igniteUuidList, igniteUuidListCollDesc)) + if (!writer.writeCollection(msg.igniteUuidList, igniteUuidListCollDesc, ctx)) return false; writer.incrementState(); case 12: - if (!writer.writeCollection(msg.affTopVersionList, affTopVersionListCollDesc)) + if (!writer.writeCollection(msg.affTopVersionList, affTopVersionListCollDesc, ctx)) return false; writer.incrementState(); case 13: - if (!writer.writeCollection(msg.boxedBooleanList, boxedBooleanListCollDesc)) + if (!writer.writeCollection(msg.boxedBooleanList, boxedBooleanListCollDesc, ctx)) return false; writer.incrementState(); case 14: - if (!writer.writeCollection(msg.boxedByteList, boxedByteListCollDesc)) + if (!writer.writeCollection(msg.boxedByteList, boxedByteListCollDesc, ctx)) return false; writer.incrementState(); case 15: - if (!writer.writeCollection(msg.boxedShortList, boxedShortListCollDesc)) + if (!writer.writeCollection(msg.boxedShortList, boxedShortListCollDesc, ctx)) return false; writer.incrementState(); case 16: - if (!writer.writeCollection(msg.boxedIntList, boxedIntListCollDesc)) + if (!writer.writeCollection(msg.boxedIntList, boxedIntListCollDesc, ctx)) return false; writer.incrementState(); case 17: - if (!writer.writeCollection(msg.boxedLongList, boxedLongListCollDesc)) + if (!writer.writeCollection(msg.boxedLongList, boxedLongListCollDesc, ctx)) return false; writer.incrementState(); case 18: - if (!writer.writeCollection(msg.boxedCharList, boxedCharListCollDesc)) + if (!writer.writeCollection(msg.boxedCharList, boxedCharListCollDesc, ctx)) return false; writer.incrementState(); case 19: - if (!writer.writeCollection(msg.boxedFloatList, boxedFloatListCollDesc)) + if (!writer.writeCollection(msg.boxedFloatList, boxedFloatListCollDesc, ctx)) return false; writer.incrementState(); case 20: - if (!writer.writeCollection(msg.boxedDoubleList, boxedDoubleListCollDesc)) + if (!writer.writeCollection(msg.boxedDoubleList, boxedDoubleListCollDesc, ctx)) return false; writer.incrementState(); case 21: - if (!writer.writeCollection(msg.messageList, messageListCollDesc)) + if (!writer.writeCollection(msg.messageList, messageListCollDesc, ctx)) return false; writer.incrementState(); case 22: - if (!writer.writeCollection(msg.gridLongListList, gridLongListListCollDesc)) + if (!writer.writeCollection(msg.gridLongListList, gridLongListListCollDesc, ctx)) return false; writer.incrementState(); case 23: - if (!writer.writeCollection(msg.boxedIntegerSet, boxedIntegerSetCollDesc)) + if (!writer.writeCollection(msg.boxedIntegerSet, boxedIntegerSetCollDesc, ctx)) return false; writer.incrementState(); case 24: - if (!writer.writeCollection(msg.bitSetSet, bitSetSetCollDesc)) + if (!writer.writeCollection(msg.bitSetSet, bitSetSetCollDesc, ctx)) return false; writer.incrementState(); case 25: - if (!writer.writeCollection(msg.cacheObjectSet, cacheObjectSetCollDesc)) + if (!writer.writeCollection(msg.cacheObjectSet, cacheObjectSetCollDesc, ctx)) return false; writer.incrementState(); @@ -256,10 +257,10 @@ public final class TestCollectionsMessageSerializer implements MessageSerializer } /** */ - @Override public final boolean readFrom(TestCollectionsMessage msg, MessageReader reader) { + @Override public final boolean readFrom(TestCollectionsMessage msg, MessageReader reader, MessageSerializationContext ctx) { switch (reader.state()) { case 0: - msg.booleanArrayList = reader.readCollection(booleanArrayListCollDesc); + msg.booleanArrayList = reader.readCollection(booleanArrayListCollDesc, ctx); if (!reader.isLastRead()) return false; @@ -267,7 +268,7 @@ public final class TestCollectionsMessageSerializer implements MessageSerializer reader.incrementState(); case 1: - msg.byteArrayList = reader.readCollection(byteArrayListCollDesc); + msg.byteArrayList = reader.readCollection(byteArrayListCollDesc, ctx); if (!reader.isLastRead()) return false; @@ -275,7 +276,7 @@ public final class TestCollectionsMessageSerializer implements MessageSerializer reader.incrementState(); case 2: - msg.shortArrayList = reader.readCollection(shortArrayListCollDesc); + msg.shortArrayList = reader.readCollection(shortArrayListCollDesc, ctx); if (!reader.isLastRead()) return false; @@ -283,7 +284,7 @@ public final class TestCollectionsMessageSerializer implements MessageSerializer reader.incrementState(); case 3: - msg.intArrayList = reader.readCollection(intArrayListCollDesc); + msg.intArrayList = reader.readCollection(intArrayListCollDesc, ctx); if (!reader.isLastRead()) return false; @@ -291,7 +292,7 @@ public final class TestCollectionsMessageSerializer implements MessageSerializer reader.incrementState(); case 4: - msg.longArrayList = reader.readCollection(longArrayListCollDesc); + msg.longArrayList = reader.readCollection(longArrayListCollDesc, ctx); if (!reader.isLastRead()) return false; @@ -299,7 +300,7 @@ public final class TestCollectionsMessageSerializer implements MessageSerializer reader.incrementState(); case 5: - msg.charArrayList = reader.readCollection(charArrayListCollDesc); + msg.charArrayList = reader.readCollection(charArrayListCollDesc, ctx); if (!reader.isLastRead()) return false; @@ -307,7 +308,7 @@ public final class TestCollectionsMessageSerializer implements MessageSerializer reader.incrementState(); case 6: - msg.floatArrayList = reader.readCollection(floatArrayListCollDesc); + msg.floatArrayList = reader.readCollection(floatArrayListCollDesc, ctx); if (!reader.isLastRead()) return false; @@ -315,7 +316,7 @@ public final class TestCollectionsMessageSerializer implements MessageSerializer reader.incrementState(); case 7: - msg.doubleArrayList = reader.readCollection(doubleArrayListCollDesc); + msg.doubleArrayList = reader.readCollection(doubleArrayListCollDesc, ctx); if (!reader.isLastRead()) return false; @@ -323,7 +324,7 @@ public final class TestCollectionsMessageSerializer implements MessageSerializer reader.incrementState(); case 8: - msg.stringList = reader.readCollection(stringListCollDesc); + msg.stringList = reader.readCollection(stringListCollDesc, ctx); if (!reader.isLastRead()) return false; @@ -331,7 +332,7 @@ public final class TestCollectionsMessageSerializer implements MessageSerializer reader.incrementState(); case 9: - msg.uuidList = reader.readCollection(uuidListCollDesc); + msg.uuidList = reader.readCollection(uuidListCollDesc, ctx); if (!reader.isLastRead()) return false; @@ -339,7 +340,7 @@ public final class TestCollectionsMessageSerializer implements MessageSerializer reader.incrementState(); case 10: - msg.bitSetList = reader.readCollection(bitSetListCollDesc); + msg.bitSetList = reader.readCollection(bitSetListCollDesc, ctx); if (!reader.isLastRead()) return false; @@ -347,7 +348,7 @@ public final class TestCollectionsMessageSerializer implements MessageSerializer reader.incrementState(); case 11: - msg.igniteUuidList = reader.readCollection(igniteUuidListCollDesc); + msg.igniteUuidList = reader.readCollection(igniteUuidListCollDesc, ctx); if (!reader.isLastRead()) return false; @@ -355,7 +356,7 @@ public final class TestCollectionsMessageSerializer implements MessageSerializer reader.incrementState(); case 12: - msg.affTopVersionList = reader.readCollection(affTopVersionListCollDesc); + msg.affTopVersionList = reader.readCollection(affTopVersionListCollDesc, ctx); if (!reader.isLastRead()) return false; @@ -363,7 +364,7 @@ public final class TestCollectionsMessageSerializer implements MessageSerializer reader.incrementState(); case 13: - msg.boxedBooleanList = reader.readCollection(boxedBooleanListCollDesc); + msg.boxedBooleanList = reader.readCollection(boxedBooleanListCollDesc, ctx); if (!reader.isLastRead()) return false; @@ -371,7 +372,7 @@ public final class TestCollectionsMessageSerializer implements MessageSerializer reader.incrementState(); case 14: - msg.boxedByteList = reader.readCollection(boxedByteListCollDesc); + msg.boxedByteList = reader.readCollection(boxedByteListCollDesc, ctx); if (!reader.isLastRead()) return false; @@ -379,7 +380,7 @@ public final class TestCollectionsMessageSerializer implements MessageSerializer reader.incrementState(); case 15: - msg.boxedShortList = reader.readCollection(boxedShortListCollDesc); + msg.boxedShortList = reader.readCollection(boxedShortListCollDesc, ctx); if (!reader.isLastRead()) return false; @@ -387,7 +388,7 @@ public final class TestCollectionsMessageSerializer implements MessageSerializer reader.incrementState(); case 16: - msg.boxedIntList = reader.readCollection(boxedIntListCollDesc); + msg.boxedIntList = reader.readCollection(boxedIntListCollDesc, ctx); if (!reader.isLastRead()) return false; @@ -395,7 +396,7 @@ public final class TestCollectionsMessageSerializer implements MessageSerializer reader.incrementState(); case 17: - msg.boxedLongList = reader.readCollection(boxedLongListCollDesc); + msg.boxedLongList = reader.readCollection(boxedLongListCollDesc, ctx); if (!reader.isLastRead()) return false; @@ -403,7 +404,7 @@ public final class TestCollectionsMessageSerializer implements MessageSerializer reader.incrementState(); case 18: - msg.boxedCharList = reader.readCollection(boxedCharListCollDesc); + msg.boxedCharList = reader.readCollection(boxedCharListCollDesc, ctx); if (!reader.isLastRead()) return false; @@ -411,7 +412,7 @@ public final class TestCollectionsMessageSerializer implements MessageSerializer reader.incrementState(); case 19: - msg.boxedFloatList = reader.readCollection(boxedFloatListCollDesc); + msg.boxedFloatList = reader.readCollection(boxedFloatListCollDesc, ctx); if (!reader.isLastRead()) return false; @@ -419,7 +420,7 @@ public final class TestCollectionsMessageSerializer implements MessageSerializer reader.incrementState(); case 20: - msg.boxedDoubleList = reader.readCollection(boxedDoubleListCollDesc); + msg.boxedDoubleList = reader.readCollection(boxedDoubleListCollDesc, ctx); if (!reader.isLastRead()) return false; @@ -427,7 +428,7 @@ public final class TestCollectionsMessageSerializer implements MessageSerializer reader.incrementState(); case 21: - msg.messageList = reader.readCollection(messageListCollDesc); + msg.messageList = reader.readCollection(messageListCollDesc, ctx); if (!reader.isLastRead()) return false; @@ -435,7 +436,7 @@ public final class TestCollectionsMessageSerializer implements MessageSerializer reader.incrementState(); case 22: - msg.gridLongListList = reader.readCollection(gridLongListListCollDesc); + msg.gridLongListList = reader.readCollection(gridLongListListCollDesc, ctx); if (!reader.isLastRead()) return false; @@ -443,7 +444,7 @@ public final class TestCollectionsMessageSerializer implements MessageSerializer reader.incrementState(); case 23: - msg.boxedIntegerSet = reader.readCollection(boxedIntegerSetCollDesc); + msg.boxedIntegerSet = reader.readCollection(boxedIntegerSetCollDesc, ctx); if (!reader.isLastRead()) return false; @@ -451,7 +452,7 @@ public final class TestCollectionsMessageSerializer implements MessageSerializer reader.incrementState(); case 24: - msg.bitSetSet = reader.readCollection(bitSetSetCollDesc); + msg.bitSetSet = reader.readCollection(bitSetSetCollDesc, ctx); if (!reader.isLastRead()) return false; @@ -459,7 +460,7 @@ public final class TestCollectionsMessageSerializer implements MessageSerializer reader.incrementState(); case 25: - msg.cacheObjectSet = reader.readCollection(cacheObjectSetCollDesc); + msg.cacheObjectSet = reader.readCollection(cacheObjectSetCollDesc, ctx); if (!reader.isLastRead()) return false; diff --git a/modules/core/src/test/resources/codegen/TestEnumSetMessageSerializer.java b/modules/core/src/test/resources/codegen/TestEnumSetMessageSerializer.java index fb0afa111fa6f..d5cc64a6abde1 100644 --- a/modules/core/src/test/resources/codegen/TestEnumSetMessageSerializer.java +++ b/modules/core/src/test/resources/codegen/TestEnumSetMessageSerializer.java @@ -17,6 +17,7 @@ package org.apache.ignite.internal; +import org.apache.ignite.internal.MessageSerializationContext; import org.apache.ignite.internal.TestEnumSetMessage; import org.apache.ignite.plugin.extensions.communication.CollectionImplementationType; import org.apache.ignite.plugin.extensions.communication.MessageCollectionItemType; @@ -46,7 +47,7 @@ public final class TestEnumSetMessageSerializer implements MessageSerializer(TransactionIsolation.class, DefaultEnumMapper.INSTANCE::encode, b -> DefaultEnumMapper.INSTANCE.decode(transactionIsolationVals, b)), CollectionImplementationType.ENUM_SET), false); /** */ - @Override public final boolean writeTo(TestEnumSetMessage msg, MessageWriter writer) { + @Override public final boolean writeTo(TestEnumSetMessage msg, MessageWriter writer, MessageSerializationContext ctx) { if (!writer.isHeaderWritten()) { if (!writer.writeHeader(msg.directType())) return false; @@ -56,19 +57,19 @@ public final class TestEnumSetMessageSerializer implements MessageSerializer { /** */ - @Override public final boolean writeTo(TestMarshallableMessage msg, MessageWriter writer) { + @Override public final boolean writeTo(TestMarshallableMessage msg, MessageWriter writer, MessageSerializationContext ctx) { if (!writer.isHeaderWritten()) { if (!writer.writeHeader(msg.directType())) return false; @@ -61,7 +62,7 @@ public final class TestMarshallableMessageSerializer implements MessageSerialize } /** */ - @Override public final boolean readFrom(TestMarshallableMessage msg, MessageReader reader) { + @Override public final boolean readFrom(TestMarshallableMessage msg, MessageReader reader, MessageSerializationContext ctx) { switch (reader.state()) { case 0: msg.iv = reader.readInt(); diff --git a/modules/core/src/test/resources/codegen/TestMarshalledArrayMapMessageSerializer.java b/modules/core/src/test/resources/codegen/TestMarshalledArrayMapMessageSerializer.java index 4f3a7c0e51fc9..52f8785c633e4 100644 --- a/modules/core/src/test/resources/codegen/TestMarshalledArrayMapMessageSerializer.java +++ b/modules/core/src/test/resources/codegen/TestMarshalledArrayMapMessageSerializer.java @@ -19,6 +19,7 @@ import java.util.List; import org.apache.ignite.internal.GridTopicMessage; +import org.apache.ignite.internal.MessageSerializationContext; import org.apache.ignite.internal.TestMarshalledArrayMapMessage; import org.apache.ignite.plugin.extensions.communication.CollectionImplementationType; import org.apache.ignite.plugin.extensions.communication.MessageArrayType; @@ -45,7 +46,7 @@ public final class TestMarshalledArrayMapMessageSerializer implements MessageSer private static final MessageArrayType mapValsCollDesc = new MessageArrayType(new MessageCollectionType(new MessageItemType(MessageCollectionItemType.MSG), CollectionImplementationType.ARRAY_LIST), List.class); /** */ - @Override public final boolean writeTo(TestMarshalledArrayMapMessage msg, MessageWriter writer) { + @Override public final boolean writeTo(TestMarshalledArrayMapMessage msg, MessageWriter writer, MessageSerializationContext ctx) { if (!writer.isHeaderWritten()) { if (!writer.writeHeader(msg.directType())) return false; @@ -55,25 +56,25 @@ public final class TestMarshalledArrayMapMessageSerializer implements MessageSer switch (writer.state()) { case 0: - if (!writer.writeObjectArray(msg.mapKeys, mapKeysCollDesc)) + if (!writer.writeObjectArray(msg.mapKeys, mapKeysCollDesc, ctx)) return false; writer.incrementState(); case 1: - if (!writer.writeObjectArray(msg.mapVals, mapValsCollDesc)) + if (!writer.writeObjectArray(msg.mapVals, mapValsCollDesc, ctx)) return false; writer.incrementState(); case 2: - if (!writer.writeObjectArray(msg.fixedMapKeys, fixedMapKeysCollDesc)) + if (!writer.writeObjectArray(msg.fixedMapKeys, fixedMapKeysCollDesc, ctx)) return false; writer.incrementState(); case 3: - if (!writer.writeObjectArray(msg.fixedMapVals, fixedMapValsCollDesc)) + if (!writer.writeObjectArray(msg.fixedMapVals, fixedMapValsCollDesc, ctx)) return false; writer.incrementState(); @@ -83,10 +84,10 @@ public final class TestMarshalledArrayMapMessageSerializer implements MessageSer } /** */ - @Override public final boolean readFrom(TestMarshalledArrayMapMessage msg, MessageReader reader) { + @Override public final boolean readFrom(TestMarshalledArrayMapMessage msg, MessageReader reader, MessageSerializationContext ctx) { switch (reader.state()) { case 0: - msg.mapKeys = reader.readObjectArray(mapKeysCollDesc); + msg.mapKeys = reader.readObjectArray(mapKeysCollDesc, ctx); if (!reader.isLastRead()) return false; @@ -94,7 +95,7 @@ public final class TestMarshalledArrayMapMessageSerializer implements MessageSer reader.incrementState(); case 1: - msg.mapVals = reader.readObjectArray(mapValsCollDesc); + msg.mapVals = reader.readObjectArray(mapValsCollDesc, ctx); if (!reader.isLastRead()) return false; @@ -102,7 +103,7 @@ public final class TestMarshalledArrayMapMessageSerializer implements MessageSer reader.incrementState(); case 2: - msg.fixedMapKeys = reader.readObjectArray(fixedMapKeysCollDesc); + msg.fixedMapKeys = reader.readObjectArray(fixedMapKeysCollDesc, ctx); if (!reader.isLastRead()) return false; @@ -110,7 +111,7 @@ public final class TestMarshalledArrayMapMessageSerializer implements MessageSer reader.incrementState(); case 3: - msg.fixedMapVals = reader.readObjectArray(fixedMapValsCollDesc); + msg.fixedMapVals = reader.readObjectArray(fixedMapValsCollDesc, ctx); if (!reader.isLastRead()) return false; diff --git a/modules/core/src/test/resources/codegen/TestMarshalledCollectionMessageSerializer.java b/modules/core/src/test/resources/codegen/TestMarshalledCollectionMessageSerializer.java index 43f7bf2f2e996..7b831f8ec04e8 100644 --- a/modules/core/src/test/resources/codegen/TestMarshalledCollectionMessageSerializer.java +++ b/modules/core/src/test/resources/codegen/TestMarshalledCollectionMessageSerializer.java @@ -17,6 +17,7 @@ package org.apache.ignite.internal; +import org.apache.ignite.internal.MessageSerializationContext; import org.apache.ignite.internal.TestMarshalledCollectionMessage; import org.apache.ignite.internal.processors.cache.version.GridCacheVersion; import org.apache.ignite.plugin.extensions.communication.MessageArrayType; @@ -36,7 +37,7 @@ public final class TestMarshalledCollectionMessageSerializer implements MessageS private static final MessageArrayType keysArrCollDesc = new MessageArrayType(new MessageItemType(MessageCollectionItemType.GRID_CACHE_VERSION), GridCacheVersion.class); /** */ - @Override public final boolean writeTo(TestMarshalledCollectionMessage msg, MessageWriter writer) { + @Override public final boolean writeTo(TestMarshalledCollectionMessage msg, MessageWriter writer, MessageSerializationContext ctx) { if (!writer.isHeaderWritten()) { if (!writer.writeHeader(msg.directType())) return false; @@ -46,7 +47,7 @@ public final class TestMarshalledCollectionMessageSerializer implements MessageS switch (writer.state()) { case 0: - if (!writer.writeObjectArray(msg.keysArr, keysArrCollDesc)) + if (!writer.writeObjectArray(msg.keysArr, keysArrCollDesc, ctx)) return false; writer.incrementState(); @@ -56,10 +57,10 @@ public final class TestMarshalledCollectionMessageSerializer implements MessageS } /** */ - @Override public final boolean readFrom(TestMarshalledCollectionMessage msg, MessageReader reader) { + @Override public final boolean readFrom(TestMarshalledCollectionMessage msg, MessageReader reader, MessageSerializationContext ctx) { switch (reader.state()) { case 0: - msg.keysArr = reader.readObjectArray(keysArrCollDesc); + msg.keysArr = reader.readObjectArray(keysArrCollDesc, ctx); if (!reader.isLastRead()) return false; diff --git a/modules/core/src/test/resources/codegen/TestMarshalledMapMessageSerializer.java b/modules/core/src/test/resources/codegen/TestMarshalledMapMessageSerializer.java index dd0fcb2253364..e3aca2bf684f3 100644 --- a/modules/core/src/test/resources/codegen/TestMarshalledMapMessageSerializer.java +++ b/modules/core/src/test/resources/codegen/TestMarshalledMapMessageSerializer.java @@ -17,6 +17,7 @@ package org.apache.ignite.internal; +import org.apache.ignite.internal.MessageSerializationContext; import org.apache.ignite.internal.TestMarshalledMapMessage; import org.apache.ignite.plugin.extensions.communication.CollectionImplementationType; import org.apache.ignite.plugin.extensions.communication.MessageCollectionItemType; @@ -38,7 +39,7 @@ public final class TestMarshalledMapMessageSerializer implements MessageSerializ private static final MessageCollectionType mapValsCollDesc = new MessageCollectionType(new MessageItemType(MessageCollectionItemType.GRID_CACHE_VERSION), CollectionImplementationType.ARRAY_LIST); /** */ - @Override public final boolean writeTo(TestMarshalledMapMessage msg, MessageWriter writer) { + @Override public final boolean writeTo(TestMarshalledMapMessage msg, MessageWriter writer, MessageSerializationContext ctx) { if (!writer.isHeaderWritten()) { if (!writer.writeHeader(msg.directType())) return false; @@ -48,13 +49,13 @@ public final class TestMarshalledMapMessageSerializer implements MessageSerializ switch (writer.state()) { case 0: - if (!writer.writeCollection(msg.mapKeys, mapKeysCollDesc)) + if (!writer.writeCollection(msg.mapKeys, mapKeysCollDesc, ctx)) return false; writer.incrementState(); case 1: - if (!writer.writeCollection(msg.mapVals, mapValsCollDesc)) + if (!writer.writeCollection(msg.mapVals, mapValsCollDesc, ctx)) return false; writer.incrementState(); @@ -64,10 +65,10 @@ public final class TestMarshalledMapMessageSerializer implements MessageSerializ } /** */ - @Override public final boolean readFrom(TestMarshalledMapMessage msg, MessageReader reader) { + @Override public final boolean readFrom(TestMarshalledMapMessage msg, MessageReader reader, MessageSerializationContext ctx) { switch (reader.state()) { case 0: - msg.mapKeys = reader.readCollection(mapKeysCollDesc); + msg.mapKeys = reader.readCollection(mapKeysCollDesc, ctx); if (!reader.isLastRead()) return false; @@ -75,7 +76,7 @@ public final class TestMarshalledMapMessageSerializer implements MessageSerializ reader.incrementState(); case 1: - msg.mapVals = reader.readCollection(mapValsCollDesc); + msg.mapVals = reader.readCollection(mapValsCollDesc, ctx); if (!reader.isLastRead()) return false; diff --git a/modules/core/src/test/resources/codegen/TestMarshalledMessageSerializer.java b/modules/core/src/test/resources/codegen/TestMarshalledMessageSerializer.java index fa81057b60403..b924687a0d06f 100644 --- a/modules/core/src/test/resources/codegen/TestMarshalledMessageSerializer.java +++ b/modules/core/src/test/resources/codegen/TestMarshalledMessageSerializer.java @@ -17,6 +17,7 @@ package org.apache.ignite.internal; +import org.apache.ignite.internal.MessageSerializationContext; import org.apache.ignite.internal.TestMarshalledMessage; import org.apache.ignite.plugin.extensions.communication.MessageReader; import org.apache.ignite.plugin.extensions.communication.MessageSerializer; @@ -29,7 +30,7 @@ */ public final class TestMarshalledMessageSerializer implements MessageSerializer { /** */ - @Override public final boolean writeTo(TestMarshalledMessage msg, MessageWriter writer) { + @Override public final boolean writeTo(TestMarshalledMessage msg, MessageWriter writer, MessageSerializationContext ctx) { if (!writer.isHeaderWritten()) { if (!writer.writeHeader(msg.directType())) return false; @@ -49,7 +50,7 @@ public final class TestMarshalledMessageSerializer implements MessageSerializer< } /** */ - @Override public final boolean readFrom(TestMarshalledMessage msg, MessageReader reader) { + @Override public final boolean readFrom(TestMarshalledMessage msg, MessageReader reader, MessageSerializationContext ctx) { switch (reader.state()) { case 0: msg.dataBytes = reader.readByteArray(); diff --git a/modules/core/src/test/resources/codegen/TestMarshalledObjectsMessageSerializer.java b/modules/core/src/test/resources/codegen/TestMarshalledObjectsMessageSerializer.java index b68578b06a615..68d62d8879123 100644 --- a/modules/core/src/test/resources/codegen/TestMarshalledObjectsMessageSerializer.java +++ b/modules/core/src/test/resources/codegen/TestMarshalledObjectsMessageSerializer.java @@ -17,6 +17,7 @@ package org.apache.ignite.internal; +import org.apache.ignite.internal.MessageSerializationContext; import org.apache.ignite.internal.TestMarshalledObjectsMessage; import org.apache.ignite.plugin.extensions.communication.CollectionImplementationType; import org.apache.ignite.plugin.extensions.communication.MessageCollectionItemType; @@ -36,7 +37,7 @@ public final class TestMarshalledObjectsMessageSerializer implements MessageSeri private static final MessageCollectionType dataBytesCollDesc = new MessageCollectionType(new MessageItemType(MessageCollectionItemType.BYTE_ARR), CollectionImplementationType.ARRAY_LIST); /** */ - @Override public final boolean writeTo(TestMarshalledObjectsMessage msg, MessageWriter writer) { + @Override public final boolean writeTo(TestMarshalledObjectsMessage msg, MessageWriter writer, MessageSerializationContext ctx) { if (!writer.isHeaderWritten()) { if (!writer.writeHeader(msg.directType())) return false; @@ -46,7 +47,7 @@ public final class TestMarshalledObjectsMessageSerializer implements MessageSeri switch (writer.state()) { case 0: - if (!writer.writeCollection(msg.dataBytes, dataBytesCollDesc)) + if (!writer.writeCollection(msg.dataBytes, dataBytesCollDesc, ctx)) return false; writer.incrementState(); @@ -56,10 +57,10 @@ public final class TestMarshalledObjectsMessageSerializer implements MessageSeri } /** */ - @Override public final boolean readFrom(TestMarshalledObjectsMessage msg, MessageReader reader) { + @Override public final boolean readFrom(TestMarshalledObjectsMessage msg, MessageReader reader, MessageSerializationContext ctx) { switch (reader.state()) { case 0: - msg.dataBytes = reader.readCollection(dataBytesCollDesc); + msg.dataBytes = reader.readCollection(dataBytesCollDesc, ctx); if (!reader.isLastRead()) return false; diff --git a/modules/core/src/test/resources/codegen/TestMessageSerializer.java b/modules/core/src/test/resources/codegen/TestMessageSerializer.java index 7aa0a054471aa..1811c09f59037 100644 --- a/modules/core/src/test/resources/codegen/TestMessageSerializer.java +++ b/modules/core/src/test/resources/codegen/TestMessageSerializer.java @@ -17,6 +17,7 @@ package org.apache.ignite.internal; +import org.apache.ignite.internal.MessageSerializationContext; import org.apache.ignite.internal.TestMessage; import org.apache.ignite.internal.processors.cache.version.GridCacheVersion; import org.apache.ignite.plugin.extensions.communication.MessageArrayType; @@ -40,7 +41,7 @@ public final class TestMessageSerializer implements MessageSerializer { + /** */ + @Override public final boolean writeTo(TestRollingUpgradeAwareMessage msg, MessageWriter writer, MessageSerializationContext ctx) { + if (!writer.isHeaderWritten()) { + if (!writer.writeHeader(msg.directType())) + return false; + + writer.onHeaderWritten(); + } + + switch (writer.state()) { + case 0: + if (!writer.writeInt(msg.plain)) + return false; + + writer.incrementState(); + + case 1: + if (ctx.includeFieldDeprecatedBy(SupportedFeatureRegistry.ROLLING_UPGRADE_FEATURE)) { + if (!writer.writeString(msg.oldFld)) + return false; + } + + writer.incrementState(); + + case 2: + if (ctx.includeFieldIntroducedBy(SupportedFeatureRegistry.ROLLING_UPGRADE_FEATURE)) { + if (!writer.writeString(msg.newFld)) + return false; + } + + writer.incrementState(); + + case 3: + if (ctx.includeFieldIntroducedBy(SupportedFeatureRegistry.ROLLING_UPGRADE_FEATURE) && ctx.includeFieldDeprecatedBy(TestFeatureRegistry.SECOND_FEATURE)) { + if (!writer.writeLong(msg.windowed)) + return false; + } + + writer.incrementState(); + + } + + return true; + } + + /** */ + @Override public final boolean readFrom(TestRollingUpgradeAwareMessage msg, MessageReader reader, MessageSerializationContext ctx) { + switch (reader.state()) { + case 0: + msg.plain = reader.readInt(); + + if (!reader.isLastRead()) + return false; + + reader.incrementState(); + + case 1: + if (ctx.includeFieldDeprecatedBy(SupportedFeatureRegistry.ROLLING_UPGRADE_FEATURE)) { + msg.oldFld = reader.readString(); + + if (!reader.isLastRead()) + return false; + } + + reader.incrementState(); + + case 2: + if (ctx.includeFieldIntroducedBy(SupportedFeatureRegistry.ROLLING_UPGRADE_FEATURE)) { + msg.newFld = reader.readString(); + + if (!reader.isLastRead()) + return false; + } + + reader.incrementState(); + + case 3: + if (ctx.includeFieldIntroducedBy(SupportedFeatureRegistry.ROLLING_UPGRADE_FEATURE) && ctx.includeFieldDeprecatedBy(TestFeatureRegistry.SECOND_FEATURE)) { + msg.windowed = reader.readLong(); + + if (!reader.isLastRead()) + return false; + } + + reader.incrementState(); + + } + + return true; + } + + /** {@inheritDoc} */ + @Override public final TestRollingUpgradeAwareMessage createMessage() { + return new TestRollingUpgradeAwareMessage(); + } +} diff --git a/modules/core/src/test/resources/codegen/TestUnknownFeatureMessage.java b/modules/core/src/test/resources/codegen/TestUnknownFeatureMessage.java new file mode 100644 index 0000000000000..03fe51ce8d790 --- /dev/null +++ b/modules/core/src/test/resources/codegen/TestUnknownFeatureMessage.java @@ -0,0 +1,28 @@ +/* + * 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; + +import org.apache.ignite.plugin.extensions.communication.Message; + +/** */ +public class TestUnknownFeatureMessage implements Message { + /** */ + @Order(0) + @IntroducedBy("NO_SUCH_FEATURE") + int fld; +} diff --git a/modules/nio/src/main/java/org/apache/ignite/internal/util/nio/GridDirectParser.java b/modules/nio/src/main/java/org/apache/ignite/internal/util/nio/GridDirectParser.java index f30fbc5cfc0ea..43f89a6ab11c8 100644 --- a/modules/nio/src/main/java/org/apache/ignite/internal/util/nio/GridDirectParser.java +++ b/modules/nio/src/main/java/org/apache/ignite/internal/util/nio/GridDirectParser.java @@ -29,6 +29,7 @@ import org.jetbrains.annotations.Nullable; import static org.apache.ignite.internal.util.CommonUtils.makeMessageType; +import static org.apache.ignite.internal.util.nio.MessageSerialization.resolveSerializationContext; /** * Parser for direct messages. @@ -86,7 +87,7 @@ public GridDirectParser(IgniteLogger log, MessageFactory msgFactory, GridNioMess if (msg != null && buf.hasRemaining()) { reader.setBuffer(buf); - finished = MessageSerialization.readFrom(msgFactory, msg, reader); + finished = MessageSerialization.readFrom(msgFactory, msg, reader, resolveSerializationContext(ses)); } if (finished) { diff --git a/modules/nio/src/main/java/org/apache/ignite/internal/util/nio/GridNioServer.java b/modules/nio/src/main/java/org/apache/ignite/internal/util/nio/GridNioServer.java index a067277648d35..4461ca0bea9c1 100644 --- a/modules/nio/src/main/java/org/apache/ignite/internal/util/nio/GridNioServer.java +++ b/modules/nio/src/main/java/org/apache/ignite/internal/util/nio/GridNioServer.java @@ -85,6 +85,7 @@ import static org.apache.ignite.failure.FailureType.SYSTEM_WORKER_TERMINATION; import static org.apache.ignite.internal.util.nio.GridNioSessionMetaKey.MSG_WRITER; import static org.apache.ignite.internal.util.nio.GridNioSessionMetaKey.NIO_OPERATION; +import static org.apache.ignite.internal.util.nio.MessageSerialization.resolveSerializationContext; /** * TCP NIO server. Due to asynchronous nature of connections processing @@ -1506,7 +1507,7 @@ private void processWriteSsl(SelectionKey key) throws IOException { List pendingRequests = new ArrayList<>(2); if (req != null) - finished = writeToBuffer(writer, buf, req, pendingRequests); + finished = writeToBuffer(ses, writer, buf, req, pendingRequests); // Fill up as many messages as possible to write buffer. while (finished) { @@ -1518,7 +1519,7 @@ private void processWriteSsl(SelectionKey key) throws IOException { if (req == null) break; - finished = writeToBuffer(writer, buf, req, pendingRequests); + finished = writeToBuffer(ses, writer, buf, req, pendingRequests); } int sesBufLimit = buf.limit(); @@ -1588,6 +1589,7 @@ private void processWriteSsl(SelectionKey key) throws IOException { } /** + * @param ses Session the message is written to. * @param writer Customizer of writing. * @param buf Buffer to write. * @param req Source of data. @@ -1595,6 +1597,7 @@ private void processWriteSsl(SelectionKey key) throws IOException { * @return {@code true} if message successfully written to buffer and {@code false} otherwise. */ private boolean writeToBuffer( + GridSelectorNioSessionImpl ses, MessageWriter writer, ByteBuffer buf, SessionWriteRequest req, @@ -1614,7 +1617,7 @@ private boolean writeToBuffer( else { writer.setBuffer(buf); - finished = MessageSerialization.writeTo(messageFactory(), msg, writer); + finished = MessageSerialization.writeTo(messageFactory(), msg, writer, resolveSerializationContext(ses)); } if (finished) { @@ -1781,14 +1784,18 @@ private void processWrite0(SelectionKey key) throws IOException { } /** - * @param writer Customizer of writing. + * @param ses Session the message is written to. * @param buf Buffer to write. * @param req Source of data. - * @param ses Session for notification about writting. + * @param writer Customizer of writing. * @return {@code true} if message successfully written to buffer and {@code false} otherwise. */ - private boolean writeToBuffer(GridSelectorNioSessionImpl ses, ByteBuffer buf, SessionWriteRequest req, - MessageWriter writer) { + private boolean writeToBuffer( + GridSelectorNioSessionImpl ses, + ByteBuffer buf, + SessionWriteRequest req, + MessageWriter writer + ) { Message msg; boolean finished; msg = (Message)req.message(); @@ -1803,7 +1810,7 @@ private boolean writeToBuffer(GridSelectorNioSessionImpl ses, ByteBuffer buf, Se else { writer.setBuffer(buf); - finished = MessageSerialization.writeTo(msgFactory, msg, writer); + finished = MessageSerialization.writeTo(msgFactory, msg, writer, resolveSerializationContext(ses)); } if (finished) { diff --git a/modules/nio/src/main/java/org/apache/ignite/internal/util/nio/GridNioSessionMetaKey.java b/modules/nio/src/main/java/org/apache/ignite/internal/util/nio/GridNioSessionMetaKey.java index fbdcc6d5d34f9..9a5836d14cc4c 100644 --- a/modules/nio/src/main/java/org/apache/ignite/internal/util/nio/GridNioSessionMetaKey.java +++ b/modules/nio/src/main/java/org/apache/ignite/internal/util/nio/GridNioSessionMetaKey.java @@ -42,7 +42,10 @@ public enum GridNioSessionMetaKey { MARSHALLER_ID, /** Message writer. */ - MSG_WRITER; + MSG_WRITER, + + /** Message serialization context. */ + MSG_SER_CTX; /** Maximum count of NIO session keys in system. */ public static final int MAX_KEYS_CNT = 64; diff --git a/modules/nio/src/main/java/org/apache/ignite/internal/util/nio/MessageSerialization.java b/modules/nio/src/main/java/org/apache/ignite/internal/util/nio/MessageSerialization.java index b436edb764d49..5f888f042ad3a 100644 --- a/modules/nio/src/main/java/org/apache/ignite/internal/util/nio/MessageSerialization.java +++ b/modules/nio/src/main/java/org/apache/ignite/internal/util/nio/MessageSerialization.java @@ -17,6 +17,7 @@ package org.apache.ignite.internal.util.nio; +import org.apache.ignite.internal.MessageSerializationContext; import org.apache.ignite.plugin.extensions.communication.Message; import org.apache.ignite.plugin.extensions.communication.MessageFactory; import org.apache.ignite.plugin.extensions.communication.MessageReader; @@ -40,11 +41,17 @@ private MessageSerialization() { * @param factory Message factory. * @param msg Message instance. * @param writer Writer. + * @param ctx Serialization context. * @param Message type. * @return Whether message was fully written. */ - public static boolean writeTo(MessageFactory factory, M msg, MessageWriter writer) { - return resolve(factory, msg).writeTo(msg, writer); + public static boolean writeTo( + MessageFactory factory, + M msg, + MessageWriter writer, + MessageSerializationContext ctx + ) { + return resolveMessageserializer(factory, msg).writeTo(msg, writer, ctx); } /** @@ -53,16 +60,31 @@ public static boolean writeTo(MessageFactory factory, M msg, * @param factory Message factory. * @param msg Message instance. * @param reader Reader. + * @param ctx Serialization context. * @param Message type. * @return Whether message was fully read. */ - public static boolean readFrom(MessageFactory factory, M msg, MessageReader reader) { - return resolve(factory, msg).readFrom(msg, reader); + public static boolean readFrom( + MessageFactory factory, + M msg, + MessageReader reader, + MessageSerializationContext ctx + ) { + return resolveMessageserializer(factory, msg).readFrom(msg, reader, ctx); + } + + /** */ + public static MessageSerializationContext resolveSerializationContext(GridNioSession ses) { + MessageSerializationContext ctx = ses.meta(GridNioSessionMetaKey.MSG_SER_CTX.ordinal()); + + assert ctx != null : "Session has no serialization context: " + ses; + + return ctx; } /** @return the serializer registered for {@code msg}'s direct type. */ @SuppressWarnings("unchecked") - private static MessageSerializer resolve(MessageFactory factory, M msg) { + private static MessageSerializer resolveMessageserializer(MessageFactory factory, M msg) { return (MessageSerializer)factory.serializer(msg.directType()); } } diff --git a/modules/nio/src/main/java/org/apache/ignite/plugin/extensions/communication/MessageReader.java b/modules/nio/src/main/java/org/apache/ignite/plugin/extensions/communication/MessageReader.java index 796acc40a191d..faf50669796dd 100644 --- a/modules/nio/src/main/java/org/apache/ignite/plugin/extensions/communication/MessageReader.java +++ b/modules/nio/src/main/java/org/apache/ignite/plugin/extensions/communication/MessageReader.java @@ -22,6 +22,7 @@ import java.util.Collection; import java.util.Map; import java.util.UUID; +import org.apache.ignite.internal.MessageSerializationContext; import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion; import org.apache.ignite.internal.processors.cache.CacheObject; import org.apache.ignite.internal.processors.cache.KeyCacheObject; @@ -196,21 +197,23 @@ public default void setBuffer(ByteBuffer buf) { /** * Reads nested message. * + * @param ctx Serialization context. * @param Type of the message. * @return Message. */ - public default T readMessage() { - return readMessage(false); + public default T readMessage(MessageSerializationContext ctx) { + return readMessage(false, ctx); } /** * Reads nested message. * * @param compress Whether message should be decompressed. + * @param ctx Serialization context. * @param Type of the message. * @return Message. */ - public T readMessage(boolean compress); + public T readMessage(boolean compress, MessageSerializationContext ctx); /** * Reads {@link CacheObject}. @@ -237,29 +240,32 @@ public default T readMessage() { * Reads array of objects. * * @param type Array component type. + * @param ctx Serialization context. * @param Type of the read object. * @return Array of objects. */ - public T[] readObjectArray(MessageArrayType type); + public T[] readObjectArray(MessageArrayType type, MessageSerializationContext ctx); /** * Reads any collection. * * @param type Collection item type. + * @param ctx Serialization context. * @param Type of the read collection. * @return Collection. */ - public > C readCollection(MessageCollectionType type); + public > C readCollection(MessageCollectionType type, MessageSerializationContext ctx); /** * Reads map. * * @param type Map type. + * @param ctx Serialization context. * @param Type of the read map. * @return Map. */ - public default > M readMap(MessageMapType type) { - return readMap(type, false); + public default > M readMap(MessageMapType type, MessageSerializationContext ctx) { + return readMap(type, false, ctx); } /** @@ -267,10 +273,11 @@ public default T readMessage() { * * @param type Map type. * @param compress Whether map should be compressed. + * @param ctx Serialization context. * @param Type of the read map. * @return Map. */ - public > M readMap(MessageMapType type, boolean compress); + public > M readMap(MessageMapType type, boolean compress, MessageSerializationContext ctx); /** @return Ignite product version. */ IgniteProductVersion readIgniteProductVersion(); diff --git a/modules/nio/src/main/java/org/apache/ignite/plugin/extensions/communication/MessageSerializer.java b/modules/nio/src/main/java/org/apache/ignite/plugin/extensions/communication/MessageSerializer.java index 86ce0d170a09f..ab66f296ed6b0 100644 --- a/modules/nio/src/main/java/org/apache/ignite/plugin/extensions/communication/MessageSerializer.java +++ b/modules/nio/src/main/java/org/apache/ignite/plugin/extensions/communication/MessageSerializer.java @@ -17,6 +17,8 @@ package org.apache.ignite.plugin.extensions.communication; +import org.apache.ignite.internal.MessageSerializationContext; + /** * Interface for message serialization logic. Resolve-and-dispatch entry points that look the serializer up from the * message factory live in {@code MessageSerialization}. @@ -27,18 +29,20 @@ public interface MessageSerializer { * * @param msg Message instance. * @param writer Writer. + * @param ctx Serialization context. * @return Whether message was fully written. */ - public boolean writeTo(M msg, MessageWriter writer); + public boolean writeTo(M msg, MessageWriter writer, MessageSerializationContext ctx); /** * Reads this message from provided byte buffer. * * @param msg Message instance. * @param reader Reader. + * @param ctx Serialization context. * @return Whether message was fully read. */ - public boolean readFrom(M msg, MessageReader reader); + public boolean readFrom(M msg, MessageReader reader, MessageSerializationContext ctx); /** * @return New instance of message. diff --git a/modules/nio/src/main/java/org/apache/ignite/plugin/extensions/communication/MessageWriter.java b/modules/nio/src/main/java/org/apache/ignite/plugin/extensions/communication/MessageWriter.java index 019bab8fa9db9..d5439362129ea 100644 --- a/modules/nio/src/main/java/org/apache/ignite/plugin/extensions/communication/MessageWriter.java +++ b/modules/nio/src/main/java/org/apache/ignite/plugin/extensions/communication/MessageWriter.java @@ -22,6 +22,7 @@ import java.util.Collection; import java.util.Map; import java.util.UUID; +import org.apache.ignite.internal.MessageSerializationContext; import org.apache.ignite.internal.processors.affinity.AffinityTopologyVersion; import org.apache.ignite.internal.processors.cache.CacheObject; import org.apache.ignite.internal.processors.cache.KeyCacheObject; @@ -246,10 +247,11 @@ public default void setBuffer(ByteBuffer buf) { * Writes nested message. * * @param val Message. + * @param ctx Serialization context. * @return Whether value was fully written. */ - public default boolean writeMessage(Message val) { - return writeMessage(val, false); + public default boolean writeMessage(Message val, MessageSerializationContext ctx) { + return writeMessage(val, false, ctx); } /** @@ -257,9 +259,10 @@ public default boolean writeMessage(Message val) { * * @param val Message. * @param compress Whether message should be compressed. + * @param ctx Serialization context. * @return Whether value was fully written. */ - public boolean writeMessage(Message val, boolean compress); + public boolean writeMessage(Message val, boolean compress, MessageSerializationContext ctx); /** * Writes {@link CacheObject}. @@ -290,32 +293,35 @@ public default boolean writeMessage(Message val) { * * @param arr Array of objects. * @param type Array component type. + * @param ctx Serialization context. * @param Type of the objects that array contains. * @return Whether array was fully written. */ - public boolean writeObjectArray(T[] arr, MessageArrayType type); + public boolean writeObjectArray(T[] arr, MessageArrayType type, MessageSerializationContext ctx); /** * Writes collection with its elements order. * * @param col Collection. * @param type Collection item type. + * @param ctx Serialization context. * @param Type of the objects that collection contains. * @return Whether value was fully written. */ - public boolean writeCollection(Collection col, MessageCollectionType type); + public boolean writeCollection(Collection col, MessageCollectionType type, MessageSerializationContext ctx); /** * Writes map. * * @param map Map. * @param type Map type. + * @param ctx Serialization context. * @param Initial key types of the map to write. * @param Initial value types of the map to write. * @return Whether value was fully written. */ - public default boolean writeMap(Map map, MessageMapType type) { - return writeMap(map, type, false); + public default boolean writeMap(Map map, MessageMapType type, MessageSerializationContext ctx) { + return writeMap(map, type, false, ctx); } /** @@ -324,11 +330,13 @@ public default boolean writeMap(Map map, MessageMapType type) { * @param map Map. * @param type Map type. * @param compress Whether map should be compressed. + * @param ctx Serialization context. * @param Initial key types of the map to write. * @param Initial value types of the map to write. * @return Whether value was fully written. */ - public boolean writeMap(Map map, MessageMapType type, boolean compress); + public boolean writeMap(Map map, MessageMapType type, boolean compress, + MessageSerializationContext ctx); /** * Writes ignite product version. diff --git a/modules/zookeeper/src/main/java/org/apache/ignite/spi/discovery/zk/internal/DiscoveryMessageParser.java b/modules/zookeeper/src/main/java/org/apache/ignite/spi/discovery/zk/internal/DiscoveryMessageParser.java index aeb7c1be8851b..451fe3d66efb9 100644 --- a/modules/zookeeper/src/main/java/org/apache/ignite/spi/discovery/zk/internal/DiscoveryMessageParser.java +++ b/modules/zookeeper/src/main/java/org/apache/ignite/spi/discovery/zk/internal/DiscoveryMessageParser.java @@ -37,6 +37,8 @@ import org.apache.ignite.plugin.extensions.communication.MessageSerializer; import org.apache.ignite.spi.IgniteSpiException; +import static org.apache.ignite.internal.MessageSerializationContext.IGNORED; + /** * Class is responsible for serializing discovery messages using RU-ready {@link MessageSerializer} mechanism. */ @@ -105,7 +107,7 @@ private void serializeMessage(Message m, OutputStream out) throws IOException { do { msgBuf.clear(); - finished = MessageSerialization.writeTo(msgFactory, m, msgWriter); + finished = MessageSerialization.writeTo(msgFactory, m, msgWriter, IGNORED); out.write(msgBuf.array(), 0, msgBuf.position()); } @@ -131,7 +133,7 @@ private T deserializeMessage(InputStream in) throws IOExcept msgBuf.rewind(); } - finished = MessageSerialization.readFrom(msgFactory, msg, msgReader); + finished = MessageSerialization.readFrom(msgFactory, msg, msgReader, IGNORED); assert read != -1 || finished : "Stream closed before message was fully read."; From 00801fcfefe004b42fc47bf6f87382d8166631b9 Mon Sep 17 00:00:00 2001 From: Mikhail Petrov Date: Fri, 28 Aug 2026 23:21:39 +0300 Subject: [PATCH 5/8] IGNITE-28851 --- .../main/java/org/apache/ignite/internal/MessageProcessor.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/codegen/src/main/java/org/apache/ignite/internal/MessageProcessor.java b/modules/codegen/src/main/java/org/apache/ignite/internal/MessageProcessor.java index 25c4b71aca9ab..7e736005c1991 100644 --- a/modules/codegen/src/main/java/org/apache/ignite/internal/MessageProcessor.java +++ b/modules/codegen/src/main/java/org/apache/ignite/internal/MessageProcessor.java @@ -284,8 +284,7 @@ private List> hierarchicalOrderedFields(TypeElement type) else if (el.getAnnotation(IntroducedBy.class) != null || el.getAnnotation(DeprecatedBy.class) != null) { processingEnv.getMessager().printMessage( Diagnostic.Kind.ERROR, - "Annotations @IntroducedBy and @DeprecatedBy guard a serialized field, so the field they are " + - "applied to must also be annotated with @Order.", + "Fields marked with @IntroducedBy or @DeprecatedBy must also be annotated with @Order", el); } } From 71f0ac54240530b315ddf8756b5a34781a771551 Mon Sep 17 00:00:00 2001 From: Mikhail Petrov Date: Sat, 29 Aug 2026 01:10:56 +0300 Subject: [PATCH 6/8] IGNITE-28851 --- .../apache/ignite/internal/DeprecatedBy.java | 46 ------------------- ...IntroducedBy.java => FeatureRegistry.java} | 22 ++++----- .../ignite/internal/MessageProcessor.java | 23 ++++------ .../internal/MessageSerializerGenerator.java | 40 +++++++--------- .../org/apache/ignite/internal/Order.java | 25 ++++++++++ .../codegen/MessageProcessorTest.java | 13 +----- .../TestPluginReleaseFeatures_2_0_0.java | 3 ++ .../TestPluginReleaseFeatures_2_1_0.java | 5 +- .../message/TestCoreMessage.java | 19 +++----- .../message/TestDefaultRegistryMessage.java | 8 +--- .../message/TestPluginMessage.java | 19 +++----- .../codegen/TestFeatureConflictMessage.java | 4 +- .../codegen/TestFeatureRegistry.java | 5 +- .../codegen/TestInvalidFeatureMessage.java | 4 +- .../codegen/TestOrderlessFeatureMessage.java | 31 ------------- .../TestRollingUpgradeAwareMessage.java | 11 ++--- ...tRollingUpgradeAwareMessageSerializer.java | 13 +++--- .../codegen/TestUnknownFeatureMessage.java | 3 +- 18 files changed, 99 insertions(+), 195 deletions(-) delete mode 100644 modules/codegen/src/main/java/org/apache/ignite/internal/DeprecatedBy.java rename modules/codegen/src/main/java/org/apache/ignite/internal/{IntroducedBy.java => FeatureRegistry.java} (62%) delete mode 100644 modules/core/src/test/resources/codegen/TestOrderlessFeatureMessage.java diff --git a/modules/codegen/src/main/java/org/apache/ignite/internal/DeprecatedBy.java b/modules/codegen/src/main/java/org/apache/ignite/internal/DeprecatedBy.java deleted file mode 100644 index df8fb672984ff..0000000000000 --- a/modules/codegen/src/main/java/org/apache/ignite/internal/DeprecatedBy.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import org.apache.ignite.internal.processors.rollingupgrade.feature.IgniteFeature; - -/** - * Marks a message field that is planned for removal in a future release. Removing the field requires introducing a new - * {@link IgniteFeature} to which this annotation must be linked. - * - *

A field annotated with this annotation is excluded from message serialization when doing so does not break - * backward compatibility during a Rolling Upgrade.

- * - * @see IgniteFeature - */ -@Retention(RetentionPolicy.SOURCE) -@Target(ElementType.FIELD) -public @interface DeprecatedBy { - /** @return Name of the Ignite feature that deprecated this field. */ - String value(); - - /** - * @return Class of the registry containing the Ignite feature with the specified name. - * By default, the Ignite Core feature registry is used. - */ - Class registry() default Void.class; -} diff --git a/modules/codegen/src/main/java/org/apache/ignite/internal/IntroducedBy.java b/modules/codegen/src/main/java/org/apache/ignite/internal/FeatureRegistry.java similarity index 62% rename from modules/codegen/src/main/java/org/apache/ignite/internal/IntroducedBy.java rename to modules/codegen/src/main/java/org/apache/ignite/internal/FeatureRegistry.java index 610a19aec4d2f..298d8615a5872 100644 --- a/modules/codegen/src/main/java/org/apache/ignite/internal/IntroducedBy.java +++ b/modules/codegen/src/main/java/org/apache/ignite/internal/FeatureRegistry.java @@ -24,23 +24,17 @@ import org.apache.ignite.internal.processors.rollingupgrade.feature.IgniteFeature; /** - * Marks a newly introduced message field. Introducing a new field requires introducing a new - * {@link IgniteFeature} to which this annotation must be linked. + * Links the annotated class to the specified {@link IgniteFeature} registry. The registry + * is used to resolve fully qualified names of features that introduced or deprecated fields + * declared by this class (see {@link Order#introducedBy()} and {@link Order#deprecatedBy()}). * - *

A field annotated with this annotation is included in message serialization only when doing so does not break - * backward compatibility during a Rolling Upgrade.

+ *

If this annotation is absent, the Ignite Core feature registry is used.

* * @see IgniteFeature */ @Retention(RetentionPolicy.SOURCE) -@Target(ElementType.FIELD) -public @interface IntroducedBy { - /** @return Name of the Ignite feature that introduced this field. */ - String value(); - - /** - * @return Class of the registry containing the Ignite feature with the specified name. - * By default, the Ignite Core feature registry is used. - */ - Class registry() default Void.class; +@Target(ElementType.TYPE) +public @interface FeatureRegistry { + /** @return Class of the associated feature registry. */ + Class value(); } diff --git a/modules/codegen/src/main/java/org/apache/ignite/internal/MessageProcessor.java b/modules/codegen/src/main/java/org/apache/ignite/internal/MessageProcessor.java index 7e736005c1991..350dc9e417266 100644 --- a/modules/codegen/src/main/java/org/apache/ignite/internal/MessageProcessor.java +++ b/modules/codegen/src/main/java/org/apache/ignite/internal/MessageProcessor.java @@ -281,12 +281,6 @@ private List> hierarchicalOrderedFields(TypeElement type) validateEnumFieldMapping(type, el); validateRollingUpgradeAnnotations(el); } - else if (el.getAnnotation(IntroducedBy.class) != null || el.getAnnotation(DeprecatedBy.class) != null) { - processingEnv.getMessager().printMessage( - Diagnostic.Kind.ERROR, - "Fields marked with @IntroducedBy or @DeprecatedBy must also be annotated with @Order", - el); - } } hierList.add(elList); @@ -296,19 +290,20 @@ else if (el.getAnnotation(IntroducedBy.class) != null || el.getAnnotation(Deprec /** */ private void validateRollingUpgradeAnnotations(Element el) { - IntroducedBy introducedAnn = el.getAnnotation(IntroducedBy.class); - DeprecatedBy deprecatedAnn = el.getAnnotation(DeprecatedBy.class); + Order ann = el.getAnnotation(Order.class); - if (introducedAnn == null && deprecatedAnn == null) + if (ann.introducedBy().isEmpty() && ann.deprecatedBy().isEmpty()) return; - String introducedFeature = introducedAnn == null + String regCls = resolveFeatureRegistry(el.getEnclosingElement()); + + String introducedFeature = ann.introducedBy().isEmpty() ? null - : resolveFeatureClassName(el, introducedAnn.value(), resolveFeatureRegistry(introducedAnn::registry)); + : resolveFeatureClassName(el, ann.introducedBy(), regCls); - String deprecatedFeature = deprecatedAnn == null + String deprecatedFeature = ann.deprecatedBy().isEmpty() ? null - : resolveFeatureClassName(el, deprecatedAnn.value(), resolveFeatureRegistry(deprecatedAnn::registry)); + : resolveFeatureClassName(el, ann.deprecatedBy(), regCls); if (introducedFeature == null || deprecatedFeature == null) return; @@ -316,7 +311,7 @@ private void validateRollingUpgradeAnnotations(Element el) { if (introducedFeature.equals(deprecatedFeature)) { processingEnv.getMessager().printMessage( Diagnostic.Kind.ERROR, - "@IntroducedBy and @DeprecatedBy must not reference the same feature when used on the same element.", + "Elements introducedBy and deprecatedBy of the @Order annotation must not reference the same feature.", el); } } diff --git a/modules/codegen/src/main/java/org/apache/ignite/internal/MessageSerializerGenerator.java b/modules/codegen/src/main/java/org/apache/ignite/internal/MessageSerializerGenerator.java index 33f642cac734e..668364394007d 100644 --- a/modules/codegen/src/main/java/org/apache/ignite/internal/MessageSerializerGenerator.java +++ b/modules/codegen/src/main/java/org/apache/ignite/internal/MessageSerializerGenerator.java @@ -30,7 +30,6 @@ import java.util.Set; import java.util.TreeSet; import java.util.UUID; -import java.util.function.Supplier; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; @@ -302,28 +301,20 @@ private String callExpr(VariableElement field, boolean write) throws Exception { /** */ @Nullable private String buildRollingUpgradeFeatureGuard(VariableElement field) { - List conditions = new ArrayList<>(); + Order ann = field.getAnnotation(Order.class); - IntroducedBy introducedAnn = field.getAnnotation(IntroducedBy.class); + if (ann.introducedBy().isEmpty() && ann.deprecatedBy().isEmpty()) + return null; - if (introducedAnn != null) { - conditions.add("ctx.includeFieldIntroducedBy(" + buildFeatureArgument( - introducedAnn.value(), - resolveFeatureRegistry(introducedAnn::registry) - ) + ")"); - } + String regCls = resolveFeatureRegistry(field.getEnclosingElement()); - DeprecatedBy deprecatedAnn = field.getAnnotation(DeprecatedBy.class); + List conditions = new ArrayList<>(); - if (deprecatedAnn != null) { - conditions.add("ctx.includeFieldDeprecatedBy(" + buildFeatureArgument( - deprecatedAnn.value(), - resolveFeatureRegistry(deprecatedAnn::registry) - ) + ")"); - } + if (!ann.introducedBy().isEmpty()) + conditions.add("ctx.includeFieldIntroducedBy(" + buildFeatureArgument(ann.introducedBy(), regCls) + ")"); - if (conditions.isEmpty()) - return null; + if (!ann.deprecatedBy().isEmpty()) + conditions.add("ctx.includeFieldDeprecatedBy(" + buildFeatureArgument(ann.deprecatedBy(), regCls) + ")"); return String.join(" && ", conditions); } @@ -336,17 +327,18 @@ private String buildFeatureArgument(String featureName, String registryCls) { } /** */ - static String resolveFeatureRegistry(Supplier> registryClsAccessor) { - String name; + static String resolveFeatureRegistry(Element declaringCls) { + FeatureRegistry ann = declaringCls.getAnnotation(FeatureRegistry.class); + + if (ann == null) + return DFLT_FEATURE_REG_CLS; try { - name = registryClsAccessor.get().getName(); + return ann.value().getName(); } catch (MirroredTypeException e) { - name = qualifiedClassName(e.getTypeMirror()); + return qualifiedClassName(e.getTypeMirror()); } - - return Void.class.getName().equals(name) ? DFLT_FEATURE_REG_CLS : name; } /** diff --git a/modules/codegen/src/main/java/org/apache/ignite/internal/Order.java b/modules/codegen/src/main/java/org/apache/ignite/internal/Order.java index 0e4562537c435..ee3aeb2ae933b 100644 --- a/modules/codegen/src/main/java/org/apache/ignite/internal/Order.java +++ b/modules/codegen/src/main/java/org/apache/ignite/internal/Order.java @@ -21,6 +21,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import org.apache.ignite.internal.processors.rollingupgrade.feature.IgniteFeature; /** * The annotation specifies the position of a field in the serialized and deserialized byte sequence of a {@code Message} class. @@ -38,4 +39,28 @@ public @interface Order { /** @return Order of the field. */ int value(); + + /** + * Marks a newly introduced message field. Introducing a new field requires introducing a new + * {@link IgniteFeature} to which this element must be linked. The feature is resolved in the registry named + * by {@link FeatureRegistry} on the declaring class. + * + *

A field with this element set is included in message serialization only when doing so does not break + * backward compatibility during a Rolling Upgrade.

+ * + * @return Name of the Ignite feature that introduced this field, or an empty string if the field is not guarded. + */ + String introducedBy() default ""; + + /** + * Marks a message field that is planned for removal in a future release. Removing the field requires introducing + * a new {@link IgniteFeature} to which this element must be linked. The feature is resolved in the registry + * named by {@link FeatureRegistry} on the declaring class. + * + *

A field with this element set is excluded from message serialization when doing so does not break + * backward compatibility during a Rolling Upgrade.

+ * + * @return Name of the Ignite feature that deprecated this field, or an empty string if the field is not guarded. + */ + String deprecatedBy() default ""; } diff --git a/modules/core/src/test/java/org/apache/ignite/internal/codegen/MessageProcessorTest.java b/modules/core/src/test/java/org/apache/ignite/internal/codegen/MessageProcessorTest.java index 29e51cf4922a2..e89ee47832b55 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/codegen/MessageProcessorTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/codegen/MessageProcessorTest.java @@ -116,20 +116,11 @@ public void testUnknownFeatureConstantRejected() { /** */ @Test - public void testGuardWithoutOrderRejected() { - Compilation compilation = compile("TestOrderlessFeatureMessage.java"); - - assertThat(compilation).failed(); - assertThat(compilation).hadErrorContaining("must also be annotated with @Order"); - } - - /** */ - @Test - public void testSameFeatureInBothAnnotationsRejected() { + public void testSameFeatureInBothGuardsRejected() { Compilation compilation = compile("TestFeatureConflictMessage.java"); assertThat(compilation).failed(); - assertThat(compilation).hadErrorContaining("must not reference the same feature when used on the same element"); + assertThat(compilation).hadErrorContaining("must not reference the same feature"); } /** */ diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/feature/TestPluginReleaseFeatures_2_0_0.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/feature/TestPluginReleaseFeatures_2_0_0.java index d661f5cff66bc..066d0403917df 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/feature/TestPluginReleaseFeatures_2_0_0.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/feature/TestPluginReleaseFeatures_2_0_0.java @@ -19,6 +19,9 @@ /** */ public class TestPluginReleaseFeatures_2_0_0 { + /** */ + public static final IgniteFeature VER_1_0_0_ID_0_FEATURE = TestPluginReleaseFeatures_1_0_0.VER_1_0_0_ID_0_FEATURE; + /** */ public static final IgniteFeature VER_2_0_0_ID_1_FEATURE = new TestPluginFeature(1); } diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/feature/TestPluginReleaseFeatures_2_1_0.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/feature/TestPluginReleaseFeatures_2_1_0.java index edb4b3b3646ac..385f7ded6a900 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/feature/TestPluginReleaseFeatures_2_1_0.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/feature/TestPluginReleaseFeatures_2_1_0.java @@ -20,7 +20,10 @@ /** */ public class TestPluginReleaseFeatures_2_1_0 { /** */ - public static final IgniteFeature VER_2_1_0_ID_1_FEATURE = new TestPluginFeature(1); + public static final IgniteFeature VER_1_0_0_ID_0_FEATURE = TestPluginReleaseFeatures_2_0_0.VER_1_0_0_ID_0_FEATURE; + + /** */ + public static final IgniteFeature VER_2_0_0_ID_1_FEATURE = TestPluginReleaseFeatures_2_0_0.VER_2_0_0_ID_1_FEATURE; /** */ public static final IgniteFeature VER_2_1_0_ID_2_FEATURE = new TestPluginFeature(2); diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/message/TestCoreMessage.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/message/TestCoreMessage.java index b22c083e22faa..843c928ad1ca0 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/message/TestCoreMessage.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/message/TestCoreMessage.java @@ -17,25 +17,22 @@ package org.apache.ignite.internal.processors.rollingupgrade.message; -import org.apache.ignite.internal.DeprecatedBy; -import org.apache.ignite.internal.IntroducedBy; +import org.apache.ignite.internal.FeatureRegistry; import org.apache.ignite.internal.Order; import org.apache.ignite.internal.managers.discovery.DiscoveryCustomMessage; -import org.apache.ignite.internal.processors.rollingupgrade.feature.TestIgniteReleaseFeatures_2_19_2; -import org.apache.ignite.internal.processors.rollingupgrade.feature.TestIgniteReleaseFeatures_2_20_0; import org.apache.ignite.internal.processors.rollingupgrade.feature.TestIgniteReleaseFeatures_2_20_1; import org.apache.ignite.lang.IgniteUuid; import org.jetbrains.annotations.Nullable; /** */ +@FeatureRegistry(TestIgniteReleaseFeatures_2_20_1.class) public class TestCoreMessage extends DiscoveryCustomMessage implements TestMessage { /** */ @Order(0) String fldA; /** */ - @Order(1) - @DeprecatedBy(value = "VER_2_20_0_ID_3_FEATURE", registry = TestIgniteReleaseFeatures_2_20_0.class) + @Order(value = 1, deprecatedBy = "VER_2_20_0_ID_3_FEATURE") String fldB; /** */ @@ -43,19 +40,15 @@ public class TestCoreMessage extends DiscoveryCustomMessage implements TestMessa String fldC; /** */ - @Order(3) - @IntroducedBy(value = "VER_2_19_2_ID_1_FEATURE", registry = TestIgniteReleaseFeatures_2_19_2.class) - @DeprecatedBy(value = "VER_2_20_0_ID_3_FEATURE", registry = TestIgniteReleaseFeatures_2_20_0.class) + @Order(value = 3, introducedBy = "VER_2_19_2_ID_1_FEATURE", deprecatedBy = "VER_2_20_0_ID_3_FEATURE") String fldD; /** */ - @Order(4) - @IntroducedBy(value = "VER_2_20_0_ID_3_FEATURE", registry = TestIgniteReleaseFeatures_2_20_0.class) + @Order(value = 4, introducedBy = "VER_2_20_0_ID_3_FEATURE") String fldE; /** */ - @Order(5) - @IntroducedBy(value = "VER_2_20_1_ID_6_FEATURE", registry = TestIgniteReleaseFeatures_2_20_1.class) + @Order(value = 5, introducedBy = "VER_2_20_1_ID_6_FEATURE") String fldF; /** */ diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/message/TestDefaultRegistryMessage.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/message/TestDefaultRegistryMessage.java index 5dd169c633526..718cf35a19b86 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/message/TestDefaultRegistryMessage.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/message/TestDefaultRegistryMessage.java @@ -17,8 +17,6 @@ package org.apache.ignite.internal.processors.rollingupgrade.message; -import org.apache.ignite.internal.DeprecatedBy; -import org.apache.ignite.internal.IntroducedBy; import org.apache.ignite.internal.Order; import org.apache.ignite.plugin.extensions.communication.Message; @@ -29,8 +27,7 @@ public class TestDefaultRegistryMessage implements Message, TestMessage { String fldA; /** */ - @Order(1) - @DeprecatedBy("ROLLING_UPGRADE_FEATURE") + @Order(value = 1, deprecatedBy = "ROLLING_UPGRADE_FEATURE") String fldB; /** */ @@ -38,8 +35,7 @@ public class TestDefaultRegistryMessage implements Message, TestMessage { String fldC; /** */ - @Order(3) - @IntroducedBy("ROLLING_UPGRADE_FEATURE") + @Order(value = 3, introducedBy = "ROLLING_UPGRADE_FEATURE") String fldD; /** */ diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/message/TestPluginMessage.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/message/TestPluginMessage.java index 78a56a8e31d9c..e26968266f147 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/message/TestPluginMessage.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/rollingupgrade/message/TestPluginMessage.java @@ -17,11 +17,8 @@ package org.apache.ignite.internal.processors.rollingupgrade.message; -import org.apache.ignite.internal.DeprecatedBy; -import org.apache.ignite.internal.IntroducedBy; +import org.apache.ignite.internal.FeatureRegistry; import org.apache.ignite.internal.Order; -import org.apache.ignite.internal.processors.rollingupgrade.feature.TestPluginReleaseFeatures_1_0_0; -import org.apache.ignite.internal.processors.rollingupgrade.feature.TestPluginReleaseFeatures_2_0_0; import org.apache.ignite.internal.processors.rollingupgrade.feature.TestPluginReleaseFeatures_2_1_0; import org.apache.ignite.plugin.extensions.communication.Message; @@ -33,14 +30,14 @@ * so this message and the core-only {@link TestCoreMessage} are kept apart rather than * folded into one that every test would have to declare the plugin for. */ +@FeatureRegistry(TestPluginReleaseFeatures_2_1_0.class) public class TestPluginMessage implements Message, TestMessage { /** */ @Order(0) String fldA; /** */ - @Order(1) - @DeprecatedBy(value = "VER_2_0_0_ID_1_FEATURE", registry = TestPluginReleaseFeatures_2_0_0.class) + @Order(value = 1, deprecatedBy = "VER_2_0_0_ID_1_FEATURE") String fldB; /** */ @@ -48,19 +45,15 @@ public class TestPluginMessage implements Message, TestMessage { String fldC; /** */ - @Order(3) - @IntroducedBy(value = "VER_1_0_0_ID_0_FEATURE", registry = TestPluginReleaseFeatures_1_0_0.class) - @DeprecatedBy(value = "VER_2_0_0_ID_1_FEATURE", registry = TestPluginReleaseFeatures_2_0_0.class) + @Order(value = 3, introducedBy = "VER_1_0_0_ID_0_FEATURE", deprecatedBy = "VER_2_0_0_ID_1_FEATURE") String fldD; /** */ - @Order(4) - @IntroducedBy(value = "VER_2_0_0_ID_1_FEATURE", registry = TestPluginReleaseFeatures_2_0_0.class) + @Order(value = 4, introducedBy = "VER_2_0_0_ID_1_FEATURE") String fldE; /** */ - @Order(5) - @IntroducedBy(value = "VER_2_1_0_ID_2_FEATURE", registry = TestPluginReleaseFeatures_2_1_0.class) + @Order(value = 5, introducedBy = "VER_2_1_0_ID_2_FEATURE") String fldF; /** */ diff --git a/modules/core/src/test/resources/codegen/TestFeatureConflictMessage.java b/modules/core/src/test/resources/codegen/TestFeatureConflictMessage.java index 281c6f6f8c0f6..550e305805d07 100644 --- a/modules/core/src/test/resources/codegen/TestFeatureConflictMessage.java +++ b/modules/core/src/test/resources/codegen/TestFeatureConflictMessage.java @@ -22,8 +22,6 @@ /** */ public class TestFeatureConflictMessage implements Message { /** */ - @Order(0) - @IntroducedBy("ROLLING_UPGRADE_FEATURE") - @DeprecatedBy("ROLLING_UPGRADE_FEATURE") + @Order(value = 0, introducedBy = "ROLLING_UPGRADE_FEATURE", deprecatedBy = "ROLLING_UPGRADE_FEATURE") int fld; } diff --git a/modules/core/src/test/resources/codegen/TestFeatureRegistry.java b/modules/core/src/test/resources/codegen/TestFeatureRegistry.java index 57b68a4fd4e26..6d72d5a721577 100644 --- a/modules/core/src/test/resources/codegen/TestFeatureRegistry.java +++ b/modules/core/src/test/resources/codegen/TestFeatureRegistry.java @@ -20,8 +20,11 @@ import org.apache.ignite.internal.processors.rollingupgrade.feature.IgniteCoreFeature; import org.apache.ignite.internal.processors.rollingupgrade.feature.IgniteFeature; -/** Second feature holder, used only to exercise a non-default {@code className()}. */ +/** Feature holder, used only to exercise a non-default {@link FeatureRegistry}. */ public class TestFeatureRegistry { + /** */ + public static final IgniteFeature FIRST_FEATURE = new IgniteCoreFeature(0); + /** */ public static final IgniteFeature SECOND_FEATURE = new IgniteCoreFeature(1); } diff --git a/modules/core/src/test/resources/codegen/TestInvalidFeatureMessage.java b/modules/core/src/test/resources/codegen/TestInvalidFeatureMessage.java index 66d1e1f30bbf9..ef170d52546a9 100644 --- a/modules/core/src/test/resources/codegen/TestInvalidFeatureMessage.java +++ b/modules/core/src/test/resources/codegen/TestInvalidFeatureMessage.java @@ -20,9 +20,9 @@ import org.apache.ignite.plugin.extensions.communication.Message; /** */ +@FeatureRegistry(TestInvalidFeatureRegistry.class) public class TestInvalidFeatureMessage implements Message { /** */ - @Order(0) - @IntroducedBy(value = "NOT_A_FEATURE", registry = TestInvalidFeatureRegistry.class) + @Order(value = 0, introducedBy = "NOT_A_FEATURE") int fld; } diff --git a/modules/core/src/test/resources/codegen/TestOrderlessFeatureMessage.java b/modules/core/src/test/resources/codegen/TestOrderlessFeatureMessage.java deleted file mode 100644 index ac033065edeea..0000000000000 --- a/modules/core/src/test/resources/codegen/TestOrderlessFeatureMessage.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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; - -import org.apache.ignite.plugin.extensions.communication.Message; - -/** */ -public class TestOrderlessFeatureMessage implements Message { - /** */ - @Order(0) - int fld; - - /** Guarded, but never serialized: no {@code @Order}. */ - @IntroducedBy("ROLLING_UPGRADE_FEATURE") - int guardedButUnordered; -} diff --git a/modules/core/src/test/resources/codegen/TestRollingUpgradeAwareMessage.java b/modules/core/src/test/resources/codegen/TestRollingUpgradeAwareMessage.java index 5da84995d659f..68ab55f826a0d 100644 --- a/modules/core/src/test/resources/codegen/TestRollingUpgradeAwareMessage.java +++ b/modules/core/src/test/resources/codegen/TestRollingUpgradeAwareMessage.java @@ -20,25 +20,22 @@ import org.apache.ignite.plugin.extensions.communication.Message; /** */ +@FeatureRegistry(TestFeatureRegistry.class) public class TestRollingUpgradeAwareMessage implements Message { /** Unconditional field. */ @Order(0) int plain; /** Written only while the feature is not agreed. */ - @Order(1) - @DeprecatedBy("ROLLING_UPGRADE_FEATURE") + @Order(value = 1, deprecatedBy = "FIRST_FEATURE") String oldFld; /** Written only once the feature is agreed. */ - @Order(2) - @IntroducedBy("ROLLING_UPGRADE_FEATURE") + @Order(value = 2, introducedBy = "FIRST_FEATURE") String newFld; /** Lived for one release window only. */ - @Order(3) - @IntroducedBy("ROLLING_UPGRADE_FEATURE") - @DeprecatedBy(value = "SECOND_FEATURE", registry = TestFeatureRegistry.class) + @Order(value = 3, introducedBy = "FIRST_FEATURE", deprecatedBy = "SECOND_FEATURE") long windowed; /** {@inheritDoc} */ diff --git a/modules/core/src/test/resources/codegen/TestRollingUpgradeAwareMessageSerializer.java b/modules/core/src/test/resources/codegen/TestRollingUpgradeAwareMessageSerializer.java index c106be57f68f3..f14fe2a71e341 100644 --- a/modules/core/src/test/resources/codegen/TestRollingUpgradeAwareMessageSerializer.java +++ b/modules/core/src/test/resources/codegen/TestRollingUpgradeAwareMessageSerializer.java @@ -19,7 +19,6 @@ import org.apache.ignite.internal.MessageSerializationContext; import org.apache.ignite.internal.TestFeatureRegistry; import org.apache.ignite.internal.TestRollingUpgradeAwareMessage; -import org.apache.ignite.internal.processors.rollingupgrade.feature.SupportedFeatureRegistry; import org.apache.ignite.plugin.extensions.communication.MessageReader; import org.apache.ignite.plugin.extensions.communication.MessageSerializer; import org.apache.ignite.plugin.extensions.communication.MessageWriter; @@ -47,7 +46,7 @@ public final class TestRollingUpgradeAwareMessageSerializer implements MessageSe writer.incrementState(); case 1: - if (ctx.includeFieldDeprecatedBy(SupportedFeatureRegistry.ROLLING_UPGRADE_FEATURE)) { + if (ctx.includeFieldDeprecatedBy(TestFeatureRegistry.FIRST_FEATURE)) { if (!writer.writeString(msg.oldFld)) return false; } @@ -55,7 +54,7 @@ public final class TestRollingUpgradeAwareMessageSerializer implements MessageSe writer.incrementState(); case 2: - if (ctx.includeFieldIntroducedBy(SupportedFeatureRegistry.ROLLING_UPGRADE_FEATURE)) { + if (ctx.includeFieldIntroducedBy(TestFeatureRegistry.FIRST_FEATURE)) { if (!writer.writeString(msg.newFld)) return false; } @@ -63,7 +62,7 @@ public final class TestRollingUpgradeAwareMessageSerializer implements MessageSe writer.incrementState(); case 3: - if (ctx.includeFieldIntroducedBy(SupportedFeatureRegistry.ROLLING_UPGRADE_FEATURE) && ctx.includeFieldDeprecatedBy(TestFeatureRegistry.SECOND_FEATURE)) { + if (ctx.includeFieldIntroducedBy(TestFeatureRegistry.FIRST_FEATURE) && ctx.includeFieldDeprecatedBy(TestFeatureRegistry.SECOND_FEATURE)) { if (!writer.writeLong(msg.windowed)) return false; } @@ -87,7 +86,7 @@ public final class TestRollingUpgradeAwareMessageSerializer implements MessageSe reader.incrementState(); case 1: - if (ctx.includeFieldDeprecatedBy(SupportedFeatureRegistry.ROLLING_UPGRADE_FEATURE)) { + if (ctx.includeFieldDeprecatedBy(TestFeatureRegistry.FIRST_FEATURE)) { msg.oldFld = reader.readString(); if (!reader.isLastRead()) @@ -97,7 +96,7 @@ public final class TestRollingUpgradeAwareMessageSerializer implements MessageSe reader.incrementState(); case 2: - if (ctx.includeFieldIntroducedBy(SupportedFeatureRegistry.ROLLING_UPGRADE_FEATURE)) { + if (ctx.includeFieldIntroducedBy(TestFeatureRegistry.FIRST_FEATURE)) { msg.newFld = reader.readString(); if (!reader.isLastRead()) @@ -107,7 +106,7 @@ public final class TestRollingUpgradeAwareMessageSerializer implements MessageSe reader.incrementState(); case 3: - if (ctx.includeFieldIntroducedBy(SupportedFeatureRegistry.ROLLING_UPGRADE_FEATURE) && ctx.includeFieldDeprecatedBy(TestFeatureRegistry.SECOND_FEATURE)) { + if (ctx.includeFieldIntroducedBy(TestFeatureRegistry.FIRST_FEATURE) && ctx.includeFieldDeprecatedBy(TestFeatureRegistry.SECOND_FEATURE)) { msg.windowed = reader.readLong(); if (!reader.isLastRead()) diff --git a/modules/core/src/test/resources/codegen/TestUnknownFeatureMessage.java b/modules/core/src/test/resources/codegen/TestUnknownFeatureMessage.java index 03fe51ce8d790..7e1db62bb29e7 100644 --- a/modules/core/src/test/resources/codegen/TestUnknownFeatureMessage.java +++ b/modules/core/src/test/resources/codegen/TestUnknownFeatureMessage.java @@ -22,7 +22,6 @@ /** */ public class TestUnknownFeatureMessage implements Message { /** */ - @Order(0) - @IntroducedBy("NO_SUCH_FEATURE") + @Order(value = 0, introducedBy = "NO_SUCH_FEATURE") int fld; } From f1f55e7e4574683a8bd489d3ad32d45a4884739f Mon Sep 17 00:00:00 2001 From: Mikhail Petrov Date: Sat, 29 Aug 2026 02:06:32 +0300 Subject: [PATCH 7/8] IGNITE-28851 --- .../apache/ignite/internal/FeatureRegistry.java | 3 ++- .../apache/ignite/internal/MessageProcessor.java | 8 ++++---- .../java/org/apache/ignite/internal/Order.java | 14 +++++++------- .../internal/MessageSerializationContext.java | 2 +- .../internal/codegen/MessageProcessorTest.java | 2 +- 5 files changed, 15 insertions(+), 14 deletions(-) diff --git a/modules/codegen/src/main/java/org/apache/ignite/internal/FeatureRegistry.java b/modules/codegen/src/main/java/org/apache/ignite/internal/FeatureRegistry.java index 298d8615a5872..a2826c968e3f3 100644 --- a/modules/codegen/src/main/java/org/apache/ignite/internal/FeatureRegistry.java +++ b/modules/codegen/src/main/java/org/apache/ignite/internal/FeatureRegistry.java @@ -26,10 +26,11 @@ /** * Links the annotated class to the specified {@link IgniteFeature} registry. The registry * is used to resolve fully qualified names of features that introduced or deprecated fields - * declared by this class (see {@link Order#introducedBy()} and {@link Order#deprecatedBy()}). + * declared by the annotated class (see {@link Order#introducedBy()} and {@link Order#deprecatedBy()}). * *

If this annotation is absent, the Ignite Core feature registry is used.

* + * @see Order * @see IgniteFeature */ @Retention(RetentionPolicy.SOURCE) diff --git a/modules/codegen/src/main/java/org/apache/ignite/internal/MessageProcessor.java b/modules/codegen/src/main/java/org/apache/ignite/internal/MessageProcessor.java index 350dc9e417266..ecd2b464757ba 100644 --- a/modules/codegen/src/main/java/org/apache/ignite/internal/MessageProcessor.java +++ b/modules/codegen/src/main/java/org/apache/ignite/internal/MessageProcessor.java @@ -299,11 +299,11 @@ private void validateRollingUpgradeAnnotations(Element el) { String introducedFeature = ann.introducedBy().isEmpty() ? null - : resolveFeatureClassName(el, ann.introducedBy(), regCls); + : resolveFeatureFullName(el, ann.introducedBy(), regCls); String deprecatedFeature = ann.deprecatedBy().isEmpty() ? null - : resolveFeatureClassName(el, ann.deprecatedBy(), regCls); + : resolveFeatureFullName(el, ann.deprecatedBy(), regCls); if (introducedFeature == null || deprecatedFeature == null) return; @@ -317,7 +317,7 @@ private void validateRollingUpgradeAnnotations(Element el) { } /** */ - @Nullable private String resolveFeatureClassName(Element el, String featureName, String regCls) { + @Nullable private String resolveFeatureFullName(Element el, String featureName, String regCls) { TypeElement registryType = processingEnv.getElementUtils().getTypeElement(regCls); if (registryType == null) { @@ -355,7 +355,7 @@ private void validateRollingUpgradeAnnotations(Element el) { } processingEnv.getMessager().printMessage( - Diagnostic.Kind.ERROR, "No such feature constant in the registry [feature=" + featureName + + Diagnostic.Kind.ERROR, "Failed to resolve feature in the registry by its name [featureName=" + featureName + ", registry=" + regCls + ']', el); return null; diff --git a/modules/codegen/src/main/java/org/apache/ignite/internal/Order.java b/modules/codegen/src/main/java/org/apache/ignite/internal/Order.java index ee3aeb2ae933b..af6bd589e87d0 100644 --- a/modules/codegen/src/main/java/org/apache/ignite/internal/Order.java +++ b/modules/codegen/src/main/java/org/apache/ignite/internal/Order.java @@ -29,10 +29,12 @@ * The {@code value} indicates the index of the field in the serialization order. * Fields annotated with {@code @Order} are processed in ascending order of their index. *

By default, it is assumed that getters and setters are named as the annotated fields, - * e.g. field 'val' should have getters and satters with name 'val' (according Ignite's to code-style). + * e.g. field 'val' should have getters and setters with name 'val' (according Ignite's to code-style). *

This annotation must be used on non-static fields, and access to those fields * should be performed strictly through corresponding getter and setter methods * following the naming convention: {@code fieldName()} for getter and {@code fieldName(Type)} for setter. + * + * @see FeatureRegistry */ @Retention(RetentionPolicy.CLASS) @Target(ElementType.FIELD) @@ -42,10 +44,9 @@ /** * Marks a newly introduced message field. Introducing a new field requires introducing a new - * {@link IgniteFeature} to which this element must be linked. The feature is resolved in the registry named - * by {@link FeatureRegistry} on the declaring class. + * {@link IgniteFeature} to which this element must be linked. * - *

A field with this element set is included in message serialization only when doing so does not break + *

An annotated field is included in message serialization only when doing so does not break * backward compatibility during a Rolling Upgrade.

* * @return Name of the Ignite feature that introduced this field, or an empty string if the field is not guarded. @@ -54,10 +55,9 @@ /** * Marks a message field that is planned for removal in a future release. Removing the field requires introducing - * a new {@link IgniteFeature} to which this element must be linked. The feature is resolved in the registry - * named by {@link FeatureRegistry} on the declaring class. + * a new {@link IgniteFeature} to which this element must be linked. * - *

A field with this element set is excluded from message serialization when doing so does not break + *

An annotated field is excluded from message serialization when doing so does not break * backward compatibility during a Rolling Upgrade.

* * @return Name of the Ignite feature that deprecated this field, or an empty string if the field is not guarded. diff --git a/modules/commons/src/main/java/org/apache/ignite/internal/MessageSerializationContext.java b/modules/commons/src/main/java/org/apache/ignite/internal/MessageSerializationContext.java index 5fe1f2373d495..3d53ffca4d302 100644 --- a/modules/commons/src/main/java/org/apache/ignite/internal/MessageSerializationContext.java +++ b/modules/commons/src/main/java/org/apache/ignite/internal/MessageSerializationContext.java @@ -21,7 +21,7 @@ import org.apache.ignite.lang.IgniteFuture; /** - * Represents a provider of data required to determine how data transmitted between nodes is serialized and deserialized. + * Represents context that determines how data transmitted between nodes is serialized and deserialized. */ public interface MessageSerializationContext { /** diff --git a/modules/core/src/test/java/org/apache/ignite/internal/codegen/MessageProcessorTest.java b/modules/core/src/test/java/org/apache/ignite/internal/codegen/MessageProcessorTest.java index e89ee47832b55..37f25a885acc8 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/codegen/MessageProcessorTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/codegen/MessageProcessorTest.java @@ -111,7 +111,7 @@ public void testUnknownFeatureConstantRejected() { Compilation compilation = compile("TestUnknownFeatureMessage.java"); assertThat(compilation).failed(); - assertThat(compilation).hadErrorContaining("No such feature constant in the registry [feature=NO_SUCH_FEATURE"); + assertThat(compilation).hadErrorContaining("Failed to resolve feature in the registry by its name [featureName=NO_SUCH_FEATURE"); } /** */ From 8838696dddfbeec415894267ec83842f3160bb04 Mon Sep 17 00:00:00 2001 From: Mikhail Petrov Date: Wed, 2 Sep 2026 11:10:02 +0300 Subject: [PATCH 8/8] IGNITE-28851 --- .../internal/MessageSerializerGenerator.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/modules/codegen/src/main/java/org/apache/ignite/internal/MessageSerializerGenerator.java b/modules/codegen/src/main/java/org/apache/ignite/internal/MessageSerializerGenerator.java index 668364394007d..1caab7e83555e 100644 --- a/modules/codegen/src/main/java/org/apache/ignite/internal/MessageSerializerGenerator.java +++ b/modules/codegen/src/main/java/org/apache/ignite/internal/MessageSerializerGenerator.java @@ -300,7 +300,7 @@ private String callExpr(VariableElement field, boolean write) throws Exception { } /** */ - @Nullable private String buildRollingUpgradeFeatureGuard(VariableElement field) { + @Nullable private String buildFeatureCondition(VariableElement field) { Order ann = field.getAnnotation(Order.class); if (ann.introducedBy().isEmpty() && ann.deprecatedBy().isEmpty()) @@ -359,17 +359,17 @@ private void writeField(VariableElement field, int opt, String writeExpr) { indent++; - String guard = buildRollingUpgradeFeatureGuard(field); + String featureCondition = buildFeatureCondition(field); - if (guard != null) { - write.add(indentedLine("if (%s) {", guard)); + if (featureCondition != null) { + write.add(indentedLine("if (%s) {", featureCondition)); indent++; } returnFalseIf(write, "!" + writeExpr); - if (guard != null) { + if (featureCondition != null) { indent--; write.add(indentedLine("}")); @@ -402,10 +402,10 @@ private void readField(VariableElement field, int opt, String readExpr) { indent++; - String guard = buildRollingUpgradeFeatureGuard(field); + String featureCondition = buildFeatureCondition(field); - if (guard != null) { - read.add(indentedLine("if (%s) {", guard)); + if (featureCondition != null) { + read.add(indentedLine("if (%s) {", featureCondition)); indent++; } @@ -415,7 +415,7 @@ private void readField(VariableElement field, int opt, String readExpr) { returnFalseIf(read, "!reader.isLastRead()"); - if (guard != null) { + if (featureCondition != null) { indent--; read.add(indentedLine("}"));