This is an automated email from the ASF dual-hosted git repository.
ppa pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git
The following commit(s) were added to refs/heads/main by this push:
new 0202698b703 IGNITE-27323 Add suggestion about DDL queries batching
(#7246)
0202698b703 is described below
commit 0202698b703224d4504aa45836ca6b3c9691052d
Author: Pavel Pereslegin <[email protected]>
AuthorDate: Tue Dec 30 13:14:37 2025 +0300
IGNITE-27323 Add suggestion about DDL queries batching (#7246)
---
.../repl/executor/ItIgnitePicocliCommandsTest.java | 3 +-
.../apache/ignite/client/handler/TestServer.java | 3 +-
.../ignite/client/handler/ClientHandlerModule.java | 13 +-
.../handler/ClientInboundMessageHandler.java | 17 ++-
.../client/handler/DdlBatchingSuggester.java | 72 ++++++++++
.../requests/sql/ClientSqlExecuteRequest.java | 9 +-
.../client/handler/DdlBatchingSuggesterTest.java | 62 ++++++++
.../ignite/client/TestClientHandlerModule.java | 3 +-
.../java/org/apache/ignite/client/TestServer.java | 3 +-
...SequentialDdlExecutionConfigurationSchema.java} | 29 ++--
...stionsClusterExtensionConfigurationSchema.java} | 26 ++--
.../SuggestionsConfigurationSchema.java} | 29 ++--
...SuggestionsDistributedConfigurationModule.java} | 31 ++--
.../client/ItClientDataConsistencyTest.java | 6 +
.../app/client/ItThinClientAuthenticationTest.java | 10 +-
.../client/ItThinClientDdlQueriesTrackerTest.java | 156 +++++++++++++++++++++
.../org/apache/ignite/internal/app/IgniteImpl.java | 8 +-
.../configuration/ignite-snapshot.bin | Bin 5679 -> 5653 bytes
18 files changed, 391 insertions(+), 89 deletions(-)
diff --git
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/core/repl/executor/ItIgnitePicocliCommandsTest.java
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/core/repl/executor/ItIgnitePicocliCommandsTest.java
index 3ce9526dfbd..e8dec2709a4 100644
---
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/core/repl/executor/ItIgnitePicocliCommandsTest.java
+++
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/core/repl/executor/ItIgnitePicocliCommandsTest.java
@@ -75,7 +75,8 @@ public class ItIgnitePicocliCommandsTest extends
CliIntegrationTest {
"ignite.security",
"ignite.sql",
"ignite.transaction",
- "ignite.system"
+ "ignite.system",
+ "ignite.suggestions"
};
private static final String[] LOCAL_CONFIGURATION_KEYS = {
diff --git
a/modules/client-handler/src/integrationTest/java/org/apache/ignite/client/handler/TestServer.java
b/modules/client-handler/src/integrationTest/java/org/apache/ignite/client/handler/TestServer.java
index fd335df4b77..40982108dba 100644
---
a/modules/client-handler/src/integrationTest/java/org/apache/ignite/client/handler/TestServer.java
+++
b/modules/client-handler/src/integrationTest/java/org/apache/ignite/client/handler/TestServer.java
@@ -142,7 +142,8 @@ public class TestServer {
clientConnectorConfiguration,
new TestLowWatermark(),
new SystemPropertiesNodeProperties(),
- Runnable::run
+ Runnable::run,
+ () -> true
);
module.startAsync(componentContext).join();
diff --git
a/modules/client-handler/src/main/java/org/apache/ignite/client/handler/ClientHandlerModule.java
b/modules/client-handler/src/main/java/org/apache/ignite/client/handler/ClientHandlerModule.java
index e77817bd3f0..6ed8229cf1c 100644
---
a/modules/client-handler/src/main/java/org/apache/ignite/client/handler/ClientHandlerModule.java
+++
b/modules/client-handler/src/main/java/org/apache/ignite/client/handler/ClientHandlerModule.java
@@ -152,6 +152,8 @@ public class ClientHandlerModule implements
IgniteComponent, PlatformComputeTran
private final ClientConnectorConfiguration clientConnectorConfiguration;
+ private final Supplier<Boolean> ddlBatchingSuggestionEnabled;
+
private final Executor partitionOperationsExecutor;
private final ConcurrentHashMap<String,
CompletableFuture<PlatformComputeConnection>> computeExecutors = new
ConcurrentHashMap<>();
@@ -176,6 +178,7 @@ public class ClientHandlerModule implements
IgniteComponent, PlatformComputeTran
* @param clientConnectorConfiguration Configuration of the connector.
* @param lowWatermark Low watermark.
* @param partitionOperationsExecutor Executor for a partition operation.
+ * @param ddlBatchingSuggestionEnabled Boolean supplier indicates whether
the suggestion related DDL batching is enabled.
*/
public ClientHandlerModule(
QueryProcessor queryProcessor,
@@ -195,7 +198,8 @@ public class ClientHandlerModule implements
IgniteComponent, PlatformComputeTran
ClientConnectorConfiguration clientConnectorConfiguration,
LowWatermark lowWatermark,
NodeProperties nodeProperties,
- Executor partitionOperationsExecutor
+ Executor partitionOperationsExecutor,
+ Supplier<Boolean> ddlBatchingSuggestionEnabled
) {
assert igniteTables != null;
assert queryProcessor != null;
@@ -212,6 +216,7 @@ public class ClientHandlerModule implements
IgniteComponent, PlatformComputeTran
assert catalogService != null;
assert placementDriver != null;
assert clientConnectorConfiguration != null;
+ assert ddlBatchingSuggestionEnabled != null;
assert lowWatermark != null;
assert nodeProperties != null;
assert partitionOperationsExecutor != null;
@@ -232,6 +237,7 @@ public class ClientHandlerModule implements
IgniteComponent, PlatformComputeTran
this.primaryReplicaTracker = new
ClientPrimaryReplicaTracker(placementDriver, catalogService, clockService,
schemaSyncService,
lowWatermark, nodeProperties);
this.clientConnectorConfiguration = clientConnectorConfiguration;
+ this.ddlBatchingSuggestionEnabled = ddlBatchingSuggestionEnabled;
this.partitionOperationsExecutor = partitionOperationsExecutor;
}
@@ -455,7 +461,10 @@ public class ClientHandlerModule implements
IgniteComponent, PlatformComputeTran
SUPPORTED_FEATURES,
Map.of(),
computeExecutors::remove,
- handshakeEventLoopSwitcher
+ handshakeEventLoopSwitcher,
+ ddlBatchingSuggestionEnabled.get()
+ ? new DdlBatchingSuggester()
+ : ignore -> {}
);
}
diff --git
a/modules/client-handler/src/main/java/org/apache/ignite/client/handler/ClientInboundMessageHandler.java
b/modules/client-handler/src/main/java/org/apache/ignite/client/handler/ClientInboundMessageHandler.java
index f2b2d7e243e..19e3f0c464c 100644
---
a/modules/client-handler/src/main/java/org/apache/ignite/client/handler/ClientInboundMessageHandler.java
+++
b/modules/client-handler/src/main/java/org/apache/ignite/client/handler/ClientInboundMessageHandler.java
@@ -163,6 +163,7 @@ import
org.apache.ignite.internal.security.authentication.event.AuthenticationEv
import
org.apache.ignite.internal.security.authentication.event.AuthenticationProviderEventParameters;
import
org.apache.ignite.internal.security.authentication.event.UserEventParameters;
import org.apache.ignite.internal.sql.engine.QueryProcessor;
+import org.apache.ignite.internal.sql.engine.SqlQueryType;
import org.apache.ignite.internal.table.IgniteTablesInternal;
import org.apache.ignite.internal.table.distributed.schema.SchemaVersions;
import org.apache.ignite.internal.table.distributed.schema.SchemaVersionsImpl;
@@ -210,6 +211,9 @@ public class ClientInboundMessageHandler
/** Connection resources. */
private final ClientResourceRegistry resources = new
ClientResourceRegistry();
+ /** Tracks the number of sequential DDL queries executed and prints
suggestion to use batching. */
+ private final Consumer<SqlQueryType> queryTypeListener;
+
/** Configuration. */
private final ClientConnectorView configuration;
@@ -292,6 +296,7 @@ public class ClientInboundMessageHandler
* @param partitionOperationsExecutor Partition operations executor.
* @param features Features.
* @param extensions Extensions.
+ * @param queryTypeListener Tracks the number of sequential DDL queries
executed and prints suggestion to use batching.
*/
public ClientInboundMessageHandler(
IgniteTablesInternal igniteTables,
@@ -312,7 +317,8 @@ public class ClientInboundMessageHandler
BitSet features,
Map<HandshakeExtension, Object> extensions,
Function<String, CompletableFuture<PlatformComputeConnection>>
computeConnectionFunc,
- HandshakeEventLoopSwitcher handshakeEventLoopSwitcher
+ HandshakeEventLoopSwitcher handshakeEventLoopSwitcher,
+ Consumer<SqlQueryType> queryTypeListener
) {
assert igniteTables != null;
assert txManager != null;
@@ -367,6 +373,8 @@ public class ClientInboundMessageHandler
this.extensions = extensions;
this.computeConnectionFunc = computeConnectionFunc;
+
+ this.queryTypeListener = queryTypeListener;
}
@Override
@@ -989,7 +997,7 @@ public class ClientInboundMessageHandler
partitionOperationsExecutor, in, requestId,
cancelHandles, queryProcessor, resources, metrics, tsTracker,
clientContext.hasFeature(SQL_PARTITION_AWARENESS),
clientContext.hasFeature(SQL_DIRECT_TX_MAPPING), txManager,
igniteTables, clockService,
notificationSender(requestId), resolveCurrentUsername(),
- clientContext.hasFeature(SQL_MULTISTATEMENT_SUPPORT)
+ clientContext.hasFeature(SQL_MULTISTATEMENT_SUPPORT),
queryTypeListener
);
case ClientOp.SQL_CURSOR_NEXT_RESULT_SET:
@@ -1291,6 +1299,11 @@ public class ClientInboundMessageHandler
return cancelHandles.size();
}
+ @TestOnly
+ public Consumer<SqlQueryType> queryTypeListener() {
+ return queryTypeListener;
+ }
+
private CompletableFuture<ClientMessageUnpacker>
sendServerToClientRequest(int serverOp, Consumer<ClientMessagePacker> writer) {
// Server and client request ids do not clash, but we use negative to
simplify the debugging.
var requestId = serverToClientRequestId.decrementAndGet();
diff --git
a/modules/client-handler/src/main/java/org/apache/ignite/client/handler/DdlBatchingSuggester.java
b/modules/client-handler/src/main/java/org/apache/ignite/client/handler/DdlBatchingSuggester.java
new file mode 100644
index 00000000000..61c714908a4
--- /dev/null
+++
b/modules/client-handler/src/main/java/org/apache/ignite/client/handler/DdlBatchingSuggester.java
@@ -0,0 +1,72 @@
+/*
+ * 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.client.handler;
+
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
+import org.apache.ignite.internal.logger.IgniteLogger;
+import org.apache.ignite.internal.logger.Loggers;
+import org.apache.ignite.internal.sql.engine.SqlQueryType;
+import org.jetbrains.annotations.TestOnly;
+
+/**
+ * Tracks the number of sequential DDL queries executed and prints
+ * the recommendation to use batch processing of DDL queries.
+ */
+public class DdlBatchingSuggester implements Consumer<SqlQueryType> {
+ /** The number of DDL commands, after which it is necessary to print the
recommendation for the user. */
+ static final int THRESHOLD = 10;
+
+ /** Logger. */
+ private static final IgniteLogger LOG =
Loggers.forClass(DdlBatchingSuggester.class);
+
+ /** Message printer. */
+ private final Consumer<String> printer;
+
+ /** Number of DDL queries processed in a row. */
+ private final AtomicInteger counter = new AtomicInteger();
+
+ DdlBatchingSuggester() {
+ this.printer = LOG::warn;
+ }
+
+ @TestOnly
+ DdlBatchingSuggester(Consumer<String> printer) {
+ this.printer = printer;
+ }
+
+ @Override
+ public void accept(SqlQueryType type) {
+ if (type != SqlQueryType.DDL) {
+ counter.set(0);
+
+ return;
+ }
+
+ if (counter.incrementAndGet() == THRESHOLD) {
+ printer.accept("Multiple DDL statements were executed
individually. For improved performance, "
+ + "consider grouping DDL statements into a single SQL
script. To disable this suggestion, "
+ + "set the cluster property
'ignite.suggestions.sequentialDdlExecution.enabled' to 'false'.");
+ }
+ }
+
+ @TestOnly
+ public int trackedQueriesCount() {
+ return counter.get();
+ }
+}
diff --git
a/modules/client-handler/src/main/java/org/apache/ignite/client/handler/requests/sql/ClientSqlExecuteRequest.java
b/modules/client-handler/src/main/java/org/apache/ignite/client/handler/requests/sql/ClientSqlExecuteRequest.java
index 42390807490..baab0d9a4a0 100644
---
a/modules/client-handler/src/main/java/org/apache/ignite/client/handler/requests/sql/ClientSqlExecuteRequest.java
+++
b/modules/client-handler/src/main/java/org/apache/ignite/client/handler/requests/sql/ClientSqlExecuteRequest.java
@@ -28,6 +28,7 @@ import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.Executor;
+import java.util.function.Consumer;
import java.util.function.Function;
import org.apache.ignite.client.handler.ClientHandlerMetricSource;
import org.apache.ignite.client.handler.ClientResourceRegistry;
@@ -42,6 +43,7 @@ import org.apache.ignite.internal.sql.engine.AsyncSqlCursor;
import org.apache.ignite.internal.sql.engine.InternalSqlRow;
import org.apache.ignite.internal.sql.engine.QueryProcessor;
import org.apache.ignite.internal.sql.engine.SqlProperties;
+import org.apache.ignite.internal.sql.engine.SqlQueryType;
import org.apache.ignite.internal.tx.InternalTransaction;
import org.apache.ignite.internal.tx.TxManager;
import org.apache.ignite.internal.util.ArrayUtils;
@@ -96,7 +98,8 @@ public class ClientSqlExecuteRequest {
ClockService clockService,
NotificationSender notificationSender,
@Nullable String username,
- boolean sqlMultistatementsSupported
+ boolean sqlMultistatementsSupported,
+ Consumer<SqlQueryType> queryTypeListener
) {
CancelHandle cancelHandle = CancelHandle.create();
cancelHandles.put(requestId, cancelHandle);
@@ -135,6 +138,7 @@ public class ClientSqlExecuteRequest {
props.pageSize(),
props.toSqlProps().userName(username),
() -> cancelHandles.remove(requestId),
+ queryTypeListener,
arguments
).thenCompose(asyncResultSet ->
ClientSqlCommon.writeResultSetAsync(resources,
asyncResultSet, metrics, props.pageSize(),
@@ -165,6 +169,7 @@ public class ClientSqlExecuteRequest {
int pageSize,
SqlProperties props,
Runnable onComplete,
+ Consumer<SqlQueryType> queryTypeListener,
@Nullable Object... arguments
) {
try {
@@ -177,6 +182,8 @@ public class ClientSqlExecuteRequest {
arguments
)
.thenCompose(cur -> {
+ queryTypeListener.accept(cur.queryType());
+
doWhenAllCursorsComplete(cur, onComplete);
return cur.requestNextAsync(pageSize)
diff --git
a/modules/client-handler/src/test/java/org/apache/ignite/client/handler/DdlBatchingSuggesterTest.java
b/modules/client-handler/src/test/java/org/apache/ignite/client/handler/DdlBatchingSuggesterTest.java
new file mode 100644
index 00000000000..909f11d5ed5
--- /dev/null
+++
b/modules/client-handler/src/test/java/org/apache/ignite/client/handler/DdlBatchingSuggesterTest.java
@@ -0,0 +1,62 @@
+/*
+ * 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.client.handler;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
+
+import java.util.concurrent.atomic.AtomicInteger;
+import org.apache.ignite.internal.sql.engine.SqlQueryType;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests for class {@link DdlBatchingSuggester}.
+ */
+public class DdlBatchingSuggesterTest {
+ @Test
+ void suggestionIsPrintedOnlyIfQueriesWereExecutedInRow() {
+ AtomicInteger counter = new AtomicInteger();
+ DdlBatchingSuggester suggester = new DdlBatchingSuggester(ignored ->
counter.incrementAndGet());
+
+ for (int i = 0; i < DdlBatchingSuggester.THRESHOLD - 1; i++) {
+ suggester.accept(SqlQueryType.DDL);
+ }
+
+ suggester.accept(SqlQueryType.QUERY);
+
+ assertThat(counter.get(), is(0));
+
+ for (int i = 0; i < DdlBatchingSuggester.THRESHOLD; i++) {
+ suggester.accept(SqlQueryType.DDL);
+ }
+
+ assertThat(counter.get(), is(1));
+ }
+
+ @Test
+ void suggestionIsPrintedOnlyOnce() {
+ AtomicInteger counter = new AtomicInteger();
+ DdlBatchingSuggester suggester = new DdlBatchingSuggester(ignored ->
counter.incrementAndGet());
+
+ for (int i = 0; i < 200; i++) {
+ suggester.accept(SqlQueryType.DDL);
+ }
+
+ assertThat(counter.get(), is(1));
+ }
+}
diff --git
a/modules/client/src/test/java/org/apache/ignite/client/TestClientHandlerModule.java
b/modules/client/src/test/java/org/apache/ignite/client/TestClientHandlerModule.java
index 39cea63d7ca..ce53ef8ecdc 100644
---
a/modules/client/src/test/java/org/apache/ignite/client/TestClientHandlerModule.java
+++
b/modules/client/src/test/java/org/apache/ignite/client/TestClientHandlerModule.java
@@ -276,7 +276,8 @@ public class TestClientHandlerModule implements
IgniteComponent {
features,
randomExtensions(),
unused -> null,
-
bootstrapFactory.handshakeEventLoopSwitcher()
+
bootstrapFactory.handshakeEventLoopSwitcher(),
+ ignore -> {}
)
);
}
diff --git
a/modules/client/src/test/java/org/apache/ignite/client/TestServer.java
b/modules/client/src/test/java/org/apache/ignite/client/TestServer.java
index 4121420d3ea..875c92ac355 100644
--- a/modules/client/src/test/java/org/apache/ignite/client/TestServer.java
+++ b/modules/client/src/test/java/org/apache/ignite/client/TestServer.java
@@ -286,7 +286,8 @@ public class TestServer implements AutoCloseable {
clientConnectorConfiguration,
new TestLowWatermark(),
new SystemPropertiesNodeProperties(),
- Runnable::run
+ Runnable::run,
+ () -> true
);
module.startAsync(componentContext).join();
diff --git
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/client/ItClientDataConsistencyTest.java
b/modules/configuration-system/src/main/java/org/apache/ignite/internal/configuration/SequentialDdlExecutionConfigurationSchema.java
similarity index 55%
copy from
modules/runner/src/integrationTest/java/org/apache/ignite/internal/client/ItClientDataConsistencyTest.java
copy to
modules/configuration-system/src/main/java/org/apache/ignite/internal/configuration/SequentialDdlExecutionConfigurationSchema.java
index 3f97c44c2e1..fcc2701565e 100644
---
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/client/ItClientDataConsistencyTest.java
+++
b/modules/configuration-system/src/main/java/org/apache/ignite/internal/configuration/SequentialDdlExecutionConfigurationSchema.java
@@ -15,26 +15,15 @@
* limitations under the License.
*/
-package org.apache.ignite.internal.client;
+package org.apache.ignite.internal.configuration;
-import org.apache.ignite.Ignite;
-import org.apache.ignite.client.IgniteClient;
-import org.apache.ignite.internal.table.ItDataConsistencyTest;
-import org.junit.jupiter.api.BeforeEach;
+import org.apache.ignite.configuration.annotation.Config;
+import org.apache.ignite.configuration.annotation.Value;
-/**
- * Test data consistency in mixed read-write load initiated from a client.
- */
-public class ItClientDataConsistencyTest extends ItDataConsistencyTest {
- private IgniteClient client;
-
- @BeforeEach
- public void startClient() {
- client = IgniteClient.builder().addresses("127.0.0.1:10800").build();
- }
-
- @Override
- protected Ignite assignNodeForIteration(int workerId) {
- return client;
- }
+/** Configuration related to DDL batching suggestion. */
+@Config
+public class SequentialDdlExecutionConfigurationSchema {
+ /** Enable/disable suggestion about DDL batching. */
+ @Value(hasDefault = true)
+ public final boolean enabled = true;
}
diff --git
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/client/ItClientDataConsistencyTest.java
b/modules/configuration-system/src/main/java/org/apache/ignite/internal/configuration/SuggestionsClusterExtensionConfigurationSchema.java
similarity index 55%
copy from
modules/runner/src/integrationTest/java/org/apache/ignite/internal/client/ItClientDataConsistencyTest.java
copy to
modules/configuration-system/src/main/java/org/apache/ignite/internal/configuration/SuggestionsClusterExtensionConfigurationSchema.java
index 3f97c44c2e1..fe056091697 100644
---
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/client/ItClientDataConsistencyTest.java
+++
b/modules/configuration-system/src/main/java/org/apache/ignite/internal/configuration/SuggestionsClusterExtensionConfigurationSchema.java
@@ -15,26 +15,16 @@
* limitations under the License.
*/
-package org.apache.ignite.internal.client;
+package org.apache.ignite.internal.configuration;
-import org.apache.ignite.Ignite;
-import org.apache.ignite.client.IgniteClient;
-import org.apache.ignite.internal.table.ItDataConsistencyTest;
-import org.junit.jupiter.api.BeforeEach;
+import org.apache.ignite.configuration.annotation.ConfigValue;
+import org.apache.ignite.configuration.annotation.ConfigurationExtension;
/**
- * Test data consistency in mixed read-write load initiated from a client.
+ * Extension for suggestions distributed configuration schema.
*/
-public class ItClientDataConsistencyTest extends ItDataConsistencyTest {
- private IgniteClient client;
-
- @BeforeEach
- public void startClient() {
- client = IgniteClient.builder().addresses("127.0.0.1:10800").build();
- }
-
- @Override
- protected Ignite assignNodeForIteration(int workerId) {
- return client;
- }
+@ConfigurationExtension
+public class SuggestionsClusterExtensionConfigurationSchema extends
ClusterConfigurationSchema {
+ @ConfigValue
+ public SuggestionsConfigurationSchema suggestions;
}
diff --git
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/client/ItClientDataConsistencyTest.java
b/modules/configuration-system/src/main/java/org/apache/ignite/internal/configuration/SuggestionsConfigurationSchema.java
similarity index 55%
copy from
modules/runner/src/integrationTest/java/org/apache/ignite/internal/client/ItClientDataConsistencyTest.java
copy to
modules/configuration-system/src/main/java/org/apache/ignite/internal/configuration/SuggestionsConfigurationSchema.java
index 3f97c44c2e1..eb2418f4d0e 100644
---
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/client/ItClientDataConsistencyTest.java
+++
b/modules/configuration-system/src/main/java/org/apache/ignite/internal/configuration/SuggestionsConfigurationSchema.java
@@ -15,26 +15,15 @@
* limitations under the License.
*/
-package org.apache.ignite.internal.client;
+package org.apache.ignite.internal.configuration;
-import org.apache.ignite.Ignite;
-import org.apache.ignite.client.IgniteClient;
-import org.apache.ignite.internal.table.ItDataConsistencyTest;
-import org.junit.jupiter.api.BeforeEach;
+import org.apache.ignite.configuration.annotation.Config;
+import org.apache.ignite.configuration.annotation.ConfigValue;
-/**
- * Test data consistency in mixed read-write load initiated from a client.
- */
-public class ItClientDataConsistencyTest extends ItDataConsistencyTest {
- private IgniteClient client;
-
- @BeforeEach
- public void startClient() {
- client = IgniteClient.builder().addresses("127.0.0.1:10800").build();
- }
-
- @Override
- protected Ignite assignNodeForIteration(int workerId) {
- return client;
- }
+/** Configuration related to system suggestions. */
+@Config
+public class SuggestionsConfigurationSchema {
+ /** DDL batching suggestion configuration. */
+ @ConfigValue
+ public SequentialDdlExecutionConfigurationSchema sequentialDdlExecution;
}
diff --git
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/client/ItClientDataConsistencyTest.java
b/modules/configuration-system/src/main/java/org/apache/ignite/internal/configuration/SuggestionsDistributedConfigurationModule.java
similarity index 53%
copy from
modules/runner/src/integrationTest/java/org/apache/ignite/internal/client/ItClientDataConsistencyTest.java
copy to
modules/configuration-system/src/main/java/org/apache/ignite/internal/configuration/SuggestionsDistributedConfigurationModule.java
index 3f97c44c2e1..75ad91ee8af 100644
---
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/client/ItClientDataConsistencyTest.java
+++
b/modules/configuration-system/src/main/java/org/apache/ignite/internal/configuration/SuggestionsDistributedConfigurationModule.java
@@ -15,26 +15,25 @@
* limitations under the License.
*/
-package org.apache.ignite.internal.client;
+package org.apache.ignite.internal.configuration;
-import org.apache.ignite.Ignite;
-import org.apache.ignite.client.IgniteClient;
-import org.apache.ignite.internal.table.ItDataConsistencyTest;
-import org.junit.jupiter.api.BeforeEach;
+import com.google.auto.service.AutoService;
+import java.util.Collection;
+import java.util.List;
+import org.apache.ignite.configuration.ConfigurationModule;
+import org.apache.ignite.configuration.annotation.ConfigurationType;
-/**
- * Test data consistency in mixed read-write load initiated from a client.
- */
-public class ItClientDataConsistencyTest extends ItDataConsistencyTest {
- private IgniteClient client;
-
- @BeforeEach
- public void startClient() {
- client = IgniteClient.builder().addresses("127.0.0.1:10800").build();
+/** {@link ConfigurationModule} for suggestions cluster wide configuration. */
+@AutoService(ConfigurationModule.class)
+public class SuggestionsDistributedConfigurationModule implements
ConfigurationModule {
+ /** {@inheritDoc} */
+ @Override
+ public ConfigurationType type() {
+ return ConfigurationType.DISTRIBUTED;
}
@Override
- protected Ignite assignNodeForIteration(int workerId) {
- return client;
+ public Collection<Class<?>> schemaExtensions() {
+ return List.of(SuggestionsClusterExtensionConfigurationSchema.class);
}
}
diff --git
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/client/ItClientDataConsistencyTest.java
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/client/ItClientDataConsistencyTest.java
index 3f97c44c2e1..253c9fcf4d7 100644
---
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/client/ItClientDataConsistencyTest.java
+++
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/client/ItClientDataConsistencyTest.java
@@ -20,6 +20,7 @@ package org.apache.ignite.internal.client;
import org.apache.ignite.Ignite;
import org.apache.ignite.client.IgniteClient;
import org.apache.ignite.internal.table.ItDataConsistencyTest;
+import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
/**
@@ -33,6 +34,11 @@ public class ItClientDataConsistencyTest extends
ItDataConsistencyTest {
client = IgniteClient.builder().addresses("127.0.0.1:10800").build();
}
+ @AfterEach
+ void closeClient() {
+ client.close();
+ }
+
@Override
protected Ignite assignNodeForIteration(int workerId) {
return client;
diff --git
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/client/ItThinClientAuthenticationTest.java
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/client/ItThinClientAuthenticationTest.java
index c131575d4cc..fe0b339069c 100644
---
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/client/ItThinClientAuthenticationTest.java
+++
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/client/ItThinClientAuthenticationTest.java
@@ -208,16 +208,16 @@ public class ItThinClientAuthenticationTest extends
ItAbstractThinClientTest {
public void testCurrentUser() {
server().sql().execute(null, "CREATE TABLE t1 (id INT PRIMARY KEY, val
VARCHAR)").close();
- IgniteClient client2WithAuth = IgniteClient.builder()
+ try (IgniteClient client2WithAuth = IgniteClient.builder()
.authenticator(BasicAuthenticator.builder()
.username(USERNAME_2)
.password(PASSWORD_2)
.build())
.addresses(getClientAddresses().toArray(new String[0]))
- .build();
-
- validateCurrentUser(clientWithAuth, USERNAME_1);
- validateCurrentUser(client2WithAuth, USERNAME_2);
+ .build()) {
+ validateCurrentUser(clientWithAuth, USERNAME_1);
+ validateCurrentUser(client2WithAuth, USERNAME_2);
+ }
}
private static void validateCurrentUser(IgniteClient client, String
expectedUsername) {
diff --git
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/client/ItThinClientDdlQueriesTrackerTest.java
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/client/ItThinClientDdlQueriesTrackerTest.java
new file mode 100644
index 00000000000..128500298da
--- /dev/null
+++
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/client/ItThinClientDdlQueriesTrackerTest.java
@@ -0,0 +1,156 @@
+/*
+ * 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.runner.app.client;
+
+import static org.apache.ignite.internal.TestWrappers.unwrapIgniteImpl;
+import static org.apache.ignite.internal.testframework.IgniteTestUtils.await;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.sameInstance;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+import org.apache.ignite.client.IgniteClient;
+import org.apache.ignite.client.handler.ClientInboundMessageHandler;
+import org.apache.ignite.client.handler.DdlBatchingSuggester;
+import
org.apache.ignite.internal.configuration.SuggestionsClusterExtensionConfiguration;
+import org.apache.ignite.internal.lang.IgniteStringFormatter;
+import org.apache.ignite.internal.sql.engine.SqlQueryType;
+import org.apache.ignite.internal.util.IgniteUtils;
+import org.apache.ignite.sql.ResultSet;
+import org.apache.ignite.sql.SqlRow;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+
+/**
+ * End-to-end tests to validate the behavior of the DDL queries suggestion
handler.
+ */
+public class ItThinClientDdlQueriesTrackerTest extends
ItAbstractThinClientTest {
+ private final List<AutoCloseable> closeables = new ArrayList<>();
+
+ @Override
+ protected int nodes() {
+ return 1;
+ }
+
+ @AfterEach
+ void tearDown() throws Exception {
+ server(0).sql().executeScript("DROP TABLE IF EXISTS t");
+
+ IgniteUtils.closeAll(closeables);
+ }
+
+ @Test
+ void ddlIsTrackedByConnection() {
+ server(0).sql().executeScript("CREATE TABLE t(id INT PRIMARY KEY)");
+
+ IgniteClient client1 = startClient();
+ client1.sql().executeScript("SELECT 1");
+ ClientInboundMessageHandler handler1 =
unwrapIgniteImpl(server(0)).clientInboundMessageHandler();
+
+ // The handler "test reference" is updated after a new connection is
established.
+ IgniteClient client2 = startClient();
+ client2.sql().executeScript("SELECT 1");
+ ClientInboundMessageHandler handler2 =
unwrapIgniteImpl(server(0)).clientInboundMessageHandler();
+
+ assertThat(handler1, not(sameInstance(handler2)));
+
+ addColumn(client1, 0);
+ addColumn(client1, 1);
+
+ assertThat(ddlQueriesInRow(handler1), is(2));
+ assertThat(ddlQueriesInRow(handler2), is(0));
+
+ addColumn(client2, 2);
+
+ assertThat(ddlQueriesInRow(handler1), is(2));
+ assertThat(ddlQueriesInRow(handler2), is(1));
+ }
+
+ @Test
+ void disableSuggestion() {
+ server(0).sql().executeScript("CREATE TABLE t(id INT PRIMARY KEY)");
+
+ SuggestionsClusterExtensionConfiguration config =
unwrapIgniteImpl(server(0)).clusterConfiguration()
+
.getConfiguration(SuggestionsClusterExtensionConfiguration.KEY);
+
+ { // Suggestion is enabled by default
+ IgniteClient client = startClient();
+ addColumn(client, 0);
+ ClientInboundMessageHandler handler =
unwrapIgniteImpl(server(0)).clientInboundMessageHandler();
+ assertThat(ddlQueriesInRow(handler), is(1));
+ }
+
+ { // Disable suggestion
+ await(config.suggestions().sequentialDdlExecution().change(c ->
c.changeEnabled(false)));
+
+ // The handler "test reference" is updated after a new connection
is established.
+ IgniteClient client = startClient();
+ addColumn(client, 1);
+ addColumn(client, 2);
+ ClientInboundMessageHandler handler =
unwrapIgniteImpl(server(0)).clientInboundMessageHandler();
+ assertThat(ddlQueriesInRow(handler), is(-1));
+ }
+
+ { // Enable suggestion
+ await(config.suggestions().sequentialDdlExecution().change(c ->
c.changeEnabled(true)));
+
+ IgniteClient client = startClient();
+
+ addColumn(client, 3);
+ addColumn(client, 4);
+ addColumn(client, 5);
+
+ ClientInboundMessageHandler handler =
unwrapIgniteImpl(server(0)).clientInboundMessageHandler();
+ assertThat(ddlQueriesInRow(handler), is(3));
+ }
+ }
+
+ private IgniteClient startClient() {
+ IgniteClient client = IgniteClient.builder()
+ .addresses(getClientAddresses().toArray(new String[0]))
+ .backgroundReconnectInterval(0)
+ .retryPolicy(null)
+ .build();
+
+ closeables.add(client);
+
+ return client;
+ }
+
+ private static void addColumn(IgniteClient client, int columnNumber) {
+ String ddlQuery = IgniteStringFormatter.format("ALTER TABLE t ADD
COLUMN col{} int;", columnNumber);
+
+ try (ResultSet<SqlRow> rs = client.sql().execute(null, ddlQuery)) {
+ assertThat(rs.hasRowSet(), is(false));
+ assertThat(rs.wasApplied(), is(true));
+ }
+ }
+
+ private static int ddlQueriesInRow(ClientInboundMessageHandler handler) {
+ Consumer<SqlQueryType> queryTypeListener = handler.queryTypeListener();
+
+ if (queryTypeListener instanceof DdlBatchingSuggester) {
+ return ((DdlBatchingSuggester)
queryTypeListener).trackedQueriesCount();
+ }
+
+ return -1;
+ }
+}
diff --git
a/modules/runner/src/main/java/org/apache/ignite/internal/app/IgniteImpl.java
b/modules/runner/src/main/java/org/apache/ignite/internal/app/IgniteImpl.java
index ff584e06e7a..91460355457 100644
---
a/modules/runner/src/main/java/org/apache/ignite/internal/app/IgniteImpl.java
+++
b/modules/runner/src/main/java/org/apache/ignite/internal/app/IgniteImpl.java
@@ -110,6 +110,8 @@ import
org.apache.ignite.internal.configuration.ConfigurationTreeGenerator;
import org.apache.ignite.internal.configuration.JdbcPortProviderImpl;
import org.apache.ignite.internal.configuration.RaftGroupOptionsConfigHelper;
import org.apache.ignite.internal.configuration.ServiceLoaderModulesProvider;
+import
org.apache.ignite.internal.configuration.SuggestionsClusterExtensionConfiguration;
+import org.apache.ignite.internal.configuration.SuggestionsConfiguration;
import org.apache.ignite.internal.configuration.SystemDistributedConfiguration;
import
org.apache.ignite.internal.configuration.SystemDistributedExtensionConfiguration;
import org.apache.ignite.internal.configuration.SystemLocalConfiguration;
@@ -1293,6 +1295,9 @@ public class IgniteImpl implements Ignite {
ClientConnectorConfiguration clientConnectorConfiguration =
nodeConfigRegistry
.getConfiguration(ClientConnectorExtensionConfiguration.KEY).clientConnector();
+ SuggestionsConfiguration suggestionsConfiguration =
clusterConfigRegistry
+
.getConfiguration(SuggestionsClusterExtensionConfiguration.KEY).suggestions();
+
clientHandlerModule = new ClientHandlerModule(
qryEngine,
distributedTblMgr,
@@ -1315,7 +1320,8 @@ public class IgniteImpl implements Ignite {
clientConnectorConfiguration,
lowWatermark,
nodeProperties,
- threadPoolsManager.partitionOperationsExecutor()
+ threadPoolsManager.partitionOperationsExecutor(),
+ () ->
suggestionsConfiguration.sequentialDdlExecution().enabled().value()
);
computeExecutor.setPlatformComputeTransport(clientHandlerModule);
diff --git
a/modules/runner/src/test/resources/compatibility/configuration/ignite-snapshot.bin
b/modules/runner/src/test/resources/compatibility/configuration/ignite-snapshot.bin
index f34374f0f1c..1dd7bd77ca2 100644
Binary files
a/modules/runner/src/test/resources/compatibility/configuration/ignite-snapshot.bin
and
b/modules/runner/src/test/resources/compatibility/configuration/ignite-snapshot.bin
differ