IGNITE-7287 Thin client: add configurable idle connection timeout
authorPavel Tupitsyn <ptupitsyn@apache.org>
Wed, 27 Dec 2017 08:17:29 +0000 (11:17 +0300)
committerPavel Tupitsyn <ptupitsyn@apache.org>
Wed, 27 Dec 2017 08:17:29 +0000 (11:17 +0300)
This closes #3287

modules/core/src/main/java/org/apache/ignite/configuration/ClientConnectorConfiguration.java
modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/ClientListenerNioListener.java
modules/core/src/main/java/org/apache/ignite/internal/processors/odbc/ClientListenerProcessor.java
modules/core/src/main/java/org/apache/ignite/internal/processors/platform/utils/PlatformConfigurationUtils.java
modules/platforms/dotnet/Apache.Ignite.Core.Tests/Client/ClientConnectionTest.cs
modules/platforms/dotnet/Apache.Ignite.Core.Tests/Config/full-config.xml
modules/platforms/dotnet/Apache.Ignite.Core.Tests/IgniteConfigurationSerializerTest.cs
modules/platforms/dotnet/Apache.Ignite.Core.Tests/IgniteConfigurationTest.cs
modules/platforms/dotnet/Apache.Ignite.Core/Configuration/ClientConnectorConfiguration.cs
modules/platforms/dotnet/Apache.Ignite.Core/IgniteConfigurationSection.xsd

