You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@ignite.apache.org by "korlov42 (via GitHub)" <gi...@apache.org> on 2023/11/02 15:14:21 UTC

[PR] IGNITE-20780 Sql. Move session expiration to IgniteSqlImpl [ignite-3]

korlov42 opened a new pull request, #2785:
URL: https://github.com/apache/ignite-3/pull/2785

   Thank you for submitting the pull request.
   
   To streamline the review process of the patch and ensure better code quality
   we ask both an author and a reviewer to verify the following:
   
   ### The Review Checklist
   - [ ] **Formal criteria:** TC status, codestyle, mandatory documentation. Also make sure to complete the following:  
   \- There is a single JIRA ticket related to the pull request.  
   \- The web-link to the pull request is attached to the JIRA ticket.  
   \- The JIRA ticket has the Patch Available state.  
   \- The description of the JIRA ticket explains WHAT was made, WHY and HOW.  
   \- The pull request title is treated as the final commit message. The following pattern must be used: IGNITE-XXXX Change summary where XXXX - number of JIRA issue.
   - [ ] **Design:** new code conforms with the design principles of the components it is added to.
   - [ ] **Patch quality:** patch cannot be split into smaller pieces, its size must be reasonable.
   - [ ] **Code quality:** code is clean and readable, necessary developer documentation is added if needed.
   - [ ] **Tests code quality:** test set covers positive/negative scenarios, happy/edge cases. Tests are effective in terms of execution time and resources.
   
   ### Notes
   - [Apache Ignite Coding Guidelines](https://cwiki.apache.org/confluence/display/IGNITE/Java+Code+Style+Guide)


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@ignite.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] IGNITE-20780 Sql. Move session expiration to IgniteSqlImpl [ignite-3]

Posted by "AMashenkov (via GitHub)" <gi...@apache.org>.
AMashenkov commented on code in PR #2785:
URL: https://github.com/apache/ignite-3/pull/2785#discussion_r1381418330


