open static method

Future<Database> open(
  1. String dirPath
)

Implementation

static Future<Database> open(String dirPath) async {
  final db = Database._(dirPath);
  await Directory(dirPath).create(recursive: true);
  db._catalogFile = File('$dirPath/catalog.json');

  // ── Step 1: Open storage layers ─────────────────────────────────────
  db.pager = await Pager.open('$dirPath/data.ndb');
  db.cache = PageCache(db.pager, capacity: 256);
  db.wal   = await Wal.open('$dirPath/wal.log');
  db.pager.setWalFlushedLsn(db.wal.flushedLsn);

  // ── Step 2: Init transaction manager V2 ─────────────────────────────
  //
  // FIX Flaw 10: real monotonic txnIds, never 0, restored across restarts.
  db.txnManager = TransactionManagerV2(
    lockManager: LockManagerV2(),
    dataDir:     dirPath,
  );

  // ── Step 3: Load catalog ─────────────────────────────────────────────
  await db._loadCatalog();

  // ── Step 4: WAL recovery (REDO committed records only) ──────────────
  //
  // FIX Flaw 3: _applyWalRecord now reads the explicit rowId from the WAL
  // payload, so UPDATE/DELETE replay is idempotent.
  final walMaxTxnId = await db._recover();

  // ── Step 5: Restore txn state from persistence ───────────────────────
  //
  // Must happen AFTER WAL recovery so walMaxTxnId is known and we never
  // re-allocate a txnId that appeared in the WAL.
  await db.txnManager.restoreFromPersistence(walMaxTxnId: walMaxTxnId);

  // ── Step 6: Recalculate row IDs from page data ───────────────────────
  for (final t in db.tables.values) {
    await t.recalcNextRowId();
  }

  // ── Step 7: Populate in-memory MVCC stores from page data ───────────
  await db._populateMvccStores();

  // ── Step 8: Load persisted statistics ───────────────────────────────
  //
  // FIX Flaw 8: stats survive restarts.
  final statsPath = '$dirPath/stats.json';
  final loadedRegistry = await StatsPersistence.load(statsPath);
  for (final ts in loadedRegistry.allStats.values) {
    db.statsRegistry.update(ts);
  }

  // ── Step 9: Wire up incremental checkpoint manager ───────────────────
  //
  // FIX Flaw 9 + Flaw 13:
  //  • Only dirty pages flushed  → O(D) not O(N)
  //  • Data pages flushed BEFORE indexes and catalog
  //  • WAL truncation AFTER checkpoint_end fsync
  db._checkpointMgr = CheckpointManager(
    cache:           db.cache,
    pager:           db.pager,
    wal:             db.wal,
    dataDir:         dirPath,
    getTables:       () => db.pageTables,
    persistTxnState: (lsn) => db.txnManager.persistStateAt(lsn),
    persistStats:    ()    => StatsPersistence.save(
                                db.statsRegistry, '$dirPath/stats.json'),
    persistCatalog:  ()    => db._saveCatalog(),
    persistIndexes:  (pts) => db._persistAllIndexes(),
  );

  print('[NebulaDB] Database opened at $dirPath  '
        '(tables=${db.tables.length})');
  return db;
}