This is an automated email from the ASF dual-hosted git repository.

RaigorJiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shardingsphere.git


The following commit(s) were added to refs/heads/master by this push:
     new d9e2030132f Fix Proxy backend executor lifecycle after repeated 
startup and shutdown (#38680)
d9e2030132f is described below

commit d9e2030132fdcd7b33a4e0cb687ed2ad39e99b3b
Author: Raigor <[email protected]>
AuthorDate: Sat May 9 15:44:11 2026 +0800

    Fix Proxy backend executor lifecycle after repeated startup and shutdown 
(#38680)
    
    * Fix Proxy backend executor lifecycle after repeated startup and shutdown
    
    * Fix test cases
---
 .../backend/context/BackendExecutorContext.java    | 27 +++++++-------
 .../DatabaseProxyConnectorFactoryTest.java         | 18 +++++++--
 .../backend/connector/ProxySQLExecutorTest.java    |  8 ++++
 .../StandardDatabaseProxyConnectorTest.java        |  8 ++++
 .../context/BackendExecutorContextTest.java        | 15 ++++----
 .../handler/distsql/rul/PreviewExecutorTest.java   |  8 ++++
 .../initializer/BootstrapInitializerTest.java      |  2 +-
 ...ySQLMultiStatementsProxyBackendHandlerTest.java | 32 ++++++++++++++--
 .../PostgreSQLBatchedStatementsExecutorTest.java   | 43 ++++++++++++++++++++--
 9 files changed, 129 insertions(+), 32 deletions(-)

diff --git 
a/proxy/backend/core/src/main/java/org/apache/shardingsphere/proxy/backend/context/BackendExecutorContext.java
 
b/proxy/backend/core/src/main/java/org/apache/shardingsphere/proxy/backend/context/BackendExecutorContext.java
index 10578a8c85d..f975423e905 100644
--- 
a/proxy/backend/core/src/main/java/org/apache/shardingsphere/proxy/backend/context/BackendExecutorContext.java
+++ 
b/proxy/backend/core/src/main/java/org/apache/shardingsphere/proxy/backend/context/BackendExecutorContext.java
@@ -45,11 +45,12 @@ public final class BackendExecutorContext {
     
     /**
      * Initialize backend executor context.
+     *
+     * <p>The proxy bootstrap lifecycle uses this method to create a fresh 
backend executor.
+     * It may explicitly restore the context from the closed state for 
repeated proxy lifecycles in the same JVM.</p>
      */
     public synchronized void init() {
-        if (null != executorEngine) {
-            executorEngine.close();
-        }
+        closeExecutorEngine();
         executorEngine = ExecutorEngine.createExecutorEngineWithSize(
                 
ProxyContext.getInstance().getContextManager().getMetaDataContexts().getMetaData().getProps().<Integer>getValue(ConfigurationPropertyKey.KERNEL_EXECUTOR_SIZE));
         lifecycleState = LifecycleState.RUNNING;
@@ -75,22 +76,20 @@ public final class BackendExecutorContext {
     }
     
     /**
-     * Close backend executor context.
+     * Shutdown backend executor context.
+     *
+     * <p>After shutdown, request-side executor lookup is rejected until the 
next explicit lifecycle initialization.</p>
      */
-    public synchronized void close() {
+    public synchronized void shutdown() {
+        closeExecutorEngine();
+        lifecycleState = LifecycleState.CLOSED;
+    }
+    
+    private void closeExecutorEngine() {
         if (null != executorEngine) {
             executorEngine.close();
             executorEngine = null;
         }
-        lifecycleState = LifecycleState.UNINITIALIZED;
-    }
-    
-    /**
-     * Shutdown backend executor context.
-     */
-    public synchronized void shutdown() {
-        close();
-        lifecycleState = LifecycleState.CLOSED;
     }
     
     private enum LifecycleState {
diff --git 
a/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/connector/DatabaseProxyConnectorFactoryTest.java
 
b/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/connector/DatabaseProxyConnectorFactoryTest.java
index 13f2545d422..f9d79eee5ea 100644
--- 
a/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/connector/DatabaseProxyConnectorFactoryTest.java
+++ 
b/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/connector/DatabaseProxyConnectorFactoryTest.java
@@ -33,9 +33,11 @@ import 
org.apache.shardingsphere.infra.session.query.QueryContext;
 import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader;
 import org.apache.shardingsphere.mode.manager.ContextManager;
 import org.apache.shardingsphere.mode.metadata.MetaDataContexts;
+import org.apache.shardingsphere.proxy.backend.context.BackendExecutorContext;
 import org.apache.shardingsphere.proxy.backend.context.ProxyContext;
 import 
org.apache.shardingsphere.test.infra.framework.extension.mock.AutoMockExtension;
 import 
org.apache.shardingsphere.test.infra.framework.extension.mock.StaticMockSettings;
+import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
 import org.junit.jupiter.params.ParameterizedTest;
@@ -62,6 +64,11 @@ class DatabaseProxyConnectorFactoryTest {
     
     private final DatabaseType databaseType = 
TypedSPILoader.getService(DatabaseType.class, "FIXTURE");
     
+    @AfterEach
+    void tearDown() {
+        BackendExecutorContext.getInstance().shutdown();
+    }
+    
     @Test
     void assertNewDatabaseProxyConnectorWithoutParameter() {
         ProxyDatabaseConnectionManager databaseConnectionManager = 
mock(ProxyDatabaseConnectionManager.class, RETURNS_DEEP_STUBS);
@@ -75,7 +82,7 @@ class DatabaseProxyConnectorFactoryTest {
         when(metaData.getDatabase("foo_db")).thenReturn(database);
         QueryContext queryContext = new QueryContext(sqlStatementContext, 
"schemaName", Collections.emptyList(), new HintValueContext(), 
mockConnectionContext(), metaData);
         ContextManager contextManager = mockContextManager(database);
-        
when(ProxyContext.getInstance().getContextManager()).thenReturn(contextManager);
+        initBackendExecutorContext(contextManager);
         DatabaseProxyConnector engine = 
DatabaseProxyConnectorFactory.newInstance(queryContext, 
databaseConnectionManager, false);
         assertThat(engine, isA(DatabaseProxyConnector.class));
     }
@@ -98,7 +105,7 @@ class DatabaseProxyConnectorFactoryTest {
         when(metaData.containsDatabase("foo_db")).thenReturn(true);
         when(metaData.getDatabase("foo_db")).thenReturn(database);
         ContextManager contextManager = mockContextManager(database);
-        
when(ProxyContext.getInstance().getContextManager()).thenReturn(contextManager);
+        initBackendExecutorContext(contextManager);
         assertThat(DatabaseProxyConnectorFactory.newInstance(
                 new QueryContext(sqlStatementContext, "schemaName", 
Collections.emptyList(), new HintValueContext(), mockConnectionContext(), 
metaData), databaseConnectionManager, false),
                 isA(DatabaseProxyConnector.class));
@@ -116,11 +123,16 @@ class DatabaseProxyConnectorFactoryTest {
         ShardingSphereMetaData metaData = new 
ShardingSphereMetaData(Collections.singleton(database), mock(), mock(), new 
ConfigurationProperties(new Properties()));
         QueryContext queryContext = new QueryContext(sqlStatementContext, 
"schemaName", parameters, new HintValueContext(), mockConnectionContext(), 
metaData);
         ContextManager contextManager = mockContextManager(database);
-        
when(ProxyContext.getInstance().getContextManager()).thenReturn(contextManager);
+        initBackendExecutorContext(contextManager);
         DatabaseProxyConnector connector = 
DatabaseProxyConnectorFactory.newInstance(queryContext, 
databaseConnectionManager, preferPreparedStatement);
         assertThat((JDBCDriverType) 
Plugins.getMemberAccessor().get(connector.getClass().getDeclaredField("driverType"),
 connector), is(expected));
     }
     
+    private void initBackendExecutorContext(final ContextManager 
contextManager) {
+        
when(ProxyContext.getInstance().getContextManager()).thenReturn(contextManager);
+        BackendExecutorContext.getInstance().init();
+    }
+    
     private ContextManager mockContextManager(final ShardingSphereDatabase 
database) {
         ShardingSphereMetaData metaData = new ShardingSphereMetaData(
                 Collections.singleton(database), mock(ResourceMetaData.class), 
mock(RuleMetaData.class), new ConfigurationProperties(new Properties()));
diff --git 
a/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/connector/ProxySQLExecutorTest.java
 
b/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/connector/ProxySQLExecutorTest.java
index fda908a4efa..898a4a652ea 100644
--- 
a/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/connector/ProxySQLExecutorTest.java
+++ 
b/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/connector/ProxySQLExecutorTest.java
@@ -51,6 +51,7 @@ import org.apache.shardingsphere.mode.manager.ContextManager;
 import 
org.apache.shardingsphere.proxy.backend.connector.jdbc.executor.ProxyJDBCExecutor;
 import 
org.apache.shardingsphere.proxy.backend.connector.jdbc.statement.JDBCBackendStatement;
 import 
org.apache.shardingsphere.proxy.backend.connector.sane.DialectSaneQueryResultEngine;
+import org.apache.shardingsphere.proxy.backend.context.BackendExecutorContext;
 import org.apache.shardingsphere.proxy.backend.context.ProxyContext;
 import org.apache.shardingsphere.proxy.backend.session.ConnectionSession;
 import 
org.apache.shardingsphere.sql.parser.statement.core.enums.TransactionIsolationLevel;
@@ -71,6 +72,7 @@ import 
org.apache.shardingsphere.test.infra.framework.extension.mock.StaticMockS
 import org.apache.shardingsphere.transaction.api.TransactionType;
 import org.apache.shardingsphere.transaction.rule.TransactionRule;
 import org.apache.shardingsphere.transaction.spi.TransactionHook;
+import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.TestInstance;
@@ -185,6 +187,12 @@ class ProxySQLExecutorTest {
         when(contextManager.getDatabase("foo_db")).thenReturn(database);
         when(contextManager.getDatabaseType()).thenReturn(fixtureDatabaseType);
         
when(ProxyContext.getInstance().getContextManager()).thenReturn(contextManager);
+        BackendExecutorContext.getInstance().init();
+    }
+    
+    @AfterEach
+    void tearDown() {
+        BackendExecutorContext.getInstance().shutdown();
     }
     
     @Test
diff --git 
a/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/connector/StandardDatabaseProxyConnectorTest.java
 
b/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/connector/StandardDatabaseProxyConnectorTest.java
index c985fdd6186..4a859280afb 100644
--- 
a/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/connector/StandardDatabaseProxyConnectorTest.java
+++ 
b/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/connector/StandardDatabaseProxyConnectorTest.java
@@ -73,6 +73,7 @@ import org.apache.shardingsphere.parser.rule.SQLParserRule;
 import 
org.apache.shardingsphere.proxy.backend.connector.jdbc.fixture.QueryHeaderBuilderFixture;
 import 
org.apache.shardingsphere.proxy.backend.connector.jdbc.statement.JDBCBackendStatement;
 import 
org.apache.shardingsphere.proxy.backend.connector.jdbc.transaction.ProxyBackendTransactionManager;
+import org.apache.shardingsphere.proxy.backend.context.BackendExecutorContext;
 import org.apache.shardingsphere.proxy.backend.context.ProxyContext;
 import org.apache.shardingsphere.proxy.backend.response.header.ResponseHeader;
 import 
org.apache.shardingsphere.proxy.backend.response.header.query.QueryHeaderBuilder;
@@ -92,6 +93,7 @@ import 
org.apache.shardingsphere.sqlfederation.engine.SQLFederationEngine;
 import org.apache.shardingsphere.sqlfederation.rule.SQLFederationRule;
 import 
org.apache.shardingsphere.test.infra.framework.extension.mock.AutoMockExtension;
 import 
org.apache.shardingsphere.test.infra.framework.extension.mock.StaticMockSettings;
+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;
@@ -160,6 +162,12 @@ class StandardDatabaseProxyConnectorTest {
         
when(databaseConnectionManager.getConnectionSession().getUsedDatabaseName()).thenReturn("foo_db");
         ContextManager contextManager = mockContextManager();
         
when(ProxyContext.getInstance().getContextManager()).thenReturn(contextManager);
+        BackendExecutorContext.getInstance().init();
+    }
+    
+    @AfterEach
+    void tearDown() {
+        BackendExecutorContext.getInstance().shutdown();
     }
     
     private ContextManager mockContextManager() {
diff --git 
a/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/context/BackendExecutorContextTest.java
 
b/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/context/BackendExecutorContextTest.java
index 614b482e181..699bbdcf0d3 100644
--- 
a/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/context/BackendExecutorContextTest.java
+++ 
b/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/context/BackendExecutorContextTest.java
@@ -48,7 +48,7 @@ class BackendExecutorContextTest {
     
     @AfterEach
     void tearDown() {
-        BackendExecutorContext.getInstance().close();
+        BackendExecutorContext.getInstance().shutdown();
     }
     
     @Test
@@ -66,28 +66,29 @@ class BackendExecutorContextTest {
         
when(ProxyContext.getInstance().getContextManager()).thenReturn(contextManager);
         BackendExecutorContext.getInstance().init();
         ExecutorEngine actual = 
BackendExecutorContext.getInstance().getExecutorEngine();
-        BackendExecutorContext.getInstance().close();
         BackendExecutorContext.getInstance().init();
         assertThat(BackendExecutorContext.getInstance().getExecutorEngine(), 
is(not(actual)));
     }
     
     @Test
-    void assertClose() {
+    void assertShutdown() {
         ContextManager contextManager = mockContextManager();
         
when(ProxyContext.getInstance().getContextManager()).thenReturn(contextManager);
         BackendExecutorContext.getInstance().init();
-        BackendExecutorContext.getInstance().close();
-        ExecutorEngine actual = 
BackendExecutorContext.getInstance().getExecutorEngine();
-        assertThat(actual, 
is(BackendExecutorContext.getInstance().getExecutorEngine()));
+        BackendExecutorContext.getInstance().shutdown();
+        assertThrows(IllegalStateException.class, () -> 
BackendExecutorContext.getInstance().getExecutorEngine());
     }
     
     @Test
-    void assertShutdown() {
+    void assertInitAfterShutdown() {
         ContextManager contextManager = mockContextManager();
         
when(ProxyContext.getInstance().getContextManager()).thenReturn(contextManager);
         BackendExecutorContext.getInstance().init();
+        final ExecutorEngine actual = 
BackendExecutorContext.getInstance().getExecutorEngine();
         BackendExecutorContext.getInstance().shutdown();
         assertThrows(IllegalStateException.class, () -> 
BackendExecutorContext.getInstance().getExecutorEngine());
+        BackendExecutorContext.getInstance().init();
+        assertThat(BackendExecutorContext.getInstance().getExecutorEngine(), 
is(not(actual)));
     }
     
     private ContextManager mockContextManager() {
diff --git 
a/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/handler/distsql/rul/PreviewExecutorTest.java
 
b/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/handler/distsql/rul/PreviewExecutorTest.java
index e30a6cb4d72..bd90c43d6d7 100644
--- 
a/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/handler/distsql/rul/PreviewExecutorTest.java
+++ 
b/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/handler/distsql/rul/PreviewExecutorTest.java
@@ -58,6 +58,7 @@ import org.apache.shardingsphere.mode.manager.ContextManager;
 import org.apache.shardingsphere.parser.rule.SQLParserRule;
 import 
org.apache.shardingsphere.parser.rule.builder.DefaultSQLParserRuleConfigurationBuilder;
 import 
org.apache.shardingsphere.proxy.backend.connector.ProxyDatabaseConnectionManager;
+import org.apache.shardingsphere.proxy.backend.context.BackendExecutorContext;
 import org.apache.shardingsphere.proxy.backend.context.ProxyContext;
 import 
org.apache.shardingsphere.sql.parser.statement.core.segment.ddl.cursor.CursorNameSegment;
 import 
org.apache.shardingsphere.sql.parser.statement.core.statement.SQLStatement;
@@ -66,6 +67,7 @@ import 
org.apache.shardingsphere.sql.parser.statement.core.statement.attribute.t
 import 
org.apache.shardingsphere.sql.parser.statement.core.value.identifier.IdentifierValue;
 import org.apache.shardingsphere.sqlfederation.context.SQLFederationContext;
 import org.apache.shardingsphere.sqlfederation.engine.SQLFederationEngine;
+import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.mockito.MockedConstruction;
@@ -108,6 +110,12 @@ class PreviewExecutorTest {
     void setUp() {
         contextManager = mockContextManager();
         ProxyContext.init(contextManager);
+        BackendExecutorContext.getInstance().init();
+    }
+    
+    @AfterEach
+    void tearDown() {
+        BackendExecutorContext.getInstance().shutdown();
     }
     
     private ContextManager mockContextManager() {
diff --git 
a/proxy/bootstrap/src/test/java/org/apache/shardingsphere/proxy/initializer/BootstrapInitializerTest.java
 
b/proxy/bootstrap/src/test/java/org/apache/shardingsphere/proxy/initializer/BootstrapInitializerTest.java
index cb0435a514c..317cdfb90ac 100644
--- 
a/proxy/bootstrap/src/test/java/org/apache/shardingsphere/proxy/initializer/BootstrapInitializerTest.java
+++ 
b/proxy/bootstrap/src/test/java/org/apache/shardingsphere/proxy/initializer/BootstrapInitializerTest.java
@@ -133,7 +133,7 @@ class BootstrapInitializerTest {
     }
     
     @Test
-    void assertInitWithRepeatedLifecycle() throws SQLException, 
ReflectiveOperationException {
+    void assertInitWithRepeatedBootstrap() throws SQLException, 
ReflectiveOperationException {
         InstanceMetaDataBuilder instanceMetaDataBuilder = 
mock(InstanceMetaDataBuilder.class);
         InstanceMetaData instanceMetaData = mock(InstanceMetaData.class);
         registerSingletonService(InstanceMetaDataBuilder.class, 
instanceMetaDataBuilder);
diff --git 
a/proxy/frontend/dialect/mysql/src/test/java/org/apache/shardingsphere/proxy/frontend/mysql/command/query/text/query/MySQLMultiStatementsProxyBackendHandlerTest.java
 
b/proxy/frontend/dialect/mysql/src/test/java/org/apache/shardingsphere/proxy/frontend/mysql/command/query/text/query/MySQLMultiStatementsProxyBackendHandlerTest.java
index b83091fffdb..368f6adb579 100644
--- 
a/proxy/frontend/dialect/mysql/src/test/java/org/apache/shardingsphere/proxy/frontend/mysql/command/query/text/query/MySQLMultiStatementsProxyBackendHandlerTest.java
+++ 
b/proxy/frontend/dialect/mysql/src/test/java/org/apache/shardingsphere/proxy/frontend/mysql/command/query/text/query/MySQLMultiStatementsProxyBackendHandlerTest.java
@@ -20,6 +20,7 @@ package 
org.apache.shardingsphere.proxy.frontend.mysql.command.query.text.query;
 import org.apache.shardingsphere.database.connector.core.type.DatabaseType;
 import org.apache.shardingsphere.infra.config.props.ConfigurationProperties;
 import org.apache.shardingsphere.infra.config.props.ConfigurationPropertyKey;
+import org.apache.shardingsphere.infra.executor.kernel.ExecutorEngine;
 import 
org.apache.shardingsphere.infra.executor.sql.execute.engine.ConnectionMode;
 import 
org.apache.shardingsphere.infra.executor.sql.prepare.driver.jdbc.StatementOption;
 import 
org.apache.shardingsphere.infra.metadata.database.ShardingSphereDatabase;
@@ -35,6 +36,7 @@ import org.apache.shardingsphere.parser.rule.SQLParserRule;
 import 
org.apache.shardingsphere.parser.rule.builder.DefaultSQLParserRuleConfigurationBuilder;
 import 
org.apache.shardingsphere.proxy.backend.connector.ProxyDatabaseConnectionManager;
 import 
org.apache.shardingsphere.proxy.backend.connector.jdbc.statement.JDBCBackendStatement;
+import org.apache.shardingsphere.proxy.backend.context.BackendExecutorContext;
 import org.apache.shardingsphere.proxy.backend.context.ProxyContext;
 import org.apache.shardingsphere.proxy.backend.response.header.ResponseHeader;
 import 
org.apache.shardingsphere.proxy.backend.response.header.update.MultiStatementsUpdateResponseHeader;
@@ -63,6 +65,8 @@ import java.util.Properties;
 import static org.hamcrest.Matchers.is;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.isA;
+import static org.hamcrest.Matchers.not;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
@@ -82,7 +86,7 @@ class MySQLMultiStatementsProxyBackendHandlerTest {
         ConnectionSession connectionSession = mockConnectionSession();
         UpdateStatement expectedStatement = mock(UpdateStatement.class);
         ContextManager contextManager = mockContextManager();
-        
when(ProxyContext.getInstance().getContextManager()).thenReturn(contextManager);
+        initBackendExecutorContext(contextManager);
         ResponseHeader actual = new 
MySQLMultiStatementsProxyBackendHandler(connectionSession, expectedStatement, 
sql).execute();
         assertThat(actual, isA(MultiStatementsUpdateResponseHeader.class));
         MultiStatementsUpdateResponseHeader actualHeader = 
(MultiStatementsUpdateResponseHeader) actual;
@@ -102,13 +106,30 @@ class MySQLMultiStatementsProxyBackendHandlerTest {
         assertThat(responseHeader.getSqlStatement(), is(expectedStatement));
     }
     
+    @Test
+    void assertExecuteAfterRepeatedProxyLifecycle() throws SQLException {
+        final String sql = "UPDATE t SET v=v+1 WHERE id=1;UPDATE t SET v=v+1 
WHERE id=2;UPDATE t SET v=v+1 WHERE id=3";
+        final ConnectionSession connectionSession = mockConnectionSession();
+        final UpdateStatement expectedStatement = mock(UpdateStatement.class);
+        ContextManager contextManager = mockContextManager();
+        initBackendExecutorContext(contextManager);
+        final ExecutorEngine previousExecutorEngine = 
BackendExecutorContext.getInstance().getExecutorEngine();
+        BackendExecutorContext.getInstance().shutdown();
+        assertThrows(IllegalStateException.class, () -> 
BackendExecutorContext.getInstance().getExecutorEngine());
+        BackendExecutorContext.getInstance().init();
+        assertThat(BackendExecutorContext.getInstance().getExecutorEngine(), 
is(not(previousExecutorEngine)));
+        ResponseHeader actual = new 
MySQLMultiStatementsProxyBackendHandler(connectionSession, expectedStatement, 
sql).execute();
+        assertThat(actual, isA(MultiStatementsUpdateResponseHeader.class));
+        assertThat(((MultiStatementsUpdateResponseHeader) 
actual).getUpdateResponseHeaders().size(), is(3));
+    }
+    
     @Test
     void assertExecuteWithSpecifiedDatabaseName() throws SQLException {
         String sql = "UPDATE foo_db.t SET v=v+1 WHERE id=1;UPDATE foo_db.t SET 
v=v+1 WHERE id=2;UPDATE foo_db.t SET v=v+1 WHERE id=3";
         ConnectionSession connectionSession = mockConnectionSession();
         UpdateStatement expectedStatement = mock(UpdateStatement.class);
         ContextManager contextManager = mockContextManager();
-        
when(ProxyContext.getInstance().getContextManager()).thenReturn(contextManager);
+        initBackendExecutorContext(contextManager);
         ResponseHeader actual = new 
MySQLMultiStatementsProxyBackendHandler(connectionSession, expectedStatement, 
sql).execute();
         assertThat(actual, isA(MultiStatementsUpdateResponseHeader.class));
         MultiStatementsUpdateResponseHeader actualHeader = 
(MultiStatementsUpdateResponseHeader) actual;
@@ -135,7 +156,7 @@ class MySQLMultiStatementsProxyBackendHandlerTest {
         ConnectionSession connectionSession = mockConnectionSession();
         UpdateStatement expectedStatement = mock(UpdateStatement.class);
         ContextManager contextManager = mockContextManager();
-        
when(ProxyContext.getInstance().getContextManager()).thenReturn(contextManager);
+        initBackendExecutorContext(contextManager);
         ResponseHeader actual = new 
MySQLMultiStatementsProxyBackendHandler(connectionSession, expectedStatement, 
sql).execute();
         assertThat(actual, isA(MultiStatementsUpdateResponseHeader.class));
         MultiStatementsUpdateResponseHeader actualHeader = 
(MultiStatementsUpdateResponseHeader) actual;
@@ -155,6 +176,11 @@ class MySQLMultiStatementsProxyBackendHandlerTest {
         assertThat(responseHeader.getSqlStatement(), is(expectedStatement));
     }
     
+    private void initBackendExecutorContext(final ContextManager 
contextManager) {
+        
when(ProxyContext.getInstance().getContextManager()).thenReturn(contextManager);
+        BackendExecutorContext.getInstance().init();
+    }
+    
     private ConnectionSession mockConnectionSession() throws SQLException {
         ConnectionSession result = mock(ConnectionSession.class, 
RETURNS_DEEP_STUBS);
         when(result.getCurrentDatabaseName()).thenReturn("foo_db");
diff --git 
a/proxy/frontend/dialect/postgresql/src/test/java/org/apache/shardingsphere/proxy/frontend/postgresql/command/query/extended/PostgreSQLBatchedStatementsExecutorTest.java
 
b/proxy/frontend/dialect/postgresql/src/test/java/org/apache/shardingsphere/proxy/frontend/postgresql/command/query/extended/PostgreSQLBatchedStatementsExecutorTest.java
index bf8ebb898b8..480754a1f36 100644
--- 
a/proxy/frontend/dialect/postgresql/src/test/java/org/apache/shardingsphere/proxy/frontend/postgresql/command/query/extended/PostgreSQLBatchedStatementsExecutorTest.java
+++ 
b/proxy/frontend/dialect/postgresql/src/test/java/org/apache/shardingsphere/proxy/frontend/postgresql/command/query/extended/PostgreSQLBatchedStatementsExecutorTest.java
@@ -25,6 +25,7 @@ import 
org.apache.shardingsphere.infra.binder.context.statement.type.dml.InsertS
 import 
org.apache.shardingsphere.infra.binder.context.statement.type.dml.UpdateStatementContext;
 import org.apache.shardingsphere.infra.config.props.ConfigurationProperties;
 import org.apache.shardingsphere.infra.config.props.ConfigurationPropertyKey;
+import org.apache.shardingsphere.infra.executor.kernel.ExecutorEngine;
 import org.apache.shardingsphere.infra.executor.sql.context.ExecutionContext;
 import org.apache.shardingsphere.infra.executor.sql.context.ExecutionUnit;
 import 
org.apache.shardingsphere.infra.executor.sql.execute.engine.ConnectionMode;
@@ -41,6 +42,7 @@ import 
org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader;
 import org.apache.shardingsphere.mode.manager.ContextManager;
 import 
org.apache.shardingsphere.proxy.backend.connector.ProxyDatabaseConnectionManager;
 import 
org.apache.shardingsphere.proxy.backend.connector.jdbc.statement.JDBCBackendStatement;
+import org.apache.shardingsphere.proxy.backend.context.BackendExecutorContext;
 import org.apache.shardingsphere.proxy.backend.context.ProxyContext;
 import org.apache.shardingsphere.proxy.backend.session.ConnectionSession;
 import 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.assignment.SetAssignmentSegment;
@@ -73,7 +75,9 @@ import java.util.Properties;
 
 import static org.hamcrest.Matchers.is;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.not;
 import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -115,7 +119,7 @@ class PostgreSQLBatchedStatementsExecutorTest {
                 new HintValueContext(), 
Arrays.asList(PostgreSQLBinaryColumnType.INT4, 
PostgreSQLBinaryColumnType.VARCHAR), Arrays.asList(0, 1));
         List<List<Object>> parameterSets = Arrays.asList(Arrays.asList(1, new 
PostgreSQLTypeUnspecifiedSQLParameter("foo")),
                 Arrays.asList(2, new 
PostgreSQLTypeUnspecifiedSQLParameter("bar")), Arrays.asList(3, new 
PostgreSQLTypeUnspecifiedSQLParameter("baz")));
-        
when(ProxyContext.getInstance().getContextManager()).thenReturn(contextManager);
+        initBackendExecutorContext(contextManager);
         PostgreSQLBatchedStatementsExecutor actual = new 
PostgreSQLBatchedStatementsExecutor(connectionSession, 
postgresqlPreparedStatement, parameterSets);
         prepareExecutionUnitParameters(actual, parameterSets);
         int actualUpdated = actual.executeBatch();
@@ -128,6 +132,32 @@ class PostgreSQLBatchedStatementsExecutorTest {
         }
     }
     
+    @Test
+    void assertExecuteBatchAfterRepeatedProxyLifecycle() throws SQLException {
+        Connection connection = mock(Connection.class, RETURNS_DEEP_STUBS);
+        
when(connection.getMetaData().getURL()).thenReturn("jdbc:postgresql://127.0.0.1/db");
+        when(databaseConnectionManager.getConnections(any(), 
nullable(String.class), anyInt(), anyInt(), 
any(ConnectionMode.class))).thenReturn(Collections.singletonList(connection));
+        PreparedStatement preparedStatement = mock(PreparedStatement.class);
+        when(preparedStatement.getConnection()).thenReturn(connection);
+        when(preparedStatement.executeBatch()).thenReturn(new int[]{1, 1, 1});
+        when(backendStatement.createStorageResource(any(ExecutionUnit.class), 
eq(connection), anyInt(), any(ConnectionMode.class), 
any(StatementOption.class), nullable(DatabaseType.class)))
+                .thenReturn(preparedStatement);
+        ContextManager contextManager = mockContextManager(databaseType);
+        initBackendExecutorContext(contextManager);
+        final ExecutorEngine previousExecutorEngine = 
BackendExecutorContext.getInstance().getExecutorEngine();
+        BackendExecutorContext.getInstance().shutdown();
+        assertThrows(IllegalStateException.class, () -> 
BackendExecutorContext.getInstance().getExecutorEngine());
+        BackendExecutorContext.getInstance().init();
+        assertThat(BackendExecutorContext.getInstance().getExecutorEngine(), 
is(not(previousExecutorEngine)));
+        PostgreSQLServerPreparedStatement postgresqlPreparedStatement = new 
PostgreSQLServerPreparedStatement("INSERT INTO t (id, col) VALUES (?, ?)", 
mockInsertStatementContext(),
+                new HintValueContext(), 
Arrays.asList(PostgreSQLBinaryColumnType.INT4, 
PostgreSQLBinaryColumnType.VARCHAR), Arrays.asList(0, 1));
+        List<List<Object>> parameterSets = Arrays.asList(Arrays.asList(1, new 
PostgreSQLTypeUnspecifiedSQLParameter("foo")),
+                Arrays.asList(2, new 
PostgreSQLTypeUnspecifiedSQLParameter("bar")), Arrays.asList(3, new 
PostgreSQLTypeUnspecifiedSQLParameter("baz")));
+        PostgreSQLBatchedStatementsExecutor actual = new 
PostgreSQLBatchedStatementsExecutor(mockConnectionSession(), 
postgresqlPreparedStatement, parameterSets);
+        prepareExecutionUnitParameters(actual, parameterSets);
+        assertThat(actual.executeBatch(), is(3));
+    }
+    
     @Test
     void assertExecuteBatchWhenExecuteBatchThrowsSQLException() throws 
SQLException {
         Connection connection = mock(Connection.class, RETURNS_DEEP_STUBS);
@@ -143,7 +173,7 @@ class PostgreSQLBatchedStatementsExecutorTest {
         PostgreSQLServerPreparedStatement postgresqlPreparedStatement = new 
PostgreSQLServerPreparedStatement("UPDATE t SET col = ? WHERE id = ?", 
mockUpdateStatementContext(),
                 new HintValueContext(), 
Arrays.asList(PostgreSQLBinaryColumnType.INT4, 
PostgreSQLBinaryColumnType.VARCHAR), Arrays.asList(0, 1));
         List<List<Object>> parameterSets = 
Collections.singletonList(Arrays.asList(10, "foo"));
-        
when(ProxyContext.getInstance().getContextManager()).thenReturn(contextManager);
+        initBackendExecutorContext(contextManager);
         PostgreSQLBatchedStatementsExecutor actual = new 
PostgreSQLBatchedStatementsExecutor(connectionSession, 
postgresqlPreparedStatement, parameterSets);
         prepareExecutionUnitParameters(actual, parameterSets);
         try {
@@ -165,7 +195,7 @@ class PostgreSQLBatchedStatementsExecutorTest {
     @Test
     void assertCreateExecutorWithoutParameterSets() throws 
ReflectiveOperationException {
         ContextManager contextManager = mockContextManager(databaseType);
-        
when(ProxyContext.getInstance().getContextManager()).thenReturn(contextManager);
+        initBackendExecutorContext(contextManager);
         ConnectionSession connectionSession = mockConnectionSession();
         PostgreSQLServerPreparedStatement postgresqlPreparedStatement = new 
PostgreSQLServerPreparedStatement("INSERT INTO t (id, col) VALUES (?, ?)", 
mockInsertStatementContext(),
                 new HintValueContext(), 
Arrays.asList(PostgreSQLBinaryColumnType.INT4, 
PostgreSQLBinaryColumnType.VARCHAR), Arrays.asList(0, 1));
@@ -182,7 +212,7 @@ class PostgreSQLBatchedStatementsExecutorTest {
     @Test
     void assertPrepareForRestOfParametersWithoutParameterAware() throws 
ReflectiveOperationException {
         ContextManager contextManager = mockContextManager(databaseType);
-        
when(ProxyContext.getInstance().getContextManager()).thenReturn(contextManager);
+        initBackendExecutorContext(contextManager);
         ConnectionSession connectionSession = mockConnectionSession();
         PostgreSQLServerPreparedStatement postgresqlPreparedStatement = new 
PostgreSQLServerPreparedStatement("UPDATE t SET col = ? WHERE id = ?", 
mockUpdateStatementContext(),
                 new HintValueContext(), 
Arrays.asList(PostgreSQLBinaryColumnType.INT4, 
PostgreSQLBinaryColumnType.VARCHAR), Arrays.asList(0, 1));
@@ -194,6 +224,11 @@ class PostgreSQLBatchedStatementsExecutorTest {
         assertThat(actualParamGroups, is(2));
     }
     
+    private void initBackendExecutorContext(final ContextManager 
contextManager) {
+        
when(ProxyContext.getInstance().getContextManager()).thenReturn(contextManager);
+        BackendExecutorContext.getInstance().init();
+    }
+    
     private InsertStatementContext mockInsertStatementContext() {
         InsertStatement insertStatement = 
InsertStatement.builder().databaseType(databaseType).table(new 
SimpleTableSegment(new TableNameSegment(0, 0, new 
IdentifierValue("t")))).build();
         InsertStatementContext result = mock(InsertStatementContext.class);

Reply via email to