mbdavid / LiteDB

LiteDB - A .NET NoSQL Document Store in a single data file
http://www.litedb.org
MIT License
8.52k stars 1.24k forks source link

[BUG] - Last changes not persisted when LiteDatabase instance stored in static field #2142

Closed brus77 closed 2 years ago

brus77 commented 2 years ago

LiteDB 5.0.11 - .NET Core 3.1

When the LiteDatabase instance is stored in a static variable, sometimes the last changes made to the database before the application was stopped are not preserved (Could it be because for static variables the Dispose method is not called?) If the .Checkpoint () method is called before the application ends, the problem does not occur.

To reproduce the issue launch the code snipped reported below multiple times (dotnet run) and it will show that some items are not deleted from the collection, when they should.

Program.cs of a .net Core console application:

using LiteDB;
using System;
using System.Linq;

namespace ConsoleApp1
{
    class Program
    {
        private static LiteDatabase db = new LiteDatabase($"Filename=mydb.db;");

        static void Main(string[] args)
        {
            var coll = db.GetCollection<TestEntity>();
            var all = coll.FindAll();
            if (all.Any())
            {
                Console.WriteLine("ERROR: there are items in the collection:");
                foreach (var item in all)
                {
                    Console.WriteLine(item.t1);
                }
                coll.DeleteAll();
            }

            TestEntity entity;
            for (int i = 1; i <= 1000; i++)
            {
                var key = i.ToString();
                entity = new TestEntity()
                {
                    t1 = key
                };
                coll.Insert(entity);
            }

            for (int i = 1000; i >= 1; i--)
            {
                var key = i.ToString();
                entity = coll.FindOne(x => x.t1 == key);
                coll.DeleteMany(x => x.t1 == key);
            }
            //db.Checkpoint();
        }
    }

    class TestEntity
    {
        public ObjectId Id { get; set; }
        public string t1 { get; set; }
    }
}
kcsombrio commented 2 years ago

Hi @brus77, this is happening exactly because LiteDB is being held by a static property of the class. LiteDB write operations to disk are designed to occur asynchronously for improved performance, so if you want to make sure your records are written to disk safely before the main thread finishes, you have to make sure the Dispose() method is called. The Dispose() method waits for the DB write thread to finish all its work before returning control to the main thread. Your solution calling Checkpoint() will also work.