This is an automated email from the ASF dual-hosted git repository.
korlov 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 5c2982065de IGNITE-27321 Thin Client. DML failed in explicit
transaction (#7216)
5c2982065de is described below
commit 5c2982065decc7e4f08845b934393a92d0d87c4a
Author: korlov42 <[email protected]>
AuthorDate: Fri Dec 12 14:40:51 2025 +0200
IGNITE-27321 Thin Client. DML failed in explicit transaction (#7216)
---
.../ignite/internal/client/sql/ClientSql.java | 26 ++++++++++--
.../runner/app/client/ItThinClientSqlTest.java | 49 ++++++++++++++++++++++
2 files changed, 72 insertions(+), 3 deletions(-)
diff --git
a/modules/client/src/main/java/org/apache/ignite/internal/client/sql/ClientSql.java
b/modules/client/src/main/java/org/apache/ignite/internal/client/sql/ClientSql.java
index a9a3afc0751..2360f0f4f95 100644
---
a/modules/client/src/main/java/org/apache/ignite/internal/client/sql/ClientSql.java
+++
b/modules/client/src/main/java/org/apache/ignite/internal/client/sql/ClientSql.java
@@ -334,9 +334,7 @@ public class ClientSql implements IgniteSql {
PartitionMappingProvider mappingProvider =
mappingProviderCache.getIfPresent(new PaCacheKey(statement));
- PartitionMapping mapping = mappingProvider != null
- ? mappingProvider.get(arguments)
- : null;
+ PartitionMapping mapping = resolveMapping(transaction,
mappingProvider, arguments);
// Write context carries request execution details over async chain.
WriteContext ctx = new WriteContext(ch.observableTimestamp(),
ClientOp.SQL_EXEC);
@@ -371,6 +369,28 @@ public class ClientSql implements IgniteSql {
)).exceptionally(ClientSql::handleException);
}
+ private static @Nullable PartitionMapping resolveMapping(
+ @Nullable Transaction transaction,
+ @Nullable PartitionMappingProvider provider,
+ @Nullable Object... arguments
+ ) {
+ if (provider == null) {
+ // Nothing to resolve.
+ return null;
+ }
+
+ if (explicitRw(transaction) && provider.directTxMode() ==
ClientDirectTxMode.NOT_SUPPORTED) {
+ // Current statement doesn't support direct transactions which is
part of the explicit RW transactions handling.
+ return null;
+ }
+
+ return provider.get(arguments);
+ }
+
+ private static boolean explicitRw(@Nullable Transaction transaction) {
+ return transaction != null && !transaction.isReadOnly();
+ }
+
private <T> PayloadReader<AsyncResultSet<T>> payloadReader(
WriteContext ctx,
@Nullable Mapper<T> mapper,
diff --git
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/client/ItThinClientSqlTest.java
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/client/ItThinClientSqlTest.java
index ad1bd9371ab..e2db70b5314 100644
---
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/client/ItThinClientSqlTest.java
+++
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/client/ItThinClientSqlTest.java
@@ -19,6 +19,9 @@ package org.apache.ignite.internal.runner.app.client;
import static org.apache.ignite.internal.testframework.IgniteTestUtils.await;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.empty;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
@@ -62,10 +65,12 @@ import org.apache.ignite.sql.SqlRow;
import org.apache.ignite.sql.Statement;
import org.apache.ignite.sql.Statement.StatementBuilder;
import org.apache.ignite.sql.async.AsyncResultSet;
+import org.apache.ignite.table.KeyValueView;
import org.apache.ignite.table.Table;
import org.apache.ignite.table.mapper.Mapper;
import org.apache.ignite.tx.Transaction;
import org.apache.ignite.tx.TransactionOptions;
+import org.awaitility.Awaitility;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Disabled;
@@ -861,6 +866,50 @@ public class ItThinClientSqlTest extends
ItAbstractThinClientTest {
}
}
+ @Test
+ void testKvAndDistributedDmlInSameTransaction() {
+ IgniteSql sql = client().sql();
+ sql.executeScript("CREATE TABLE my_table (id INT PRIMARY KEY, val
INT)");
+
+ KeyValueView<Integer, Integer> view = client().tables()
+ .table("my_table")
+ .keyValueView(Integer.class, Integer.class);
+
+ // First, we need to await table creation.
+ for (int i = 0; i < 10; i++) {
+ view.put(null, i, 0);
+ }
+
+ Transaction tx = client().transactions().begin();
+
+ try {
+ // Then run few operations to make sure transaction is initialized
with commitPartition.
+ for (int i = 0; i < 10; i++) {
+ view.put(tx, i, 0);
+ }
+
+ // Run first batch of distributed DML queries. PartitionAwareness
meta may be not prepared yet.
+ for (int i = 0; i < 5; i++) {
+ try (ResultSet<?> ignored = sql.execute(tx, "UPDATE my_table
SET val = val * 10 WHERE id = ?", i)) {
+ // NO-OP
+ }
+ }
+
+ // Hence, let's wait for PA meta to be ready.
+ Awaitility.await().until(() -> ((ClientSql)
sql).partitionAwarenessCachedMetas(), is(not(empty())));
+
+ // And retry distributed DML queries one more time. Here, even
though PA meta is available, we expect
+ // these statements to be executed in proxy mode and no exceptions
should be thrown.
+ for (int i = 0; i < 5; i++) {
+ try (ResultSet<?> ignored = sql.execute(tx, "UPDATE my_table
SET val = val * 10 WHERE id = ?", i)) {
+ // NO-OP
+ }
+ }
+ } finally {
+ tx.rollback();
+ }
+ }
+
private static class Pojo {
public int num;