index 1c33a00..d2520d2 100644 (file)
@@ -42,6 +42,9 @@ public class ClientConnectorConfiguration {
     /** Default size of thread pool. */
     public static final int DFLT_THREAD_POOL_SIZE = IgniteConfiguration.DFLT_PUBLIC_THREAD_CNT;
 
+    /** Default idle timeout. */
+    public static final int DFLT_IDLE_TIMEOUT = 0;
+
     /** Host. */
     private String host;
 
@@ -66,6 +69,9 @@ public class ClientConnectorConfiguration {
     /** Thread pool size. */
     private int threadPoolSize = DFLT_THREAD_POOL_SIZE;
 
+    /** Idle timeout. */
+    private long idleTimeout = DFLT_IDLE_TIMEOUT;
+
     /**
      * Creates SQL connector configuration with all default values.
      */
@@ -89,6 +95,7 @@ public class ClientConnectorConfiguration {
         sockSndBufSize = cfg.getSocketSendBufferSize();
         tcpNoDelay = cfg.isTcpNoDelay();
         threadPoolSize = cfg.getThreadPoolSize();
+        idleTimeout = cfg.getIdleTimeout();
     }
 
     /**
@@ -268,6 +275,32 @@ public class ClientConnectorConfiguration {
         return this;
     }
 
+    /**
+     * Gets idle timeout for client connections.
+     * If no packets come within idle timeout, the connection is closed.
+     * Zero or negative means no timeout.
+     *
+     * @return Idle timeout in milliseconds.
+     */
+    public long getIdleTimeout() {
+        return idleTimeout;
+    }
+
+    /**
+     * Sets idle timeout for client connections.
+     * If no packets come within idle timeout, the connection is closed.
+     * Zero or negative means no timeout.
+     *
+     * @param idleTimeout Idle timeout in milliseconds.
+     * @see #getIdleTimeout()
+     * @return {@code this} for chaining.
+     */
+    public ClientConnectorConfiguration setIdleTimeout(long idleTimeout) {
+        this.idleTimeout = idleTimeout;
+
+        return this;
+    }
+
     /** {@inheritDoc} */
     @Override public String toString() {
         return S.toString(ClientConnectorConfiguration.class, this);
index faecab3..b41e240 100644 (file)
@@ -158,6 +158,11 @@ public class ClientListenerNioListener extends GridNioServerListenerAdapter<byte
         }
     }
 
+    /** {@inheritDoc} */
+    @Override public void onSessionIdleTimeout(GridNioSession ses) {
+        ses.close();
+    }
+
     /**
      * Perform handshake.
      *
index 06977c8..ebd5156 100644 (file)
@@ -123,6 +123,7 @@ public class ClientListenerProcessor extends GridProcessorAdapter {
                         };
 
                         int maxOpenCursors = cliConnCfg.getMaxOpenCursorsPerConnection();
+                        long idleTimeout = cliConnCfg.getIdleTimeout();
 
                         GridNioServer<byte[]> srv0 = GridNioServer.<byte[]>builder()
                             .address(hostAddr)
@@ -139,7 +140,7 @@ public class ClientListenerProcessor extends GridProcessorAdapter {
                             .socketReceiveBufferSize(cliConnCfg.getSocketReceiveBufferSize())
                             .filters(filters)
                             .directMode(false)
-                            .idleTimeout(Long.MAX_VALUE)
+                            .idleTimeout(idleTimeout > 0 ? idleTimeout : Long.MAX_VALUE)
                             .build();
 
                         srv0.start();
index 6a265eb..b048f48 100644 (file)
@@ -1589,7 +1589,8 @@ public class PlatformConfigurationUtils {
                 .setSocketReceiveBufferSize(in.readInt())
                 .setTcpNoDelay(in.readBoolean())
                 .setMaxOpenCursorsPerConnection(in.readInt())
-                .setThreadPoolSize(in.readInt());
+                .setThreadPoolSize(in.readInt())
+                .setIdleTimeout(in.readLong());
     }
 
     /**
@@ -1611,6 +1612,7 @@ public class PlatformConfigurationUtils {
             w.writeBoolean(cfg.isTcpNoDelay());
             w.writeInt(cfg.getMaxOpenCursorsPerConnection());
             w.writeInt(cfg.getThreadPoolSize());
+            w.writeLong(cfg.getIdleTimeout());
         } else {
             w.writeBoolean(false);
         }
index 2b92eb7..0bf3c37 100644 (file)
@@ -272,6 +272,40 @@ namespace Apache.Ignite.Core.Tests.Client
         }
 
         /// <summary>
+        /// Tests the <see cref="ClientConnectorConfiguration.IdleTimeout"/> property.
+        /// </summary>
+        [Test]
+        [Category(TestUtils.CategoryIntensive)]
+        public void TestIdleTimeout()
+        {
+            var cfg = new IgniteConfiguration(TestUtils.GetTestConfiguration())
+            {
+                ClientConnectorConfiguration = new ClientConnectorConfiguration
+                {
+                    IdleTimeout = TimeSpan.FromMilliseconds(100)
+                }
+            };
+
+            var ignite = Ignition.Start(cfg);
+            Assert.AreEqual(100, ignite.GetConfiguration().ClientConnectorConfiguration.IdleTimeout.TotalMilliseconds);
+
+            using (var client = StartClient())
+            {
+                var cache = client.GetOrCreateCache<int, int>("foo");
+                cache[1] = 1;
+                Assert.AreEqual(1, cache[1]);
+                
+                Thread.Sleep(90);
+                Assert.AreEqual(1, cache[1]);
+                
+                // Idle check frequency is 2 seconds.
+                Thread.Sleep(4000);
+                var ex = Assert.Throws<SocketException>(() => cache.Get(1));
+                Assert.AreEqual(SocketError.ConnectionAborted, ex.SocketErrorCode);
+            }
+        }
+
+        /// <summary>
         /// Starts the client.
         /// </summary>
         private static IIgniteClient StartClient()
index 1fc42ad..aff48f5 100644 (file)
         </memoryPolicies>
     </memoryConfiguration>
     <sqlConnectorConfiguration host='bar' port='10' portRange='11' socketSendBufferSize='12' socketReceiveBufferSize='13' tcpNoDelay='true' maxOpenCursorsPerConnection='14' threadPoolSize='15' />
-    <clientConnectorConfiguration host='bar' port='10' portRange='11' socketSendBufferSize='12' socketReceiveBufferSize='13' tcpNoDelay='true' maxOpenCursorsPerConnection='14' threadPoolSize='15' />
+    <clientConnectorConfiguration host='bar' port='10' portRange='11' socketSendBufferSize='12' socketReceiveBufferSize='13' tcpNoDelay='true' maxOpenCursorsPerConnection='14' threadPoolSize='15' idleTimeout = "00:00:19" />
     <persistentStoreConfiguration alwaysWriteFullPages='true' checkpointingFrequency='00:00:1' checkpointingPageBufferSize='2' 
                                   checkpointingThreads='3' lockWaitTime='00:00:04' persistentStorePath='foo' tlbSize='5' 
                                   walArchivePath='bar' walFlushFrequency='00:00:06' walFsyncDelayNanos='7' walHistorySize='8' 
index 167854d..5512975 100644 (file)
@@ -250,6 +250,7 @@ namespace Apache.Ignite.Core.Tests
             Assert.IsTrue(client.TcpNoDelay);
             Assert.AreEqual(14, client.MaxOpenCursorsPerConnection);
             Assert.AreEqual(15, client.ThreadPoolSize);
+            Assert.AreEqual(19, client.IdleTimeout.TotalSeconds);
 
             var pers = cfg.PersistentStoreConfiguration;
 
@@ -895,7 +896,8 @@ namespace Apache.Ignite.Core.Tests
                     SocketReceiveBufferSize = 5,
                     SocketSendBufferSize = 6,
                     TcpNoDelay = false,
-                    ThreadPoolSize = 7
+                    ThreadPoolSize = 7,
+                    IdleTimeout = TimeSpan.FromMinutes(5)
                 },
                 PersistentStoreConfiguration = new PersistentStoreConfiguration
                 {
index 4d71929..0b1b499 100644 (file)
@@ -594,6 +594,7 @@ namespace Apache.Ignite.Core.Tests
             Assert.AreEqual(ClientConnectorConfiguration.DefaultSocketBufferSize, cfg.SocketSendBufferSize);
             Assert.AreEqual(ClientConnectorConfiguration.DefaultTcpNoDelay, cfg.TcpNoDelay);
             Assert.AreEqual(ClientConnectorConfiguration.DefaultThreadPoolSize, cfg.ThreadPoolSize);
+            Assert.AreEqual(ClientConnectorConfiguration.DefaultIdleTimeout, cfg.IdleTimeout);
         }
 
         /// <summary>
index 8c23d99..a5b2403 100644 (file)
 
 namespace Apache.Ignite.Core.Configuration
 {
+    using System;
     using System.ComponentModel;
     using System.Diagnostics;
     using Apache.Ignite.Core.Binary;
+    using Apache.Ignite.Core.Impl.Binary;
 
     /// <summary>
     /// Client connector configuration (ODBC, JDBC, Thin Client).
@@ -55,6 +57,11 @@ namespace Apache.Ignite.Core.Configuration
         /// Default SQL connector thread pool size.
         /// </summary>
         public static readonly int DefaultThreadPoolSize = IgniteConfiguration.DefaultThreadPoolSize;
+        
+        /// <summary>
+        /// Default idle timeout.
+        /// </summary>
+        public static TimeSpan DefaultIdleTimeout = TimeSpan.Zero;
 
         /// <summary>
         /// Initializes a new instance of the <see cref="ClientConnectorConfiguration"/> class.
@@ -68,6 +75,7 @@ namespace Apache.Ignite.Core.Configuration
             TcpNoDelay = DefaultTcpNoDelay;
             MaxOpenCursorsPerConnection = DefaultMaxOpenCursorsPerConnection;
             ThreadPoolSize = DefaultThreadPoolSize;
+            IdleTimeout = DefaultIdleTimeout;
         }
 
         /// <summary>
@@ -85,6 +93,7 @@ namespace Apache.Ignite.Core.Configuration
             TcpNoDelay = reader.ReadBoolean();
             MaxOpenCursorsPerConnection = reader.ReadInt();
             ThreadPoolSize = reader.ReadInt();
+            IdleTimeout = reader.ReadLongAsTimespan();
         }
 
         /// <summary>
@@ -102,6 +111,7 @@ namespace Apache.Ignite.Core.Configuration
             writer.WriteBoolean(TcpNoDelay);
             writer.WriteInt(MaxOpenCursorsPerConnection);
             writer.WriteInt(ThreadPoolSize);
+            writer.WriteTimeSpanAsLong(IdleTimeout);
         }
 
         /// <summary>
@@ -155,5 +165,12 @@ namespace Apache.Ignite.Core.Configuration
         /// Gets or sets the size of the thread pool.
         /// </summary>
         public int ThreadPoolSize { get; set; }
+        
+        /// <summary>
+        /// Gets or sets idle timeout for client connections on the server side.
+        /// If no packets come within idle timeout, the connection is closed by the server.
+        /// Zero or negative means no timeout.
+        /// </summary>
+        public TimeSpan IdleTimeout { get; set; }
     }
 }
index 5d76cde..190b3ad 100644 (file)
                                 <xs:documentation>SQL connector thread pool size.</xs:documentation>
                             </xs:annotation>
                         </xs:attribute>
+                        <xs:attribute name="idleTimeout" type="xs:string">
+                            <xs:annotation>
+                                <xs:documentation>Idle timeout for client connections on the server side. If no packets come within idle timeout, the connection is closed by the server. Zero or negative for no timeout.</xs:documentation>
+                            </xs:annotation>
+                        </xs:attribute>
                     </xs:complexType>
                 </xs:element>
                 <xs:element name="persistentStoreConfiguration" minOccurs="0">