[ 
https://issues.apache.org/jira/browse/IGNITE-18091?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17634256#comment-17634256
 ] 

Denis Chudov edited comment on IGNITE-18091 at 11/15/22 9:44 AM:
-----------------------------------------------------------------

*Correct* means that the behavior matches the expectations.
||N||Scenario||Ignite 3||MVP||Expected||
|1|*testWaitDie0* \\ var tx1 = beginTx(); \\ var tx2 = beginTx(); \\  \\ var 
key1 = key("test"); \\  \\ xlock(tx2, key1).join(); \\  \\ CompletableFuture<?> 
xlockFutTx1 = xlock(tx1, key1); \\ assertFalse(xlockFutTx1.isDone()); \\ 
commitTx(tx2); \\ xlockFutTx1.join();|Tx2 waits on attempt to acquire 
x-lock.\\*Note:* works correctly with reverse transaction order.|correct|X-lock 
for tx2 fails, as tx2 younger than tx1 and is not allowed to wait for tx1, it 
should DIE instead.|
|2|*testWaitDie1* \\  \\ var tx1 = beginTx(); \\ var tx2 = beginTx(); \\  \\ 
var key1 = key("test"); \\  \\ xlock(tx2, key1).join(); \\  \\ 
CompletableFuture<?> xlockFutTx1 = xlock(tx1, key1); \\ 
assertFalse(xlockFutTx1.isDone()); \\  \\ commitTx(tx2); \\ 
xlockFutTx1.join();|Tx1 fails on attempt of acquiring the x-lock \\  \\ Note: 
works correctly with reverse transaction order.|correct|tx2 successfully 
acquires x-lock. \\ tx1 waits on attempt of acquiring x-lock. \\ After tx2 is 
committed, the future for tx1 is completed without exception. \\ |
|3|*testWaitDieSlocks0* \\  \\ var tx1 = beginTx(); \\ var tx2 = beginTx(); \\  
\\ var key1 = key("test"); \\  \\ slock(tx1, key1).join(); \\ slock(tx2, 
key1).join(); \\  \\ CompletableFuture<?> xlockTx1 = xlock(tx1, key1); \\ 
assertFalse(xlockTx1.isDone()); \\  \\ commitTx(tx2); \\ 
xlockTx1.join();|X-lock for tx1 returns future that is completed exceptionally 
with LockException \\  \\ Note: works correctly with reverse transaction 
order.|correct|Tx1 waits for tx2 to commit, then x-lock for tx1 is acquired.|
|4|*testWaitDieSlocks1* \\  \\ var tx1 = beginTx(); \\ var tx2 = beginTx(); \\  
\\ var key1 = key("test"); \\  \\ slock(tx1, key1).join(); \\ slock(tx2, 
key1).join(); \\  \\ assertThrowsException(() -> xlock(tx2, key1));|Tx2 waits 
on attempt to acquire x-lock. \\  \\ Note: works correctly with reverse 
transaction order.|correct|Tx2 should fail on attempt to acquire x-lock, as tx1 
is holding s-lock and tx2 can’t wait for tx1.|
|5|*testWaitDieSlocks2* \\  \\ var tx1 = beginTx(); \\ var tx2 = beginTx(); \\  
\\ var key1 = key("test"); \\  \\ slock(tx1, key1).join(); \\ slock(tx2, 
key1).join(); \\  \\ CompletableFuture<?> xlockTx1 = xlock(tx1, key1); \\ 
assertFalse(xlockTx1.isDone()); \\  \\ assertThrowsException(() -> xlock(tx2, 
key1)); \\  \\ rollbackTx(tx2); \\  \\ xlockTx1.join();|Tx2 waits on attempt to 
acquire x-lock.|correct|Tx1 should wait after attempt to acquire x-lock, as tx2 
is holding s-lock. \\ Tx2 should fail on attempt to acquire x-lock, as tx1 is 
holding s-lock and tx2 can’t wait for tx1. \\ Then tx2 rolls back, and tx1 
successfully acquires x-lock. \\  \\ |
|6|*testNonFair* \\  \\ var tx1 = beginTx(); \\ var tx2 = beginTx(); \\ var tx3 
= beginTx(); \\  \\ var k = key("test"); \\  \\ slock(tx3, k).join(); \\  \\ 
CompletableFuture<?> futTx2 = xlock(tx2, k); \\ assertFalse(futTx2.isDone()); 
\\  \\ CompletableFuture<?> futTx1 = xlock(tx1, k); \\ 
assertFalse(futTx1.isDone()); \\  \\ commitTx(tx3); \\  \\ futTx2.join(); \\  
\\ assertFalse(futTx1.isDone()); \\  \\ commitTx(tx2); \\  \\ 
futTx1.join();|Tx2 waits on attempt to acquire x-lock. \\  \\ Note: works 
correctly with reverse transaction order.|correct|Tx1 and tx2 wait for tx3 to 
commit in order to acquire x-lock. After tx3 is committed, tx2 as more young 
acquires x-lock first, and tx1 continues to wait. After tx2 is committed, tx1 
acquires x-lock. So conflict between waiting transactions is resolved in an 
order allowing all transactions to succeed.|
|7|*testReenterWithConflict* \\  \\ var tx1 = beginTx(); \\ var tx2 = 
beginTx(); \\  \\ var k = key("test"); \\  \\ slock(tx2, k).join(); \\ 
slock(tx1, k).join(); \\  \\ CompletableFuture<?> futTx1 = xlock(tx1, k); \\ 
assertFalse(futTx1.isDone()); \\  \\ commitTx(tx2); \\  \\ futTx1.join();|Tx1 
waits on attempt to acquire x-lock. \\  \\ Note: works correctly with reverse 
transaction order.|correct|Tx1 should wait for tx1 to commit in order to 
upgrade s-lock to x-lock.|
|8|*testReenterWithConflictAndAbort* \\  \\ var tx1 = beginTx(); \\ var tx2 = 
beginTx(); \\  \\ var k = key("test"); \\  \\ slock(tx2, k).join(); \\ 
slock(tx1, k).join(); \\  \\    assertThrowsException(() -> xlock(tx2, k));|Tx2 
waits for tx1 \\  \\    Note: works correctly with reverse transaction 
order.|correct|Tx2 is not allowed to wait for tx1 to commit.|
|9|*testReenterAllowed* \\  \\ var tx1 = beginTx(); \\  \\ var k = key("test"); 
\\  \\ slock(tx1, k).join(); \\ xlock(tx1, k).join();|correct|correct|Reenter 
allowed|
|10|*testNonFairConflictWithAlreadyWaiting* \\  \\ var tx1 = beginTx(); \\ var 
tx2 = beginTx(); \\ var tx3 = beginTx(); \\  \\ var k = key("test"); \\  \\ 
slock(tx2, k).join(); \\  \\ CompletableFuture<?> futTx1 = xlock(tx1, k); \\ 
assertFalse(futTx1.isDone()); \\  \\ slock(tx3, k).join(); \\  \\ 
assertFalse(futTx1.isDone());|Tx1 fails on attempt to acquire x-lock. \\  \\ 
Note: works correctly with reverse transaction order.|correct|In non-fair lock 
waiting order, tx3 is allowed to acquire s-lock that is compatible with s-lock 
that is held by tx2, in spite of tx1 that started to wait for x-lock before. 
Tx1 continues to wait.|
|11|*testNonFairConflictWithAlreadyWaitingWithAbort* \\  \\ var tx1 = 
beginTx(); \\ var tx2 = beginTx(); \\ var tx3 = beginTx(); \\  \\ var k = 
key("test"); \\  \\ slock(tx3, k).join(); \\  \\ CompletableFuture<?> futTx2 = 
xlock(tx2, k); \\ assertFalse(futTx2.isDone()); \\  \\ slock(tx1, k).join(); \\ 
 \\ commitTx(tx3); \\  \\ assertThrowsException(() -> futTx2);|Tx1 fails on 
