212 lines
6.1 KiB
C#
212 lines
6.1 KiB
C#
using ZB.MOM.WW.CBDD.Core.Storage;
|
|
using ZB.MOM.WW.CBDD.Core.Transactions;
|
|
|
|
namespace ZB.MOM.WW.CBDD.Tests;
|
|
|
|
public class StorageEngineTransactionProtocolTests
|
|
{
|
|
[Fact]
|
|
public void PrepareTransaction_Should_ReturnFalse_For_Unknown_Transaction()
|
|
{
|
|
var dbPath = NewDbPath();
|
|
try
|
|
{
|
|
using var storage = new StorageEngine(dbPath, PageFileConfig.Default);
|
|
storage.PrepareTransaction(999_999).ShouldBeFalse();
|
|
}
|
|
finally
|
|
{
|
|
CleanupFiles(dbPath);
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void CommitTransaction_With_TransactionObject_Should_Throw_When_Not_Active()
|
|
{
|
|
var dbPath = NewDbPath();
|
|
try
|
|
{
|
|
using var storage = new StorageEngine(dbPath, PageFileConfig.Default);
|
|
var detached = new Transaction(123, storage);
|
|
|
|
Should.Throw<InvalidOperationException>(() => storage.CommitTransaction(detached));
|
|
}
|
|
finally
|
|
{
|
|
CleanupFiles(dbPath);
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void CommitTransaction_With_TransactionObject_Should_Commit_Writes()
|
|
{
|
|
var dbPath = NewDbPath();
|
|
try
|
|
{
|
|
using var storage = new StorageEngine(dbPath, PageFileConfig.Default);
|
|
using var txn = storage.BeginTransaction();
|
|
|
|
var pageId = storage.AllocatePage();
|
|
var data = new byte[storage.PageSize];
|
|
data[0] = 0xAB;
|
|
|
|
storage.WritePage(pageId, txn.TransactionId, data);
|
|
storage.CommitTransaction(txn);
|
|
|
|
storage.ActiveTransactionCount.ShouldBe(0);
|
|
|
|
var readBuffer = new byte[storage.PageSize];
|
|
storage.ReadPage(pageId, 0, readBuffer);
|
|
readBuffer[0].ShouldBe((byte)0xAB);
|
|
}
|
|
finally
|
|
{
|
|
CleanupFiles(dbPath);
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void CommitTransaction_ById_With_NoWrites_Should_Not_Throw()
|
|
{
|
|
var dbPath = NewDbPath();
|
|
try
|
|
{
|
|
using var storage = new StorageEngine(dbPath, PageFileConfig.Default);
|
|
storage.CommitTransaction(424242);
|
|
}
|
|
finally
|
|
{
|
|
CleanupFiles(dbPath);
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void MarkTransactionCommitted_Should_Move_Cache_And_Clear_ActiveCount()
|
|
{
|
|
var dbPath = NewDbPath();
|
|
try
|
|
{
|
|
using var storage = new StorageEngine(dbPath, PageFileConfig.Default);
|
|
using var txn = storage.BeginTransaction();
|
|
|
|
var pageId = storage.AllocatePage();
|
|
var data = new byte[storage.PageSize];
|
|
data[5] = 0x5A;
|
|
storage.WritePage(pageId, txn.TransactionId, data);
|
|
|
|
storage.ActiveTransactionCount.ShouldBe(1);
|
|
storage.MarkTransactionCommitted(txn.TransactionId);
|
|
storage.ActiveTransactionCount.ShouldBe(0);
|
|
|
|
var readBuffer = new byte[storage.PageSize];
|
|
storage.ReadPage(pageId, 0, readBuffer);
|
|
readBuffer[5].ShouldBe((byte)0x5A);
|
|
}
|
|
finally
|
|
{
|
|
CleanupFiles(dbPath);
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void RollbackTransaction_Should_Discard_Uncommitted_Write()
|
|
{
|
|
var dbPath = NewDbPath();
|
|
try
|
|
{
|
|
using var storage = new StorageEngine(dbPath, PageFileConfig.Default);
|
|
|
|
var pageId = storage.AllocatePage();
|
|
var baseline = new byte[storage.PageSize];
|
|
baseline[0] = 0x11;
|
|
storage.WritePageImmediate(pageId, baseline);
|
|
|
|
using var txn = storage.BeginTransaction();
|
|
var changed = new byte[storage.PageSize];
|
|
changed[0] = 0x99;
|
|
storage.WritePage(pageId, txn.TransactionId, changed);
|
|
|
|
storage.ActiveTransactionCount.ShouldBe(1);
|
|
storage.RollbackTransaction(txn.TransactionId);
|
|
storage.ActiveTransactionCount.ShouldBe(0);
|
|
|
|
var readBuffer = new byte[storage.PageSize];
|
|
storage.ReadPage(pageId, 0, readBuffer);
|
|
readBuffer[0].ShouldBe((byte)0x11);
|
|
}
|
|
finally
|
|
{
|
|
CleanupFiles(dbPath);
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void Transaction_MarkCommitted_Should_Transition_State()
|
|
{
|
|
var dbPath = NewDbPath();
|
|
try
|
|
{
|
|
using var storage = new StorageEngine(dbPath, PageFileConfig.Default);
|
|
using var txn = storage.BeginTransaction();
|
|
|
|
var pageId = storage.AllocatePage();
|
|
var data = new byte[storage.PageSize];
|
|
data[3] = 0x33;
|
|
storage.WritePage(pageId, txn.TransactionId, data);
|
|
|
|
txn.MarkCommitted();
|
|
|
|
txn.State.ShouldBe(TransactionState.Committed);
|
|
storage.ActiveTransactionCount.ShouldBe(0);
|
|
|
|
var readBuffer = new byte[storage.PageSize];
|
|
storage.ReadPage(pageId, 0, readBuffer);
|
|
readBuffer[3].ShouldBe((byte)0x33);
|
|
}
|
|
finally
|
|
{
|
|
CleanupFiles(dbPath);
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void Transaction_Prepare_Should_Write_Wal_And_Transition_State()
|
|
{
|
|
var dbPath = NewDbPath();
|
|
try
|
|
{
|
|
using var storage = new StorageEngine(dbPath, PageFileConfig.Default);
|
|
using var txn = storage.BeginTransaction();
|
|
|
|
var pageId = storage.AllocatePage();
|
|
var data = new byte[storage.PageSize];
|
|
data[11] = 0x7B;
|
|
storage.WritePage(pageId, txn.TransactionId, data);
|
|
|
|
txn.Prepare().ShouldBeTrue();
|
|
txn.State.ShouldBe(TransactionState.Preparing);
|
|
|
|
txn.Commit();
|
|
txn.State.ShouldBe(TransactionState.Committed);
|
|
}
|
|
finally
|
|
{
|
|
CleanupFiles(dbPath);
|
|
}
|
|
}
|
|
|
|
private static string NewDbPath()
|
|
=> Path.Combine(Path.GetTempPath(), $"storage_txn_{Guid.NewGuid():N}.db");
|
|
|
|
private static void CleanupFiles(string dbPath)
|
|
{
|
|
if (File.Exists(dbPath)) File.Delete(dbPath);
|
|
|
|
var walPath = Path.ChangeExtension(dbPath, ".wal");
|
|
if (File.Exists(walPath)) File.Delete(walPath);
|
|
|
|
var altWalPath = dbPath + "-wal";
|
|
if (File.Exists(altWalPath)) File.Delete(altWalPath);
|
|
}
|
|
}
|