100100import org .apache .ignite .internal .util .typedef .C1 ;
101101import org .apache .ignite .internal .util .typedef .F ;
102102import org .apache .ignite .internal .util .typedef .P1 ;
103+ import org .apache .ignite .internal .util .typedef .T2 ;
103104import org .apache .ignite .internal .util .typedef .X ;
104105import org .apache .ignite .internal .util .typedef .internal .LT ;
105106import org .apache .ignite .internal .util .typedef .internal .S ;
@@ -2834,6 +2835,16 @@ private class RingMessageWorker extends MessageWorker<TcpDiscoveryAbstractMessag
28342835 /** Socket. */
28352836 private Socket sock ;
28362837
2838+ // This serializer is used exclusively for serializing messages sent to clients,
2839+ // as it represents a special case within the RingMessageWorker workflow.
2840+ // Generally, both serialization and deserialization of messages should be handled by TcpDiscoveryIoSession.
2841+ // However, there are scenarios where the session is not available, yet messages still need to be sent to clients.
2842+ // A typical example is a single server with one or more connected clients.
2843+ // To address this, we use TcpDiscoveryMessageSerializer, which includes some code copied from TcpDiscoveryIoSession
2844+ // and can be instantiated independently of any active session.
2845+ /** */
2846+ private final TcpDiscoveryMessageSerializer clientMsgSer = new TcpDiscoveryMessageSerializer (spi );
2847+
28372848 /** IO session. */
28382849 private TcpDiscoveryIoSession ses ;
28392850
@@ -3241,19 +3252,36 @@ private void sendMessageToClients(TcpDiscoveryAbstractMessage msg) {
32413252 if (spi .ensured (msg ))
32423253 msgHist .add (msg );
32433254
3255+ if (clientMsgWorkers .isEmpty ())
3256+ return ;
3257+
3258+ byte [] msgBytes = null ;
3259+
3260+ if (!(msg instanceof TcpDiscoveryNodeAddedMessage )) {
3261+ try {
3262+ msgBytes = clientMsgSer .serializeMessage (msg );
3263+ }
3264+ catch (IgniteCheckedException | IOException e ) {
3265+ U .error (log , "Failed to serialize message: " + msg , e );
3266+
3267+ return ;
3268+ }
3269+ }
3270+
32443271 for (ClientMessageWorker clientMsgWorker : clientMsgWorkers .values ()) {
3272+ TcpDiscoveryAbstractMessage msg0 = msg ;
3273+
32453274 if (msg instanceof TcpDiscoveryNodeAddedMessage ) {
32463275 TcpDiscoveryNodeAddedMessage nodeAddedMsg = (TcpDiscoveryNodeAddedMessage )msg ;
32473276
32483277 if (clientMsgWorker .clientNodeId .equals (nodeAddedMsg .node ().id ())) {
3249- msg = new TcpDiscoveryNodeAddedMessage (nodeAddedMsg );
3278+ msg0 = new TcpDiscoveryNodeAddedMessage (nodeAddedMsg );
32503279
3251- prepareNodeAddedMessage (msg , clientMsgWorker .clientNodeId , null );
3280+ prepareNodeAddedMessage (msg0 , clientMsgWorker .clientNodeId , null );
32523281 }
32533282 }
32543283
3255- // TODO Investigate possible optimizations: https://issues.apache.org/jira/browse/IGNITE-27722
3256- clientMsgWorker .addMessage (msg );
3284+ clientMsgWorker .addMessage (msg0 , msgBytes );
32573285 }
32583286 }
32593287 }
@@ -7584,12 +7612,20 @@ private class StatisticsPrinter extends IgniteSpiThread {
75847612 }
75857613
75867614 /** */
7587- private class ClientMessageWorker extends MessageWorker <TcpDiscoveryAbstractMessage > {
7615+ private class ClientMessageWorker extends MessageWorker <T2 < TcpDiscoveryAbstractMessage , byte []> > {
75887616 /** Node ID. */
75897617 private final UUID clientNodeId ;
75907618
7619+ // The code responsible for sending and receiving messages to and from client nodes represents a special case in ServerImpl,
7620+ // as it is split into two separate components.
7621+ // One part, ClientMessageWorker, handles only message sending to clients and does not process responses.
7622+ // The other part, which reads messages from clients, is implemented in SocketReader.
7623+ // Due to this separation, we don't require a full TcpDiscoveryIoSession here
7624+ // and can instead extract just the message-writing functionality.
7625+ // At the same time, we aim to keep both reading and writing logic encapsulated within TcpDiscoveryIoSession.
7626+ // As a result, we need to copy some code from TcpDiscoveryIoSession into the new class, TcpDiscoveryMessageSerializer.
75917627 /** */
7592- private final TcpDiscoveryIoSession ses ;
7628+ private final TcpDiscoveryMessageSerializer clientMsgSer ;
75937629
75947630 /** Socket. */
75957631 private final Socket sock ;
@@ -7619,7 +7655,7 @@ private ClientMessageWorker(Socket sock, UUID clientNodeId, IgniteLogger log) {
76197655 this .sock = sock ;
76207656 this .clientNodeId = clientNodeId ;
76217657
7622- ses = createSession ( sock );
7658+ clientMsgSer = new TcpDiscoveryMessageSerializer ( spi );
76237659
76247660 lastMetricsUpdateMsgTimeNanos = System .nanoTime ();
76257661 }
@@ -7651,10 +7687,20 @@ void metrics(ClusterMetrics metrics) {
76517687 * @param msg Message.
76527688 */
76537689 void addMessage (TcpDiscoveryAbstractMessage msg ) {
7690+ addMessage (msg , null );
7691+ }
7692+
7693+ /**
7694+ * @param msg Message.
7695+ * @param msgBytes Optional message bytes.
7696+ */
7697+ void addMessage (TcpDiscoveryAbstractMessage msg , @ Nullable byte [] msgBytes ) {
7698+ T2 <TcpDiscoveryAbstractMessage , byte []> t = new T2 <>(msg , msgBytes );
7699+
76547700 if (msg .highPriority ())
7655- queue .addFirst (msg );
7701+ queue .addFirst (t );
76567702 else
7657- queue .add (msg );
7703+ queue .add (t );
76587704
76597705 DebugLogger log = messageLogger (msg );
76607706
@@ -7663,14 +7709,14 @@ void addMessage(TcpDiscoveryAbstractMessage msg) {
76637709 }
76647710
76657711 /** {@inheritDoc} */
7666- @ Override protected void processMessage (TcpDiscoveryAbstractMessage msg ) {
7712+ @ Override protected void processMessage (T2 < TcpDiscoveryAbstractMessage , byte []> msgT ) {
76677713 boolean success = false ;
76687714
7715+ TcpDiscoveryAbstractMessage msg = msgT .get1 ();
7716+
76697717 try {
76707718 assert msg .verified () : msg ;
76717719
7672- byte [] msgBytes = ses .serializeMessage (msg );
7673-
76747720 DebugLogger msgLog = messageLogger (msg );
76757721
76767722 if (msg instanceof TcpDiscoveryClientAckResponse ) {
@@ -7692,8 +7738,8 @@ else if (msgLog.isDebugEnabled()) {
76927738 + getLocalNodeId () + ", rmtNodeId=" + clientNodeId + ", msg=" + msg + ']' );
76937739 }
76947740
7695- spi . writeToSocket (sock , msg , msgBytes , spi .failureDetectionTimeoutEnabled () ?
7696- spi .clientFailureDetectionTimeout () : spi . getSocketTimeout ());
7741+ writeToSocket (msgT , spi .failureDetectionTimeoutEnabled () ? spi . clientFailureDetectionTimeout () :
7742+ spi .getSocketTimeout ());
76977743 }
76987744 }
76997745 else {
@@ -7704,7 +7750,7 @@ else if (msgLog.isDebugEnabled()) {
77047750
77057751 assert topologyInitialized (msg ) : msg ;
77067752
7707- spi . writeToSocket (sock , msg , msgBytes , spi .getEffectiveSocketTimeout (false ));
7753+ writeToSocket (msgT , spi .getEffectiveSocketTimeout (false ));
77087754 }
77097755
77107756 boolean clientFailed = msg instanceof TcpDiscoveryNodeFailedMessage &&
@@ -7733,6 +7779,17 @@ else if (msgLog.isDebugEnabled()) {
77337779 }
77347780 }
77357781
7782+ /**
7783+ * @param msgT Message tuple.
7784+ * @param timeout Timeout.
7785+ */
7786+ private void writeToSocket (T2 <TcpDiscoveryAbstractMessage , byte []> msgT , long timeout )
7787+ throws IgniteCheckedException , IOException {
7788+ byte [] msgBytes = msgT .get2 () == null ? clientMsgSer .serializeMessage (msgT .get1 ()) : msgT .get2 ();
7789+
7790+ spi .writeToSocket (sock , msgT .get1 (), msgBytes , timeout );
7791+ }
7792+
77367793 /**
77377794 * @param msg Message.
77387795 * @return {@code True} if topology initialized.
0 commit comments