From 6359b666e0de5833ac6897dd20d9a6f8994aa117 Mon Sep 17 00:00:00 2001 From: Criterionist <1229089076@qq.com> Date: Sat, 16 Jan 2021 01:05:13 +0800 Subject: [PATCH 01/20] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=8A=B6=E6=80=81?= =?UTF-8?q?=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../transaction/TransactionContextImpl.java | 98 ++++++++++++++----- .../transaction/TransactionContextTest.java | 3 - 2 files changed, 72 insertions(+), 29 deletions(-) diff --git a/src/main/java/net/kaaass/rumbase/transaction/TransactionContextImpl.java b/src/main/java/net/kaaass/rumbase/transaction/TransactionContextImpl.java index bb803bd..7fc5f6d 100644 --- a/src/main/java/net/kaaass/rumbase/transaction/TransactionContextImpl.java +++ b/src/main/java/net/kaaass/rumbase/transaction/TransactionContextImpl.java @@ -8,6 +8,8 @@ import java.util.ArrayList; import java.util.List; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; /** * 事务上下文的实现 @@ -36,6 +38,10 @@ public class TransactionContextImpl implements TransactionContext { */ @Getter private final List snapshot; + /** + * 状态互斥锁 + */ + private final Lock statusLock = new ReentrantLock(); /** * 事务状态 */ @@ -123,11 +129,18 @@ public TransactionContextImpl(int xid, TransactionIsolation isolation, Transacti */ @Override public void start() { - this.status = TransactionStatus.ACTIVE; - if (manager != null) { - manager.changeTransactionStatus(xid, TransactionStatus.ACTIVE); + statusLock.lock(); + try { + if (!this.status.equals(TransactionStatus.PREPARING)) { + return; + } + this.status = TransactionStatus.ACTIVE; + if (manager != null) { + manager.changeTransactionStatus(xid, TransactionStatus.ACTIVE); + } + } finally { + statusLock.unlock(); } - } /** @@ -135,15 +148,23 @@ public void start() { */ @Override public void commit() { - // 修改状态 - this.status = TransactionStatus.COMMITTED; - if (manager != null) { - manager.changeTransactionStatus(xid, TransactionStatus.COMMITTED); + statusLock.lock(); + try { + if (!this.status.equals(TransactionStatus.ACTIVE)) { + return; + } + // 修改状态 + this.status = TransactionStatus.COMMITTED; + if (manager != null) { + manager.changeTransactionStatus(xid, TransactionStatus.COMMITTED); + } + + // 释放锁 + LockTable lockTable = LockTableImpl.getInstance(); + lockTable.release(xid); + } finally { + statusLock.unlock(); } - - // 释放锁 - LockTable lockTable = LockTableImpl.getInstance(); - lockTable.release(xid); } /** @@ -151,15 +172,24 @@ public void commit() { */ @Override public void rollback() { - // 修改状态 - this.status = TransactionStatus.ABORTED; - if (manager != null) { - manager.changeTransactionStatus(xid, TransactionStatus.ABORTED); + statusLock.lock(); + try { + if (!this.status.equals(TransactionStatus.ACTIVE)) { + return; + } + + // 修改状态 + this.status = TransactionStatus.ABORTED; + if (manager != null) { + manager.changeTransactionStatus(xid, TransactionStatus.ABORTED); + } + + // 释放锁 + LockTable lockTable = LockTableImpl.getInstance(); + lockTable.release(xid); + } finally { + statusLock.unlock(); } - - // 释放锁 - LockTable lockTable = LockTableImpl.getInstance(); - lockTable.release(xid); } /** @@ -170,9 +200,18 @@ public void rollback() { */ @Override public void sharedLock(long uuid, String tableName) throws DeadlockException { - //TODO 加锁 - LockTable lockTable = LockTableImpl.getInstance(); - lockTable.addSharedLock(xid, uuid, tableName); + statusLock.lock(); + try { + if (!this.status.equals(TransactionStatus.ACTIVE)) { + return; + } + + LockTable lockTable = LockTableImpl.getInstance(); + lockTable.addSharedLock(xid, uuid, tableName); + } finally { + statusLock.unlock(); + } + } /** @@ -183,9 +222,16 @@ public void sharedLock(long uuid, String tableName) throws DeadlockException { */ @Override public void exclusiveLock(long uuid, String tableName) throws DeadlockException { - //TODO 加锁 - LockTable lockTable = LockTableImpl.getInstance(); - lockTable.addExclusiveLock(xid, uuid, tableName); + statusLock.lock(); + try { + if (!this.status.equals(TransactionStatus.ACTIVE)) { + return; + } + LockTable lockTable = LockTableImpl.getInstance(); + lockTable.addExclusiveLock(xid, uuid, tableName); + } finally { + statusLock.unlock(); + } } } diff --git a/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java b/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java index 9b97c6d..b084afe 100644 --- a/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java +++ b/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java @@ -75,7 +75,6 @@ public void testCreateTransaction() throws IOException, FileException { */ @Test public void testChangeStatus() throws IOException, FileException { - // TODO 将Mock类改成实现类 var manager = new TransactionManagerImpl("test_gen_files/test_change.log"); var committedTransaction = manager.createTransactionContext(TransactionIsolation.READ_COMMITTED); // 事务初始状态 @@ -101,7 +100,6 @@ public void testChangeStatus() throws IOException, FileException { */ @Test public void testTransactionPersistence() throws IOException, FileException { - // TODO 将Mock类改成实现类 var manager = new TransactionManagerImpl("test_gen_files/test_persistence.log"); // 事务创建,事务状态记录数改变 var transaction1 = manager.createTransactionContext(TransactionIsolation.READ_UNCOMMITTED); @@ -156,7 +154,6 @@ public void testTransactionRecovery() throws IOException, FileException { */ @Test public void testAddLock() throws IOException, FileException { - // TODO 将Mock类改成实现类 var manager = new TransactionManagerImpl("test_gen_files/test_add_lock.log"); var transaction1 = manager.createTransactionContext(TransactionIsolation.READ_UNCOMMITTED); var transaction2 = manager.createTransactionContext(TransactionIsolation.READ_UNCOMMITTED); From 2a27a11d10b71887d8a5246f39c972a026597ade Mon Sep 17 00:00:00 2001 From: Criterionist <1229089076@qq.com> Date: Sat, 16 Jan 2021 01:23:04 +0800 Subject: [PATCH 02/20] =?UTF-8?q?=E5=AE=8C=E5=96=84=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E7=94=A8=E4=BE=8B=EF=BC=8C=E4=BF=AE=E6=AD=A3=E6=AD=BB=E9=94=81?= =?UTF-8?q?=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rumbase/transaction/TransactionContextTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java b/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java index b084afe..9c3c186 100644 --- a/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java +++ b/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java @@ -1,10 +1,8 @@ package net.kaaass.rumbase.transaction; -import junit.framework.TestCase; import lombok.extern.slf4j.Slf4j; import net.kaaass.rumbase.page.exception.FileException; import net.kaaass.rumbase.transaction.exception.DeadlockException; -import org.junit.AfterClass; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; @@ -204,7 +202,7 @@ public void testDeadlock() throws IOException, FileException, InterruptedExcepti String tableName = "test"; AtomicBoolean deadlockDetect = new AtomicBoolean(false); - new Thread(() -> { + Thread thread = new Thread(() -> { try { Thread.sleep(3); } catch (InterruptedException e) { @@ -217,7 +215,9 @@ public void testDeadlock() throws IOException, FileException, InterruptedExcepti transaction2.rollback(); e.printStackTrace(); } - }).start(); + }); + thread.start(); + thread.join(); try { transaction1.exclusiveLock(1, tableName); transaction2.exclusiveLock(2, tableName); From 151fddce1511c22f850e62d4d1b3b72346111f33 Mon Sep 17 00:00:00 2001 From: Criterionist <1229089076@qq.com> Date: Sat, 16 Jan 2021 01:42:52 +0800 Subject: [PATCH 03/20] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E7=8A=B6=E6=80=81?= =?UTF-8?q?=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../transaction/TransactionContext.java | 11 ++++--- .../transaction/TransactionContextImpl.java | 21 ++++++------ .../exception/StatusException.java | 31 +++++++++++++++++ .../transaction/lock/LockTableManager.java | 1 + .../transaction/TransactionContextTest.java | 33 ++++++++++++------- 5 files changed, 70 insertions(+), 27 deletions(-) create mode 100644 src/main/java/net/kaaass/rumbase/transaction/exception/StatusException.java diff --git a/src/main/java/net/kaaass/rumbase/transaction/TransactionContext.java b/src/main/java/net/kaaass/rumbase/transaction/TransactionContext.java index f0f1692..bd8ec79 100644 --- a/src/main/java/net/kaaass/rumbase/transaction/TransactionContext.java +++ b/src/main/java/net/kaaass/rumbase/transaction/TransactionContext.java @@ -1,6 +1,7 @@ package net.kaaass.rumbase.transaction; import net.kaaass.rumbase.transaction.exception.DeadlockException; +import net.kaaass.rumbase.transaction.exception.StatusException; import java.util.List; @@ -61,17 +62,17 @@ static TransactionContext empty() { /** * 事务开始 */ - void start(); + void start() throws StatusException; /** * 事务提交 */ - void commit(); + void commit() throws StatusException; /** * 事务撤销 */ - void rollback(); + void rollback() throws StatusException; /** * 对记录加共享锁 @@ -80,7 +81,7 @@ static TransactionContext empty() { * @param tableName 表字段 * @throws DeadlockException 发生死锁异常 */ - void sharedLock(long uuid, String tableName) throws DeadlockException; + void sharedLock(long uuid, String tableName) throws DeadlockException, StatusException; /** * 对记录加排他锁 @@ -89,5 +90,5 @@ static TransactionContext empty() { * @param tableName 表字段 * @throws DeadlockException 发生死锁异常 */ - void exclusiveLock(long uuid, String tableName) throws DeadlockException; + void exclusiveLock(long uuid, String tableName) throws DeadlockException, StatusException; } diff --git a/src/main/java/net/kaaass/rumbase/transaction/TransactionContextImpl.java b/src/main/java/net/kaaass/rumbase/transaction/TransactionContextImpl.java index 7fc5f6d..4006ae2 100644 --- a/src/main/java/net/kaaass/rumbase/transaction/TransactionContextImpl.java +++ b/src/main/java/net/kaaass/rumbase/transaction/TransactionContextImpl.java @@ -3,6 +3,7 @@ import lombok.Getter; import lombok.Setter; import net.kaaass.rumbase.transaction.exception.DeadlockException; +import net.kaaass.rumbase.transaction.exception.StatusException; import net.kaaass.rumbase.transaction.lock.LockTable; import net.kaaass.rumbase.transaction.lock.LockTableImpl; @@ -128,11 +129,11 @@ public TransactionContextImpl(int xid, TransactionIsolation isolation, Transacti * 开始事务 */ @Override - public void start() { + public void start() throws StatusException { statusLock.lock(); try { if (!this.status.equals(TransactionStatus.PREPARING)) { - return; + throw new StatusException(1); } this.status = TransactionStatus.ACTIVE; if (manager != null) { @@ -147,11 +148,11 @@ public void start() { * 提交事务 */ @Override - public void commit() { + public void commit() throws StatusException { statusLock.lock(); try { if (!this.status.equals(TransactionStatus.ACTIVE)) { - return; + throw new StatusException(1); } // 修改状态 this.status = TransactionStatus.COMMITTED; @@ -171,11 +172,11 @@ public void commit() { * 中止事务 */ @Override - public void rollback() { + public void rollback() throws StatusException { statusLock.lock(); try { if (!this.status.equals(TransactionStatus.ACTIVE)) { - return; + throw new StatusException(1); } // 修改状态 @@ -199,11 +200,11 @@ public void rollback() { * @param tableName 表字段 */ @Override - public void sharedLock(long uuid, String tableName) throws DeadlockException { + public void sharedLock(long uuid, String tableName) throws DeadlockException, StatusException { statusLock.lock(); try { if (!this.status.equals(TransactionStatus.ACTIVE)) { - return; + throw new StatusException(1); } LockTable lockTable = LockTableImpl.getInstance(); @@ -221,11 +222,11 @@ public void sharedLock(long uuid, String tableName) throws DeadlockException { * @param tableName 表字段 */ @Override - public void exclusiveLock(long uuid, String tableName) throws DeadlockException { + public void exclusiveLock(long uuid, String tableName) throws DeadlockException, StatusException { statusLock.lock(); try { if (!this.status.equals(TransactionStatus.ACTIVE)) { - return; + throw new StatusException(1); } LockTable lockTable = LockTableImpl.getInstance(); lockTable.addExclusiveLock(xid, uuid, tableName); diff --git a/src/main/java/net/kaaass/rumbase/transaction/exception/StatusException.java b/src/main/java/net/kaaass/rumbase/transaction/exception/StatusException.java new file mode 100644 index 0000000..6801f39 --- /dev/null +++ b/src/main/java/net/kaaass/rumbase/transaction/exception/StatusException.java @@ -0,0 +1,31 @@ +package net.kaaass.rumbase.transaction.exception; + + +import net.kaaass.rumbase.exception.RumbaseException; + +import java.util.HashMap; +import java.util.Map; + +/** + * E6002 事务状态异常 + *

+ * E6002-1 事务状态异常 + * + * @author criki + */ +public class StatusException extends RumbaseException { + + public static final Map REASONS = new HashMap<>() {{ + put(1, "事务状态异常"); + }}; + + /** + * 事务状态异常 + * + * @param subId 子错误号 + */ + public StatusException(int subId) { + super(6001, subId, REASONS.get(subId)); + } + +} diff --git a/src/main/java/net/kaaass/rumbase/transaction/lock/LockTableManager.java b/src/main/java/net/kaaass/rumbase/transaction/lock/LockTableManager.java index 77bd2b2..6b37c95 100644 --- a/src/main/java/net/kaaass/rumbase/transaction/lock/LockTableManager.java +++ b/src/main/java/net/kaaass/rumbase/transaction/lock/LockTableManager.java @@ -12,6 +12,7 @@ * * @author criki */ +@Deprecated public class LockTableManager { /** * 表名与锁表的映射 diff --git a/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java b/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java index 9c3c186..52aa78e 100644 --- a/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java +++ b/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java @@ -3,6 +3,7 @@ import lombok.extern.slf4j.Slf4j; import net.kaaass.rumbase.page.exception.FileException; import net.kaaass.rumbase.transaction.exception.DeadlockException; +import net.kaaass.rumbase.transaction.exception.StatusException; import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; @@ -40,12 +41,10 @@ public static void removeDir(File dir) { @BeforeClass public static void createTmpDir() { File dir = new File("test_gen_files"); - if (!dir.exists()) { - dir.mkdir(); - } else { + if (dir.exists()) { removeDir(dir); - dir.mkdir(); } + dir.mkdir(); } /** @@ -72,7 +71,7 @@ public void testCreateTransaction() throws IOException, FileException { * 测试事务变化 */ @Test - public void testChangeStatus() throws IOException, FileException { + public void testChangeStatus() throws IOException, FileException, StatusException { var manager = new TransactionManagerImpl("test_gen_files/test_change.log"); var committedTransaction = manager.createTransactionContext(TransactionIsolation.READ_COMMITTED); // 事务初始状态 @@ -97,7 +96,7 @@ public void testChangeStatus() throws IOException, FileException { * 测试事务持久化 */ @Test - public void testTransactionPersistence() throws IOException, FileException { + public void testTransactionPersistence() throws IOException, FileException, StatusException { var manager = new TransactionManagerImpl("test_gen_files/test_persistence.log"); // 事务创建,事务状态记录数改变 var transaction1 = manager.createTransactionContext(TransactionIsolation.READ_UNCOMMITTED); @@ -130,7 +129,7 @@ public void testTransactionPersistence() throws IOException, FileException { * 测试事务状态复原 */ @Test - public void testTransactionRecovery() throws IOException, FileException { + public void testTransactionRecovery() throws IOException, FileException, StatusException { var manager = new TransactionManagerImpl("test_gen_files/test_recovery.log"); // 事务创建,事务状态记录数改变 var transaction1 = manager.createTransactionContext(TransactionIsolation.READ_UNCOMMITTED); @@ -151,7 +150,7 @@ public void testTransactionRecovery() throws IOException, FileException { * 测试事务上锁 */ @Test - public void testAddLock() throws IOException, FileException { + public void testAddLock() throws IOException, FileException, StatusException { var manager = new TransactionManagerImpl("test_gen_files/test_add_lock.log"); var transaction1 = manager.createTransactionContext(TransactionIsolation.READ_UNCOMMITTED); var transaction2 = manager.createTransactionContext(TransactionIsolation.READ_UNCOMMITTED); @@ -166,7 +165,11 @@ public void testAddLock() throws IOException, FileException { e.printStackTrace(); } log.info("transaction2 commit"); - transaction2.commit(); + try { + transaction2.commit(); + } catch (StatusException e) { + e.printStackTrace(); + } }).start(); try { transaction1.exclusiveLock(1, tableName); @@ -186,7 +189,7 @@ public void testAddLock() throws IOException, FileException { transaction1.commit(); transaction2.rollback(); - } catch (DeadlockException e) { + } catch (DeadlockException | StatusException e) { e.printStackTrace(); } } @@ -195,7 +198,7 @@ public void testAddLock() throws IOException, FileException { * 测试死锁 */ @Test - public void testDeadlock() throws IOException, FileException, InterruptedException { + public void testDeadlock() throws IOException, FileException, InterruptedException, StatusException { var manager = new TransactionManagerImpl("test_gen_files/test_deadlock.log"); var transaction1 = manager.createTransactionContext(TransactionIsolation.READ_UNCOMMITTED); var transaction2 = manager.createTransactionContext(TransactionIsolation.READ_UNCOMMITTED); @@ -212,7 +215,13 @@ public void testDeadlock() throws IOException, FileException, InterruptedExcepti transaction2.exclusiveLock(1, tableName); } catch (DeadlockException e) { deadlockDetect.set(true); - transaction2.rollback(); + try { + transaction2.rollback(); + } catch (StatusException statusException) { + statusException.printStackTrace(); + } + e.printStackTrace(); + } catch (StatusException e) { e.printStackTrace(); } }); From 5d6689fd13ac55cc5b44b23e7f2c696d2ee58292 Mon Sep 17 00:00:00 2001 From: Criterionist <1229089076@qq.com> Date: Sat, 16 Jan 2021 01:46:03 +0800 Subject: [PATCH 04/20] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E4=BA=8B=E5=8A=A1?= =?UTF-8?q?=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../transaction/TransactionContextTest.java | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java b/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java index 52aa78e..ecce8ff 100644 --- a/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java +++ b/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java @@ -155,9 +155,11 @@ public void testAddLock() throws IOException, FileException, StatusException { var transaction1 = manager.createTransactionContext(TransactionIsolation.READ_UNCOMMITTED); var transaction2 = manager.createTransactionContext(TransactionIsolation.READ_UNCOMMITTED); String tableName = "test"; - + transaction1.start(); + transaction2.start(); // 互斥锁 + TransactionContext finalTransaction = transaction2; new Thread(() -> { try { Thread.sleep(100); @@ -166,14 +168,14 @@ public void testAddLock() throws IOException, FileException, StatusException { } log.info("transaction2 commit"); try { - transaction2.commit(); + finalTransaction.commit(); } catch (StatusException e) { e.printStackTrace(); } }).start(); try { transaction1.exclusiveLock(1, tableName); - transaction2.exclusiveLock(2, tableName); + finalTransaction.exclusiveLock(2, tableName); transaction1.exclusiveLock(2, tableName); log.info("transaction1 got exclusiveLock on 2"); } catch (DeadlockException e) { @@ -181,6 +183,12 @@ public void testAddLock() throws IOException, FileException, StatusException { } transaction1.commit(); + + transaction1 = manager.createTransactionContext(TransactionIsolation.READ_UNCOMMITTED); + transaction2 = manager.createTransactionContext(TransactionIsolation.READ_UNCOMMITTED); + + transaction1.start(); + transaction2.start(); try { transaction1.sharedLock(1, tableName); transaction2.sharedLock(1, tableName); @@ -204,6 +212,9 @@ public void testDeadlock() throws IOException, FileException, InterruptedExcepti var transaction2 = manager.createTransactionContext(TransactionIsolation.READ_UNCOMMITTED); String tableName = "test"; + transaction1.start(); + transaction2.start(); + AtomicBoolean deadlockDetect = new AtomicBoolean(false); Thread thread = new Thread(() -> { try { From f5af9507521858271440cf01919c264cb9264654 Mon Sep 17 00:00:00 2001 From: KAAAsS Date: Sat, 16 Jan 2021 01:53:46 +0800 Subject: [PATCH 05/20] =?UTF-8?q?=E7=94=B1=E4=BA=8E=E7=AD=BE=E5=90=8D?= =?UTF-8?q?=E5=8F=98=E6=9B=B4=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kaaass/rumbase/record/MvccRecordStorage.java | 5 +++++ .../record/exception/RecordNotFoundException.java | 2 ++ .../kaaass/rumbase/record/MvccReadCommitTest.java | 11 ++++++----- .../rumbase/record/MvccReadRepeatableTest.java | 15 ++++++++------- .../transaction/TransactionContextTest.java | 5 +++-- 5 files changed, 24 insertions(+), 14 deletions(-) diff --git a/src/main/java/net/kaaass/rumbase/record/MvccRecordStorage.java b/src/main/java/net/kaaass/rumbase/record/MvccRecordStorage.java index 52807a5..b160e42 100644 --- a/src/main/java/net/kaaass/rumbase/record/MvccRecordStorage.java +++ b/src/main/java/net/kaaass/rumbase/record/MvccRecordStorage.java @@ -13,6 +13,7 @@ import net.kaaass.rumbase.transaction.TransactionIsolation; import net.kaaass.rumbase.transaction.TransactionStatus; import net.kaaass.rumbase.transaction.exception.DeadlockException; +import net.kaaass.rumbase.transaction.exception.StatusException; import java.util.Optional; @@ -56,6 +57,8 @@ public Optional queryOptional(TransactionContext txContext, long recordI txContext.sharedLock(recordId, this.identifiedName); } catch (DeadlockException e) { throw new NeedRollbackException(2, e); + } catch (StatusException e) { + throw new RecordNotFoundException(3, e); } } // 读取数据 @@ -91,6 +94,8 @@ public void delete(TransactionContext txContext, long recordId) throws RecordNot txContext.exclusiveLock(recordId, this.identifiedName); } catch (DeadlockException e) { throw new NeedRollbackException(2, e); + } catch (StatusException e) { + throw new RecordNotFoundException(3, e); } var xid = txContext.getXid(); if (xid == 0) { diff --git a/src/main/java/net/kaaass/rumbase/record/exception/RecordNotFoundException.java b/src/main/java/net/kaaass/rumbase/record/exception/RecordNotFoundException.java index fd5857b..0c8644b 100644 --- a/src/main/java/net/kaaass/rumbase/record/exception/RecordNotFoundException.java +++ b/src/main/java/net/kaaass/rumbase/record/exception/RecordNotFoundException.java @@ -10,6 +10,7 @@ *

* E5001-1 物理记录不存在 * E5001-2 由于事务性,记录不可见 + * E5001-3 事务未处于活动状态,不可访问记录 * * @author kaaass */ @@ -18,6 +19,7 @@ public class RecordNotFoundException extends RumbaseException { public static final Map REASONS = new HashMap<>() {{ put(1, "物理记录不存在"); put(2, "由于事务隔离或已经被删除,记录不可见"); + put(3, "事务未处于活动状态,不可访问记录"); }}; /** diff --git a/src/test/java/net/kaaass/rumbase/record/MvccReadCommitTest.java b/src/test/java/net/kaaass/rumbase/record/MvccReadCommitTest.java index fac8a73..f9cd2e0 100644 --- a/src/test/java/net/kaaass/rumbase/record/MvccReadCommitTest.java +++ b/src/test/java/net/kaaass/rumbase/record/MvccReadCommitTest.java @@ -7,6 +7,7 @@ import net.kaaass.rumbase.transaction.TransactionIsolation; import net.kaaass.rumbase.transaction.TransactionManager; import net.kaaass.rumbase.transaction.TransactionManagerImpl; +import net.kaaass.rumbase.transaction.exception.StatusException; import java.io.IOException; @@ -33,7 +34,7 @@ public void testReadSelf() throws RecordNotFoundException { } } - public void testReadOther() throws RecordNotFoundException { + public void testReadOther() throws RecordNotFoundException, StatusException { var storage = RecordManager.fromFile(PATH + "testReadOther"); var manager = new FakeTxManager(TransactionIsolation.READ_COMMITTED); // 创建事务12 @@ -60,7 +61,7 @@ public void testReadOther() throws RecordNotFoundException { assertTrue("tx3 see a1 after commit", storage.queryOptional(tx3, a1).isPresent()); } - public void testDelete() throws RecordNotFoundException { + public void testDelete() throws RecordNotFoundException, StatusException { var storage = RecordManager.fromFile(PATH + "testDelete"); var manager = new FakeTxManager(TransactionIsolation.READ_COMMITTED); // 创建事务1、记录a1a2 @@ -85,7 +86,7 @@ public void testDelete() throws RecordNotFoundException { assertTrue("tx3 blind a2 after commit", storage.queryOptional(tx3, a2).isEmpty()); } - public void testReadSelfReal() throws RecordNotFoundException, IOException, FileException { + public void testReadSelfReal() throws RecordNotFoundException, IOException, FileException, StatusException { var storage = RecordManager.fromFile(PATH + "testReadSelfReal"); var manager = new TransactionManagerImpl(); // 创建事务1 @@ -108,7 +109,7 @@ public void testReadSelfReal() throws RecordNotFoundException, IOException, File tx2.commit(); } - public void testReadOtherReal() throws RecordNotFoundException, IOException, FileException { + public void testReadOtherReal() throws RecordNotFoundException, IOException, FileException, StatusException { var storage = RecordManager.fromFile(PATH + "testReadOtherReal"); var manager = new TransactionManagerImpl(); // 创建事务12 @@ -139,7 +140,7 @@ public void testReadOtherReal() throws RecordNotFoundException, IOException, Fil tx3.commit(); } - public void testDeleteReal() throws RecordNotFoundException, IOException, FileException { + public void testDeleteReal() throws RecordNotFoundException, IOException, FileException, StatusException { var storage = RecordManager.fromFile(PATH + "testDeleteReal"); var manager = new TransactionManagerImpl(); // 创建事务1、记录a1a2 diff --git a/src/test/java/net/kaaass/rumbase/record/MvccReadRepeatableTest.java b/src/test/java/net/kaaass/rumbase/record/MvccReadRepeatableTest.java index ac0fce2..f458546 100644 --- a/src/test/java/net/kaaass/rumbase/record/MvccReadRepeatableTest.java +++ b/src/test/java/net/kaaass/rumbase/record/MvccReadRepeatableTest.java @@ -9,6 +9,7 @@ import net.kaaass.rumbase.transaction.TransactionIsolation; import net.kaaass.rumbase.transaction.TransactionManager; import net.kaaass.rumbase.transaction.TransactionManagerImpl; +import net.kaaass.rumbase.transaction.exception.StatusException; import java.io.IOException; @@ -35,7 +36,7 @@ public void testReadSelf() throws RecordNotFoundException { } } - public void testReadOther() throws RecordNotFoundException { + public void testReadOther() throws RecordNotFoundException, StatusException { var storage = RecordManager.fromFile(PATH + "testReadOther"); var manager = new FakeTxManager(TransactionIsolation.REPEATABLE_READ); // 创建事务12 @@ -62,7 +63,7 @@ public void testReadOther() throws RecordNotFoundException { assertTrue("tx3 blind a1 after commit", storage.queryOptional(tx3, a1).isEmpty()); } - public void testDelete() throws RecordNotFoundException { + public void testDelete() throws RecordNotFoundException, StatusException { var storage = RecordManager.fromFile(PATH + "testDelete"); var manager = new FakeTxManager(TransactionIsolation.REPEATABLE_READ); // 创建事务1、记录a1a2 @@ -87,7 +88,7 @@ public void testDelete() throws RecordNotFoundException { assertTrue("tx3 see a2 after commit", storage.queryOptional(tx3, a2).isPresent()); } - public void testVersionSkip() throws RecordNotFoundException, NeedRollbackException { + public void testVersionSkip() throws RecordNotFoundException, NeedRollbackException, StatusException { var storage = RecordManager.fromFile(PATH + "testDelete"); var manager = new FakeTxManager(TransactionIsolation.REPEATABLE_READ); // 创建公共版本 @@ -108,7 +109,7 @@ public void testVersionSkip() throws RecordNotFoundException, NeedRollbackExcept } } - public void testReadSelfReal() throws RecordNotFoundException, IOException, FileException { + public void testReadSelfReal() throws RecordNotFoundException, IOException, FileException, StatusException { var storage = RecordManager.fromFile(PATH + "testReadSelfReal"); var manager = new TransactionManagerImpl(); // 创建事务1 @@ -131,7 +132,7 @@ public void testReadSelfReal() throws RecordNotFoundException, IOException, File tx2.commit(); } - public void testReadOtherReal() throws RecordNotFoundException, IOException, FileException { + public void testReadOtherReal() throws RecordNotFoundException, IOException, FileException, StatusException { var storage = RecordManager.fromFile(PATH + "testReadOtherReal"); var manager = new TransactionManagerImpl(); // 创建事务12 @@ -163,7 +164,7 @@ public void testReadOtherReal() throws RecordNotFoundException, IOException, Fil tx3.commit(); } - public void testDeleteReal() throws RecordNotFoundException, IOException, FileException { + public void testDeleteReal() throws RecordNotFoundException, IOException, FileException, StatusException { var storage = RecordManager.fromFile(PATH + "testDeleteReal"); var manager = new TransactionManagerImpl(); // 创建事务1、记录a1a2 @@ -193,7 +194,7 @@ public void testDeleteReal() throws RecordNotFoundException, IOException, FileEx tx3.commit(); } - public void testVersionSkipReal() throws RecordNotFoundException, NeedRollbackException, IOException, FileException { + public void testVersionSkipReal() throws RecordNotFoundException, NeedRollbackException, IOException, FileException, StatusException { var storage = RecordManager.fromFile(PATH + "testDeleteReal"); var manager = new TransactionManagerImpl(); // 创建公共版本 diff --git a/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java b/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java index ecce8ff..20ddec7 100644 --- a/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java +++ b/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java @@ -12,6 +12,8 @@ import java.io.IOException; import java.util.concurrent.atomic.AtomicBoolean; +import static junit.framework.TestCase.assertTrue; + /** * 测试事务上下文 * @@ -247,7 +249,6 @@ public void testDeadlock() throws IOException, FileException, InterruptedExcepti transaction2.rollback(); e.printStackTrace(); } - Thread.sleep(10); - Assert.assertTrue("Deadlock should be detected", deadlockDetect.get()); + assertTrue("Deadlock should be detected", deadlockDetect.get()); } } From 1caa16d32387559c019d04357e4a54b6bc2a5299 Mon Sep 17 00:00:00 2001 From: Criterionist <1229089076@qq.com> Date: Sat, 16 Jan 2021 02:00:17 +0800 Subject: [PATCH 06/20] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=AF=B9=E8=B6=85?= =?UTF-8?q?=E7=BA=A7=E4=BA=8B=E5=8A=A1=E7=9A=84=E7=89=B9=E5=88=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rumbase/transaction/TransactionContextImpl.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/net/kaaass/rumbase/transaction/TransactionContextImpl.java b/src/main/java/net/kaaass/rumbase/transaction/TransactionContextImpl.java index 4006ae2..643f127 100644 --- a/src/main/java/net/kaaass/rumbase/transaction/TransactionContextImpl.java +++ b/src/main/java/net/kaaass/rumbase/transaction/TransactionContextImpl.java @@ -132,7 +132,7 @@ public TransactionContextImpl(int xid, TransactionIsolation isolation, Transacti public void start() throws StatusException { statusLock.lock(); try { - if (!this.status.equals(TransactionStatus.PREPARING)) { + if (this.xid != 0 && !this.status.equals(TransactionStatus.PREPARING)) { throw new StatusException(1); } this.status = TransactionStatus.ACTIVE; @@ -151,7 +151,7 @@ public void start() throws StatusException { public void commit() throws StatusException { statusLock.lock(); try { - if (!this.status.equals(TransactionStatus.ACTIVE)) { + if (this.xid != 0 && !this.status.equals(TransactionStatus.ACTIVE)) { throw new StatusException(1); } // 修改状态 @@ -175,7 +175,7 @@ public void commit() throws StatusException { public void rollback() throws StatusException { statusLock.lock(); try { - if (!this.status.equals(TransactionStatus.ACTIVE)) { + if (this.xid != 0 && !this.status.equals(TransactionStatus.ACTIVE)) { throw new StatusException(1); } @@ -203,7 +203,7 @@ public void rollback() throws StatusException { public void sharedLock(long uuid, String tableName) throws DeadlockException, StatusException { statusLock.lock(); try { - if (!this.status.equals(TransactionStatus.ACTIVE)) { + if (this.xid != 0 && !this.status.equals(TransactionStatus.ACTIVE)) { throw new StatusException(1); } @@ -225,7 +225,7 @@ public void sharedLock(long uuid, String tableName) throws DeadlockException, St public void exclusiveLock(long uuid, String tableName) throws DeadlockException, StatusException { statusLock.lock(); try { - if (!this.status.equals(TransactionStatus.ACTIVE)) { + if (this.xid != 0 && !this.status.equals(TransactionStatus.ACTIVE)) { throw new StatusException(1); } LockTable lockTable = LockTableImpl.getInstance(); From 70e69ef4357c2f5c72c8ca2fb75b6f2459a3a472 Mon Sep 17 00:00:00 2001 From: Criterionist <1229089076@qq.com> Date: Sat, 16 Jan 2021 02:04:20 +0800 Subject: [PATCH 07/20] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E4=BA=8B=E5=8A=A1?= =?UTF-8?q?=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../net/kaaass/rumbase/transaction/TransactionContextTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java b/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java index 20ddec7..88dc83c 100644 --- a/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java +++ b/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java @@ -239,7 +239,6 @@ public void testDeadlock() throws IOException, FileException, InterruptedExcepti } }); thread.start(); - thread.join(); try { transaction1.exclusiveLock(1, tableName); transaction2.exclusiveLock(2, tableName); @@ -249,6 +248,7 @@ public void testDeadlock() throws IOException, FileException, InterruptedExcepti transaction2.rollback(); e.printStackTrace(); } + thread.join(); assertTrue("Deadlock should be detected", deadlockDetect.get()); } } From 69dd1c69e8d1399738e83903ababa456b54bcddc Mon Sep 17 00:00:00 2001 From: KAAAsS Date: Sat, 16 Jan 2021 02:18:09 +0800 Subject: [PATCH 08/20] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E6=AD=BB=E9=94=81?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E5=8F=AF=E8=83=BD=E7=9A=84=E5=BB=B6=E8=BF=9F?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rumbase/transaction/TransactionContextTest.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java b/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java index 88dc83c..a458303 100644 --- a/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java +++ b/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java @@ -217,10 +217,13 @@ public void testDeadlock() throws IOException, FileException, InterruptedExcepti transaction1.start(); transaction2.start(); + AtomicBoolean syncPoint = new AtomicBoolean(false); AtomicBoolean deadlockDetect = new AtomicBoolean(false); Thread thread = new Thread(() -> { try { - Thread.sleep(3); + while (!syncPoint.get()) { + Thread.sleep(3); + } } catch (InterruptedException e) { e.printStackTrace(); } @@ -228,12 +231,12 @@ public void testDeadlock() throws IOException, FileException, InterruptedExcepti transaction2.exclusiveLock(1, tableName); } catch (DeadlockException e) { deadlockDetect.set(true); + e.printStackTrace(); try { transaction2.rollback(); } catch (StatusException statusException) { statusException.printStackTrace(); } - e.printStackTrace(); } catch (StatusException e) { e.printStackTrace(); } @@ -242,11 +245,12 @@ public void testDeadlock() throws IOException, FileException, InterruptedExcepti try { transaction1.exclusiveLock(1, tableName); transaction2.exclusiveLock(2, tableName); + syncPoint.set(true); transaction1.exclusiveLock(2, tableName); } catch (DeadlockException e) { deadlockDetect.set(true); - transaction2.rollback(); e.printStackTrace(); + transaction2.rollback(); } thread.join(); assertTrue("Deadlock should be detected", deadlockDetect.get()); From d8ae86eaca469919f8580f8cfe1430ac79d4f33d Mon Sep 17 00:00:00 2001 From: Criterionist <1229089076@qq.com> Date: Sat, 16 Jan 2021 02:22:08 +0800 Subject: [PATCH 09/20] =?UTF-8?q?=E4=B8=BA=E9=87=8A=E6=94=BE=E9=94=81?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=90=8C=E6=AD=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../transaction/lock/LockTableImpl.java | 31 +++++++++++-------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/src/main/java/net/kaaass/rumbase/transaction/lock/LockTableImpl.java b/src/main/java/net/kaaass/rumbase/transaction/lock/LockTableImpl.java index 7b617cf..c4eb037 100644 --- a/src/main/java/net/kaaass/rumbase/transaction/lock/LockTableImpl.java +++ b/src/main/java/net/kaaass/rumbase/transaction/lock/LockTableImpl.java @@ -133,21 +133,26 @@ public void addExclusiveLock(int xid, long uuid, String tableName) throws Deadlo */ @Override public void release(int xid) { - Set dataItemSet = new HashSet<>(); - List sharedLocks = TxList.sharedLocks.get(xid); - List exclusiveLocks = TxList.exclusiveLocks.get(xid); - log.info("{}'s sharedLocks: {}", xid, sharedLocks); - log.info("{}'s exclusiveLocks: {}", xid, exclusiveLocks); - if (sharedLocks != null) { - dataItemSet.addAll(sharedLocks); - } - if (exclusiveLocks != null) { + lock.lock(); + try { + Set dataItemSet = new HashSet<>(); + List sharedLocks = TxList.sharedLocks.get(xid); + List exclusiveLocks = TxList.exclusiveLocks.get(xid); + log.info("{}'s sharedLocks: {}", xid, sharedLocks); + log.info("{}'s exclusiveLocks: {}", xid, exclusiveLocks); + if (sharedLocks != null) { + dataItemSet.addAll(sharedLocks); + } + if (exclusiveLocks != null) { - dataItemSet.addAll(exclusiveLocks); - } + dataItemSet.addAll(exclusiveLocks); + } - for (DataItemId id : dataItemSet) { - release(xid, id); + for (DataItemId id : dataItemSet) { + release(xid, id); + } + } finally { + lock.unlock(); } } From f17e156a066b7b6d1702a7f4a4e700c3bc94847b Mon Sep 17 00:00:00 2001 From: KAAAsS Date: Sat, 16 Jan 2021 12:49:12 +0800 Subject: [PATCH 10/20] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E8=8B=A5=E5=B9=B2?= =?UTF-8?q?=E8=B0=83=E8=AF=95=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../net/kaaass/rumbase/transaction/lock/Graph.java | 7 +++++++ .../rumbase/transaction/lock/LockTableImpl.java | 11 +++++++---- .../net/kaaass/rumbase/transaction/lock/TxItem.java | 9 +++++++++ .../net/kaaass/rumbase/transaction/lock/TxList.java | 7 +++++++ .../rumbase/transaction/TransactionContextTest.java | 1 + 5 files changed, 31 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/kaaass/rumbase/transaction/lock/Graph.java b/src/main/java/net/kaaass/rumbase/transaction/lock/Graph.java index 1301612..43d1075 100644 --- a/src/main/java/net/kaaass/rumbase/transaction/lock/Graph.java +++ b/src/main/java/net/kaaass/rumbase/transaction/lock/Graph.java @@ -94,4 +94,11 @@ private boolean dfs(int u) { visited.put(u, 1); return false; } + + @Override + public String toString() { + return "Graph{" + + "waitGraph=" + waitGraph + + '}'; + } } diff --git a/src/main/java/net/kaaass/rumbase/transaction/lock/LockTableImpl.java b/src/main/java/net/kaaass/rumbase/transaction/lock/LockTableImpl.java index c4eb037..700997c 100644 --- a/src/main/java/net/kaaass/rumbase/transaction/lock/LockTableImpl.java +++ b/src/main/java/net/kaaass/rumbase/transaction/lock/LockTableImpl.java @@ -246,8 +246,11 @@ private boolean deadlockCheck() { // 建图 // 遍历每一个等待队列 - for (TxList list : lockTable.values()) { + var lockTableView = Collections.unmodifiableMap(lockTable); + log.debug("Lock table: {}", lockTableView); + for (TxList list : lockTableView.values()) { List waitingTxs = new ArrayList<>(list.locks); + log.debug("locks: {}", list.locks); // 对等待队列中建立等待关系 for (int i = 0; i < waitingTxs.size() - 1; i++) { TxItem frontItem = waitingTxs.get(i); @@ -261,7 +264,7 @@ private boolean deadlockCheck() { continue; } - log.info("[CREATING GRAPH] add edge : {} -> {}", backItem.xid, frontItem.xid); + log.debug("[CREATING GRAPH] add edge : {} -> {}", backItem.xid, frontItem.xid); // backItem等待frontItem graph.addEdge(backItem.xid, frontItem.xid); @@ -272,13 +275,13 @@ private boolean deadlockCheck() { // 邻近的后面的锁等待该锁 TxItem backItem = waitingTxs.get(i + 1); // backItem等待frontItem - log.info("[CREATING GRAPH] add edge : {} -> {}", backItem.xid, frontItem.xid); + log.debug("[CREATING GRAPH] add edge : {} -> {}", backItem.xid, frontItem.xid); graph.addEdge(backItem.xid, frontItem.xid); } } } - log.info("create graph successful!"); + log.debug("create graph successful: {}", graph); // 图成环,则有死锁 return graph.hasLoop(); } diff --git a/src/main/java/net/kaaass/rumbase/transaction/lock/TxItem.java b/src/main/java/net/kaaass/rumbase/transaction/lock/TxItem.java index 9bafce6..2db6513 100644 --- a/src/main/java/net/kaaass/rumbase/transaction/lock/TxItem.java +++ b/src/main/java/net/kaaass/rumbase/transaction/lock/TxItem.java @@ -93,4 +93,13 @@ public void abort() { lock.unlock(); } } + + @Override + public String toString() { + return "TxItem{" + + "granted=" + granted + + ", xid=" + xid + + ", mode=" + mode + + '}'; + } } diff --git a/src/main/java/net/kaaass/rumbase/transaction/lock/TxList.java b/src/main/java/net/kaaass/rumbase/transaction/lock/TxList.java index 2ad3386..a264c62 100644 --- a/src/main/java/net/kaaass/rumbase/transaction/lock/TxList.java +++ b/src/main/java/net/kaaass/rumbase/transaction/lock/TxList.java @@ -131,4 +131,11 @@ public void weakInsert(int xid, DataItemId id, LockMode mode, boolean granted) { // 加入等待队列 locks.add(item); } + + @Override + public String toString() { + return "TxList{" + + "locks=" + locks + + '}'; + } } diff --git a/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java b/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java index a458303..9822398 100644 --- a/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java +++ b/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java @@ -254,5 +254,6 @@ public void testDeadlock() throws IOException, FileException, InterruptedExcepti } thread.join(); assertTrue("Deadlock should be detected", deadlockDetect.get()); + transaction1.commit(); } } From d02ccb76f76267f0e59fdb8044bb934dc911847f Mon Sep 17 00:00:00 2001 From: KAAAsS Date: Sat, 16 Jan 2021 12:56:12 +0800 Subject: [PATCH 11/20] =?UTF-8?q?=E8=A7=A3=E5=86=B3=E6=AD=BB=E9=94=81?= =?UTF-8?q?=E7=9B=91=E6=B5=8B=E7=9A=84=E5=B9=B6=E5=8F=91=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rumbase/transaction/lock/LockTableImpl.java | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/main/java/net/kaaass/rumbase/transaction/lock/LockTableImpl.java b/src/main/java/net/kaaass/rumbase/transaction/lock/LockTableImpl.java index 700997c..3fb180d 100644 --- a/src/main/java/net/kaaass/rumbase/transaction/lock/LockTableImpl.java +++ b/src/main/java/net/kaaass/rumbase/transaction/lock/LockTableImpl.java @@ -75,13 +75,15 @@ private void addLockTemplate(int xid, DataItemId id, LockMode mode) throws Deadl // 判断是否能加锁 boolean canGrant = list.canGrant(mode); log.info("{} can grant {} lock : {}", xid, mode, canGrant); - // 虚加锁 - list.weakInsert(xid, id, mode, canGrant); - // 检测死锁 - if (deadlockCheck()) { - log.info("deadlock"); - list.pop(); - throw new DeadlockException(1); + synchronized (this) { + // 虚加锁 + list.weakInsert(xid, id, mode, canGrant); + // 检测死锁 + if (deadlockCheck()) { + log.info("deadlock"); + list.pop(); + throw new DeadlockException(1); + } } // 可以加锁 From f4cfd54ddef3e1d5ced25cd5bc364629ab25167f Mon Sep 17 00:00:00 2001 From: KAAAsS Date: Sat, 16 Jan 2021 13:26:20 +0800 Subject: [PATCH 12/20] =?UTF-8?q?=E4=BD=BF=E7=94=A8=E5=B9=B6=E5=8F=91?= =?UTF-8?q?=E5=BA=A6=E8=BE=83=E4=BD=8E=E7=9A=84=E6=96=B9=E6=B3=95=E8=A7=A3?= =?UTF-8?q?=E5=86=B3=E4=BA=86=E6=AD=BB=E9=94=81=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../transaction/lock/LockTableImpl.java | 12 ++- .../transaction/TransactionContextTest.java | 81 ++++++++++--------- 2 files changed, 51 insertions(+), 42 deletions(-) diff --git a/src/main/java/net/kaaass/rumbase/transaction/lock/LockTableImpl.java b/src/main/java/net/kaaass/rumbase/transaction/lock/LockTableImpl.java index 3fb180d..ee354ee 100644 --- a/src/main/java/net/kaaass/rumbase/transaction/lock/LockTableImpl.java +++ b/src/main/java/net/kaaass/rumbase/transaction/lock/LockTableImpl.java @@ -75,6 +75,7 @@ private void addLockTemplate(int xid, DataItemId id, LockMode mode) throws Deadl // 判断是否能加锁 boolean canGrant = list.canGrant(mode); log.info("{} can grant {} lock : {}", xid, mode, canGrant); + // TODO 并发度差,最好改成读写锁 synchronized (this) { // 虚加锁 list.weakInsert(xid, id, mode, canGrant); @@ -90,10 +91,13 @@ private void addLockTemplate(int xid, DataItemId id, LockMode mode) throws Deadl // 对于互斥锁,如果发生等待,在等待处即已释放,此处无需释放 canUnlock = canGrant; - // 移除虚锁 - list.pop(); - // 正式加锁 - list.insert(xid, id, mode, canGrant); + // TODO 并发度差,最好改成读写锁 + synchronized (list) { + // 移除虚锁 + list.pop(); + // 正式加锁 + list.insert(xid, id, mode, canGrant); + } } finally { if (canUnlock) { list.mutexLock.unlock(); diff --git a/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java b/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java index 9822398..ae75c9b 100644 --- a/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java +++ b/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java @@ -210,50 +210,55 @@ public void testAddLock() throws IOException, FileException, StatusException { @Test public void testDeadlock() throws IOException, FileException, InterruptedException, StatusException { var manager = new TransactionManagerImpl("test_gen_files/test_deadlock.log"); - var transaction1 = manager.createTransactionContext(TransactionIsolation.READ_UNCOMMITTED); - var transaction2 = manager.createTransactionContext(TransactionIsolation.READ_UNCOMMITTED); - String tableName = "test"; - - transaction1.start(); - transaction2.start(); - - AtomicBoolean syncPoint = new AtomicBoolean(false); - AtomicBoolean deadlockDetect = new AtomicBoolean(false); - Thread thread = new Thread(() -> { - try { - while (!syncPoint.get()) { - Thread.sleep(3); + for (int i = 0; i < 50; i++) { + log.info("============= Test times {} =============", i); + var transaction1 = manager.createTransactionContext(TransactionIsolation.READ_UNCOMMITTED); + var transaction2 = manager.createTransactionContext(TransactionIsolation.READ_UNCOMMITTED); + String tableName = "test"; + + transaction1.start(); + transaction2.start(); + + AtomicBoolean syncPoint = new AtomicBoolean(false); + AtomicBoolean deadlockDetect = new AtomicBoolean(false); + Thread thread = new Thread(() -> { + try { + while (!syncPoint.get()) { + Thread.sleep(3); + } + } catch (InterruptedException e) { + e.printStackTrace(); } - } catch (InterruptedException e) { - e.printStackTrace(); - } + try { + transaction2.exclusiveLock(1, tableName); + } catch (DeadlockException e) { + deadlockDetect.set(true); + e.printStackTrace(); + try { + transaction2.rollback(); + } catch (StatusException statusException) { + statusException.printStackTrace(); + } + } catch (StatusException e) { + e.printStackTrace(); + } + }); + thread.start(); try { - transaction2.exclusiveLock(1, tableName); + transaction1.exclusiveLock(1, tableName); + transaction2.exclusiveLock(2, tableName); + syncPoint.set(true); + transaction1.exclusiveLock(2, tableName); } catch (DeadlockException e) { deadlockDetect.set(true); e.printStackTrace(); - try { - transaction2.rollback(); - } catch (StatusException statusException) { - statusException.printStackTrace(); - } - } catch (StatusException e) { - e.printStackTrace(); + log.info("rollback tx2"); + transaction2.rollback(); } - }); - thread.start(); - try { - transaction1.exclusiveLock(1, tableName); - transaction2.exclusiveLock(2, tableName); - syncPoint.set(true); - transaction1.exclusiveLock(2, tableName); - } catch (DeadlockException e) { - deadlockDetect.set(true); - e.printStackTrace(); - transaction2.rollback(); + thread.join(); + assertTrue("Deadlock should be detected", deadlockDetect.get()); + transaction1.commit(); + log.info("tx1 committed"); } - thread.join(); - assertTrue("Deadlock should be detected", deadlockDetect.get()); - transaction1.commit(); } } From 10a5c166e5aa656ee959fc576ee7b6d9826b5c7a Mon Sep 17 00:00:00 2001 From: KAAAsS Date: Sat, 16 Jan 2021 13:36:04 +0800 Subject: [PATCH 13/20] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=B8=89=E7=BA=BF?= =?UTF-8?q?=E7=A8=8B=E6=AD=BB=E9=94=81=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../transaction/TransactionContextTest.java | 97 +++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java b/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java index ae75c9b..cc2290f 100644 --- a/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java +++ b/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java @@ -261,4 +261,101 @@ public void testDeadlock() throws IOException, FileException, InterruptedExcepti log.info("tx1 committed"); } } + + @Test + public void testDeadlock3() throws IOException, FileException, InterruptedException, StatusException { + var manager = new TransactionManagerImpl("test_gen_files/test_deadlock3.log"); + for (int i = 0; i < 1; i++) { + log.info("============= Test times {} =============", i); + var tx1 = manager.createTransactionContext(TransactionIsolation.READ_UNCOMMITTED); + var tx2 = manager.createTransactionContext(TransactionIsolation.READ_UNCOMMITTED); + var tx3 = manager.createTransactionContext(TransactionIsolation.READ_UNCOMMITTED); + String tableName = "test"; + + tx1.start(); + tx2.start(); + tx3.start(); + + AtomicBoolean syncPoint = new AtomicBoolean(false); + AtomicBoolean deadlockDetect = new AtomicBoolean(false); + var thread2 = new Thread(() -> { + try { + while (!syncPoint.get()) { + Thread.sleep(3); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + try { + tx2.exclusiveLock(3, tableName); + } catch (DeadlockException e) { + deadlockDetect.set(true); + e.printStackTrace(); + try { + log.info("rollback tx2"); + tx2.rollback(); + } catch (StatusException statusException) { + statusException.printStackTrace(); + } + } catch (StatusException e) { + e.printStackTrace(); + } + log.info("thread2 alive"); + }); + var thread3 = new Thread(() -> { + try { + while (!syncPoint.get()) { + Thread.sleep(3); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + try { + tx3.exclusiveLock(1, tableName); + } catch (DeadlockException e) { + deadlockDetect.set(true); + e.printStackTrace(); + try { + log.info("rollback tx3"); + tx3.rollback(); + } catch (StatusException statusException) { + statusException.printStackTrace(); + } + } catch (StatusException e) { + e.printStackTrace(); + } + log.info("thread3 alive"); + }); + thread2.start(); + thread3.start(); + try { + tx1.exclusiveLock(1, tableName); + tx2.exclusiveLock(2, tableName); + tx3.exclusiveLock(3, tableName); + syncPoint.set(true); + tx1.exclusiveLock(2, tableName); + } catch (DeadlockException e) { + deadlockDetect.set(true); + e.printStackTrace(); + log.info("rollback tx1"); + tx1.rollback(); + } + log.info("thread1 alive"); + thread2.join(); + thread3.join(); + assertTrue("Deadlock should be detected", deadlockDetect.get()); + if (tx1.getStatus() == TransactionStatus.ACTIVE) { + tx1.commit(); + log.info("tx1 committed"); + } + if (tx2.getStatus() == TransactionStatus.ACTIVE) { + tx2.commit(); + log.info("tx2 committed"); + } + if (tx3.getStatus() == TransactionStatus.ACTIVE) { + tx3.commit(); + log.info("tx3 committed"); + } + } + } } From 36d4d0e33c6e33209970993a10fb20f6513def88 Mon Sep 17 00:00:00 2001 From: KAAAsS Date: Sat, 16 Jan 2021 13:51:58 +0800 Subject: [PATCH 14/20] =?UTF-8?q?=E6=B5=8B=E8=AF=95=E7=BB=93=E6=9D=9F?= =?UTF-8?q?=E6=97=B6=E5=88=A0=E9=99=A4xid.log?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../net/kaaass/rumbase/transaction/TransactionContextTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java b/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java index cc2290f..f682332 100644 --- a/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java +++ b/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java @@ -47,6 +47,7 @@ public static void createTmpDir() { removeDir(dir); } dir.mkdir(); + new File("xid.log").deleteOnExit(); } /** From 369b091627f3498b0e6a5d4a7bdd57253f3f451b Mon Sep 17 00:00:00 2001 From: KAAAsS Date: Sat, 16 Jan 2021 18:19:43 +0800 Subject: [PATCH 15/20] =?UTF-8?q?=E6=9A=82=E6=97=B6=E5=B1=8F=E8=94=BD3?= =?UTF-8?q?=E7=BA=BF=E7=A8=8B=E6=AD=BB=E9=94=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kaaass/rumbase/transaction/TransactionContextTest.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java b/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java index f682332..96149b7 100644 --- a/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java +++ b/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java @@ -6,6 +6,7 @@ import net.kaaass.rumbase.transaction.exception.StatusException; import org.junit.Assert; import org.junit.BeforeClass; +import org.junit.Ignore; import org.junit.Test; import java.io.File; @@ -263,8 +264,9 @@ public void testDeadlock() throws IOException, FileException, InterruptedExcepti } } - @Test + @Ignore public void testDeadlock3() throws IOException, FileException, InterruptedException, StatusException { + // FIXME 三线程死锁问题仍不能解决 var manager = new TransactionManagerImpl("test_gen_files/test_deadlock3.log"); for (int i = 0; i < 1; i++) { log.info("============= Test times {} =============", i); From 97c6f4e564cd43e38329a40c55e29cd1b5454238 Mon Sep 17 00:00:00 2001 From: KAAAsS Date: Sat, 16 Jan 2021 18:34:02 +0800 Subject: [PATCH 16/20] =?UTF-8?q?=E6=9B=B4=E6=94=B9=E5=8D=95=E5=85=83?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E6=97=A5=E5=BF=97=E6=96=87=E4=BB=B6=E7=9A=84?= =?UTF-8?q?=E4=BD=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 ++ .../net/kaaass/rumbase/record/MvccReadCommitTest.java | 6 +++--- .../net/kaaass/rumbase/record/MvccReadRepeatableTest.java | 8 ++++---- .../rumbase/transaction/TransactionContextTest.java | 2 +- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index c66cfe5..fe93c2a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ testFile test_gen_files +xid.log +metadata.db # IntelliJ IDEA .idea diff --git a/src/test/java/net/kaaass/rumbase/record/MvccReadCommitTest.java b/src/test/java/net/kaaass/rumbase/record/MvccReadCommitTest.java index f9cd2e0..35a8bce 100644 --- a/src/test/java/net/kaaass/rumbase/record/MvccReadCommitTest.java +++ b/src/test/java/net/kaaass/rumbase/record/MvccReadCommitTest.java @@ -88,7 +88,7 @@ public void testDelete() throws RecordNotFoundException, StatusException { public void testReadSelfReal() throws RecordNotFoundException, IOException, FileException, StatusException { var storage = RecordManager.fromFile(PATH + "testReadSelfReal"); - var manager = new TransactionManagerImpl(); + var manager = new TransactionManagerImpl("build/xid.log"); // 创建事务1 var tx1 = manager.createTransactionContext(TransactionIsolation.READ_COMMITTED); tx1.start(); @@ -111,7 +111,7 @@ public void testReadSelfReal() throws RecordNotFoundException, IOException, File public void testReadOtherReal() throws RecordNotFoundException, IOException, FileException, StatusException { var storage = RecordManager.fromFile(PATH + "testReadOtherReal"); - var manager = new TransactionManagerImpl(); + var manager = new TransactionManagerImpl("build/xid.log"); // 创建事务12 var tx1 = manager.createTransactionContext(TransactionIsolation.READ_COMMITTED); tx1.start(); @@ -142,7 +142,7 @@ public void testReadOtherReal() throws RecordNotFoundException, IOException, Fil public void testDeleteReal() throws RecordNotFoundException, IOException, FileException, StatusException { var storage = RecordManager.fromFile(PATH + "testDeleteReal"); - var manager = new TransactionManagerImpl(); + var manager = new TransactionManagerImpl("build/xid.log"); // 创建事务1、记录a1a2 var tx1 = manager.createTransactionContext(TransactionIsolation.READ_COMMITTED); tx1.start(); diff --git a/src/test/java/net/kaaass/rumbase/record/MvccReadRepeatableTest.java b/src/test/java/net/kaaass/rumbase/record/MvccReadRepeatableTest.java index f458546..f218f7d 100644 --- a/src/test/java/net/kaaass/rumbase/record/MvccReadRepeatableTest.java +++ b/src/test/java/net/kaaass/rumbase/record/MvccReadRepeatableTest.java @@ -111,7 +111,7 @@ public void testVersionSkip() throws RecordNotFoundException, NeedRollbackExcept public void testReadSelfReal() throws RecordNotFoundException, IOException, FileException, StatusException { var storage = RecordManager.fromFile(PATH + "testReadSelfReal"); - var manager = new TransactionManagerImpl(); + var manager = new TransactionManagerImpl("build/xid.log"); // 创建事务1 var tx1 = manager.createTransactionContext(TransactionIsolation.REPEATABLE_READ); tx1.start(); @@ -134,7 +134,7 @@ public void testReadSelfReal() throws RecordNotFoundException, IOException, File public void testReadOtherReal() throws RecordNotFoundException, IOException, FileException, StatusException { var storage = RecordManager.fromFile(PATH + "testReadOtherReal"); - var manager = new TransactionManagerImpl(); + var manager = new TransactionManagerImpl("build/xid.log"); // 创建事务12 var tx1 = manager.createTransactionContext(TransactionIsolation.REPEATABLE_READ); tx1.start(); @@ -166,7 +166,7 @@ public void testReadOtherReal() throws RecordNotFoundException, IOException, Fil public void testDeleteReal() throws RecordNotFoundException, IOException, FileException, StatusException { var storage = RecordManager.fromFile(PATH + "testDeleteReal"); - var manager = new TransactionManagerImpl(); + var manager = new TransactionManagerImpl("build/xid.log"); // 创建事务1、记录a1a2 var tx1 = manager.createTransactionContext(TransactionIsolation.REPEATABLE_READ); tx1.start(); @@ -196,7 +196,7 @@ public void testDeleteReal() throws RecordNotFoundException, IOException, FileEx public void testVersionSkipReal() throws RecordNotFoundException, NeedRollbackException, IOException, FileException, StatusException { var storage = RecordManager.fromFile(PATH + "testDeleteReal"); - var manager = new TransactionManagerImpl(); + var manager = new TransactionManagerImpl("build/xid.log"); // 创建公共版本 var r = storage.insert(TransactionContext.empty(), new byte[]{0x1, 0x2, 0x3, 0x4}); // 进行版本跳跃操作 diff --git a/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java b/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java index 96149b7..ebe5b22 100644 --- a/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java +++ b/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java @@ -48,7 +48,7 @@ public static void createTmpDir() { removeDir(dir); } dir.mkdir(); - new File("xid.log").deleteOnExit(); + new File("build/xid.log").deleteOnExit(); } /** From 8df5c827c007334d7817de4b7cee7b308143d420 Mon Sep 17 00:00:00 2001 From: KAAAsS Date: Sun, 17 Jan 2021 15:03:20 +0800 Subject: [PATCH 17/20] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E7=AD=BE=E5=90=8D?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../net/kaaass/rumbase/server/Session.java | 29 +++++++++++++---- .../kaaass/rumbase/table/TableManager.java | 5 +-- .../rumbase/record/MvccReadCommitTest.java | 11 ++++--- .../record/MvccReadRepeatableTest.java | 15 +++++---- .../transaction/TransactionContextTest.java | 32 ++++++++++++++----- 5 files changed, 64 insertions(+), 28 deletions(-) diff --git a/src/main/java/net/kaaass/rumbase/server/Session.java b/src/main/java/net/kaaass/rumbase/server/Session.java index bcb033d..5db2376 100644 --- a/src/main/java/net/kaaass/rumbase/server/Session.java +++ b/src/main/java/net/kaaass/rumbase/server/Session.java @@ -19,6 +19,7 @@ import net.kaaass.rumbase.table.exception.TableExistenceException; import net.kaaass.rumbase.transaction.TransactionContext; import net.kaaass.rumbase.transaction.TransactionIsolation; +import net.kaaass.rumbase.transaction.exception.StatusException; import java.io.*; import java.net.Socket; @@ -150,6 +151,7 @@ public void onClose() { } catch (RumbaseRuntimeException e) { log.warn("退出会话 {} 时提交事务失败", sessionId, e); say(e); + } catch (StatusException ignored) { } } // 删除活跃会话 @@ -383,10 +385,15 @@ private void checkAutoCommitAfter(boolean rollback) { if (autoCommit) { try { assert currentContext != null; - if (rollback) { - currentContext.rollback(); - } else { - currentContext.commit(); + try { + if (rollback) { + currentContext.rollback(); + } else { + currentContext.commit(); + } + } catch (StatusException e) { + log.warn("自动提交事务失败,会话 {} ", sessionId, e); + say(e); } currentContext = null; } finally { @@ -416,7 +423,12 @@ public Boolean visit(CommitStatement statement) { return false; } // 提交事务 - currentContext.commit(); + try { + currentContext.commit(); + } catch (StatusException e) { + say(e); + return false; + } say("成功提交事务" + currentContext.getXid() + "\n"); currentContext = null; return false; @@ -429,7 +441,12 @@ public Boolean visit(RollbackStatement statement) { return false; } // 回滚事务 - currentContext.rollback(); + try { + currentContext.rollback(); + } catch (StatusException e) { + say(e); + return false; + } say("成功回滚事务" + currentContext.getXid() + "\n"); currentContext = null; return false; diff --git a/src/main/java/net/kaaass/rumbase/table/TableManager.java b/src/main/java/net/kaaass/rumbase/table/TableManager.java index ecb925a..5695809 100644 --- a/src/main/java/net/kaaass/rumbase/table/TableManager.java +++ b/src/main/java/net/kaaass/rumbase/table/TableManager.java @@ -11,6 +11,7 @@ import net.kaaass.rumbase.table.exception.TableExistenceException; import net.kaaass.rumbase.table.field.VarcharField; import net.kaaass.rumbase.transaction.TransactionContext; +import net.kaaass.rumbase.transaction.exception.StatusException; import java.io.File; import java.util.*; @@ -55,7 +56,7 @@ public class TableManager { * * @param context 事务context */ - public void commit(TransactionContext context) { + public void commit(TransactionContext context) throws StatusException { context.commit(); } @@ -64,7 +65,7 @@ public void commit(TransactionContext context) { * * @param context 事务context */ - public void abort(TransactionContext context) { + public void abort(TransactionContext context) throws StatusException { context.rollback(); } diff --git a/src/test/java/net/kaaass/rumbase/record/MvccReadCommitTest.java b/src/test/java/net/kaaass/rumbase/record/MvccReadCommitTest.java index e12d1bb..772c9e8 100644 --- a/src/test/java/net/kaaass/rumbase/record/MvccReadCommitTest.java +++ b/src/test/java/net/kaaass/rumbase/record/MvccReadCommitTest.java @@ -8,6 +8,7 @@ import net.kaaass.rumbase.transaction.TransactionIsolation; import net.kaaass.rumbase.transaction.TransactionManager; import net.kaaass.rumbase.transaction.TransactionManagerImpl; +import net.kaaass.rumbase.transaction.exception.StatusException; import org.junit.AfterClass; import org.junit.Assert; import org.junit.BeforeClass; @@ -50,7 +51,7 @@ public void testReadSelf() throws RecordNotFoundException { } @Test - public void testReadOther() throws RecordNotFoundException { + public void testReadOther() throws RecordNotFoundException, StatusException { var storage = RecordManager.fromFile(PATH + "testReadOther"); var manager = new FakeTxManager(TransactionIsolation.READ_COMMITTED); // 创建事务12 @@ -78,7 +79,7 @@ public void testReadOther() throws RecordNotFoundException { } @Test - public void testDelete() throws RecordNotFoundException { + public void testDelete() throws RecordNotFoundException, StatusException { var storage = RecordManager.fromFile(PATH + "testDelete"); var manager = new FakeTxManager(TransactionIsolation.READ_COMMITTED); // 创建事务1、记录a1a2 @@ -104,7 +105,7 @@ public void testDelete() throws RecordNotFoundException { } @Test - public void testReadSelfReal() throws RecordNotFoundException, IOException, FileException { + public void testReadSelfReal() throws RecordNotFoundException, IOException, FileException, StatusException { var storage = RecordManager.fromFile(PATH + "testReadSelfReal"); var manager = new TransactionManagerImpl(); // 创建事务1 @@ -128,7 +129,7 @@ public void testReadSelfReal() throws RecordNotFoundException, IOException, File } @Test - public void testReadOtherReal() throws RecordNotFoundException, IOException, FileException { + public void testReadOtherReal() throws RecordNotFoundException, IOException, FileException, StatusException { var storage = RecordManager.fromFile(PATH + "testReadOtherReal"); var manager = new TransactionManagerImpl(); // 创建事务12 @@ -160,7 +161,7 @@ public void testReadOtherReal() throws RecordNotFoundException, IOException, Fil } @Test - public void testDeleteReal() throws RecordNotFoundException, IOException, FileException { + public void testDeleteReal() throws RecordNotFoundException, IOException, FileException, StatusException { var storage = RecordManager.fromFile(PATH + "testDeleteReal"); var manager = new TransactionManagerImpl(); // 创建事务1、记录a1a2 diff --git a/src/test/java/net/kaaass/rumbase/record/MvccReadRepeatableTest.java b/src/test/java/net/kaaass/rumbase/record/MvccReadRepeatableTest.java index 3f35268..91d1e2e 100644 --- a/src/test/java/net/kaaass/rumbase/record/MvccReadRepeatableTest.java +++ b/src/test/java/net/kaaass/rumbase/record/MvccReadRepeatableTest.java @@ -10,6 +10,7 @@ import net.kaaass.rumbase.transaction.TransactionIsolation; import net.kaaass.rumbase.transaction.TransactionManager; import net.kaaass.rumbase.transaction.TransactionManagerImpl; +import net.kaaass.rumbase.transaction.exception.StatusException; import org.junit.AfterClass; import org.junit.Assert; import org.junit.BeforeClass; @@ -52,7 +53,7 @@ public void testReadSelf() throws RecordNotFoundException { } @Test - public void testReadOther() throws RecordNotFoundException { + public void testReadOther() throws RecordNotFoundException, StatusException { var storage = RecordManager.fromFile(PATH + "testReadOther"); var manager = new FakeTxManager(TransactionIsolation.REPEATABLE_READ); // 创建事务12 @@ -80,7 +81,7 @@ public void testReadOther() throws RecordNotFoundException { } @Test - public void testDelete() throws RecordNotFoundException { + public void testDelete() throws RecordNotFoundException, StatusException { var storage = RecordManager.fromFile(PATH + "testDelete"); var manager = new FakeTxManager(TransactionIsolation.REPEATABLE_READ); // 创建事务1、记录a1a2 @@ -106,7 +107,7 @@ public void testDelete() throws RecordNotFoundException { } @Test - public void testVersionSkip() throws RecordNotFoundException, NeedRollbackException { + public void testVersionSkip() throws RecordNotFoundException, NeedRollbackException, StatusException { var storage = RecordManager.fromFile(PATH + "testDelete"); var manager = new FakeTxManager(TransactionIsolation.REPEATABLE_READ); // 创建公共版本 @@ -128,7 +129,7 @@ public void testVersionSkip() throws RecordNotFoundException, NeedRollbackExcept } @Test - public void testReadSelfReal() throws RecordNotFoundException, IOException, FileException { + public void testReadSelfReal() throws RecordNotFoundException, IOException, FileException, StatusException { var storage = RecordManager.fromFile(PATH + "testReadSelfReal"); var manager = new TransactionManagerImpl(); // 创建事务1 @@ -152,7 +153,7 @@ public void testReadSelfReal() throws RecordNotFoundException, IOException, File } @Test - public void testReadOtherReal() throws RecordNotFoundException, IOException, FileException { + public void testReadOtherReal() throws RecordNotFoundException, IOException, FileException, StatusException { var storage = RecordManager.fromFile(PATH + "testReadOtherReal"); var manager = new TransactionManagerImpl(); // 创建事务12 @@ -185,7 +186,7 @@ public void testReadOtherReal() throws RecordNotFoundException, IOException, Fil } @Test - public void testDeleteReal() throws RecordNotFoundException, IOException, FileException { + public void testDeleteReal() throws RecordNotFoundException, IOException, FileException, StatusException { var storage = RecordManager.fromFile(PATH + "testDeleteReal"); var manager = new TransactionManagerImpl(); // 创建事务1、记录a1a2 @@ -216,7 +217,7 @@ public void testDeleteReal() throws RecordNotFoundException, IOException, FileEx } @Test - public void testVersionSkipReal() throws RecordNotFoundException, NeedRollbackException, IOException, FileException { + public void testVersionSkipReal() throws RecordNotFoundException, NeedRollbackException, IOException, FileException, StatusException { var storage = RecordManager.fromFile(PATH + "testDeleteReal"); var manager = new TransactionManagerImpl(); // 创建公共版本 diff --git a/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java b/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java index fa4fa23..6dc4f0e 100644 --- a/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java +++ b/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java @@ -4,6 +4,7 @@ import net.kaaass.rumbase.FileUtil; import net.kaaass.rumbase.page.exception.FileException; import net.kaaass.rumbase.transaction.exception.DeadlockException; +import net.kaaass.rumbase.transaction.exception.StatusException; import org.junit.AfterClass; import org.junit.Assert; import org.junit.BeforeClass; @@ -57,7 +58,7 @@ public void testCreateTransaction() throws IOException, FileException { * 测试事务变化 */ @Test - public void testChangeStatus() throws IOException, FileException { + public void testChangeStatus() throws IOException, FileException, StatusException { var manager = new TransactionManagerImpl("test_gen_files/test_change.log"); var committedTransaction = manager.createTransactionContext(TransactionIsolation.READ_COMMITTED); // 事务初始状态 @@ -82,7 +83,7 @@ public void testChangeStatus() throws IOException, FileException { * 测试事务持久化 */ @Test - public void testTransactionPersistence() throws IOException, FileException { + public void testTransactionPersistence() throws IOException, FileException, StatusException { var manager = new TransactionManagerImpl("test_gen_files/test_persistence.log"); // 事务创建,事务状态记录数改变 var transaction1 = manager.createTransactionContext(TransactionIsolation.READ_UNCOMMITTED); @@ -115,7 +116,7 @@ public void testTransactionPersistence() throws IOException, FileException { * 测试事务状态复原 */ @Test - public void testTransactionRecovery() throws IOException, FileException { + public void testTransactionRecovery() throws IOException, FileException, StatusException { var manager = new TransactionManagerImpl("test_gen_files/test_recovery.log"); // 事务创建,事务状态记录数改变 var transaction1 = manager.createTransactionContext(TransactionIsolation.READ_UNCOMMITTED); @@ -136,12 +137,14 @@ public void testTransactionRecovery() throws IOException, FileException { * 测试事务上锁 */ @Test - public void testAddLock() throws IOException, FileException { + public void testAddLock() throws IOException, FileException, StatusException { var manager = new TransactionManagerImpl("test_gen_files/test_add_lock.log"); var transaction1 = manager.createTransactionContext(TransactionIsolation.READ_UNCOMMITTED); var transaction2 = manager.createTransactionContext(TransactionIsolation.READ_UNCOMMITTED); String tableName = "test"; + transaction1.start(); + transaction2.start(); // 互斥锁 new Thread(() -> { @@ -151,7 +154,11 @@ public void testAddLock() throws IOException, FileException { e.printStackTrace(); } log.info("transaction2 commit"); - transaction2.commit(); + try { + transaction2.commit(); + } catch (StatusException e) { + e.printStackTrace(); + } }).start(); try { transaction1.exclusiveLock(1, tableName); @@ -171,7 +178,7 @@ public void testAddLock() throws IOException, FileException { transaction1.commit(); transaction2.rollback(); - } catch (DeadlockException e) { + } catch (DeadlockException | StatusException e) { e.printStackTrace(); } } @@ -180,12 +187,15 @@ public void testAddLock() throws IOException, FileException { * 测试死锁 */ @Test - public void testDeadlock() throws IOException, FileException, InterruptedException { + public void testDeadlock() throws IOException, FileException, InterruptedException, StatusException { var manager = new TransactionManagerImpl("test_gen_files/test_deadlock.log"); var transaction1 = manager.createTransactionContext(TransactionIsolation.READ_UNCOMMITTED); var transaction2 = manager.createTransactionContext(TransactionIsolation.READ_UNCOMMITTED); String tableName = "test"; + transaction1.start(); + transaction2.start(); + AtomicBoolean deadlockDetect = new AtomicBoolean(false); new Thread(() -> { try { @@ -197,7 +207,13 @@ public void testDeadlock() throws IOException, FileException, InterruptedExcepti transaction2.exclusiveLock(1, tableName); } catch (DeadlockException e) { deadlockDetect.set(true); - transaction2.rollback(); + try { + transaction2.rollback(); + } catch (StatusException statusException) { + statusException.printStackTrace(); + } + e.printStackTrace(); + } catch (StatusException e) { e.printStackTrace(); } }).start(); From e4d043bbce64e80556786299b5a93b15510966b6 Mon Sep 17 00:00:00 2001 From: KAAAsS Date: Sun, 17 Jan 2021 15:25:06 +0800 Subject: [PATCH 18/20] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E6=AD=BB=E9=94=81?= =?UTF-8?q?=E6=A3=80=E6=B5=8B=E6=B5=8B=E8=AF=95=E7=9A=84=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E4=B8=A2=E5=A4=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../transaction/TransactionContextTest.java | 172 ++++++++++++++---- 1 file changed, 140 insertions(+), 32 deletions(-) diff --git a/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java b/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java index 6dc4f0e..2dbfbe6 100644 --- a/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java +++ b/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java @@ -5,15 +5,14 @@ import net.kaaass.rumbase.page.exception.FileException; import net.kaaass.rumbase.transaction.exception.DeadlockException; import net.kaaass.rumbase.transaction.exception.StatusException; -import org.junit.AfterClass; -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.Test; +import org.junit.*; import java.io.File; import java.io.IOException; import java.util.concurrent.atomic.AtomicBoolean; +import static junit.framework.TestCase.assertTrue; + /** * 测试事务上下文 * @@ -189,44 +188,153 @@ public void testAddLock() throws IOException, FileException, StatusException { @Test public void testDeadlock() throws IOException, FileException, InterruptedException, StatusException { var manager = new TransactionManagerImpl("test_gen_files/test_deadlock.log"); - var transaction1 = manager.createTransactionContext(TransactionIsolation.READ_UNCOMMITTED); - var transaction2 = manager.createTransactionContext(TransactionIsolation.READ_UNCOMMITTED); - String tableName = "test"; + for (int i = 0; i < 50; i++) { + log.info("============= Test times {} =============", i); + var transaction1 = manager.createTransactionContext(TransactionIsolation.READ_UNCOMMITTED); + var transaction2 = manager.createTransactionContext(TransactionIsolation.READ_UNCOMMITTED); + String tableName = "test"; - transaction1.start(); - transaction2.start(); + transaction1.start(); + transaction2.start(); - AtomicBoolean deadlockDetect = new AtomicBoolean(false); - new Thread(() -> { + AtomicBoolean syncPoint = new AtomicBoolean(false); + AtomicBoolean deadlockDetect = new AtomicBoolean(false); + Thread thread = new Thread(() -> { + try { + while (!syncPoint.get()) { + Thread.sleep(3); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + try { + transaction2.exclusiveLock(1, tableName); + } catch (DeadlockException e) { + deadlockDetect.set(true); + e.printStackTrace(); + try { + transaction2.rollback(); + } catch (StatusException statusException) { + statusException.printStackTrace(); + } + } catch (StatusException e) { + e.printStackTrace(); + } + }); + thread.start(); try { - Thread.sleep(3); - } catch (InterruptedException e) { + transaction1.exclusiveLock(1, tableName); + transaction2.exclusiveLock(2, tableName); + syncPoint.set(true); + transaction1.exclusiveLock(2, tableName); + } catch (DeadlockException e) { + deadlockDetect.set(true); e.printStackTrace(); + log.info("rollback tx2"); + transaction2.rollback(); } + thread.join(); + assertTrue("Deadlock should be detected", deadlockDetect.get()); + transaction1.commit(); + log.info("tx1 committed"); + } + } + + @Ignore + public void testDeadlock3() throws IOException, FileException, InterruptedException, StatusException { + // FIXME 三线程死锁问题仍不能解决 + var manager = new TransactionManagerImpl("test_gen_files/test_deadlock3.log"); + for (int i = 0; i < 1; i++) { + log.info("============= Test times {} =============", i); + var tx1 = manager.createTransactionContext(TransactionIsolation.READ_UNCOMMITTED); + var tx2 = manager.createTransactionContext(TransactionIsolation.READ_UNCOMMITTED); + var tx3 = manager.createTransactionContext(TransactionIsolation.READ_UNCOMMITTED); + String tableName = "test"; + + tx1.start(); + tx2.start(); + tx3.start(); + + AtomicBoolean syncPoint = new AtomicBoolean(false); + AtomicBoolean deadlockDetect = new AtomicBoolean(false); + var thread2 = new Thread(() -> { + try { + while (!syncPoint.get()) { + Thread.sleep(3); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + try { + tx2.exclusiveLock(3, tableName); + } catch (DeadlockException e) { + deadlockDetect.set(true); + e.printStackTrace(); + try { + log.info("rollback tx2"); + tx2.rollback(); + } catch (StatusException statusException) { + statusException.printStackTrace(); + } + } catch (StatusException e) { + e.printStackTrace(); + } + log.info("thread2 alive"); + }); + var thread3 = new Thread(() -> { + try { + while (!syncPoint.get()) { + Thread.sleep(3); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + try { + tx3.exclusiveLock(1, tableName); + } catch (DeadlockException e) { + deadlockDetect.set(true); + e.printStackTrace(); + try { + log.info("rollback tx3"); + tx3.rollback(); + } catch (StatusException statusException) { + statusException.printStackTrace(); + } + } catch (StatusException e) { + e.printStackTrace(); + } + log.info("thread3 alive"); + }); + thread2.start(); + thread3.start(); try { - transaction2.exclusiveLock(1, tableName); + tx1.exclusiveLock(1, tableName); + tx2.exclusiveLock(2, tableName); + tx3.exclusiveLock(3, tableName); + syncPoint.set(true); + tx1.exclusiveLock(2, tableName); } catch (DeadlockException e) { deadlockDetect.set(true); - try { - transaction2.rollback(); - } catch (StatusException statusException) { - statusException.printStackTrace(); - } - e.printStackTrace(); - } catch (StatusException e) { e.printStackTrace(); + log.info("rollback tx1"); + tx1.rollback(); + } + log.info("thread1 alive"); + thread2.join(); + thread3.join(); + assertTrue("Deadlock should be detected", deadlockDetect.get()); + if (tx1.getStatus() == TransactionStatus.ACTIVE) { + tx1.commit(); + log.info("tx1 committed"); + } + if (tx2.getStatus() == TransactionStatus.ACTIVE) { + tx2.commit(); + log.info("tx2 committed"); + } + if (tx3.getStatus() == TransactionStatus.ACTIVE) { + tx3.commit(); + log.info("tx3 committed"); } - }).start(); - try { - transaction1.exclusiveLock(1, tableName); - transaction2.exclusiveLock(2, tableName); - transaction1.exclusiveLock(2, tableName); - } catch (DeadlockException e) { - deadlockDetect.set(true); - transaction2.rollback(); - e.printStackTrace(); } - Thread.sleep(10); - Assert.assertTrue("Deadlock should be detected", deadlockDetect.get()); } } From 1ffb8d0445324dd8ba0d0b4ce7a1501da6cc0540 Mon Sep 17 00:00:00 2001 From: KAAAsS Date: Sun, 17 Jan 2021 15:33:37 +0800 Subject: [PATCH 19/20] =?UTF-8?q?=E5=87=8F=E5=B0=91=E6=AD=BB=E9=94=81?= =?UTF-8?q?=E6=A3=80=E6=B5=8B=E7=9A=84=E6=B5=8B=E8=AF=95=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kaaass/rumbase/transaction/TransactionContextTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java b/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java index 2dbfbe6..189b90d 100644 --- a/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java +++ b/src/test/java/net/kaaass/rumbase/transaction/TransactionContextTest.java @@ -188,7 +188,7 @@ public void testAddLock() throws IOException, FileException, StatusException { @Test public void testDeadlock() throws IOException, FileException, InterruptedException, StatusException { var manager = new TransactionManagerImpl("test_gen_files/test_deadlock.log"); - for (int i = 0; i < 50; i++) { + for (int i = 0; i < 5; i++) { log.info("============= Test times {} =============", i); var transaction1 = manager.createTransactionContext(TransactionIsolation.READ_UNCOMMITTED); var transaction2 = manager.createTransactionContext(TransactionIsolation.READ_UNCOMMITTED); @@ -202,7 +202,7 @@ public void testDeadlock() throws IOException, FileException, InterruptedExcepti Thread thread = new Thread(() -> { try { while (!syncPoint.get()) { - Thread.sleep(3); + Thread.sleep(10); } } catch (InterruptedException e) { e.printStackTrace(); From b868d2baf00de3bc30845d1fbc3376fb96508b84 Mon Sep 17 00:00:00 2001 From: KAAAsS Date: Sun, 17 Jan 2021 15:37:17 +0800 Subject: [PATCH 20/20] =?UTF-8?q?=E5=BE=AE=E8=B0=83=E5=8A=A0=E9=94=81?= =?UTF-8?q?=E7=9A=84=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/net/kaaass/rumbase/transaction/lock/LockTableImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/kaaass/rumbase/transaction/lock/LockTableImpl.java b/src/main/java/net/kaaass/rumbase/transaction/lock/LockTableImpl.java index ee354ee..3acac7a 100644 --- a/src/main/java/net/kaaass/rumbase/transaction/lock/LockTableImpl.java +++ b/src/main/java/net/kaaass/rumbase/transaction/lock/LockTableImpl.java @@ -92,7 +92,7 @@ private void addLockTemplate(int xid, DataItemId id, LockMode mode) throws Deadl canUnlock = canGrant; // TODO 并发度差,最好改成读写锁 - synchronized (list) { + synchronized (LockTableImpl.class) { // 移除虚锁 list.pop(); // 正式加锁