attempt to acquire x-lock.|Future for tx2 doesn’t complete neither 
exceptionally nor normally.|Tx2 waits for x-lock, as it is older than tx3 that 
is holding s-lock. But after that, tx1 also acquires s-lock, and succeeds 
because of non-fair lock waiting order. After that, tx3 is committed. Now tx2 
waits only for tx1, but according to waitDie deadlock prevention policy, it is 
not allowed to do that, the future for tx2 should complete exceptionally.|
|12|*testNonFairTakeFirstCompatible* \\  \\ var tx1 = beginTx(); \\ var tx2 = 
beginTx(); \\ var tx3 = beginTx(); \\ var tx4 = beginTx(); \\  \\ var k = 
key("test"); \\  \\ slock(tx4, k).join(); \\  \\ CompletableFuture<?> futTx2 = 
xlock(tx2, k); \\ assertFalse(futTx2.isDone()); \\  \\ slock(tx1, k).join(); \\ 
slock(tx3, k).join(); \\  \\ assertFalse(futTx2.isDone()); \\  \\ 
commitTx(tx1); \\ commitTx(tx3); \\ commitTx(tx4); \\  \\ futTx2.join();| \\ 
Tx2 fails in an attempt to acquire x-lock.| \\ correct| \\ S-locks for tx1 and 
tx3 are acquired successfully regardless of order comparing to tx2.| 
|13|*testLockOrderAfterRelease* \\  \\ var tx1 = beginTx(); \\ var tx2 = 
beginTx(); \\ var tx3 = beginTx(); \\ var tx4 = beginTx(); \\  \\ var k = 
key("test"); \\  \\ xlock(tx4, k).join(); \\  \\ CompletableFuture<?> futTx3 = 
slock(tx3, k); \\ assertFalse(futTx3.isDone()); \\  \\ CompletableFuture<?> 
futTx2 = xlock(tx2, k); \\ assertFalse(futTx2.isDone()); \\  \\ 
CompletableFuture<?> futTx1 = slock(tx1, k); \\ assertFalse(futTx1.isDone()); 
\\  \\ commitTx(tx4); \\  \\ futTx3.join(); \\ assertFalse(futTx1.isDone()); \\ 
assertFalse(futTx2.isDone()); \\  \\ commitTx(tx3); \\  \\ futTx2.join(); \\  
\\ commitTx(tx2); \\  \\ futTx1.join(); \\  \\ | \\ Tx3 fails in an attempt to 
acquire s-lock.| \\ correct| \\ After x-lock for tx4 is released, only s-lock 
for tx3 should be acquired. In spite of the fact that s-lock for tx1 is 
compatible, tx1 still should wait because if it acquires lock, tx2 will have to 
be aborted, as it isn’t allowed to wait for tx1.| 
|14| \\ *testMultipleCompatibleLocksAcquiredAfterIncompatibleReleased* \\  \\ 
var tx1 = beginTx(); \\ var tx2 = beginTx(); \\ var tx3 = beginTx(); \\  \\ var 
k = key("test"); \\  \\ xlock(tx3, k).join(); \\  \\ CompletableFuture<?> 
futTx2 = slock(tx2, k); \\ assertFalse(futTx2.isDone()); \\  \\ 
CompletableFuture<?> futTx1 = slock(tx1, k); \\ assertFalse(futTx1.isDone()); 
\\  \\ commitTx(tx3); \\  \\ futTx2.join(); \\ futTx1.join(); \\  \\ | \\ Tx2 
fails in an attempt to acquire s-lock. \\  \\ Note: works correctly with 
reverse transaction order.| \\ Lock for tx1 is not acquired.| \\ After x-lock 
for tx3 is released, s-locks for both tx1 and tx2 could be acquired as they are 
compatible with each other.| 


