Skip to content

Insert into a new collection on an existing DB file fails: 'Not enough space' on an unreadable/stale page #153

Description

@IAFahim

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    • Status
      Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions