Skip to content

Commit

Permalink
Fix issue with leaking transaction in TransactionMonitor.GetTransacti…
Browse files Browse the repository at this point in the history
…on when failed to get transaction lock. (litedb-org#1994)

Co-authored-by: alexander.bondarev <alex@strangeloopgames.com>
  • Loading branch information
mirasrael and slgmirasrael authored Dec 30, 2021
1 parent 048b22e commit 852adcf
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 1 deletion.
47 changes: 47 additions & 0 deletions LiteDB.Tests/Engine/Transactions_Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

namespace LiteDB.Tests.Engine
{
using System;

public class Transactions_Tests
{
[Fact]
Expand Down Expand Up @@ -222,5 +224,50 @@ public void Test_Transaction_States()
person.Count().Should().Be(20);
}
}

private class BlockingStream : MemoryStream
{
public readonly AutoResetEvent Blocked = new AutoResetEvent(false);
public readonly ManualResetEvent ShouldUnblock = new ManualResetEvent(false);
public bool ShouldBlock;

public override void Write(byte[] buffer, int offset, int count)
{
if (this.ShouldBlock)
{
this.Blocked.Set();
this.ShouldUnblock.WaitOne();
this.Blocked.Reset();
}
base.Write(buffer, offset, count);
}
}

[Fact]
public void Test_Transaction_ReleaseWhenFailToStart()
{
var blockingStream = new BlockingStream();
var db = new LiteDatabase(blockingStream) { Timeout = TimeSpan.FromSeconds(1) };
Thread lockerThread = null;
try
{
lockerThread = new Thread(() =>
{
db.GetCollection<Person>().Insert(new Person());
blockingStream.ShouldBlock = true;
db.Checkpoint();
db.Dispose();
});
lockerThread.Start();
blockingStream.Blocked.WaitOne(1000).Should().BeTrue();
Assert.Throws<LiteException>(() => db.GetCollection<Person>().Insert(new Person())).Message.Should().Contain("timeout");
Assert.Throws<LiteException>(() => db.GetCollection<Person>().Insert(new Person())).Message.Should().Contain("timeout");
}
finally
{
blockingStream.ShouldUnblock.Set();
lockerThread?.Join();
}
}
}
}
16 changes: 15 additions & 1 deletion LiteDB/Engine/Services/TransactionMonitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,21 @@ public TransactionService GetTransaction(bool create, bool queryOnly, out bool i
// enter in lock transaction after release _transaction lock
if (alreadyLock == false)
{
_locker.EnterTransaction();
try
{
_locker.EnterTransaction();
}
catch
{
transaction.Dispose();
lock (_transactions)
{
// return pages
_freePages += transaction.MaxTransactionSize;
_transactions.Remove(transaction.TransactionID);
}
throw;
}
}

// do not store in thread query-only transaction
Expand Down

0 comments on commit 852adcf

Please sign in to comment.