mbdavid / LiteDB

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

Regression [BUG] LiteDB.LiteException: ReadFull must read PAGE_SIZE bytes [{0}] #2523

Open michelmoorlag opened 3 months ago

michelmoorlag commented 3 months ago

Version On 5.0.20 everything works fine but when I upgrade to 5.0.21 I can't save anything

Describe the bug When I try this code:

var collection = _liteDatabaseAsync.GetCollection<T>();
return collection.UpsertAsync(entity);

it will throw this exception: {LiteDB.Async.LiteAsyncException: LiteDB encounter an error. Details in the inner exception. ---> LiteDB.LiteException: ReadFull must read PAGE_SIZE bytes [{0}]

Code to Reproduce I have a repositry that implements this code that used to run fine:

public Task SaveAsync<T>(T entity) where T : DataObject
{
    try
    {
        var collection = _liteDatabaseAsync.GetCollection<T>();
        return collection.UpsertAsync(entity);
    }
    catch (Exception ex)
    {
        Debug.WriteLine(ex);
        throw;
    }
}

where _liteDatabaseAsync is of type: ILiteDatabaseAsync created like this _liteDatabaseAsync = new LiteDatabaseAsync(_connectionString);

Expected behavior I expect that the new versions are backwards comaptible and that above code inserts or updates the given entity without exception.

Screenshots/Stacktrace

{LiteDB.Async.LiteAsyncException: LiteDB encounter an error. Details in the inner exception. ---> LiteDB.LiteException: ReadFull must read PAGE_SIZE bytes [{0}]
  at LiteDB.Constants.ENSURE (System.Boolean conditional, System.String format, System.Object[] args) [0x00022] in <74012b958fcc45cfa90d6855b44c81c5>:0 
  at LiteDB.Engine.DiskService+<ReadFull>d__23.MoveNext () [0x000b5] in <74012b958fcc45cfa90d6855b44c81c5>:0 
  at LiteDB.Engine.WalIndexService+<>c__DisplayClass19_0+<<CheckpointInternal>g__source|0>d.MoveNext () [0x000d5] in <74012b958fcc45cfa90d6855b44c81c5>:0 
  at LiteDB.Engine.DiskService.WriteDataDisk (System.Collections.Generic.IEnumerable`1[T] pages) [0x0006e] in <74012b958fcc45cfa90d6855b44c81c5>:0 
  at LiteDB.Engine.WalIndexService.CheckpointInternal () [0x00020] in <74012b958fcc45cfa90d6855b44c81c5>:0 
  at LiteDB.Engine.WalIndexService.TryCheckpoint () [0x0002f] in <74012b958fcc45cfa90d6855b44c81c5>:0 
  at LiteDB.Engine.LiteEngine.CommitAndReleaseTransaction (LiteDB.Engine.TransactionService transaction) [0x0004a] in <74012b958fcc45cfa90d6855b44c81c5>:0 
  at LiteDB.Engine.LiteEngine.AutoTransaction[T] (System.Func`2[T,TResult] fn) [0x00050] in <74012b958fcc45cfa90d6855b44c81c5>:0 
  at LiteDB.Engine.LiteEngine.Upsert (System.String collection, System.Collections.Generic.IEnumerable`1[T] docs, LiteDB.BsonAutoId autoId) [0x0004d] in <74012b958fcc45cfa90d6855b44c81c5>:0 
  at LiteDB.LiteCollection`1[T].Upsert (System.Collections.Generic.IEnumerable`1[T] entities) [0x00021] in <74012b958fcc45cfa90d6855b44c81c5>:0 
  at LiteDB.LiteCollection`1[T].Upsert (T entity) [0x00013] in <74012b958fcc45cfa90d6855b44c81c5>:0 
  at LiteDB.Async.LiteCollectionAsync`1+<>c__DisplayClass55_0[T].<UpsertAsync>b__0 () [0x0000b] in <c9f9109ef3444de596197a6ad09e3854>:0 
  at LiteDB.Async.LiteDatabaseAsync+<>c__DisplayClass37_0`1[T].<EnqueueAsync>g__Function|0 () [0x00000] in <c9f9109ef3444de596197a6ad09e3854>:0 
   --- End of inner exception stack trace ---
  at PSExampleApp.Core.Services.MeasurementService.SaveMeasurements () [0x00045] in C:\Repos\mux16app\PSExampleApp.Core\Services\MeasurementService.cs:303 }
a38782615 commented 3 months ago

mark

NongBenz commented 3 months ago

Same 5.0.20 works but 5.0.21 gives this error.

fabiorme commented 3 months ago

I also have the same problem, I updated from version 5.0.17 to 5.0.21 and I encounter several problems with deleting and updating collections.

var col = db.GetCollection(); col.DeleteMany(x => x.Stato == StatoTavolo.Libero);

var document = BsonMapper.Global.ToDocument(item); var col = db.GetCollection(type.Name); col.Upsert(document);

Error: LiteDB.LiteException: 'ReadFull must read PAGE_SIZE bytes [{0}]'

Blue101black commented 1 month ago

I can consistently get the error by spamming the following, two FindById calls on two seperate LiteDb files and then an UpsertCall on one of the LiteDb files.

Doesn't happen in 5.0.20

5.0.21 seems to have a dependency on System.Buffer, maybe the issue?

JKamsker commented 1 month ago

@Blue101black Can you please provide a repro?

Blue101black commented 1 month ago

@JKamsker I cannot provide a repo sorry, but I can give some details.

Using Xamarin Forms (4.8.0.1687) as a hybrid app with a webview that runs Vue.js. Only build/run the app on Android (We should definitely upgrade to .Net Android)

I communicate back to C# land from Vue via Javascript interface on the WebView. I have a lock in place to make sure only one request can take place at a time.

Spamming a UI button that sends a request and does the below, interacting with two LiteDb instances. Here is an example of the code (renaming things randomly for privacy):

public Item AddItem(Guid parentId)
{
    var newItem = new Item()
    {
        Id = Guid.NewGuid(),
        ParentId = parentId,
        Quantity = 1,
        RecordedAt = _recordTimeService.GetCurrentDateTimeOffset()
    };

    _database.Upsert<Item>(newItem);

    return newItem;
}

GetOrDefault just a wrapper around FindById

public T GetOrDefault<T>(Guid id) where T : class, IEntity, new()
{
    return GetCollection<T>().FindById(id);
}
public DateTimeOffset GetCurrentDateTimeOffset()
{
    var timeItem = _database.GetOrDefault<TimeItem>(_state.CurrentRecordId);
    if (timeItem == null || !timeItem.ShiftId.HasValue || !timeItem.StartAt.HasValue)
    {
        return _timeService.DateTimeOffsetNow;
    }

    var shift = _database2.GetOrDefault<Shift>(timeItem.ShiftId.Value);
    if (shift == null || !shift.StartTime.HasValue || !shift.EndTime.HasValue)
    {
        return _timeService.DateTimeOffsetNow;
    }

    var timeDate = (DateTimeOffset)timeItem.StartAt;
    var currentDate = _timeService.DateTimeOffsetNow;
    var time = new TimeSpan(currentDate.Hour, currentDate.Minute, currentDate.Second);

    // Shift spans over midnight
    if (shift.StartTime > shift.EndTime)
    {
        // Time is outside shift bounds.
        if (time < shift.StartTime && time > shift.EndTime)
        {
            time = (TimeSpan)shift.StartTime;
            return new DateTimeOffset(timeDate.Year, timeDate.Month, timeDate.Day, time.Hours, time.Minutes, time.Seconds, timeDate.Offset);
        }
        // Time is between midnight and endTime
        else if (time < shift.EndTime && time > new TimeSpan(0, 0, 0))
        {
            var newDate = timeDate.AddDays(1);
            return new DateTimeOffset(newDate.Year, newDate.Month, newDate.Day, time.Hours, time.Minutes, time.Seconds, newDate.Offset);
        }
    }
    // Time is outside shift bounds.
    else if (time < shift.StartTime || time > shift.EndTime)
    {
        time = (TimeSpan)shift.StartTime;
        return new DateTimeOffset(timeDate.Year, timeDate.Month, timeDate.Day, time.Hours, time.Minutes, time.Seconds, timeDate.Offset);
    }

    // Time is within shift bounds.
    return new DateTimeOffset(timeDate.Year, timeDate.Month, timeDate.Day, time.Hours, time.Minutes, time.Seconds, timeDate.Offset);
}
nexnmoon commented 4 days ago

DiskService.cs file... public int WriteLogDisk(IEnumerable pages)

Shouldn't there be a stream.FlushToDisk(); line before the function returns?