was (Author: denis chudov):
*Correct* means that the behavior matches the expectations.
||N||Scenario||Ignite 3||MVP||Expected||
|1|*testWaitDie0* 
var tx1 = beginTx(); 
var tx2 = beginTx(); 
 
var key1 = key("test"); 
 
xlock(tx2, key1).join(); 
 
CompletableFuture<?> xlockFutTx1 = xlock(tx1, key1); 
assertFalse(xlockFutTx1.isDone()); 
commitTx(tx2); 
xlockFutTx1.join();|Tx2 waits on attempt to acquire x-lock.
*Note:* works correctly with reverse transaction order.|correct|X-lock for tx2 
fails, as tx2 younger than tx1 and is not allowed to wait for tx1, it should 
DIE instead.|
|2|*testWaitDie1*
 
var tx1 = beginTx();var tx2 = beginTx();
 
var key1 = key("test");
 
xlock(tx2, key1).join();
CompletableFuture<?> xlockFutTx1 = xlock(tx1, key1);
{_}assertFalse{_}(xlockFutTx1.isDone());
 
commitTx(tx2);
xlockFutTx1.join();|Tx1 fails on attempt of acquiring the x-lock
 
*Note:* works correctly with reverse transaction order.|correct|tx2 
successfully acquires x-lock.
tx1 waits on attempt of acquiring x-lock.
After tx2 is committed, the future for tx1 is completed without exception.|
|3|*testWaitDieSlocks0*
 
*var tx1 = beginTx();var tx2 = beginTx();
*
*var key1 = key("test");
*
*slock(tx1, key1).join();slock(tx2, key1).join();*
*CompletableFuture<?> xlockTx1 = xlock(tx1, key1);

{_}assertFalse{_}(xlockTx1.isDone());*
*commitTx(tx2);xlockTx1.join();*|X-lock for tx1 returns future that is 
completed exceptionally with LockException

*Note:* works correctly with reverse transaction order.|correct|Tx1 waits for 
tx2 to commit, then x-lock for tx1 is acquired.|

 

> Compare deadlock prevention implementations and work out decisions about 
> correct behavior in corner cases
> ---------------------------------------------------------------------------------------------------------
>
>                 Key: IGNITE-18091
>                 URL: https://issues.apache.org/jira/browse/IGNITE-18091
>             Project: Ignite
>          Issue Type: Task
>            Reporter: Denis Chudov
>            Assignee: Denis Chudov
>            Priority: Major
>              Labels: ignite-3
>
> *Motivation*
> Today we have several possible implementations of deadlock prevention: AI 3 
> [1], transactions POC [2], and possible implementation based on concurrency 
> control paper [3]. Moreover, we have a multiple granularity lock model [4] 
> where some lock modes are compatible, others are not, which allows 
> reenterability in some cases, and sharing of locks between transactions. We 
> should understand the differences of behavior of each implementation in 
> different scenarios, and how it matches with our expectations.
> *Definition of done*
> Table with a set of scenarios and description of behavior, including the 
> expected one.
> [1] org.apache.ignite.internal.tx.impl.HeapLockManager
> [2] https://github.com/ascherbakoff/ai3-txn-mvp
> [3] https://dl.acm.org/doi/pdf/10.1145/320251.320260
> [4] https://web.stanford.edu/class/cs245/readings/granularity-of-locks.pdf



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to