##########
modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/api/IgniteSqlImpl.java:
##########
@@ -54,7 +82,7 @@ public Session createSession() {
     /** {@inheritDoc} */
     @Override
     public SessionBuilder sessionBuilder() {
-        return new SessionBuilderImpl(qryProc, transactions, new HashMap<>());
+        return new SessionBuilderImpl(sessions, qryProc, transactions, new HashMap<>());

Review Comment:
   You pass sessions collection to SessionBuilderImpl, which leaks to the user side.
   SessionBuilderImpl has no locks/flags that prevents putting data into collections, when node is stopping. After executor will be stopped, noone will cleanup the collection.
   
   Maybe, we can pass a listener interface with `onSessionCreate()` method and do all stuff outside the builder?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@ignite.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] IGNITE-20780 Sql. Move session expiration to IgniteSqlImpl [ignite-3]

Posted by "xtern (via GitHub)" <gi...@apache.org>.
xtern commented on code in PR #2785:
URL: https://github.com/apache/ignite-3/pull/2785#discussion_r1382923570


##########
modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/SqlQueryProcessor.java:
##########
@@ -512,15 +497,11 @@ private AsyncSqlCursor<List<Object>> executePlan(
 
                     @Override
                     public CompletableFuture<BatchedResult<List<Object>>> requestNextAsync(int rows) {
-                        session.touch();

Review Comment:
   can we remove `session` from the parameters of the `executePlan` method?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@ignite.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] IGNITE-20780 Sql. Move session expiration to IgniteSqlImpl [ignite-3]

Posted by "korlov42 (via GitHub)" <gi...@apache.org>.
korlov42 commented on code in PR #2785:
URL: https://github.com/apache/ignite-3/pull/2785#discussion_r1381623172


##########
modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/api/IgniteSqlImpl.java:
##########
@@ -29,20 +39,38 @@
 /**
  * Embedded implementation of the Ignite SQL query facade.
  */
-public class IgniteSqlImpl implements IgniteSql {
+public class IgniteSqlImpl implements IgniteSql, IgniteComponent {
+    private static final IgniteLogger LOG = Loggers.forClass(IgniteSqlImpl.class);
+
+    /** Session expiration check period in milliseconds. */
+    private static final long SESSION_EXPIRE_CHECK_PERIOD = TimeUnit.SECONDS.toMillis(1);
+
     private final QueryProcessor qryProc;
 
     private final IgniteTransactions transactions;
 
+    /** Session expiration worker. */
+    private final ScheduledExecutorService executor;
+
+    private final ConcurrentMap<SessionId, SessionImpl> sessions = new ConcurrentHashMap<>();
+
     /**
      * Constructor.
      *
      * @param qryProc Query processor.
      * @param transactions Transactions facade.
      */
-    public IgniteSqlImpl(QueryProcessor qryProc, IgniteTransactions transactions) {
+    public IgniteSqlImpl(
+            String nodeName,

Review Comment:
   I was thinking about using a common scheduler, but we don't have common pools yet



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@ignite.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] IGNITE-20780 Sql. Move session expiration to IgniteSqlImpl [ignite-3]

Posted by "korlov42 (via GitHub)" <gi...@apache.org>.
korlov42 commented on code in PR #2785:
URL: https://github.com/apache/ignite-3/pull/2785#discussion_r1381621460


##########
modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/api/IgniteSqlImpl.java:
##########
@@ -54,7 +82,7 @@ public Session createSession() {
     /** {@inheritDoc} */
     @Override
     public SessionBuilder sessionBuilder() {
-        return new SessionBuilderImpl(qryProc, transactions, new HashMap<>());
+        return new SessionBuilderImpl(sessions, qryProc, transactions, new HashMap<>());

Review Comment:
   now it's guarded with busyLock



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@ignite.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] IGNITE-20780 Sql. Move session expiration to IgniteSqlImpl [ignite-3]

Posted by "AMashenkov (via GitHub)" <gi...@apache.org>.
AMashenkov commented on code in PR #2785:
URL: https://github.com/apache/ignite-3/pull/2785#discussion_r1381418330


##########
modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/api/IgniteSqlImpl.java:
##########
@@ -54,7 +82,7 @@ public Session createSession() {
     /** {@inheritDoc} */
     @Override
     public SessionBuilder sessionBuilder() {
-        return new SessionBuilderImpl(qryProc, transactions, new HashMap<>());
+        return new SessionBuilderImpl(sessions, qryProc, transactions, new HashMap<>());

Review Comment:
   You pass sessions collection to SessionBuilderImpl, which leaks to the user side.
   SessionBuilderImpl has no locks/flags that prevents putting data into collections, when node is stopping. After executor will be stopped, noone will cleanup the collection.
   
   Maybe, we can pass a callback method reference (e.g.  `onSessionCreate()`) and do all stuff in the method, but outside the builder?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@ignite.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] IGNITE-20780 Sql. Move session expiration to IgniteSqlImpl [ignite-3]

Posted by "korlov42 (via GitHub)" <gi...@apache.org>.
korlov42 commented on code in PR #2785:
URL: https://github.com/apache/ignite-3/pull/2785#discussion_r1383122833


##########
modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/SqlQueryProcessor.java:
##########
@@ -512,15 +497,11 @@ private AsyncSqlCursor<List<Object>> executePlan(
 
                     @Override
                     public CompletableFuture<BatchedResult<List<Object>>> requestNextAsync(int rows) {
-                        session.touch();

Review Comment:
   removed, thanks!



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@ignite.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] IGNITE-20780 Sql. Move session expiration to IgniteSqlImpl [ignite-3]

Posted by "AMashenkov (via GitHub)" <gi...@apache.org>.
AMashenkov commented on code in PR #2785:
URL: https://github.com/apache/ignite-3/pull/2785#discussion_r1381408052


##########
modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/api/IgniteSqlImpl.java:
##########
@@ -29,20 +39,38 @@
 /**
  * Embedded implementation of the Ignite SQL query facade.
  */
-public class IgniteSqlImpl implements IgniteSql {
+public class IgniteSqlImpl implements IgniteSql, IgniteComponent {
+    private static final IgniteLogger LOG = Loggers.forClass(IgniteSqlImpl.class);
+
+    /** Session expiration check period in milliseconds. */
+    private static final long SESSION_EXPIRE_CHECK_PERIOD = TimeUnit.SECONDS.toMillis(1);
+
     private final QueryProcessor qryProc;
 
     private final IgniteTransactions transactions;
 
+    /** Session expiration worker. */
+    private final ScheduledExecutorService executor;
+
+    private final ConcurrentMap<SessionId, SessionImpl> sessions = new ConcurrentHashMap<>();

Review Comment:
   Should we cleanup collection on component stop?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@ignite.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] IGNITE-20780 Sql. Move session expiration to IgniteSqlImpl [ignite-3]

Posted by "korlov42 (via GitHub)" <gi...@apache.org>.
korlov42 commented on code in PR #2785:
URL: https://github.com/apache/ignite-3/pull/2785#discussion_r1381745432


##########
modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/api/AsyncResultSetImpl.java:
##########
@@ -108,8 +122,10 @@ public boolean wasApplied() {
     public Iterable<T> currentPage() {
         requireResultSet();
 
+        expirationTracker.touch();

Review Comment:
   I would prefer not to. `requireResultSet` does exactly what you expect it to do by looking at its name. Touch of the expiration tracker will be a side effect. We may move it inside, but then we need to come up with a proper name like `ensureResultSetAwailableAndTouchExpirationTracker`



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@ignite.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] IGNITE-20780 Sql. Move session expiration to IgniteSqlImpl [ignite-3]

Posted by "AMashenkov (via GitHub)" <gi...@apache.org>.
AMashenkov commented on code in PR #2785:
URL: https://github.com/apache/ignite-3/pull/2785#discussion_r1382987389


##########
modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/api/SessionImplTest.java:
##########
@@ -0,0 +1,219 @@
+/*
+ * 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.sql.api;
+
+import static org.apache.ignite.internal.sql.engine.util.SqlTestUtils.assertThrowsSqlException;
+import static org.apache.ignite.internal.testframework.IgniteTestUtils.await;
+import static org.apache.ignite.lang.ErrorGroups.Sql.SESSION_CLOSED_ERR;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.hamcrest.Matchers.nullValue;
+import static org.hamcrest.Matchers.sameInstance;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.UUID;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+import org.apache.ignite.internal.sql.engine.AsyncSqlCursor;
+import org.apache.ignite.internal.sql.engine.QueryProcessor;
+import org.apache.ignite.internal.sql.engine.SqlQueryType;
+import org.apache.ignite.internal.sql.engine.session.SessionId;
+import org.apache.ignite.internal.testframework.BaseIgniteAbstractTest;
+import org.apache.ignite.internal.util.AsyncCursor.BatchedResult;
+import org.apache.ignite.internal.util.IgniteSpinBusyLock;
+import org.apache.ignite.sql.BatchedArguments;
+import org.apache.ignite.sql.Session.SessionBuilder;
+import org.apache.ignite.sql.async.AsyncResultSet;
+import org.apache.ignite.tx.IgniteTransactions;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+/**
+ * Tests to verify {@link SessionImpl}.
+ */
+@SuppressWarnings({"resource", "ThrowableNotThrown"})
+@ExtendWith(MockitoExtension.class)
+class SessionImplTest extends BaseIgniteAbstractTest {
+    private final ConcurrentMap<SessionId, SessionImpl> sessions = new ConcurrentHashMap<>();
+
+    private final AtomicLong clock = new AtomicLong();
+
+    @Mock
+    private QueryProcessor queryProcessor;
+
+    @BeforeEach
+    void setUp() {
+        clock.set(1L);
+
+        when(queryProcessor.closeSession(any()))
+                .thenReturn(CompletableFuture.completedFuture(null));
+    }
+
+    @AfterEach
+    void cleanUp() {
+        sessions.values().forEach(SessionImpl::close);
+
+        sessions.clear();
+    }
+
+    @Test
+    void sessionExpiredAfterIdleTimeout() {
+        SessionImpl session = newSession(2);
+        assertThat(session.expired(), is(false));
+
+        //period is small to expire session
+        clock.addAndGet(1);
+        assertThat(session.expired(), is(false));
+
+        //period is enough to session expired
+        clock.addAndGet(2);
+        assertThat(session.expired(), is(true));
+    }
+
+    @Test
+    void sessionCleanUpsItselfFromSessionsMapOnClose() {
+        SessionImpl session = newSession(3);
+        assertThat(sessions.get(session.id()), sameInstance(session));
+
+        session.close();
+
+        assertThat(sessions.get(session.id()), nullValue());
+    }
+
+    @Test
+    @SuppressWarnings("unchecked")
+    void sessionsShouldNotExpireWhenNewQueryExecuted() {
+        AsyncSqlCursor<List<Object>> result = mock(AsyncSqlCursor.class);
+
+        when(result.requestNextAsync(anyInt()))
+                .thenReturn(CompletableFuture.completedFuture(new BatchedResult<>(List.of(List.of(0L)), false)));
+
+        when(queryProcessor.querySingleAsync(any(), any(), any(), any(), any(Object[].class)))
+                .thenReturn(CompletableFuture.completedFuture(result));
+
+        SessionImpl session = newSession(3);
+
+        clock.addAndGet(2);
+
+        await(session.executeAsync(null, "SELECT 1", 1));
+
+        clock.addAndGet(2);
+        assertThat(session.expired(), is(false));
+
+        await(session.executeBatchAsync(null, "SELECT 1", BatchedArguments.of()));
+
+        clock.addAndGet(2);
+        assertThat(session.expired(), is(false));
+
+        clock.addAndGet(2);
+
+        assertThrowsSqlException(
+                SESSION_CLOSED_ERR,
+                "Session is closed",
+                () -> await(session.executeAsync(null, "SELECT 1"))
+        );
+
+        assertThrowsSqlException(
+                SESSION_CLOSED_ERR,
+                "Session is closed",
+                () -> await(session.executeBatchAsync(null, "SELECT 1", BatchedArguments.of()))
+        );
+    }
+
+    @Test
+    @SuppressWarnings("unchecked")
+    void sessionsShouldNotExpireWhenResultSetAreProcessed() {
+        AsyncSqlCursor<List<Object>> result = mock(AsyncSqlCursor.class);
+
+        when(result.requestNextAsync(anyInt()))
+                .thenReturn(CompletableFuture.completedFuture(new BatchedResult<>(List.of(List.of(0L)), false)));
+        when(result.queryType())
+                .thenReturn(SqlQueryType.QUERY);
+
+        when(queryProcessor.querySingleAsync(any(), any(), any(), any(), any(Object[].class)))
+                .thenReturn(CompletableFuture.completedFuture(result));
+
+        SessionImpl session = newSession(3);
+
+        AsyncResultSet<?> rs = await(session.executeAsync(null, "SELECT 1", 1));
+
+        assertThat(rs, notNullValue());
+
+        clock.addAndGet(2);
+        assertThat(session.expired(), is(false));
+
+        {
+            rs.currentPage();
+
+            clock.addAndGet(2);
+            assertThat(session.expired(), is(false));
+        }
+
+        {
+            rs.currentPage();
+
+            clock.addAndGet(2);
+            assertThat(session.expired(), is(false));
+        }
+
+        {
+            rs.fetchNextPage();
+
+            clock.addAndGet(2);
+            assertThat(session.expired(), is(false));
+        }
+
+        {
+            rs.fetchNextPage();
+
+            clock.addAndGet(2);
+            assertThat(session.expired(), is(false));
+        }
+    }
+
+    private SessionImpl newSession(long idleTimeout) {
+        when(queryProcessor.createSession(any()))
+                .thenAnswer(ignored -> new SessionId(UUID.randomUUID()));
+
+        SessionBuilder builder = new SessionBuilderImpl(
+                new IgniteSpinBusyLock(),
+                sessions,
+                queryProcessor,
+                mock(IgniteTransactions.class),
+                clock::get,
+                new HashMap<>()
+        );
+
+        return (SessionImpl) builder
+                .idleTimeout(idleTimeout, TimeUnit.MILLISECONDS)
+                .build();
+    }
+}

Review Comment:
   ```suggestion
   }
   
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@ignite.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] IGNITE-20780 Sql. Move session expiration to IgniteSqlImpl [ignite-3]

Posted by "korlov42 (via GitHub)" <gi...@apache.org>.
korlov42 commented on code in PR #2785:
URL: https://github.com/apache/ignite-3/pull/2785#discussion_r1381620890


##########
modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/api/IgniteSqlImpl.java:
##########
@@ -68,4 +96,25 @@ public Statement createStatement(String query) {
     public StatementBuilder statementBuilder() {
         return new StatementBuilderImpl();
     }
+
+    @Override
+    public void start() {
+        executor.scheduleWithFixedDelay(
+                () -> {
+                    for (SessionImpl session : sessions.values()) {
+                        if (session.expired()) {
+                            session.closeAsync();
+                        }
+                    }
+                },
+                SESSION_EXPIRE_CHECK_PERIOD,
+                SESSION_EXPIRE_CHECK_PERIOD,
+                TimeUnit.MILLISECONDS
+        );
+    }
+
+    @Override
+    public void stop() throws Exception {
+        executor.shutdownNow();

Review Comment:
   done



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@ignite.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] IGNITE-20780 Sql. Move session expiration to IgniteSqlImpl [ignite-3]

Posted by "ygerzhedovich (via GitHub)" <gi...@apache.org>.
ygerzhedovich commented on code in PR #2785:
URL: https://github.com/apache/ignite-3/pull/2785#discussion_r1381679390


##########
modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/api/AsyncResultSetImpl.java:
##########
@@ -108,8 +122,10 @@ public boolean wasApplied() {
     public Iterable<T> currentPage() {
         requireResultSet();
 
+        expirationTracker.touch();

Review Comment:
   maybe we could move both touch of tracker into `requireResultSet()` - WDYT?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@ignite.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] IGNITE-20780 Sql. Move session expiration to IgniteSqlImpl [ignite-3]

Posted by "AMashenkov (via GitHub)" <gi...@apache.org>.
AMashenkov commented on code in PR #2785:
URL: https://github.com/apache/ignite-3/pull/2785#discussion_r1381394650


##########
modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/api/IgniteSqlImpl.java:
##########
@@ -29,20 +39,38 @@
 /**
  * Embedded implementation of the Ignite SQL query facade.
  */
-public class IgniteSqlImpl implements IgniteSql {
+public class IgniteSqlImpl implements IgniteSql, IgniteComponent {
+    private static final IgniteLogger LOG = Loggers.forClass(IgniteSqlImpl.class);
+
+    /** Session expiration check period in milliseconds. */
+    private static final long SESSION_EXPIRE_CHECK_PERIOD = TimeUnit.SECONDS.toMillis(1);
+
     private final QueryProcessor qryProc;
 
     private final IgniteTransactions transactions;
 
+    /** Session expiration worker. */
+    private final ScheduledExecutorService executor;
+
+    private final ConcurrentMap<SessionId, SessionImpl> sessions = new ConcurrentHashMap<>();
+
     /**
      * Constructor.
      *
      * @param qryProc Query processor.
      * @param transactions Transactions facade.
      */
-    public IgniteSqlImpl(QueryProcessor qryProc, IgniteTransactions transactions) {
+    public IgniteSqlImpl(
+            String nodeName,

Review Comment:
   I don't like the idea to pass node name to components just to create thread pool.
   Also, with this approach, we can't use spy on thread factory in test to verify the component stops correctly.
   
   WDYT, if we will have NodeThreadFactory and pass the factory to components, instead of nodeName?
   Feel free keeping changes as is, we always can fix this another ticket.



##########
modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/api/IgniteSqlImpl.java:
##########
@@ -29,20 +39,38 @@
 /**
  * Embedded implementation of the Ignite SQL query facade.
  */
-public class IgniteSqlImpl implements IgniteSql {
+public class IgniteSqlImpl implements IgniteSql, IgniteComponent {
+    private static final IgniteLogger LOG = Loggers.forClass(IgniteSqlImpl.class);
+
+    /** Session expiration check period in milliseconds. */
+    private static final long SESSION_EXPIRE_CHECK_PERIOD = TimeUnit.SECONDS.toMillis(1);
+
     private final QueryProcessor qryProc;
 
     private final IgniteTransactions transactions;
 
+    /** Session expiration worker. */
+    private final ScheduledExecutorService executor;
+
+    private final ConcurrentMap<SessionId, SessionImpl> sessions = new ConcurrentHashMap<>();
+
     /**
      * Constructor.
      *
      * @param qryProc Query processor.
      * @param transactions Transactions facade.
      */
-    public IgniteSqlImpl(QueryProcessor qryProc, IgniteTransactions transactions) {
+    public IgniteSqlImpl(
+            String nodeName,

Review Comment:
   I don't like the idea to pass node name to components just to create thread pool.
   Also, with this approach, we can't use spy on thread factory in test to verify the component stops correctly.
   
   WDYT, if we will have NodeThreadFactory and pass the factory to components, instead of nodeName?
   Feel free keeping changes as is, we always can fix this in another ticket.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@ignite.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] IGNITE-20780 Sql. Move session expiration to IgniteSqlImpl [ignite-3]

Posted by "AMashenkov (via GitHub)" <gi...@apache.org>.
AMashenkov commented on code in PR #2785:
URL: https://github.com/apache/ignite-3/pull/2785#discussion_r1382987086


##########
modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/api/IgniteSqlImplTest.java:
##########
@@ -0,0 +1,119 @@
+/*
+ * 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.sql.api;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.empty;
+import static org.hamcrest.Matchers.is;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+import org.apache.ignite.internal.sql.engine.QueryProcessor;
+import org.apache.ignite.internal.sql.engine.session.SessionId;
+import org.apache.ignite.internal.testframework.BaseIgniteAbstractTest;
+import org.apache.ignite.internal.testframework.IgniteTestUtils;
+import org.apache.ignite.lang.IgniteException;
+import org.apache.ignite.sql.Session;
+import org.apache.ignite.sql.Session.SessionBuilder;
+import org.apache.ignite.tx.IgniteTransactions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+/**
+ * Tests to verify {@link IgniteSqlImpl}.
+ */
+@SuppressWarnings({"ThrowableNotThrown", "resource"})
+@ExtendWith(MockitoExtension.class)
+class IgniteSqlImplTest extends BaseIgniteAbstractTest {
+    @Test
+    void allSessionsAreClosedAfterFacadeIsStopped() throws Exception {
+        IgniteSqlImpl facade = newSqlFacade();
+
+        SessionBuilder builder = facade.sessionBuilder();
+
+        List<Session> sessions = new ArrayList<>();
+
+        sessions.add(builder.build());
+        sessions.add(builder.build());
+        sessions.add(facade.sessionBuilder().build());
+        sessions.add(facade.createSession());
+
+        for (Session session : sessions) {
+            assertThat(session.closed(), is(false));
+        }
+
+        facade.stop();
+
+        for (Session session : sessions) {
+            assertThat(session.closed(), is(true));
+        }
+
+        assertThat(facade.sessions(), empty());
+    }
+
+    @Test
+    void itsImpossibleToCreateSessionsAfterFacadeIsStopped() throws Exception {
+        IgniteSqlImpl facade = newSqlFacade();
+
+        SessionBuilder builder = facade.sessionBuilder();
+
+        Session templateSession = builder.build();
+
+        facade.stop();
+
+        IgniteTestUtils.assertThrows(
+                IgniteException.class,
+                builder::build,
+                "Node is stopping"
+        );
+
+        IgniteTestUtils.assertThrows(
+                IgniteException.class,
+                () -> facade.sessionBuilder().build(),
+                "Node is stopping"
+        );
+
+        IgniteTestUtils.assertThrows(
+                IgniteException.class,
+                facade::createSession,
+                "Node is stopping"
+        );
+
+        IgniteTestUtils.assertThrows(
+                IgniteException.class,
+                () -> templateSession.toBuilder().build(),
+                "Node is stopping"
+        );
+
+        assertThat(facade.sessions(), empty());
+    }
+
+    private static IgniteSqlImpl newSqlFacade() {
+        QueryProcessor queryProcessor = mock(QueryProcessor.class);
+
+        when(queryProcessor.createSession(any()))
+                .thenAnswer(ignored -> new SessionId(UUID.randomUUID()));
+
+        return new IgniteSqlImpl("test", queryProcessor, mock(IgniteTransactions.class));
+    }
+}

Review Comment:
   ```suggestion
   }
   
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@ignite.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] IGNITE-20780 Sql. Move session expiration to IgniteSqlImpl [ignite-3]

Posted by "korlov42 (via GitHub)" <gi...@apache.org>.
korlov42 commented on code in PR #2785:
URL: https://github.com/apache/ignite-3/pull/2785#discussion_r1383122487


##########
modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/api/SessionImplTest.java:
##########
@@ -0,0 +1,219 @@
+/*
+ * 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.sql.api;
+
+import static org.apache.ignite.internal.sql.engine.util.SqlTestUtils.assertThrowsSqlException;
+import static org.apache.ignite.internal.testframework.IgniteTestUtils.await;
+import static org.apache.ignite.lang.ErrorGroups.Sql.SESSION_CLOSED_ERR;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.hamcrest.Matchers.nullValue;
+import static org.hamcrest.Matchers.sameInstance;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.UUID;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+import org.apache.ignite.internal.sql.engine.AsyncSqlCursor;
+import org.apache.ignite.internal.sql.engine.QueryProcessor;
+import org.apache.ignite.internal.sql.engine.SqlQueryType;
+import org.apache.ignite.internal.sql.engine.session.SessionId;
+import org.apache.ignite.internal.testframework.BaseIgniteAbstractTest;
+import org.apache.ignite.internal.util.AsyncCursor.BatchedResult;
+import org.apache.ignite.internal.util.IgniteSpinBusyLock;
+import org.apache.ignite.sql.BatchedArguments;
+import org.apache.ignite.sql.Session.SessionBuilder;
+import org.apache.ignite.sql.async.AsyncResultSet;
+import org.apache.ignite.tx.IgniteTransactions;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+/**
+ * Tests to verify {@link SessionImpl}.
+ */
+@SuppressWarnings({"resource", "ThrowableNotThrown"})
+@ExtendWith(MockitoExtension.class)
+class SessionImplTest extends BaseIgniteAbstractTest {
+    private final ConcurrentMap<SessionId, SessionImpl> sessions = new ConcurrentHashMap<>();
+
+    private final AtomicLong clock = new AtomicLong();
+
+    @Mock
+    private QueryProcessor queryProcessor;
+
+    @BeforeEach
+    void setUp() {
+        clock.set(1L);
+
+        when(queryProcessor.closeSession(any()))
+                .thenReturn(CompletableFuture.completedFuture(null));
+    }
+
+    @AfterEach
+    void cleanUp() {
+        sessions.values().forEach(SessionImpl::close);
+
+        sessions.clear();
+    }
+
+    @Test
+    void sessionExpiredAfterIdleTimeout() {
+        SessionImpl session = newSession(2);
+        assertThat(session.expired(), is(false));
+
+        //period is small to expire session
+        clock.addAndGet(1);
+        assertThat(session.expired(), is(false));
+
+        //period is enough to session expired
+        clock.addAndGet(2);
+        assertThat(session.expired(), is(true));
+    }
+
+    @Test
+    void sessionCleanUpsItselfFromSessionsMapOnClose() {
+        SessionImpl session = newSession(3);
+        assertThat(sessions.get(session.id()), sameInstance(session));
+
+        session.close();
+
+        assertThat(sessions.get(session.id()), nullValue());
+    }
+
+    @Test
+    @SuppressWarnings("unchecked")
+    void sessionsShouldNotExpireWhenNewQueryExecuted() {
+        AsyncSqlCursor<List<Object>> result = mock(AsyncSqlCursor.class);
+
+        when(result.requestNextAsync(anyInt()))
+                .thenReturn(CompletableFuture.completedFuture(new BatchedResult<>(List.of(List.of(0L)), false)));
+
+        when(queryProcessor.querySingleAsync(any(), any(), any(), any(), any(Object[].class)))
+                .thenReturn(CompletableFuture.completedFuture(result));
+
+        SessionImpl session = newSession(3);
+
+        clock.addAndGet(2);
+
+        await(session.executeAsync(null, "SELECT 1", 1));
+
+        clock.addAndGet(2);
+        assertThat(session.expired(), is(false));
+
+        await(session.executeBatchAsync(null, "SELECT 1", BatchedArguments.of()));
+
+        clock.addAndGet(2);
+        assertThat(session.expired(), is(false));
+
+        clock.addAndGet(2);
+
+        assertThrowsSqlException(
+                SESSION_CLOSED_ERR,
+                "Session is closed",
+                () -> await(session.executeAsync(null, "SELECT 1"))
+        );
+
+        assertThrowsSqlException(
+                SESSION_CLOSED_ERR,
+                "Session is closed",
+                () -> await(session.executeBatchAsync(null, "SELECT 1", BatchedArguments.of()))
+        );
+    }
+
+    @Test
+    @SuppressWarnings("unchecked")
+    void sessionsShouldNotExpireWhenResultSetAreProcessed() {
+        AsyncSqlCursor<List<Object>> result = mock(AsyncSqlCursor.class);
+
+        when(result.requestNextAsync(anyInt()))
+                .thenReturn(CompletableFuture.completedFuture(new BatchedResult<>(List.of(List.of(0L)), false)));
+        when(result.queryType())
+                .thenReturn(SqlQueryType.QUERY);
+
+        when(queryProcessor.querySingleAsync(any(), any(), any(), any(), any(Object[].class)))
+                .thenReturn(CompletableFuture.completedFuture(result));
+
+        SessionImpl session = newSession(3);
+
+        AsyncResultSet<?> rs = await(session.executeAsync(null, "SELECT 1", 1));
+
+        assertThat(rs, notNullValue());
+
+        clock.addAndGet(2);
+        assertThat(session.expired(), is(false));
+
+        {
+            rs.currentPage();
+
+            clock.addAndGet(2);
+            assertThat(session.expired(), is(false));
+        }
+
+        {
+            rs.currentPage();
+
+            clock.addAndGet(2);
+            assertThat(session.expired(), is(false));
+        }
+
+        {
+            rs.fetchNextPage();
+
+            clock.addAndGet(2);
+            assertThat(session.expired(), is(false));
+        }
+
+        {
+            rs.fetchNextPage();
+
+            clock.addAndGet(2);
+            assertThat(session.expired(), is(false));
+        }
+    }
+
+    private SessionImpl newSession(long idleTimeout) {
+        when(queryProcessor.createSession(any()))
+                .thenAnswer(ignored -> new SessionId(UUID.randomUUID()));
+
+        SessionBuilder builder = new SessionBuilderImpl(
+                new IgniteSpinBusyLock(),
+                sessions,
+                queryProcessor,
+                mock(IgniteTransactions.class),
+                clock::get,
+                new HashMap<>()
+        );
+
+        return (SessionImpl) builder
+                .idleTimeout(idleTimeout, TimeUnit.MILLISECONDS)
+                .build();
+    }
+}

Review Comment:
   done



##########
modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/api/IgniteSqlImplTest.java:
##########
@@ -0,0 +1,119 @@
+/*
+ * 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.sql.api;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.empty;
+import static org.hamcrest.Matchers.is;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+import org.apache.ignite.internal.sql.engine.QueryProcessor;
+import org.apache.ignite.internal.sql.engine.session.SessionId;
+import org.apache.ignite.internal.testframework.BaseIgniteAbstractTest;
+import org.apache.ignite.internal.testframework.IgniteTestUtils;
+import org.apache.ignite.lang.IgniteException;
+import org.apache.ignite.sql.Session;
+import org.apache.ignite.sql.Session.SessionBuilder;
+import org.apache.ignite.tx.IgniteTransactions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+/**
+ * Tests to verify {@link IgniteSqlImpl}.
+ */
+@SuppressWarnings({"ThrowableNotThrown", "resource"})
+@ExtendWith(MockitoExtension.class)
+class IgniteSqlImplTest extends BaseIgniteAbstractTest {
+    @Test
+    void allSessionsAreClosedAfterFacadeIsStopped() throws Exception {
+        IgniteSqlImpl facade = newSqlFacade();
+
+        SessionBuilder builder = facade.sessionBuilder();
+
+        List<Session> sessions = new ArrayList<>();
+
+        sessions.add(builder.build());
+        sessions.add(builder.build());
+        sessions.add(facade.sessionBuilder().build());
+        sessions.add(facade.createSession());
+
+        for (Session session : sessions) {
+            assertThat(session.closed(), is(false));
+        }
+
+        facade.stop();
+
+        for (Session session : sessions) {
+            assertThat(session.closed(), is(true));
+        }
+
+        assertThat(facade.sessions(), empty());
+    }
+
+    @Test
+    void itsImpossibleToCreateSessionsAfterFacadeIsStopped() throws Exception {
+        IgniteSqlImpl facade = newSqlFacade();
+
+        SessionBuilder builder = facade.sessionBuilder();
+
+        Session templateSession = builder.build();
+
+        facade.stop();
+
+        IgniteTestUtils.assertThrows(
+                IgniteException.class,
+                builder::build,
+                "Node is stopping"
+        );
+
+        IgniteTestUtils.assertThrows(
+                IgniteException.class,
+                () -> facade.sessionBuilder().build(),
+                "Node is stopping"
+        );
+
+        IgniteTestUtils.assertThrows(
+                IgniteException.class,
+                facade::createSession,
+                "Node is stopping"
+        );
+
+        IgniteTestUtils.assertThrows(
+                IgniteException.class,
+                () -> templateSession.toBuilder().build(),
+                "Node is stopping"
+        );
+
+        assertThat(facade.sessions(), empty());
+    }
+
+    private static IgniteSqlImpl newSqlFacade() {
+        QueryProcessor queryProcessor = mock(QueryProcessor.class);
+
+        when(queryProcessor.createSession(any()))
+                .thenAnswer(ignored -> new SessionId(UUID.randomUUID()));
+
+        return new IgniteSqlImpl("test", queryProcessor, mock(IgniteTransactions.class));
+    }
+}

Review Comment:
   done



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@ignite.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] IGNITE-20780 Sql. Move session expiration to IgniteSqlImpl [ignite-3]

Posted by "korlov42 (via GitHub)" <gi...@apache.org>.
korlov42 merged PR #2785:
URL: https://github.com/apache/ignite-3/pull/2785


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@ignite.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] IGNITE-20780 Sql. Move session expiration to IgniteSqlImpl [ignite-3]

Posted by "AMashenkov (via GitHub)" <gi...@apache.org>.
AMashenkov commented on code in PR #2785:
URL: https://github.com/apache/ignite-3/pull/2785#discussion_r1381407551


##########
modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/api/IgniteSqlImpl.java:
##########
@@ -29,20 +39,38 @@
 /**
  * Embedded implementation of the Ignite SQL query facade.
  */
-public class IgniteSqlImpl implements IgniteSql {
+public class IgniteSqlImpl implements IgniteSql, IgniteComponent {
+    private static final IgniteLogger LOG = Loggers.forClass(IgniteSqlImpl.class);
+
+    /** Session expiration check period in milliseconds. */
+    private static final long SESSION_EXPIRE_CHECK_PERIOD = TimeUnit.SECONDS.toMillis(1);
+
     private final QueryProcessor qryProc;
 
     private final IgniteTransactions transactions;
 
+    /** Session expiration worker. */
+    private final ScheduledExecutorService executor;
+
+    private final ConcurrentMap<SessionId, SessionImpl> sessions = new ConcurrentHashMap<>();
+
     /**
      * Constructor.
      *
      * @param qryProc Query processor.
      * @param transactions Transactions facade.
      */
-    public IgniteSqlImpl(QueryProcessor qryProc, IgniteTransactions transactions) {
+    public IgniteSqlImpl(
+            String nodeName,

Review Comment:
   Maybe it make sense to have shared scheduler thead and reuse it here.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@ignite.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] IGNITE-20780 Sql. Move session expiration to IgniteSqlImpl [ignite-3]

Posted by "AMashenkov (via GitHub)" <gi...@apache.org>.
AMashenkov commented on code in PR #2785:
URL: https://github.com/apache/ignite-3/pull/2785#discussion_r1381408052


##########
modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/api/IgniteSqlImpl.java:
##########
@@ -29,20 +39,38 @@
 /**
  * Embedded implementation of the Ignite SQL query facade.
  */
-public class IgniteSqlImpl implements IgniteSql {
+public class IgniteSqlImpl implements IgniteSql, IgniteComponent {
+    private static final IgniteLogger LOG = Loggers.forClass(IgniteSqlImpl.class);
+
+    /** Session expiration check period in milliseconds. */
+    private static final long SESSION_EXPIRE_CHECK_PERIOD = TimeUnit.SECONDS.toMillis(1);
+
     private final QueryProcessor qryProc;
 
     private final IgniteTransactions transactions;
 
+    /** Session expiration worker. */
+    private final ScheduledExecutorService executor;
+
+    private final ConcurrentMap<SessionId, SessionImpl> sessions = new ConcurrentHashMap<>();

Review Comment:
   Should we cleanup collection on component stop?
   



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@ignite.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] IGNITE-20780 Sql. Move session expiration to IgniteSqlImpl [ignite-3]

Posted by "AMashenkov (via GitHub)" <gi...@apache.org>.
AMashenkov commented on code in PR #2785:
URL: https://github.com/apache/ignite-3/pull/2785#discussion_r1381408052


##########
modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/api/IgniteSqlImpl.java:
##########
@@ -29,20 +39,38 @@
 /**
  * Embedded implementation of the Ignite SQL query facade.
  */
-public class IgniteSqlImpl implements IgniteSql {
+public class IgniteSqlImpl implements IgniteSql, IgniteComponent {
+    private static final IgniteLogger LOG = Loggers.forClass(IgniteSqlImpl.class);
+
+    /** Session expiration check period in milliseconds. */
+    private static final long SESSION_EXPIRE_CHECK_PERIOD = TimeUnit.SECONDS.toMillis(1);
+
     private final QueryProcessor qryProc;
 
     private final IgniteTransactions transactions;
 
+    /** Session expiration worker. */
+    private final ScheduledExecutorService executor;
+
+    private final ConcurrentMap<SessionId, SessionImpl> sessions = new ConcurrentHashMap<>();

Review Comment:
   Should we cleanup collection on component stop?
   



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@ignite.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] IGNITE-20780 Sql. Move session expiration to IgniteSqlImpl [ignite-3]

Posted by "AMashenkov (via GitHub)" <gi...@apache.org>.
AMashenkov commented on code in PR #2785:
URL: https://github.com/apache/ignite-3/pull/2785#discussion_r1381413138


##########
modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/api/IgniteSqlImpl.java:
##########
@@ -68,4 +96,25 @@ public Statement createStatement(String query) {
     public StatementBuilder statementBuilder() {
         return new StatementBuilderImpl();
     }
+
+    @Override
+    public void start() {
+        executor.scheduleWithFixedDelay(
+                () -> {
+                    for (SessionImpl session : sessions.values()) {
+                        if (session.expired()) {
+                            session.closeAsync();
+                        }
+                    }
+                },
+                SESSION_EXPIRE_CHECK_PERIOD,
+                SESSION_EXPIRE_CHECK_PERIOD,
+                TimeUnit.MILLISECONDS
+        );
+    }
+
+    @Override
+    public void stop() throws Exception {
+        executor.shutdownNow();

Review Comment:
   Should we close sessions on stop?
   ```suggestion
           executor.shutdownNow();
           sessions.values().foreach(SessionImpl::closeAsync);
           sessions.clear();
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@ignite.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] IGNITE-20780 Sql. Move session expiration to IgniteSqlImpl [ignite-3]

Posted by "ygerzhedovich (via GitHub)" <gi...@apache.org>.
ygerzhedovich commented on code in PR #2785:
URL: https://github.com/apache/ignite-3/pull/2785#discussion_r1381886205


##########
modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/api/SessionImplTest.java:
##########
@@ -0,0 +1,220 @@
+/*
+ * 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.sql.api;
+
+import static org.apache.ignite.internal.sql.engine.util.SqlTestUtils.assertThrowsSqlException;
+import static org.apache.ignite.internal.testframework.IgniteTestUtils.await;
+import static org.apache.ignite.lang.ErrorGroups.Sql.SESSION_CLOSED_ERR;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.hamcrest.Matchers.nullValue;
+import static org.hamcrest.Matchers.sameInstance;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.UUID;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+import org.apache.ignite.internal.sql.engine.AsyncSqlCursor;
+import org.apache.ignite.internal.sql.engine.QueryProcessor;
+import org.apache.ignite.internal.sql.engine.SqlQueryType;
+import org.apache.ignite.internal.sql.engine.session.SessionId;
+import org.apache.ignite.internal.testframework.BaseIgniteAbstractTest;
+import org.apache.ignite.internal.util.AsyncCursor.BatchedResult;
+import org.apache.ignite.internal.util.IgniteSpinBusyLock;
+import org.apache.ignite.sql.BatchedArguments;
+import org.apache.ignite.sql.Session.SessionBuilder;
+import org.apache.ignite.sql.async.AsyncResultSet;
+import org.apache.ignite.tx.IgniteTransactions;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.RepeatedTest;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+/**
+ * Tests to verify {@link SessionImpl}.
+ */
+@SuppressWarnings({"resource", "ThrowableNotThrown"})
+@ExtendWith(MockitoExtension.class)
+class SessionImplTest extends BaseIgniteAbstractTest {
+    private final ConcurrentMap<SessionId, SessionImpl> sessions = new ConcurrentHashMap<>();
+
+    private final AtomicLong clock = new AtomicLong();
+
+    @Mock
+    private QueryProcessor queryProcessor;
+
+    @BeforeEach
+    void setUp() {
+        clock.set(1L);
+
+        when(queryProcessor.closeSession(any()))
+                .thenReturn(CompletableFuture.completedFuture(null));
+    }
+
+    @AfterEach
+    void cleanUp() {
+        sessions.values().forEach(SessionImpl::close);
+
+        sessions.clear();
+    }
+
+    @Test
+    void sessionExpiredAfterIdleTimeout() {
+        SessionImpl session = newSession(2);
+        assertThat(session.expired(), is(false));
+
+        //period is small to expire session
+        clock.addAndGet(1);
+        assertThat(session.expired(), is(false));
+
+        //period is enough to session expired
+        clock.addAndGet(2);
+        assertThat(session.expired(), is(true));
+    }
+
+    @RepeatedTest(10)

Review Comment:
   What reason to run it few times? 
   If it really required then let's add some comment about the reason



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@ignite.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] IGNITE-20780 Sql. Move session expiration to IgniteSqlImpl [ignite-3]

Posted by "korlov42 (via GitHub)" <gi...@apache.org>.
korlov42 commented on code in PR #2785:
URL: https://github.com/apache/ignite-3/pull/2785#discussion_r1381901338


##########
modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/api/SessionImplTest.java:
##########
@@ -0,0 +1,220 @@
+/*
+ * 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.sql.api;
+
+import static org.apache.ignite.internal.sql.engine.util.SqlTestUtils.assertThrowsSqlException;
+import static org.apache.ignite.internal.testframework.IgniteTestUtils.await;
+import static org.apache.ignite.lang.ErrorGroups.Sql.SESSION_CLOSED_ERR;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.hamcrest.Matchers.nullValue;
+import static org.hamcrest.Matchers.sameInstance;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.UUID;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+import org.apache.ignite.internal.sql.engine.AsyncSqlCursor;
+import org.apache.ignite.internal.sql.engine.QueryProcessor;
+import org.apache.ignite.internal.sql.engine.SqlQueryType;
+import org.apache.ignite.internal.sql.engine.session.SessionId;
+import org.apache.ignite.internal.testframework.BaseIgniteAbstractTest;
+import org.apache.ignite.internal.util.AsyncCursor.BatchedResult;
+import org.apache.ignite.internal.util.IgniteSpinBusyLock;
+import org.apache.ignite.sql.BatchedArguments;
+import org.apache.ignite.sql.Session.SessionBuilder;
+import org.apache.ignite.sql.async.AsyncResultSet;
+import org.apache.ignite.tx.IgniteTransactions;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.RepeatedTest;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+/**
+ * Tests to verify {@link SessionImpl}.
+ */
+@SuppressWarnings({"resource", "ThrowableNotThrown"})
+@ExtendWith(MockitoExtension.class)
+class SessionImplTest extends BaseIgniteAbstractTest {
+    private final ConcurrentMap<SessionId, SessionImpl> sessions = new ConcurrentHashMap<>();
+
+    private final AtomicLong clock = new AtomicLong();
+
+    @Mock
+    private QueryProcessor queryProcessor;
+
+    @BeforeEach
+    void setUp() {
+        clock.set(1L);
+
+        when(queryProcessor.closeSession(any()))
+                .thenReturn(CompletableFuture.completedFuture(null));
+    }
+
+    @AfterEach
+    void cleanUp() {
+        sessions.values().forEach(SessionImpl::close);
+
+        sessions.clear();
+    }
+
+    @Test
+    void sessionExpiredAfterIdleTimeout() {
+        SessionImpl session = newSession(2);
+        assertThat(session.expired(), is(false));
+
+        //period is small to expire session
+        clock.addAndGet(1);
+        assertThat(session.expired(), is(false));
+
+        //period is enough to session expired
+        clock.addAndGet(2);
+        assertThat(session.expired(), is(true));
+    }
+
+    @RepeatedTest(10)

Review Comment:
   no reason, replaced with regular test



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@ignite.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org