This is an automated email from the ASF dual-hosted git repository.
ptupitsyn 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 5b38be74f89 IGNITE-26555 Fix inconsistent RO TX read timestamp on
client (#6672)
5b38be74f89 is described below
commit 5b38be74f89a8a5ddf5b115e26aed5e7e27e47cb
Author: Pavel Tupitsyn <[email protected]>
AuthorDate: Thu Oct 2 12:09:58 2025 +0300
IGNITE-26555 Fix inconsistent RO TX read timestamp on client (#6672)
Obtain read timestamp when `ClientLazyTransaction` is created (not later)
to match embedded behavior.
---
.../internal/client/tx/ClientLazyTransaction.java | 8 ++++++--
.../ignite/internal/client/tx/ClientTransactions.java | 5 ++---
.../ignite/internal/client/tx/DirectTxUtils.java | 2 +-
.../app/client/ItThinClientTransactionsTest.java | 19 +++++++++++++++++++
4 files changed, 28 insertions(+), 6 deletions(-)
diff --git
a/modules/client/src/main/java/org/apache/ignite/internal/client/tx/ClientLazyTransaction.java
b/modules/client/src/main/java/org/apache/ignite/internal/client/tx/ClientLazyTransaction.java
index a816c050434..8e2a4e634cd 100644
---
a/modules/client/src/main/java/org/apache/ignite/internal/client/tx/ClientLazyTransaction.java
+++
b/modules/client/src/main/java/org/apache/ignite/internal/client/tx/ClientLazyTransaction.java
@@ -35,14 +35,14 @@ import org.jetbrains.annotations.Nullable;
* Lazy client transaction. Will be actually started on the first operation.
*/
public class ClientLazyTransaction implements Transaction {
- private final HybridTimestampTracker observableTimestamp;
+ private final long observableTimestamp;
private final @Nullable TransactionOptions options;
private volatile CompletableFuture<ClientTransaction> tx;
ClientLazyTransaction(HybridTimestampTracker observableTimestamp,
@Nullable TransactionOptions options) {
- this.observableTimestamp = observableTimestamp;
+ this.observableTimestamp = observableTimestamp.getLong();
this.options = options;
}
@@ -200,4 +200,8 @@ public class ClientLazyTransaction implements Transaction {
return tx0.join();
}
+
+ public long observableTimestamp() {
+ return observableTimestamp;
+ }
}
diff --git
a/modules/client/src/main/java/org/apache/ignite/internal/client/tx/ClientTransactions.java
b/modules/client/src/main/java/org/apache/ignite/internal/client/tx/ClientTransactions.java
index 8127b5ad474..71557894d5c 100644
---
a/modules/client/src/main/java/org/apache/ignite/internal/client/tx/ClientTransactions.java
+++
b/modules/client/src/main/java/org/apache/ignite/internal/client/tx/ClientTransactions.java
@@ -28,7 +28,6 @@ import org.apache.ignite.internal.client.PayloadInputChannel;
import org.apache.ignite.internal.client.ReliableChannel;
import org.apache.ignite.internal.client.proto.ClientMessageUnpacker;
import org.apache.ignite.internal.client.proto.ClientOp;
-import org.apache.ignite.internal.hlc.HybridTimestampTracker;
import org.apache.ignite.tx.IgniteTransactions;
import org.apache.ignite.tx.Transaction;
import org.apache.ignite.tx.TransactionOptions;
@@ -78,7 +77,7 @@ public class ClientTransactions implements IgniteTransactions
{
static CompletableFuture<ClientTransaction> beginAsync(
ReliableChannel ch,
@Nullable TransactionOptions options,
- HybridTimestampTracker observableTimestamp,
+ long observableTimestamp,
Supplier<CompletableFuture<ClientChannel>> channelResolver
) {
boolean readOnly = options != null && options.readOnly();
@@ -89,7 +88,7 @@ public class ClientTransactions implements IgniteTransactions
{
w -> {
w.out().packBoolean(readOnly);
w.out().packLong(timeout);
- w.out().packLong(observableTimestamp.get().longValue());
+ w.out().packLong(observableTimestamp);
},
r -> readTx(r, readOnly, timeout),
channelResolver,
diff --git
a/modules/client/src/main/java/org/apache/ignite/internal/client/tx/DirectTxUtils.java
b/modules/client/src/main/java/org/apache/ignite/internal/client/tx/DirectTxUtils.java
index 3f927cd38c5..e0e08bfbded 100644
---
a/modules/client/src/main/java/org/apache/ignite/internal/client/tx/DirectTxUtils.java
+++
b/modules/client/src/main/java/org/apache/ignite/internal/client/tx/DirectTxUtils.java
@@ -128,7 +128,7 @@ public class DirectTxUtils {
if (ctx.firstReqFut != null) {
ClientLazyTransaction tx0 = (ClientLazyTransaction) tx;
out.out().packLong(TX_ID_FIRST_DIRECT);
- out.out().packLong(ctx.tracker.get().longValue());
+ out.out().packLong(tx0.observableTimestamp());
out.out().packBoolean(tx.isReadOnly());
out.out().packLong(tx0.timeout());
} else {
diff --git
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/client/ItThinClientTransactionsTest.java
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/client/ItThinClientTransactionsTest.java
index c249bc99a1e..1357420abc2 100644
---
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/client/ItThinClientTransactionsTest.java
+++
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/client/ItThinClientTransactionsTest.java
@@ -969,6 +969,25 @@ public class ItThinClientTransactionsTest extends
ItAbstractThinClientTest {
tx0.commit();
}
+ @ParameterizedTest
+ @ValueSource(booleans = {true, false})
+ void testReadOnlyTxDoesNotSeeUpdatesAfterStart(boolean server) {
+ //noinspection resource
+ Ignite ignite = server ? server() : client();
+ KeyValueView<Integer, String> kvView =
ignite.tables().table(TABLE_NAME).keyValueView(Integer.class, String.class);
+
+ // Start RO TX, don't access anything yet.
+ // Lazy client TX should record the start time at this point.
+ Transaction tx = ignite.transactions().begin(new
TransactionOptions().readOnly(true));
+
+ // Put outside of TX.
+ kvView.put(null, 123, "123");
+
+ // RO tx does not see the value.
+ String val = kvView.get(tx, 123);
+ assertNull(val, "Read-only transaction should not see values committed
after its start");
+ }
+
@AfterEach
protected void validateInflights() throws NoSuchFieldException {
System.out.println("DBG: validateInflights");