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

vpyatkov 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 85b995d410 IGNITE-18051 Extend test coverage for lock model (#1398)
85b995d410 is described below

commit 85b995d410f6a17202b80a2416e11df93a337a22
Author: Vladislav Pyatkov <[email protected]>
AuthorDate: Thu Dec 1 16:02:16 2022 +0300

    IGNITE-18051 Extend test coverage for lock model (#1398)
---
 .../ignite/internal/table/ItTableScanTest.java     | 352 ++++++++++++++++++++-
 .../replicator/PartitionReplicaListener.java       |   4 +-
 .../ignite/internal/tx/impl/HeapLockManager.java   |   4 +-
 .../internal/tx/AbstractLockManagerTest.java       | 244 ++++++++++++++
 4 files changed, 599 insertions(+), 5 deletions(-)

diff --git 
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/table/ItTableScanTest.java
 
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/table/ItTableScanTest.java
index 2ffbdc25db..dcdff459fb 100644
--- 
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/table/ItTableScanTest.java
+++ 
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/table/ItTableScanTest.java
@@ -17,9 +17,13 @@
 
 package org.apache.ignite.internal.table;
 
+import static 
org.apache.ignite.internal.testframework.matchers.CompletableFutureMatcher.willCompleteSuccessfully;
+import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
@@ -30,19 +34,26 @@ import java.util.concurrent.Flow.Publisher;
 import java.util.concurrent.Flow.Subscriber;
 import java.util.concurrent.Flow.Subscription;
 import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Function;
 import java.util.stream.IntStream;
 import org.apache.ignite.internal.app.IgniteImpl;
 import org.apache.ignite.internal.schema.BinaryRow;
+import org.apache.ignite.internal.schema.ByteBufferRow;
+import org.apache.ignite.internal.schema.Column;
+import org.apache.ignite.internal.schema.NativeTypes;
+import org.apache.ignite.internal.schema.SchemaDescriptor;
 import org.apache.ignite.internal.schema.configuration.TablesConfiguration;
+import org.apache.ignite.internal.schema.row.Row;
+import org.apache.ignite.internal.schema.row.RowAssembler;
 import org.apache.ignite.internal.sql.engine.AbstractBasicIntegrationTest;
 import org.apache.ignite.internal.testframework.IgniteTestUtils;
 import org.apache.ignite.internal.tx.InternalTransaction;
 import org.apache.ignite.lang.IgniteStringFormatter;
 import org.apache.ignite.table.Tuple;
 import org.apache.ignite.tx.IgniteTransactions;
-import org.jetbrains.annotations.NotNull;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
 
 /**
@@ -58,6 +69,15 @@ public class ItTableScanTest extends 
AbstractBasicIntegrationTest {
     /** Ids to insert. */
     private static final List<Integer> ROW_IDS = List.of(1, 2, 5, 6, 7, 10, 
53);
 
+    private static final SchemaDescriptor SCHEMA = new SchemaDescriptor(
+            1,
+            new Column[]{new Column("key", NativeTypes.INT32, false)},
+            new Column[]{
+                    new Column("valInt", NativeTypes.INT32, false),
+                    new Column("valInt", NativeTypes.STRING, false)
+            }
+    );
+
     @BeforeEach
     public void beforeTest() {
         TableImpl table = getOrCreateTable();
@@ -147,12 +167,294 @@ public class ItTableScanTest extends 
AbstractBasicIntegrationTest {
         //assertEquals(ROW_IDS.size() + 1, scannedRows.size());
     }
 
+    @Test
+    public void testUpsertDuringPureTableScan() throws Exception {
+        pureTableScan(tx -> {
+            TableImpl table = getOrCreateTable();
+
+            InternalTable internalTable = table.internalTable();
+
+            return internalTable.upsert(createKeyValueRow(3), tx)
+                    .thenApply(unused -> 1);
+        });
+    }
+
+    @Test
+    @Disabled("IGNITE-18294 Multiple lock intentions support")
+    public void testUpsertAllDuringPureTableScan() throws Exception {
+        pureTableScan(tx -> {
+            TableImpl table = getOrCreateTable();
+
+            InternalTable internalTable = table.internalTable();
+
+            return internalTable.upsertAll(List.of(createKeyValueRow(3), 
createKeyValueRow(60)), tx)
+                    .thenApply(unused -> 2);
+        });
+    }
+
+    @Test
+    public void testGetAndUpsertDuringPureTableScan() throws Exception {
+        pureTableScan(tx -> {
+            TableImpl table = getOrCreateTable();
+
+            InternalTable internalTable = table.internalTable();
+
+            return internalTable.getAndUpsert(createKeyValueRow(3), tx)
+                    .thenApply(previous -> {
+                        assertNull(previous);
+
+                        return 1;
+                    });
+        });
+    }
+
+    @Test
+    public void testInsertDuringPureTableScan() throws Exception {
+        pureTableScan(tx -> {
+            TableImpl table = getOrCreateTable();
+
+            InternalTable internalTable = table.internalTable();
+
+            return internalTable.insert(createKeyValueRow(3), tx)
+                    .thenApply(inserted -> {
+                        assertTrue(inserted);
+
+                        return 1;
+                    });
+        });
+    }
+
+    @Test
+    @Disabled("IGNITE-18294 Multiple lock intentions support")
+    public void testInsertAllDuringPureTableScan() throws Exception {
+        pureTableScan(tx -> {
+            TableImpl table = getOrCreateTable();
+
+            InternalTable internalTable = table.internalTable();
+
+            return internalTable.insertAll(List.of(createKeyValueRow(3), 
createKeyValueRow(60)), tx)
+                    .thenApply(notInsertedRows -> {
+                        assertTrue(notInsertedRows.isEmpty());
+
+                        return 2;
+                    });
+        });
+    }
+
+    @Test
+    public void testReplaceDuringPureTableScan() throws Exception {
+        pureTableScan(tx -> {
+            TableImpl table = getOrCreateTable();
+
+            InternalTable internalTable = table.internalTable();
+
+            return internalTable.replace(createKeyValueRow(6), tx)
+                    .thenApply(inserted -> {
+                        assertTrue(inserted);
+
+                        return 0;
+                    });
+        });
+    }
+
+    @Test
+    @Disabled("IGNITE-18299 Value comparison in table operations")
+    public void testReplaceOldDuringPureTableScan() throws Exception {
+        pureTableScan(tx -> {
+            TableImpl table = getOrCreateTable();
+
+            InternalTable internalTable = table.internalTable();
+
+            return internalTable.replace(createOldKeyValueRow(6), 
createKeyValueRow(6), tx)
+                    .thenApply(inserted -> {
+                        assertTrue(inserted);
+
+                        return 0;
+                    });
+        });
+    }
+
+    @Test
+    public void testGetAndReplaceDuringPureTableScan() throws Exception {
+        pureTableScan(tx -> {
+            TableImpl table = getOrCreateTable();
+
+            InternalTable internalTable = table.internalTable();
+
+            return internalTable.getAndReplace(createKeyValueRow(6), tx)
+                    .thenApply(previous -> {
+                        assertNotNull(previous);
+
+                        return 0;
+                    });
+        });
+    }
+
+    @Test
+    public void testDeleteDuringPureTableScan() throws Exception {
+        pureTableScan(tx -> {
+            TableImpl table = getOrCreateTable();
+
+            InternalTable internalTable = table.internalTable();
+
+            return internalTable.delete(createKeyRow(6), tx)
+                    .thenApply(deleted -> {
+                        assertTrue(deleted);
+
+                        return -1;
+                    });
+        });
+    }
+
+    @Test
+    @Disabled("IGNITE-18294 Multiple lock intentions support")
+    public void testDeleteAllDuringPureTableScan() throws Exception {
+        pureTableScan(tx -> {
+            TableImpl table = getOrCreateTable();
+
+            InternalTable internalTable = table.internalTable();
+
+            return internalTable.deleteAll(List.of(createKeyRow(6), 
createKeyRow(10)), tx)
+                    .thenApply(deletedRows -> {
+                        assertEquals(2, deletedRows.size());
+
+                        return -2;
+                    });
+        });
+    }
+
+    @Test
+    @Disabled("IGNITE-18299 Value comparison in table operations")
+    public void testDeleteExactDuringPureTableScan() throws Exception {
+        pureTableScan(tx -> {
+            TableImpl table = getOrCreateTable();
+
+            InternalTable internalTable = table.internalTable();
+
+            return internalTable.deleteExact(createOldKeyValueRow(6), tx)
+                    .thenApply(deleted -> {
+                        assertTrue(deleted);
+
+                        return -1;
+                    });
+        });
+    }
+
+    @Test
+    @Disabled("IGNITE-18299 Value comparison in table operations IGNITE-18294 
Multiple lock intentions support")
+    public void testDeleteAllExactDuringPureTableScan() throws Exception {
+        pureTableScan(tx -> {
+            TableImpl table = getOrCreateTable();
+
+            InternalTable internalTable = table.internalTable();
+
+            return 
internalTable.deleteAllExact(List.of(createOldKeyValueRow(6), 
createOldKeyValueRow(10)), tx)
+                    .thenApply(deletedRows -> {
+                        assertEquals(2, deletedRows.size());
+
+                        return -2;
+                    });
+        });
+    }
+
+    @Test
+    public void testGetAndDeleteDuringPureTableScan() throws Exception {
+        pureTableScan(tx -> {
+            TableImpl table = getOrCreateTable();
+
+            InternalTable internalTable = table.internalTable();
+
+            return internalTable.getAndDelete(createKeyRow(6), tx)
+                    .thenApply(deleted -> {
+                        assertNotNull(deleted);
+
+                        return -1;
+                    });
+        });
+    }
+
+    /**
+     * The method executes an operation, encapsulated in closure, during a 
pure table scan.
+     *
+     * @param txOperationAction An closure to apply during the scan operation.
+     * @throws Exception If failed.
+     */
+    public void pureTableScan(Function<InternalTransaction, 
CompletableFuture<Integer>> txOperationAction) throws Exception {
+        TableImpl table = getOrCreateTable();
+
+        InternalTable internalTable = table.internalTable();
+
+        InternalTransaction tx = (InternalTransaction) 
CLUSTER_NODES.get(0).transactions().begin();
+
+        log.info("Old transaction [id=" + tx.id());
+
+        ArrayList<ByteBuffer> scannedRows = new ArrayList<>();
+
+        Publisher<BinaryRow> publisher = internalTable.scan(0, null, null, 
null, null, 0, null);
+
+        CompletableFuture<Void> scanned = new CompletableFuture<>();
+
+        Subscription subscription = subscribeToPublisher(scannedRows, 
publisher, scanned);
+
+        subscription.request(1);
+
+        IgniteTestUtils.waitForCondition(() -> !scannedRows.isEmpty(), 10_000);
+
+        assertEquals(1, scannedRows.size());
+        assertFalse(scanned.isDone());
+
+        var txOpFut = txOperationAction.apply(tx);
+
+        assertFalse(txOpFut.isDone());
+
+        subscription.request(2);
+
+        IgniteTestUtils.waitForCondition(() -> scannedRows.size() == 3, 
10_000);
+
+        assertEquals(3, scannedRows.size());
+        assertFalse(scanned.isDone());
+        assertFalse(txOpFut.isDone());
+
+        subscription.request(1_000); // Request so much entries here to close 
the publisher.
+
+        IgniteTestUtils.await(scanned);
+
+        assertThat(txOpFut, willCompleteSuccessfully());
+
+        tx.commit();
+
+        assertEquals(ROW_IDS.size(), scannedRows.size());
+
+        var pub = internalTable.scan(0, null, null, null, null, 0, null);
+
+        assertEquals(ROW_IDS.size() + txOpFut.get(), scanAllRows(pub).size());
+    }
+
+    /**
+     * Scans all rows form given publisher.
+     *
+     * @param publisher Publisher.
+     * @return List of scanned rows.
+     * @throws Exception If failed.
+     */
+    private ArrayList<ByteBuffer> scanAllRows(Publisher<BinaryRow> publisher) 
throws Exception {
+        ArrayList<ByteBuffer> scannedRows = new ArrayList<>();
+        CompletableFuture<Void> scanned = new CompletableFuture<>();
+
+        Subscription subscription = subscribeToPublisher(scannedRows, 
publisher, scanned);
+
+        subscription.request(1_000); // Request so much entries here to close 
the publisher.
+
+        IgniteTestUtils.waitForCondition(() -> scanned.isDone(), 10_000);
+
+        return scannedRows;
+    }
+
     /**
      * Loads data.
      *
      * @param table Ignite table.
      */
-    @NotNull
     private static void loadData(TableImpl table) {
         ROW_IDS.forEach(id -> insertRow(id));
 
@@ -250,4 +552,50 @@ public class ItTableScanTest extends 
AbstractBasicIntegrationTest {
         sql(IgniteStringFormatter.format("INSERT INTO {} (key, valInt, valStr) 
VALUES ({}, {}, '{}');",
                 TABLE_NAME, rowId, rowId, "Str_" + rowId));
     }
+
+    /**
+     * Creates an entire row with new value.
+     *
+     * @param id Primary key.
+     * @return Entire row.
+     */
+    private static Row createKeyValueRow(int id) {
+        RowAssembler rowBuilder = new RowAssembler(SCHEMA, 0, 0);
+
+        rowBuilder.appendInt(id);
+        rowBuilder.appendInt(id);
+        rowBuilder.appendString("StrNew_" + id);
+
+        return new Row(SCHEMA, new ByteBufferRow(rowBuilder.toBytes()));
+    }
+
+    /**
+     * Creates an entire row with old value.
+     *
+     * @param id Primary key.
+     * @return Entire row.
+     */
+    private static Row createOldKeyValueRow(int id) {
+        RowAssembler rowBuilder = new RowAssembler(SCHEMA, 0, 0);
+
+        rowBuilder.appendInt(id);
+        rowBuilder.appendInt(id);
+        rowBuilder.appendString("Str_" + id);
+
+        return new Row(SCHEMA, new ByteBufferRow(rowBuilder.toBytes()));
+    }
+
+    /**
+     * Creates a key row from primary key.
+     *
+     * @param id Primary key.
+     * @return Key row.
+     */
+    private static Row createKeyRow(int id) {
+        RowAssembler rowBuilder = new RowAssembler(SCHEMA, 0, 0);
+
+        rowBuilder.appendInt(id);
+
+        return new Row(SCHEMA, new ByteBufferRow(rowBuilder.toBytes()));
+    }
 }
diff --git 
a/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/replicator/PartitionReplicaListener.java
 
b/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/replicator/PartitionReplicaListener.java
index cee18b3a9e..f719ac1900 100644
--- 
a/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/replicator/PartitionReplicaListener.java
+++ 
b/modules/table/src/main/java/org/apache/ignite/internal/table/distributed/replicator/PartitionReplicaListener.java
@@ -1388,7 +1388,7 @@ public class PartitionReplicaListener implements 
ReplicaListener {
                         return completedFuture(false);
                     }
 
-                    return takeLocksForDelete(searchRow, rowId, txId)
+                    return takeLocksForDelete(row, rowId, txId)
                             .thenCompose(ignored -> 
applyCmdWithExceptionHandling(
                                     updateCommand(commitPartitionId, 
rowId.uuid(), null, txId)))
                             .thenApply(ignored -> true);
@@ -1400,7 +1400,7 @@ public class PartitionReplicaListener implements 
ReplicaListener {
                         return completedFuture(null);
                     }
 
-                    return takeLocksForDelete(searchRow, rowId, txId)
+                    return takeLocksForDelete(row, rowId, txId)
                             .thenCompose(ignored -> 
applyCmdWithExceptionHandling(
                                     updateCommand(commitPartitionId, 
rowId.uuid(), null, txId)))
                             .thenApply(ignored -> row);
diff --git 
a/modules/transactions/src/main/java/org/apache/ignite/internal/tx/impl/HeapLockManager.java
 
b/modules/transactions/src/main/java/org/apache/ignite/internal/tx/impl/HeapLockManager.java
index e1ea18a87b..0eb6217873 100644
--- 
a/modules/transactions/src/main/java/org/apache/ignite/internal/tx/impl/HeapLockManager.java
+++ 
b/modules/transactions/src/main/java/org/apache/ignite/internal/tx/impl/HeapLockManager.java
@@ -351,6 +351,8 @@ public class HeapLockManager implements LockManager {
                 WaiterImpl tmp = entry.getValue();
 
                 if (!tmp.locked() && isWaiterReadyToNotify(tmp, true)) {
+                    assert tmp.locked() : "This waiter in not locked for 
notification [waiter=" + tmp + ']';
+
                     toNotify.add(tmp);
                 }
             }
@@ -359,7 +361,7 @@ public class HeapLockManager implements LockManager {
                 WaiterImpl tmp = entry.getValue();
 
                 if (!tmp.locked() && isWaiterReadyToNotify(tmp, false)) {
-                    assert !tmp.locked();
+                    assert !tmp.locked() : "Only failed waiter can be notified 
here [waiter=" + tmp + ']';
 
                     toNotify.add(tmp);
                     toFail.add(tmp.txId());
diff --git 
a/modules/transactions/src/test/java/org/apache/ignite/internal/tx/AbstractLockManagerTest.java
 
b/modules/transactions/src/test/java/org/apache/ignite/internal/tx/AbstractLockManagerTest.java
index 514443791e..10f8a16695 100644
--- 
a/modules/transactions/src/test/java/org/apache/ignite/internal/tx/AbstractLockManagerTest.java
+++ 
b/modules/transactions/src/test/java/org/apache/ignite/internal/tx/AbstractLockManagerTest.java
@@ -17,11 +17,13 @@
 
 package org.apache.ignite.internal.tx;
 
+import static 
org.apache.ignite.internal.testframework.matchers.CompletableFutureMatcher.willSucceedFast;
 import static org.apache.ignite.internal.tx.LockMode.IS;
 import static org.apache.ignite.internal.tx.LockMode.IX;
 import static org.apache.ignite.internal.tx.LockMode.S;
 import static org.apache.ignite.internal.tx.LockMode.SIX;
 import static org.apache.ignite.internal.tx.LockMode.X;
+import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNull;
@@ -45,6 +47,7 @@ import 
org.apache.ignite.internal.testframework.IgniteTestUtils;
 import org.apache.ignite.lang.IgniteBiTuple;
 import org.apache.ignite.lang.IgniteException;
 import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
 
 /**
@@ -834,6 +837,247 @@ public abstract class AbstractLockManagerTest extends 
IgniteAbstractTest {
         assertFalse(tx2SharedLock.isDone());
     }
 
+    @Test
+    @Disabled("IGNITE-18294 Multiple lock intentions support")
+    public void testLockingOverloadAndUpgrade() {
+        LockKey key = new LockKey("test");
+
+        UUID tx1 = Timestamp.nextVersion().toUuid();
+        UUID tx2 = Timestamp.nextVersion().toUuid();
+
+        var tx1Lock = lockManager.acquire(tx2, key, X);
+
+        assertTrue(tx1Lock.isDone());
+
+        var tx2sLock = lockManager.acquire(tx1, key, S);
+
+        assertFalse(tx2sLock.isDone());
+
+        var tx2xLock = lockManager.acquire(tx1, key, X);
+
+        assertFalse(tx2xLock.isDone());
+
+        lockManager.release(tx1Lock.join());
+
+        assertThat(tx2sLock, willSucceedFast());
+        assertThat(tx2xLock, willSucceedFast());
+    }
+
+    @Test
+    @Disabled("IGNITE-18294 Multiple lock intentions support")
+    public void testLockingOverload() {
+        LockKey key = new LockKey("test");
+
+        UUID tx1 = Timestamp.nextVersion().toUuid();
+        UUID tx2 = Timestamp.nextVersion().toUuid();
+
+        var tx1Lock = lockManager.acquire(tx2, key, X);
+
+        assertTrue(tx1Lock.isDone());
+
+        var tx2xLock = lockManager.acquire(tx1, key, X);
+
+        assertFalse(tx2xLock.isDone());
+
+        var tx2s1Lock = lockManager.acquire(tx1, key, S);
+        var tx2s2Lock = lockManager.acquire(tx1, key, S);
+
+        assertFalse(tx2s1Lock.isDone());
+        assertFalse(tx2s2Lock.isDone());
+
+        lockManager.release(tx1Lock.join());
+
+        assertThat(tx2xLock, willSucceedFast());
+        assertThat(tx2s1Lock, willSucceedFast());
+        assertThat(tx2s2Lock, willSucceedFast());
+    }
+
+    @Test
+    @Disabled("IGNITE-18294 Multiple lock intentions support")
+    public void testFailUpgrade() {
+        LockKey key = new LockKey("test");
+
+        UUID tx1 = Timestamp.nextVersion().toUuid();
+        UUID tx2 = Timestamp.nextVersion().toUuid();
+        UUID tx3 = Timestamp.nextVersion().toUuid();
+
+        var tx1Lock = lockManager.acquire(tx1, key, S);
+        var tx2Lock = lockManager.acquire(tx2, key, S);
+        var tx3Lock = lockManager.acquire(tx3, key, S);
+
+        assertTrue(tx1Lock.isDone());
+        assertTrue(tx2Lock.isDone());
+        assertTrue(tx3Lock.isDone());
+
+        var tx1xLock = lockManager.acquire(tx1, key, X);
+        var tx2xLock = lockManager.acquire(tx2, key, X);
+
+        assertFalse(tx1xLock.isDone());
+        assertFalse(tx2xLock.isDone());
+
+        lockManager.release(tx3Lock.join());
+
+        assertTrue(tx1xLock.isDone());
+        assertFalse(tx2xLock.isDone());
+
+        lockManager.release(tx1xLock.join());
+
+        assertThat(tx2xLock, willSucceedFast());
+    }
+
+    @Test
+    @Disabled("IGNITE-18294 Multiple lock intentions support")
+    public void testDowngradeTargetLock() {
+        LockKey key = new LockKey("test");
+
+        UUID tx1 = Timestamp.nextVersion().toUuid();
+        UUID tx2 = Timestamp.nextVersion().toUuid();
+
+        var tx1Lock = lockManager.acquire(tx1, key, S);
+        var tx2Lock = lockManager.acquire(tx2, key, S);
+
+        assertThat(tx1Lock, willSucceedFast());
+        assertThat(tx2Lock, willSucceedFast());
+
+        var tx1IxLock = lockManager.acquire(tx1, key, IX);
+
+        assertFalse(tx1IxLock.isDone());
+
+        assertEquals(SIX, lockManager.locks(tx1).next().lockMode());
+
+        lockManager.release(tx1, key, S);
+
+        assertFalse(tx1IxLock.isDone());
+        assertEquals(IX, lockManager.locks(tx1).next().lockMode());
+
+        lockManager.release(tx2, key, S);
+
+        assertThat(tx1IxLock, willSucceedFast());
+    }
+
+    @Test
+    public void testFailWait() {
+        LockKey key = new LockKey("test");
+
+        UUID tx1 = Timestamp.nextVersion().toUuid();
+        UUID tx2 = Timestamp.nextVersion().toUuid();
+        UUID tx3 = Timestamp.nextVersion().toUuid();
+
+        var tx3Lock = lockManager.acquire(tx3, key, S);
+
+        assertThat(tx3Lock, willSucceedFast());
+
+        var tx2Lock = lockManager.acquire(tx2, key, X);
+
+        assertFalse(tx2Lock.isDone());
+
+        var tx1Lock = lockManager.acquire(tx1, key, X);
+
+        assertFalse(tx1Lock.isDone());
+
+        lockManager.release(tx3, key, S);
+
+        expectConflict(tx2Lock);
+
+        assertThat(tx1Lock, willSucceedFast());
+    }
+
+    @Test
+    public void testWaitInOrder() {
+        LockKey key = new LockKey("test");
+
+        UUID tx1 = Timestamp.nextVersion().toUuid();
+        UUID tx2 = Timestamp.nextVersion().toUuid();
+        UUID tx3 = Timestamp.nextVersion().toUuid();
+
+        var tx3IxLock = lockManager.acquire(tx3, key, IX);
+        var tx3Lock = lockManager.acquire(tx3, key, S);
+
+        assertThat(tx3IxLock, willSucceedFast());
+        assertThat(tx3Lock, willSucceedFast());
+
+        var tx2Lock = lockManager.acquire(tx2, key, IX);
+
+        assertFalse(tx2Lock.isDone());
+
+        var tx1Lock = lockManager.acquire(tx1, key, X);
+
+        assertFalse(tx1Lock.isDone());
+
+        lockManager.release(tx3, key, S);
+
+        assertThat(tx2Lock, willSucceedFast());
+
+        lockManager.release(tx3, key, IX);
+        lockManager.release(tx2, key, IX);
+
+        assertThat(tx3Lock, willSucceedFast());
+    }
+
+    @Test
+    public void testWaitNotInOrder() {
+        LockKey key = new LockKey("test");
+
+        UUID tx1 = Timestamp.nextVersion().toUuid();
+        UUID tx2 = Timestamp.nextVersion().toUuid();
+        UUID tx3 = Timestamp.nextVersion().toUuid();
+
+        var tx3IxLock = lockManager.acquire(tx3, key, IX);
+        var tx3Lock = lockManager.acquire(tx3, key, S);
+
+        assertThat(tx3IxLock, willSucceedFast());
+        assertThat(tx3Lock, willSucceedFast());
+
+        var tx2Lock = lockManager.acquire(tx2, key, X);
+
+        assertFalse(tx2Lock.isDone());
+
+        var tx1Lock = lockManager.acquire(tx1, key, IX);
+
+        assertFalse(tx1Lock.isDone());
+
+        lockManager.release(tx3, key, S);
+
+        assertThat(tx1Lock, willSucceedFast());
+
+        lockManager.release(tx1, key, IX);
+        lockManager.release(tx3, key, IX);
+
+        assertThat(tx2Lock, willSucceedFast());
+    }
+
+    @Test
+    public void testWaitFailNotInOrder() {
+        LockKey key = new LockKey("test");
+
+        UUID tx1 = Timestamp.nextVersion().toUuid();
+        UUID tx2 = Timestamp.nextVersion().toUuid();
+        UUID tx3 = Timestamp.nextVersion().toUuid();
+
+        var tx3IxLock = lockManager.acquire(tx3, key, IX);
+        var tx3Lock = lockManager.acquire(tx3, key, S);
+
+        assertThat(tx3IxLock, willSucceedFast());
+        assertThat(tx3Lock, willSucceedFast());
+
+        var tx2Lock = lockManager.acquire(tx2, key, X);
+
+        assertFalse(tx2Lock.isDone());
+
+        var tx1Lock = lockManager.acquire(tx1, key, IX);
+
+        assertFalse(tx1Lock.isDone());
+
+        lockManager.release(tx3, key, S);
+
+        assertThat(tx1Lock, willSucceedFast());
+
+        lockManager.release(tx3, key, IX);
+        lockManager.release(tx1, key, IX);
+
+        expectConflict(tx2Lock);
+    }
+
     /**
      * Do test single key multithreaded.
      *

Reply via email to