mbdavid / LiteDB

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

[BUG] DB get operation fails with LiteDB ENSURE: invalid segment position #2059

Open vkost opened 3 years ago

vkost commented 3 years ago

Version Which LiteDB version/OS/.NET framework version are you using. (REQUIRED) 5.0.10 / Windows 7 / .NET 4.6

Describe the bug A clear and concise description of what the bug is. I use LiteDB to store simple data (only one table per file) for a Win32 executable. It works great, but today one of my clients complained that his app won't work as expected. Looking at the logs, I saw the error LiteDB ENSURE: invalid segment position. At that moment, my app is attempting a read from the database, using a field with a key.

Code to Reproduce Write a small snippet to isolate your bug and could be possible to our team test. (REQUIRED) Snippet is very simple, but I'd have to send you the corrupted file too, and it's over 15MB

Expected behavior A clear and concise description of what you expected to happen.

Screenshots/Stacktrace If applicable, add screenshots/stacktrace

Additional context Add any other context about the problem here.

Thank you for a great lib!

lbnascimento commented 3 years ago

@vkost You can send the datafile to me at lbnascimento@inf.ufrgs.br

vkost commented 3 years ago

Hi @lbnascimento Thanks for the quick response. I've just sent the corrupted file to you via WeTransfer - hope that works for you, if not please let me know and I'll send it via email directly. To reproduce the problem, here's the snippet that crashes (you'll notice it's very basic)

try { using (var db = new LiteDatabase(Constants.SimpleStructureLiteDbCnn)) { var col = db.GetCollection("SimpleRecord");

                var qList = col.Query()
                    .Where(x => x.InvNum == invNum && x.Zastareo == false && x.CisError == false)
                    .ToList();

                SimpleRecord postojeciRacun = qList.OrderByDescending(t => t.SendDateTime).DefaultIfEmpty().First();
                if (postojeciRacun != null) result = true;
            }

        }
        catch (IOException iex)
        {
            throw iex;
        }
        catch (Exception ex)
        {
            result = true;
            LogLiteDbDataBroker.LogExceptionSave(ex);
        }

There's a field in the SimpleRecord table named InvNum - this is what I'm passing into the method as a parameter. It's a simple string, so you can use this to test:

xp148jm576/43500001/2021/dk499zp719 xp148jm576/43500011/2021/dk499zp719 xp148jm576/943500037/2021/dk499zp719

You should find the matching records. The procedure checks if the record with the specific InvNum value already exists, and reports that back to the calling method.

The reason I'm catching the IOException and re-throwing it is, on some systems, I had problems with the file locks due to backup procedures outside of my app.

If you need any more help I'm here

Thank you