Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] The read lock is being released without being held #2017

Open
laurentiucocanu opened this issue Jun 14, 2021 · 7 comments
Open

[BUG] The read lock is being released without being held #2017

laurentiucocanu opened this issue Jun 14, 2021 · 7 comments
Labels

Comments

@laurentiucocanu
Copy link

laurentiucocanu commented Jun 14, 2021

Version
5.0.10/Windows 10/NET 5

Describe the bug
Trying to read and update records in a parallel foreach throws an exception with the following message: "The read lock is being released without being held."
It seems to run fine in 5.0.3, but not in 5.0.10.

Code to Reproduce

    public class Target
    {
        [BsonId]
        public ObjectId Id { get; set; }

        public string Name { get; set; }

        public DateTime LastUpdateCheck { get; set; }
    }

    [Fact]
    public void InsertReadTest()
    {
        var dbFullPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
        try
        {
            using (var db = new LiteDatabase(dbFullPath))
            {
                var table = db.GetCollection<Target>("targets");
                for (int i = 0; i < 10000; i++)
                {
                    table.Insert(new Target
                    {
                        Name = $"Name_{i}",
                        LastUpdateCheck = DateTime.UtcNow
                    });
                }
                var targetIds = table.FindAll().Select(t => t.Id);
                Parallel.ForEach(targetIds, targetId =>
                {
                    var target = table.FindOne(ur => ur.Id == targetId);
                    target.LastUpdateCheck = DateTime.UtcNow;
                    table.Update(target);
                });
            }
        }
        finally
        {
            File.Delete(dbFullPath);
        }
    }

Expected behavior
No exception thrown.

Screenshots/Stacktrace
The read lock is being released without being held.

