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] LiteDB ENSURE: slot position must be empty before use #2129

Closed justdmitry closed 2 years ago

justdmitry commented 2 years ago

Version LiteDB 5.0.11 in .NET 6.0.1 in Ubuntu 18.04

Describe the bug Insert (and InsertBulk) fail with "LiteDB ENSURE: slot position must be empty before use" when inserting new items into collection second time (after DeleteAll).

Code to Reproduce

db.GetCollection<SwapChance>(nameof(SwapChances)).DeleteAll();
...
var newData = new List<SwapChance>();
...
db.GetCollection<SwapChance>(nameof(SwapChances)).InsertBulk(newData);

Expected behavior Insert operations should succeed.

Screenshots/Stacktrace

System.Exception: LiteDB ENSURE: slot position must be empty before use
    at LiteDB.Constants.ENSURE(Boolean conditional, String message)
    at LiteDB.Engine.BasePage.InternalInsert(UInt16 bytesLength, Byte& index)
    at LiteDB.Engine.IndexService.AddNode(CollectionIndex index, BsonValue key, PageAddress dataBlock, Byte level, IndexNode last)
    at LiteDB.Engine.LiteEngine.InsertDocument(Snapshot snapshot, BsonDocument doc, BsonAutoId autoId, IndexService indexer, DataService data)
    at LiteDB.Engine.LiteEngine.<>c__DisplayClass7_0.<Insert>b__0(TransactionService transaction)
    at LiteDB.Engine.LiteEngine.AutoTransaction[T](Func`2 fn)
    at LiteDB.Engine.LiteEngine.Insert(String collection, IEnumerable`1 docs, BsonAutoId autoId)
    at LiteDB.LiteCollection`1.InsertBulk(IEnumerable`1 entities, Int32 batchSize)
    at Cardamon.Web.Services.SwapDetectorTask.RunAsync(ITask currentTask, IServiceProvider scopeServiceProvider, CancellationToken cancellationToken) in /root/cardamon/webapp/Services/SwapDetectorTask.cs:line 70 
    at RecurrentTasks.TaskRunner`1.MainLoop(TimeSpan firstRunDelay, CancellationToken cancellationToken)

Additional context This collection holds about 150 records. By timer (once per 15min), collection is cleared with DeleteAll and re-populated (usually with same data, changes are rare). First time (once after collection created after deletion) everything works Ok. On second round, exception is raised. Tried to replace InsertBulk with one-by-one Insert - several records (~30-40) are inserted, then exception. Tried to remove collection and rebuild database - no luck.

Sample of data being saved (via System.Text.Json):

{"Id":{"Timestamp":1640277567,"Machine":1250326,"Pid":4773,"Increment":7412641,"CreationTime":"2021-12-23T16:39:27Z"},
"Rarity":"Uncommon","Template1Id":12345,"Template2Id":123456,"Accounts1to2":["11111.wam","22222.wam","33333.wam","44444.wam","55555.wam"],"Accounts2to1":["66666.wam"]}
justdmitry commented 2 years ago

Tried to write to other collection in same database - same failure. Tried to create separate DB with only one collection - same failure.

justdmitry commented 2 years ago

I found that bug is related with index somehow.

Here is my class:

  public class SwapChance
  {
      public ObjectId Id { get; set; }

      public string Rarity { get; set; } = string.Empty;

      public int Template1Id { get; set; }

      public int Template2Id { get; set; }

      public List<string> Accounts1to2 { get; set; } = new List<string>();

      public List<string> Accounts2to1 { get; set; } = new List<string>();
  }

And I create this indexes to be able to search by individual account in lists:

collection.EnsureIndex(x => x.Accounts1to2);
collection.EnsureIndex(x => x.Accounts2to1);

If I remove indexes (recreate collection without them) then everything works Ok.

kcsombrio commented 2 years ago

Hi @justdmitry, thanks for reporting this and all your tests and context, it's important for us to understand the situation and simulate the problem. So, regarding this issue, I already find the problem, I'm currently working to find the best fix for that

kcsombrio commented 2 years ago

Hi @justdmitry, just committed a fix for this issue in the master branch