Summary
Opening an existing .db file with a DbContext that declares a new collection (one not present when the file was created) makes the first insert into that collection throw:
System.InvalidOperationException: Not enough space: need 54, have 0
| PageId=67 | SlotCount=0 | Start=0 | End=0 | FSI=16306
This blocks a normal schema-evolution scenario: ship app v1 with collection A, then v2 adds collection B — existing user files crash on first write to B.
Minimal repro (BLite 5.1.1, net10.0)
using BLite.Bson;
using BLite.Core;
using BLite.Core.Collections;
using ReproApp;
const string Path = "repro.db";
// Pass 1: create db + collection A, insert, close.
if (!File.Exists(Path))
{
using (var db = new ADbContext(Path))
{
await db.A.InsertAsync(new A { Name = "one" });
await db.SaveChangesAsync();
}
Console.WriteLine("pass 1: created collection A");
}
// Pass 2: reopen same file, insert into NEW collection B -> throws.
using (var db = new BDbContext(Path))
{
await db.B.InsertAsync(new B { Name = "two" });
await db.SaveChangesAsync();
Console.WriteLine("pass 2: collection B OK");
}
namespace ReproApp
{
public class A { public ObjectId Id { get; set; } public string Name { get; set; } = ""; }
public class B { public ObjectId Id { get; set; } public string Name { get; set; } = ""; }
public partial class ADbContext : DocumentDbContext
{
public DocumentCollection<ObjectId, A> A { get; set; } = null!;
public ADbContext(string path) : base(path) => InitializeCollections();
}
public partial class BDbContext : DocumentDbContext
{
public DocumentCollection<ObjectId, B> B { get; set; } = null!;
public BDbContext(string path) : base(path) => InitializeCollections();
}
}
Output:
pass 1: created collection A
Unhandled exception. System.InvalidOperationException: Not enough space: need 54, have 0 | PageId=67 | SlotCount=0 | Start=0 | End=0 | FSI=16306
at BLite.Core.Collections.DocumentCollection`2.InsertIntoPage(...)
Hypothesis (unverified)
In FreeSpaceIndexProvider, a single FreeSpaceIndex is shared across all collections when UsesSeparateCollectionFiles == false. For the new collection the FSI believes page 67 has ~16 KB free (fresh-page size), but the page's on-disk header reads SlotCount=0, FreeSpaceStart=0, FreeSpaceEnd=0 with a non-Empty PageType, so the zeroed-page recovery branch in InsertIntoPage (PageType == Empty && FreeSpaceEnd == 0) doesn't trigger. Possibly an allocated-but-never-initialized (or recycled) page getting a full-size FSI entry.
Environment
- BLite 5.1.1 (
net10.0 target), .NET 10.0.4xx SDK, Linux x64
- Reproduces 100% with the steps above on a fresh file
Generated with Devin
Summary
Opening an existing
.dbfile with aDbContextthat declares a new collection (one not present when the file was created) makes the first insert into that collection throw:This blocks a normal schema-evolution scenario: ship app v1 with collection A, then v2 adds collection B — existing user files crash on first write to B.
Minimal repro (BLite 5.1.1, net10.0)
Output:
Hypothesis (unverified)
In
FreeSpaceIndexProvider, a singleFreeSpaceIndexis shared across all collections whenUsesSeparateCollectionFiles == false. For the new collection the FSI believes page 67 has ~16 KB free (fresh-page size), but the page's on-disk header readsSlotCount=0, FreeSpaceStart=0, FreeSpaceEnd=0with a non-EmptyPageType, so the zeroed-page recovery branch inInsertIntoPage(PageType == Empty && FreeSpaceEnd == 0) doesn't trigger. Possibly an allocated-but-never-initialized (or recycled) page getting a full-size FSI entry.Environment
net10.0target), .NET 10.0.4xx SDK, Linux x64Generated with Devin