at System.Threading.Tasks.TaskReplicator.Run[TState](ReplicatableUserAction1 action, ParallelOptions options, Boolean stopOnFirstFailure) at System.Threading.Tasks.Parallel.PartitionerForEachWorker[TSource,TLocal](Partitioner1 source, ParallelOptions parallelOptions, Action1 simpleBody, Action2 bodyWithState, Action3 bodyWithStateAndIndex, Func4 bodyWithStateAndLocal, Func5 bodyWithEverything, Func1 localInit, Action1 localFinally) --- End of stack trace from previous location --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw(Exception source) at System.Threading.Tasks.Parallel.ThrowSingleCancellationExceptionOrOtherException(ICollection exceptions, CancellationToken cancelToken, Exception otherException) at System.Threading.Tasks.Parallel.PartitionerForEachWorker[TSource,TLocal](Partitioner1 source, ParallelOptions parallelOptions, Action1 simpleBody, Action2 bodyWithState, Action3 bodyWithStateAndIndex, Func4 bodyWithStateAndLocal, Func5 bodyWithEverything, Func1 localInit, Action1 localFinally) at System.Threading.Tasks.Parallel.ForEachWorker[TSource,TLocal](IEnumerable1 source, ParallelOptions parallelOptions, Action1 body, Action2 bodyWithState, Action3 bodyWithStateAndIndex, Func4 bodyWithStateAndLocal, Func5 bodyWithEverything, Func1 localInit, Action1 localFinally) at System.Threading.Tasks.Parallel.ForEach[TSource](IEnumerable1 source, Action`1 body)

Inner exception:
at System.Threading.ReaderWriterLockSlim.ExitReadLock()
at LiteDB.Engine.TransactionMonitor.ReleaseTransaction(TransactionService transaction)
at LiteDB.Engine.QueryExecutor.<>c__DisplayClass10_0.<g__RunQuery|0>d.MoveNext()
at LiteDB.BsonDataReader.Read()
at LiteDB.LiteQueryable1.<ToDocuments>d__26.MoveNext() at System.Linq.Enumerable.SelectEnumerableIterator2.MoveNext()
at System.Collections.Concurrent.Partitioner.DynamicPartitionerForIEnumerable1.InternalPartitionEnumerable.GrabChunk_Buffered(KeyValuePair2[] destArray, Int32 requestedChunkSize, Int32& actualNumElementsGrabbed)
at System.Collections.Concurrent.Partitioner.DynamicPartitionEnumerator_Abstract2.MoveNext() at System.Threading.Tasks.Parallel.<>c__DisplayClass44_02.b__1(IEnumerator& partitionState, Int32 timeout, Boolean& replicationDelegateYieldedBeforeCompletion)
--- End of stack trace from previous location ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw(Exception source)
at System.Threading.Tasks.Parallel.<>c__DisplayClass44_02.<PartitionerForEachWorker>b__1(IEnumerator& partitionState, Int32 timeout, Boolean& replicationDelegateYieldedBeforeCompletion) at System.Threading.Tasks.TaskReplicator.Replica1.ExecuteAction(Boolean& yieldedBeforeCompletion)
at System.Threading.Tasks.TaskReplicator.Replica.Execute()

@nabeelio
Copy link

Also getting this, but not getting a full stack trace in the error reporting I use of where it's originating from. .NET 4.8, latest LiteDB

System.Threading.ReaderWriterLockSlim.ExitReadLock():122
LiteDB.Engine.TransactionMonitor.ReleaseTransaction(TransactionService transaction):123
LiteDB.Engine.TransactionService.Dispose(Boolean dispose):439
LiteDB.Engine.TransactionService.Finalize():16

@Rickedb
Copy link

Rickedb commented Jan 24, 2022

Any updates? Facing the same bug when using Tasks

@gorillapower
Copy link

Im also getting these on .NET 6.0

@ysinsane
Copy link

Me too

@tdsalty
Copy link

tdsalty commented Jun 20, 2022

Also getting on .NET 6 - using singleton and no transactions whatsoever. Are there implicit transactions?

@ultravelocity
Copy link

Sadly still getting the same issue in .NET 6

@IbrahimElshafey
Copy link

I getting the same error today at 13-07-2023
My code:

 public void AddFailedRequest(FailedRequest failedRequest)
        {
            using (var db = new LiteDatabase(Constants.FailedRequestsDb))
            {
                var failedRequests = db.GetCollection<FailedRequest>(Constants.FailedRequestsCollection);
                failedRequest.Created = DateTime.Now;
                failedRequests.Insert(failedRequest);
                //db.Commit();
            }
        }
public void HandleFailedRequestes()
        {
            _ = CallFailedRequests();
            async Task CallFailedRequests()
            {
                try
                {
                    _logger.LogInformation("Start handling failed requests.");
                    using (var db = new LiteDatabase(Constants.FailedRequestsDb))
                    {
                        var failedRequests = db.GetCollection<FailedRequest>(Constants.FailedRequestsCollection);
                        foreach (var request in failedRequests.FindAll())
                        {
                            _logger.LogInformation($"Found `{failedRequests.Count()}` failed request.");
                            try
                            {
                                var client = _httpClientFactory.CreateClient();
                                var response = await client.PostAsync(
                                    request.ActionUrl,
                                    new ByteArrayContent(request.Body));
                                response.EnsureSuccessStatusCode();
                                var result = await response.Content.ReadAsStringAsync();
                                if (!(result == "1" || result == "-1"))
                                    throw new Exception("Expected result must be 1 or -1");
                                failedRequests.Delete(request.Id);
                            }
                            catch (Exception)
                            {
                                request.AttemptsCount++;
                                _logger.LogInformation(
                                    $"Request `{request.Id}` failed againg for `{request.AttemptsCount}` times");
                                request.LastAttemptDate = DateTime.Now;
                                failedRequests.Update(request);
                                throw;
                            }
                        }
                    }
                    _logger.LogInformation("End handling failed requests.");
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "Error when handling failed requests.");
                }
                finally
                {
                    await Task.Delay(_settings.CheckFailedRequestEvery);
                    await CallFailedRequests();
                }
            }
        }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

8 participants